From a4ecaf1ff513ada29d00f3234cf4a567393be36e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Mar 2024 11:22:08 -0800 Subject: [PATCH 001/414] increment version number Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba3ec7bce9e..cdccb8e4b9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.13.0.0 LANGUAGES CXX) +project(Z3 VERSION 4.13.1.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 2805cbaf13d..d76e03ba658 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 13, 0, 0) # express a default build version or pick up ci build version + set_version(4, 13, 0, 1) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 51533dd8e2b..00e27507e1c 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '13' - Patch: '0' + Patch: '1' ReleaseVersion: $(Major).$(Minor).$(Patch) AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.buildId) diff --git a/scripts/release.yml b/scripts/release.yml index 7fcd93b16da..8f1242f8294 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.13.0' + ReleaseVersion: '4.13.1' stages: From dcaacf5e9bc8876c88746fbd2bdd99be875a2703 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Mar 2024 15:21:26 -0800 Subject: [PATCH 002/414] add rewrite glue for instantiating equalities, #7154 Signed-off-by: Nikolaj Bjorner --- src/ast/macros/macro_manager.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index bbe7f245ce7..b7c94b1b5f7 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -319,14 +319,21 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { if (m.proofs_enabled()) { expr_ref instance = s(q->get_expr(), num, subst_args.data()); expr* eq, * lhs, * rhs; + + expr* q_inst = m.mk_or(m.mk_not(q), instance); + proof * qi_pr = m.mk_quant_inst(q_inst, num, subst_args.data()); if (m.is_not(instance, eq) && m.is_eq(eq, lhs, rhs)) { + expr_ref instance2(m); if (revert) - instance = m.mk_eq(m.mk_not(lhs), rhs); + instance2 = m.mk_eq(m.mk_not(lhs), rhs); else - instance = m.mk_eq(lhs, m.mk_not(rhs)); + instance2 = m.mk_eq(lhs, m.mk_not(rhs)); + expr* q_inst2 = m.mk_or(m.mk_not(q), instance2); + proof* eq_pr = m.mk_rewrite(q_inst, q_inst2); + qi_pr = m.mk_modus_ponens(qi_pr, eq_pr); + instance = instance2; } SASSERT(m.is_eq(instance)); - proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.data()); proof * q_pr = mm.m_decl2macro_pr.find(d); proof * prs[2] = { qi_pr, q_pr }; p = m.mk_unit_resolution(2, prs); From 361e04a18e3ca460fdfe8c1d0b34ac1a120106c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Mar 2024 10:27:12 -0800 Subject: [PATCH 003/414] port fixes to intblast Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 12 +++++++++++- src/math/lp/lar_solver.h | 1 + src/sat/smt/arith_axioms.cpp | 12 ++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index ef61c220953..9272e0298ae 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1531,7 +1531,7 @@ namespace lp { SASSERT(all_vars_are_registered(coeffs)); lar_term* t = new lar_term(coeffs); subst_known_terms(t); - SASSERT(t->is_empty() == false); + SASSERT (!t->is_empty()); m_terms.push_back(t); lpvar ret = A_r().column_count(); add_row_from_term_no_constraint(t, ext_i); @@ -2266,12 +2266,22 @@ namespace lp { return false; } + bool lar_solver::are_equal(lpvar j, lpvar k) { + vector> coeffs; + coeffs.push_back(std::make_pair(mpq(1), j)); + coeffs.push_back(std::make_pair(mpq(-1), k)); + lar_term t(coeffs); + subst_known_terms(&t); + return t.is_empty(); + } + std::pair lar_solver::add_equality(lpvar j, lpvar k) { vector> coeffs; coeffs.push_back(std::make_pair(mpq(1), j)); coeffs.push_back(std::make_pair(mpq(-1), k)); unsigned ej = add_term(coeffs, UINT_MAX); // UINT_MAX is the external null var + if (get_column_value(j) != get_column_value(k)) set_status(lp_status::UNKNOWN); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 4d71d0181da..ec40fcb2409 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -559,6 +559,7 @@ class lar_solver : public column_namer { return m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis(); } + bool are_equal(lpvar j, lpvar k); std::pair add_equality(lpvar j, lpvar k); u_dependency* get_bound_constraint_witnesses_for_column(unsigned j) { diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index b2dd5e96908..7270e708083 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -331,15 +331,17 @@ namespace arith { expr_ref x(a.mk_mod(_x, a.mk_int(N)), m); expr_ref y(a.mk_mod(_y, a.mk_int(N)), m); + // 0 <= n < 2^sz + + add_clause(mk_literal(a.mk_ge(n, a.mk_int(0)))); + add_clause(mk_literal(a.mk_le(n, a.mk_int(N - 1)))); + if (a.is_band(n)) { - - // 0 <= x&y < 2^sz + // x&y <= x // x&y <= y // TODO? x = y => x&y = x - add_clause(mk_literal(a.mk_ge(n, a.mk_int(0)))); - add_clause(mk_literal(a.mk_le(n, a.mk_int(N - 1)))); add_clause(mk_literal(a.mk_le(n, x))); add_clause(mk_literal(a.mk_le(n, y))); } @@ -537,6 +539,8 @@ namespace arith { euf::enode* n2 = var2enode(v2); lpvar w1 = register_theory_var_in_lar_solver(v1); lpvar w2 = register_theory_var_in_lar_solver(v2); + if (lp().are_equal(w1, w2)) + return; auto cs = lp().add_equality(w1, w2); add_eq_constraint(cs.first, n1, n2); add_eq_constraint(cs.second, n1, n2); From 7bbe3fb2b6e095bc15e5e23e4843049e7846b761 Mon Sep 17 00:00:00 2001 From: someplaceguy Date: Sat, 9 Mar 2024 23:13:42 +0000 Subject: [PATCH 004/414] fix (get-proof) command to respect option pp.simplify_implies (#7157) --- src/ast/ast_smt_pp.cpp | 10 +++++----- src/cmd_context/basic_cmds.cpp | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index bea669438c3..0da4f1c129c 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -986,7 +986,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { ast_mark sort_mark; for (sort* s : decls.get_sorts()) { if (!(*m_is_declared)(s)) { - smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, true, m_simplify_implies, 0); p.pp_sort_decl(sort_mark, s); } } @@ -994,7 +994,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { for (unsigned i = 0; i < decls.get_num_decls(); ++i) { func_decl* d = decls.get_func_decls()[i]; if (!(*m_is_declared)(d)) { - smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, true, m_simplify_implies, 0); p(d); strm << "\n"; } @@ -1003,20 +1003,20 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { #endif for (expr* a : m_assumptions) { - smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); + smt_printer p(strm, m, ql, rn, m_logic, false, m_simplify_implies, 1); strm << "(assert\n "; p(a); strm << ")\n"; } for (expr* a : m_assumptions_star) { - smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); + smt_printer p(strm, m, ql, rn, m_logic, false, m_simplify_implies, 1); strm << "(assert\n "; p(a); strm << ")\n"; } - smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, false, m_simplify_implies, 0); if (m.is_bool(n)) { if (!m.is_true(n)) { strm << "(assert\n "; diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index a2757d955ba..c93e4432f30 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -202,6 +202,7 @@ ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { cmd_is_declared isd(ctx); pp.set_is_declared(&isd); pp.set_logic(ctx.get_logic()); + pp.set_simplify_implies(params.simplify_implies()); pp.display_smt2(ctx.regular_stream(), pr); ctx.regular_stream() << std::endl; } From 0b3bbc297248849cb17c29e0fbaa23f61bb45716 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Mar 2024 18:19:36 -0700 Subject: [PATCH 005/414] #7158 Signed-off-by: Nikolaj Bjorner --- src/api/api_util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/api_util.h b/src/api/api_util.h index 0ff2c8dddef..174d7514499 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -110,6 +110,7 @@ inline param_descrs * to_param_descrs_ptr(Z3_param_descrs p) { return p == nullp Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ + CHECK_IS_EXPR(n, nullptr); \ expr * _n = to_expr(n); \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 1, &_n); \ mk_c(c)->save_ast_trail(a); \ @@ -127,6 +128,8 @@ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n) { \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ + CHECK_IS_EXPR(n1, nullptr); \ + CHECK_IS_EXPR(n2, nullptr); \ expr * args[2] = { to_expr(n1), to_expr(n2) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 2, args); \ mk_c(c)->save_ast_trail(a); \ From 5704e8d15460236d09678b2f514023446445d510 Mon Sep 17 00:00:00 2001 From: Jakob Rath Date: Thu, 14 Mar 2024 16:48:38 +0100 Subject: [PATCH 006/414] fix intblast is_bounded (#7163) --- src/sat/smt/intblast_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 2c373f6b935..f4491896bcf 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -467,7 +467,7 @@ namespace intblast { bool solver::is_bounded(expr* x, rational const& N) { return any_of(m_vars, [&](expr* v) { - return is_translated(v) && translated(v) == x && bv.get_bv_size(v) <= N; + return is_translated(v) && translated(v) == x && bv_size(v) <= N; }); } @@ -536,7 +536,7 @@ namespace intblast { * Perform simplifications that are claimed sound when the bit-vector interpretations of * mod/div always guard the mod and dividend to be non-zero. * Potentially shady area is for arithmetic expressions created by int2bv. - * They will be guarded by a modulus which dose not disappear. + * They will be guarded by a modulus which does not disappear. */ expr* solver::amod(expr* bv_expr, expr* x, rational const& N) { rational v; From 6450a7a0b847e1d6338c836e8b70dae2b916b30f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:54:01 -0700 Subject: [PATCH 007/414] Bump docker/build-push-action from 5.1.0 to 5.2.0 (#7159) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.1.0...v5.2.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 5bb7d2cad03..3122f6e70c6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v5.2.0 with: context: . push: true From b8a69987c31a74ac0a611b99ca97bb3edd28dc86 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 17 Mar 2024 16:33:32 -0700 Subject: [PATCH 008/414] fix #7165 --- src/api/python/z3/z3.py | 4 ++-- src/tactic/goal.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 16db39afd60..9a3dadda23e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -5445,10 +5445,10 @@ def EnumSort(name, values, ctx=None): num = len(values) _val_names = (Symbol * num)() for i in range(num): - _val_names[i] = to_symbol(values[i]) + _val_names[i] = to_symbol(values[i], ctx) _values = (FuncDecl * num)() _testers = (FuncDecl * num)() - name = to_symbol(name) + name = to_symbol(name, ctx) S = DatatypeSortRef(Z3_mk_enumeration_sort(ctx.ref(), name, num, _val_names, _values, _testers), ctx) V = [] for i in range(num): diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 23e3ff969eb..43cecf92d74 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -696,7 +696,7 @@ bool goal::is_cnf() const { if (!is_literal(lit)) return false; } - if (!is_literal(f)) + else if (!is_literal(f)) return false; } return true; From 18365907a27e03a99092238646b1d7a65fc20ca3 Mon Sep 17 00:00:00 2001 From: cctv130 <133784576+cctv130@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:29:27 +0800 Subject: [PATCH 009/414] Update util.h (#7169) --- src/util/util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/util.h b/src/util/util.h index a4bf78073ab..f05e4f9f4ec 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -142,6 +142,7 @@ static inline unsigned get_num_1bits(uint64_t v) { v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0F; uint64_t r = (v * 0x0101010101010101) >> 56; SASSERT(c == r); + return r; #endif } From 1a7437144c63a33cee9043c5bb370318836e329d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:25:58 -0700 Subject: [PATCH 010/414] Bump docker/build-push-action from 5.2.0 to 5.3.0 (#7170) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.2.0...v5.3.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 3122f6e70c6..d93e7eeefea 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v5.2.0 + uses: docker/build-push-action@v5.3.0 with: context: . push: true From a9054bc73b7632047f93190b7a6609c82fae42de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=91=E9=98=B3?= <91933474+XiangYyang@users.noreply.github.com> Date: Wed, 20 Mar 2024 17:31:23 +0800 Subject: [PATCH 011/414] fix warning C4244 in util.h (#7171) Add a static cast to avoid warning C4244 on MSVC --- src/util/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/util.h b/src/util/util.h index f05e4f9f4ec..cf0146b12c1 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -142,7 +142,7 @@ static inline unsigned get_num_1bits(uint64_t v) { v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0F; uint64_t r = (v * 0x0101010101010101) >> 56; SASSERT(c == r); - return r; + return static_cast(r); #endif } From 730f9ad9b7186137f9f6280c686399bb572ae5c5 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 19 Mar 2024 09:42:36 -1000 Subject: [PATCH 012/414] Nikolaj's fix in add_zero_assumption Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_explain.cpp | 75 ++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 87fda76ef2c..4a60b895f3b 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -277,11 +277,13 @@ namespace nlsat { } }; - void add_zero_assumption(polynomial_ref & p) { + void add_zero_assumption(polynomial_ref& p) { // If p is of the form p1^n1 * ... * pk^nk, // then only the factors that are zero in the current interpretation needed to be considered. // I don't want to create a nested conjunction in the clause. // Then, I assert p_i1 * ... * p_im != 0 + bool is_linear = true; + unsigned x = max_var(p); { restore_factors _restore(m_factors, m_factors_save); factor(p, m_factors); @@ -294,14 +296,37 @@ namespace nlsat { if (is_zero(sign(f))) { m_zero_fs.push_back(m_factors.get(i)); m_is_even.push_back(false); - } + is_linear &= m_pm.degree(f, x) <= 1; + } + } + } + + if (!is_linear) { + scoped_anum_vector& roots = m_roots_tmp; + roots.reset(); + m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); + unsigned num_roots = roots.size(); + if (num_roots > 0) { + anum const& x_val = m_assignment.value(x); + for (unsigned i = 0; i < num_roots; i++) { + int s = m_am.compare(x_val, roots[i]); + if (s != 0) + continue; + add_root_literal(atom::ROOT_EQ, x, i + 1, p); + return; + } + display(verbose_stream() << "polynomial ", p); + m_solver.display(verbose_stream()); + UNREACHABLE(); } } SASSERT(!m_zero_fs.empty()); // one of the factors must be zero in the current interpretation, since p is zero in it. + literal l = m_solver.mk_ineq_literal(atom::EQ, m_zero_fs.size(), m_zero_fs.data(), m_is_even.data()); l.neg(); TRACE("nlsat_explain", tout << "adding (zero assumption) literal:\n"; display(tout, l); tout << "\n";); add_literal(l); + } void add_simple_assumption(atom::kind k, poly * p, bool sign = false) { @@ -649,6 +674,52 @@ namespace nlsat { } } + void add_zero_assumption_on_factor(polynomial_ref& f) { + display(std::cout << "zero factors \n", f); + } + // this function also explains the value 0, if met + bool coeffs_are_zeroes(polynomial_ref &s) { + restore_factors _restore(m_factors, m_factors_save); + factor(s, m_factors); + unsigned num_factors = m_factors.size(); + m_zero_fs.reset(); + m_is_even.reset(); + polynomial_ref f(m_pm); + bool have_zero = false; + for (unsigned i = 0; i < num_factors; i++) { + f = m_factors.get(i); + // std::cout << "f=";display(std::cout, f) << "\n"; + if (coeffs_are_zeroes_in_factor(f)) { + have_zero = true; + break; + } + } + if (!have_zero) + return false; + var x = max_var(f); + unsigned n = degree(f, x); + auto c = polynomial_ref(this->m_pm); + for (unsigned j = 0; j <= n; j++) { + c = m_pm.coeff(s, x, j); + SASSERT(sign(c) == 0); + ensure_sign(c); + } + return true; + } + + + bool coeffs_are_zeroes_in_factor(polynomial_ref & s) { + var x = max_var(s); + unsigned n = degree(s, x); + auto c = polynomial_ref(this->m_pm); + for (unsigned j = 0; j <= n; j++) { + c = m_pm.coeff(s, x, j); + if (sign(c) != 0) + return false; + } + return true; + } + /** \brief Add v-psc(p, q, x) into m_todo */ From 70d2263a85f9fb0bd289125a5b9b404017bb40fb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Mar 2024 12:29:55 -0700 Subject: [PATCH 013/414] cast, updated nlexplain --- src/nlsat/nlsat_explain.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 4a60b895f3b..4bbc5cb03b2 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -277,6 +277,7 @@ namespace nlsat { } }; + void add_zero_assumption(polynomial_ref& p) { // If p is of the form p1^n1 * ... * pk^nk, // then only the factors that are zero in the current interpretation needed to be considered. @@ -300,7 +301,7 @@ namespace nlsat { } } } - + if (!is_linear) { scoped_anum_vector& roots = m_roots_tmp; roots.reset(); @@ -312,6 +313,7 @@ namespace nlsat { int s = m_am.compare(x_val, roots[i]); if (s != 0) continue; + TRACE("nlsat_explain", tout << "adding (zero assumption) root " << "\n"); add_root_literal(atom::ROOT_EQ, x, i + 1, p); return; } @@ -321,12 +323,11 @@ namespace nlsat { } } SASSERT(!m_zero_fs.empty()); // one of the factors must be zero in the current interpretation, since p is zero in it. - + literal l = m_solver.mk_ineq_literal(atom::EQ, m_zero_fs.size(), m_zero_fs.data(), m_is_even.data()); l.neg(); TRACE("nlsat_explain", tout << "adding (zero assumption) literal:\n"; display(tout, l); tout << "\n";); add_literal(l); - } void add_simple_assumption(atom::kind k, poly * p, bool sign = false) { From f840d5d9650d20e0f6cd650bef92c21e6d15da15 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Mar 2024 21:27:00 -0700 Subject: [PATCH 014/414] na Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_explain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 4bbc5cb03b2..27d0531afda 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -1094,12 +1094,12 @@ namespace nlsat { if (x < max_x) add_cell_lits(ps, x); while (true) { + TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; + display(tout, ps); tout << "\n";); if (all_univ(ps, x) && m_todo.empty()) { m_todo.reset(); break; } - TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; - display(tout, ps); tout << "\n";); add_lc(ps, x); psc_discriminant(ps, x); psc_resultant(ps, x); From 27a9b8bd03dc7d6daa43d510e7ca14d97ceb7f58 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Mar 2024 21:32:18 -0700 Subject: [PATCH 015/414] fix project minor version Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index d76e03ba658..ce2e7cb7e54 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 13, 0, 1) # express a default build version or pick up ci build version + set_version(4, 13, 1, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): From 530c6fc625d85ed8b22d27e0b066896f1b669f28 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Mar 2024 22:05:30 -0700 Subject: [PATCH 016/414] fix ##7175 - don't add user macros/functions when smtlib2_compliant=true Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 019b7fd4f8f..e883d0e9d5b 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1894,6 +1894,8 @@ void cmd_context::add_declared_functions(model& mdl) { model_params p; if (!p.user_functions()) return; + if (m_params.m_smtlib2_compliant) + return; for (auto const& kv : m_func_decls) { func_decl* f = kv.m_value.first(); if (f->get_family_id() == null_family_id && !mdl.has_interpretation(f)) { @@ -2066,7 +2068,10 @@ void cmd_context::complete_model(model_ref& md) const { if (m_macros.find(k, decls)) body = decls.find(f->get_arity(), f->get_domain()); + if (body && m_params.m_smtlib2_compliant) + continue; sort * range = f->get_range(); + if (!body) body = m().get_some_value(range); if (f->get_arity() > 0) { From 6455de9dd32b0bb75e86383cda0d8f172cb6ef7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Mar 2024 09:39:13 -0700 Subject: [PATCH 017/414] fix #7179 Ensure that flat associative rewriting is disabled if rewriter.flat is set to false. --- src/ast/rewriter/th_rewriter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index ee8b79be53e..844c51940e9 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -17,6 +17,7 @@ Module Name: --*/ #include "params/rewriter_params.hpp" +#include "params/poly_rewriter_params.hpp" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" @@ -83,7 +84,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { void updt_local_params(params_ref const & _p) { rewriter_params p(_p); - m_flat = true; + poly_rewriter_params pp(_p); + m_flat = pp.flat(); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); m_pull_cheap_ite = p.pull_cheap_ite(); From 648e05754c37220f1fbb5943b4e69545d82b7bae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Mar 2024 09:40:50 -0700 Subject: [PATCH 018/414] #7178 copy build tool chains used for manylinux arm64 into ubuntu builds alternatively, just remove ubuntuArm64 and use the many-linux builds for Arm64? --- scripts/nightly.yaml | 17 +++++++++++++---- scripts/release.yml | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 00e27507e1c..c448b5dd781 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -114,10 +114,19 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - script: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz?rev=33c6e30e5ac64e6dba8f0431f2c35f1b&hash=9918A05BF47621B632C7A5C8D2BB438FB80A4480' + - script: mkdir -p /tmp/arm-toolchain/ + - script: tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1 + - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/bin' + - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin' + - script: echo $PATH + - script: stat /tmp/arm-toolchain/bin/aarch64-none-linux-gnu-gcc + - script: python scripts/mk_unix_dist.py --nodotnet --nojava --arch=arm64 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: artifactName: 'UbuntuArm64' diff --git a/scripts/release.yml b/scripts/release.yml index 8f1242f8294..3125595d781 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -119,10 +119,19 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - script: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz?rev=33c6e30e5ac64e6dba8f0431f2c35f1b&hash=9918A05BF47621B632C7A5C8D2BB438FB80A4480' + - script: mkdir -p /tmp/arm-toolchain/ + - script: tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1 + - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/bin' + - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin' + - script: echo $PATH + - script: stat /tmp/arm-toolchain/bin/aarch64-none-linux-gnu-gcc + - script: python scripts/mk_unix_dist.py --nodotnet --nojava --arch=arm64 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: artifactName: 'UbuntuArm64' From 80642e5a7caf858541a163569c3013afbe39ed14 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 23 Mar 2024 18:38:14 +0000 Subject: [PATCH 019/414] Add check for libatomic requirement to Python build system (#7184) * Add check for libatomic requirement to Python build system * More thorough check * Fix typos --- scripts/mk_util.py | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 014b0e40f83..3e6da0b22b8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -314,6 +314,26 @@ def test_fpmath(cc): FPMATH_FLAGS="" return "UNKNOWN" +def test_atomic_required(cc): + t = TempFile('tstatomic.cpp') + t.add(""" + #include + std::atomic x; + std::atomic y; + std::atomic z; + std::atomic w; + int main() { + ++z; + ++y; + ++w; + return ++x; + } + """) + t.commit() + fails_without = exec_compiler_cmd([cc, CPPFLAGS, '', 'tstatomic.cpp', LDFLAGS, '']) != 0 + ok_with = exec_compiler_cmd([cc, CPPFLAGS, '', 'tstatomic.cpp', LDFLAGS + ' -latomic', '']) == 0 + return fails_without and ok_with + def find_jni_h(path): for root, dirs, files in os.walk(path): @@ -555,19 +575,19 @@ def set_version(major, minor, build, revision): print("Set Assembly Version (BUILD):", VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) return - # use parameters to set up version if not provided by script args + # use parameters to set up version if not provided by script args VER_MAJOR = major VER_MINOR = minor VER_BUILD = build VER_TWEAK = revision - # update VER_TWEAK base on github + # update VER_TWEAK base on github if GIT_DESCRIBE: branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) VER_TWEAK = int(check_output(['git', 'rev-list', '--count', 'HEAD'])) - + print("Set Assembly Version (DEFAULT):", VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) - + def get_version(): return (VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) @@ -1858,7 +1878,7 @@ def mk_makefile(self, out): os.path.join('api', 'java', 'Native')) elif IS_OSX and IS_ARCH_ARM64: out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) -arch arm64 %s$(OBJ_EXT) libz3$(SO_EXT)\n' % - os.path.join('api', 'java', 'Native')) + os.path.join('api', 'java', 'Native')) else: out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' % os.path.join('api', 'java', 'Native')) @@ -2600,6 +2620,9 @@ def mk_config(): CXXFLAGS = '%s -fvisibility=hidden -fvisibility-inlines-hidden -c' % CXXFLAGS FPMATH = test_fpmath(CXX) CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) + atomic_required = test_atomic_required(CXX) + if atomic_required: + LDFLAGS = '%s -latomic' % LDFLAGS if LOG_SYNC: CXXFLAGS = '%s -DZ3_LOG_SYNC' % CXXFLAGS if SINGLE_THREADED: @@ -2710,6 +2733,7 @@ def mk_config(): print('Prefix: %s' % PREFIX) print('64-bit: %s' % is64()) print('FP math: %s' % FPMATH) + print('libatomic: %s' % ('required' if atomic_required else 'not required')) print("Python pkg dir: %s" % PYTHON_PACKAGE_DIR) if GPROF: print('gprof: enabled') @@ -2854,7 +2878,7 @@ def update_version(): revision = VER_TWEAK print("UpdateVersion:", get_full_version_string(major, minor, build, revision)) - + if major is None or minor is None or build is None or revision is None: raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()") if not ONLY_MAKEFILES: @@ -3068,7 +3092,7 @@ def mk_bindings(api_files): z3py_output_dir=get_z3py_dir(), dotnet_output_dir=dotnet_output_dir, java_input_dir=java_input_dir, - java_output_dir=java_output_dir, + java_output_dir=java_output_dir, java_package_name=java_package_name, ml_output_dir=ml_output_dir, ml_src_dir=ml_output_dir From c18a42cf5b0db2f45475ff84266237e10a361a72 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Mar 2024 16:14:24 -0400 Subject: [PATCH 020/414] change signed projection to include root object. --- src/nlsat/nlsat_explain.cpp | 11 ++++++++++- src/qe/nlqsat.cpp | 6 ++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 27d0531afda..618394ac1d6 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -1744,7 +1744,16 @@ namespace nlsat { solve_eq(x, eq_index, ps); } else { - project_pairs(x, eq_index, ps); + add_zero_assumption(p); + + for (unsigned j = 0; j < ps.size(); ++j) { + if (j == eq_index) + continue; + p = ps.get(j); + int s = sign(p); + atom::kind k = (s == 0)?(atom::EQ):((s < 0)?(atom::LT):(atom::GT)); + add_simple_assumption(k, p, false); + } } return; } diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index d81048c3d39..8a6f910bc26 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -480,8 +480,10 @@ namespace qe { num_scopes = 2*(level()/2); } else { - SASSERT(clevel.max() + 2 <= level()); - num_scopes = level() - clevel.max(); + if (clevel.max() + 2 <= level()) + num_scopes = level() - clevel.max(); + else + num_scopes = 2; // the projection contains auxiliary variables from root objects. SASSERT(num_scopes >= 2); } From 111fcb9366fbcf6a8b946a09481e32c27afab98d Mon Sep 17 00:00:00 2001 From: Steven Moy Date: Wed, 27 Mar 2024 19:06:58 -0700 Subject: [PATCH 021/414] Implement API to set exit action to exception (#7192) * Implement API to set exit action to exception * Turn on exit_action_to_throw_exception upon API context creation --- src/api/api_context.cpp | 2 ++ src/util/debug.cpp | 31 +++++++++++++++++++++++++++++++ src/util/debug.h | 19 ++++++++++++++----- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 344224dd386..2f1f5071221 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include +#include "util/debug.h" #include "util/z3_version.h" #include "api/api_context.h" #include "ast/ast_pp.h" @@ -393,6 +394,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_context_rc(c); memory::initialize(UINT_MAX); + set_default_exit_action(exit_action::throw_exception); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), true)); RETURN_Z3(r); Z3_CATCH_RETURN_NO_HANDLE(nullptr); diff --git a/src/util/debug.cpp b/src/util/debug.cpp index c9ca9fc312a..ee57ac4b330 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -75,6 +75,37 @@ bool is_debug_enabled(const char * tag) { return g_enabled_debug_tags->contains(tag); } +atomic g_default_exit_action(exit_action::exit); + +exit_action get_default_exit_action() { + return g_default_exit_action; +} + +void set_default_exit_action(exit_action a) { + g_default_exit_action = a; +} + +void invoke_exit_action(unsigned int code) { + exit_action a = get_default_exit_action(); + switch (a) { + case exit_action::exit: + exit(code); + case exit_action::throw_exception: + switch (code) { + case ERR_INTERNAL_FATAL: + throw default_exception("internal fatal"); + case ERR_UNREACHABLE: + throw default_exception("unreachable"); + case ERR_NOT_IMPLEMENTED_YET: + throw default_exception("not implemented yet"); + default: + throw default_exception("unknown"); + } + default: + exit(code); + } +} + atomic g_default_debug_action(debug_action::ask); debug_action get_default_debug_action() { diff --git a/src/util/debug.h b/src/util/debug.h index 5f092b181ed..5e9ea9b2cb3 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -35,6 +35,14 @@ enum class debug_action { debug_action get_default_debug_action(); void set_default_debug_action(debug_action a); +enum class exit_action { + exit, + throw_exception, +}; +exit_action get_default_exit_action(); +void set_default_exit_action(exit_action a); +void invoke_exit_action(unsigned int code); + #include "util/error_codes.h" #include "util/warning.h" @@ -56,7 +64,7 @@ void set_default_debug_action(debug_action a); #endif #ifdef NO_Z3_DEBUGGER -#define INVOKE_DEBUGGER() exit(ERR_INTERNAL_FATAL) +#define INVOKE_DEBUGGER() invoke_exit_action(ERR_INTERNAL_FATAL) #else #ifdef _WINDOWS #define INVOKE_DEBUGGER() __debugbreak() @@ -71,6 +79,7 @@ void enable_debug(const char * tag); void disable_debug(const char * tag); bool is_debug_enabled(const char * tag); + #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) @@ -85,25 +94,25 @@ bool is_debug_enabled(const char * tag); #ifdef Z3DEBUG # define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNEXPECTED CODE WAS REACHED."); INVOKE_DEBUGGER();) #else -# define UNREACHABLE() { notify_assertion_violation(__FILE__, __LINE__, "UNEXPECTED CODE WAS REACHED."); exit(ERR_UNREACHABLE); } ((void) 0) +# define UNREACHABLE() { notify_assertion_violation(__FILE__, __LINE__, "UNEXPECTED CODE WAS REACHED."); invoke_exit_action(ERR_UNREACHABLE); } ((void) 0) #endif #ifdef Z3DEBUG # define NOT_IMPLEMENTED_YET() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "NOT IMPLEMENTED YET!"); INVOKE_DEBUGGER();) #else -# define NOT_IMPLEMENTED_YET() { notify_assertion_violation(__FILE__, __LINE__, "NOT IMPLEMENTED YET!"); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) +# define NOT_IMPLEMENTED_YET() { notify_assertion_violation(__FILE__, __LINE__, "NOT IMPLEMENTED YET!"); invoke_exit_action(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #endif #define VERIFY(_x_) if (!(_x_)) { \ notify_assertion_violation(__FILE__, __LINE__, "Failed to verify: " #_x_ "\n"); \ - exit(ERR_UNREACHABLE); \ + invoke_exit_action(ERR_UNREACHABLE); \ } #define VERIFY_EQ(LHS, RHS) \ if (!((LHS) == (RHS))) { \ notify_assertion_violation(__FILE__, __LINE__, "Failed to verify: " #LHS " == " #RHS "\n"); \ std::cerr << "LHS value: " << (LHS) << "\nRHS value: " << (RHS) << "\n"; \ - exit(ERR_UNREACHABLE); \ + invoke_exit_action(ERR_UNREACHABLE); \ } #define ENSURE(_x_) VERIFY(_x_) From 51f1e2655c5c3acdf226169348eee1e0c2b4c309 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Mar 2024 12:59:02 -0700 Subject: [PATCH 022/414] updates to sls Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_eqs.cpp | 1 + src/ast/sls/bv_sls.cpp | 138 +++++++++++++++++++++++------- src/ast/sls/bv_sls.h | 19 ++-- src/ast/sls/bv_sls_eval.cpp | 8 +- src/ast/sls/bv_sls_fixed.cpp | 8 +- src/ast/sls/bv_sls_terms.cpp | 65 ++++++++------ src/ast/sls/bv_sls_terms.h | 10 ++- src/ast/sls/sls_engine.cpp | 8 +- src/ast/sls/sls_engine.h | 6 +- src/ast/sls/sls_valuation.cpp | 39 ++++++--- src/params/sls_params.pyg | 1 + src/sat/smt/sls_solver.cpp | 3 +- src/tactic/sls/sls_tactic.cpp | 33 +++---- 13 files changed, 234 insertions(+), 105 deletions(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index c97cab7c9ed..d3e4116b758 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -329,6 +329,7 @@ namespace euf { m_config.m_context_solve = p.get_bool("context_solve", tp.solve_eqs_context_solve()); for (auto* ex : m_extract_plugins) ex->updt_params(p); + m_rewriter.updt_params(p); } void solve_eqs::collect_param_descrs(param_descrs& r) { diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index f80a362ba32..6a176016f97 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -25,12 +25,15 @@ Module Name: namespace bv { - sls::sls(ast_manager& m): + sls::sls(ast_manager& m, params_ref const& p): m(m), bv(m), m_terms(m), - m_eval(m) - {} + m_eval(m), + m_engine(m, p) + { + updt_params(p); + } void sls::init() { m_terms.init(); @@ -67,20 +70,48 @@ namespace bv { } } + void sls::init_repair_candidates() { + m_to_repair.reset(); + ptr_vector todo; + expr_fast_mark1 mark; + for (auto index : m_repair_roots) + todo.push_back(m_terms.term(index)); + for (unsigned i = 0; i < todo.size(); ++i) { + expr* e = todo[i]; + if (mark.is_marked(e)) + continue; + mark.mark(e); + if (!is_app(e)) + continue; + for (expr* arg : *to_app(e)) + todo.push_back(arg); + + if (is_uninterp_const(e)) + m_to_repair.insert(e->get_id()); + + } + } + + void sls::reinit_eval() { + init_repair_candidates(); + + if (m_to_repair.empty()) + return; + std::function eval = [&](expr* e, unsigned i) { - auto should_keep = [&]() { - return m_rand() % 100 <= 92; - }; - if (m.is_bool(e)) { - if (m_eval.is_fixed0(e) || should_keep()) - return m_eval.bval0(e); - } + unsigned id = e->get_id(); + bool keep = (m_rand() % 100 <= 50) || !m_to_repair.contains(id); + if (m.is_bool(e) && (m_eval.is_fixed0(e) || keep)) + return m_eval.bval0(e); else if (bv.is_bv(e)) { auto& w = m_eval.wval(e); - if (w.fixed.get(i) || should_keep()) - return w.get_bit(i); - } + if (w.fixed.get(i) || keep) + return w.get_bit(i); + //auto const& z = m_engine.get_value(e); + //return rational(z).get_bit(i); + } + return m_rand() % 2 == 0; }; m_eval.init_eval(m_terms.assertions(), eval); @@ -119,7 +150,7 @@ namespace bv { return { false, nullptr }; } - lbool sls::search() { + lbool sls::search1() { // init and init_eval were invoked unsigned n = 0; for (; n++ < m_config.m_max_repairs && m.inc(); ) { @@ -127,7 +158,6 @@ namespace bv { if (!e) return l_true; - trace_repair(down, e); ++m_stats.m_moves; @@ -140,16 +170,32 @@ namespace bv { return l_undef; } + lbool sls::search2() { + lbool res = l_undef; + if (m_stats.m_restarts == 0) + res = m_engine(); + else if (m_stats.m_restarts % 1000 == 0) + res = m_engine.search_loop(); + if (res != l_undef) + m_engine_model = true; + return res; + } + lbool sls::operator()() { lbool res = l_undef; m_stats.reset(); m_stats.m_restarts = 0; + m_engine_model = false; + do { - res = search(); + res = search1(); if (res != l_undef) break; trace(); + res = search2(); + if (res != l_undef) + break; reinit_eval(); } while (m.inc() && m_stats.m_restarts++ < m_config.m_max_restarts); @@ -158,34 +204,60 @@ namespace bv { } void sls::try_repair_down(app* e) { - unsigned n = e->get_num_args(); if (n == 0) { - if (m.is_bool(e)) - m_eval.set(e, m_eval.bval1(e)); - else + if (m.is_bool(e)) { + m_eval.set(e, m_eval.bval1(e)); + } + else { VERIFY(m_eval.wval(e).commit_eval()); + } for (auto p : m_terms.parents(e)) m_repair_up.insert(p->get_id()); + return; } - unsigned s = m_rand(n); - for (unsigned i = 0; i < n; ++i) { - auto j = (i + s) % n; - if (m_eval.try_repair(e, j)) { - set_repair_down(e->get_arg(j)); + if (n == 2) { + auto d1 = get_depth(e->get_arg(0)); + auto d2 = get_depth(e->get_arg(1)); + unsigned s = m_rand(d1 + d2 + 2); + if (s <= d1 && m_eval.try_repair(e, 0)) { + set_repair_down(e->get_arg(0)); + return; + } + if (m_eval.try_repair(e, 1)) { + set_repair_down(e->get_arg(1)); + return; + } + if (m_eval.try_repair(e, 0)) { + set_repair_down(e->get_arg(0)); return; } } - // search a new root / random walk to repair + else { + unsigned s = m_rand(n); + for (unsigned i = 0; i < n; ++i) { + auto j = (i + s) % n; + if (m_eval.try_repair(e, j)) { + set_repair_down(e->get_arg(j)); + return; + } + } + } + // repair was not successful, so reset the state to find a different way to repair + init_repair(); } void sls::try_repair_up(app* e) { - if (m_terms.is_assertion(e) || !m_eval.repair_up(e)) - m_repair_roots.insert(e->get_id()); + if (m_terms.is_assertion(e)) + m_repair_roots.insert(e->get_id()); + else if (!m_eval.repair_up(e)) { + //m_repair_roots.insert(e->get_id()); + init_repair(); + } else { if (!eval_is_correct(e)) { verbose_stream() << "incorrect eval #" << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; @@ -224,7 +296,10 @@ namespace bv { } model_ref sls::get_model() { - model_ref mdl = alloc(model, m); + if (m_engine_model) + return m_engine.get_model(); + + model_ref mdl = alloc(model, m); auto& terms = m_eval.sort_assertions(m_terms.assertions()); for (expr* e : terms) { if (!re_eval_is_correct(to_app(e))) { @@ -273,7 +348,12 @@ namespace bv { void sls::updt_params(params_ref const& _p) { sls_params p(_p); m_config.m_max_restarts = p.max_restarts(); + m_config.m_max_repairs = p.max_repairs(); m_rand.set_seed(p.random_seed()); + m_terms.updt_params(_p); + params_ref q = _p; + q.set_uint("max_restarts", 10); + m_engine.updt_params(q); } void sls::trace_repair(bool down, expr* e) { diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index bbcd59aea95..ae7d7cb57ef 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -26,6 +26,7 @@ Module Name: #include "ast/sls/sls_valuation.h" #include "ast/sls/bv_sls_terms.h" #include "ast/sls/bv_sls_eval.h" +#include "ast/sls/sls_engine.h" #include "ast/bv_decl_plugin.h" #include "model/model.h" @@ -49,6 +50,8 @@ namespace bv { ptr_vector m_todo; random_gen m_rand; config m_config; + sls_engine m_engine; + bool m_engine_model = false; std::pair next_to_repair(); @@ -59,19 +62,23 @@ namespace bv { void try_repair_up(app* e); void set_repair_down(expr* e) { m_repair_down = e->get_id(); } - lbool search(); + lbool search1(); + lbool search2(); void reinit_eval(); void init_repair(); void trace(); void trace_repair(bool down, expr* e); + indexed_uint_set m_to_repair; + void init_repair_candidates(); + public: - sls(ast_manager& m); + sls(ast_manager& m, params_ref const& p); /** * Add constraints */ - void assert_expr(expr* e) { m_terms.assert_expr(e); } + void assert_expr(expr* e) { m_terms.assert_expr(e); m_engine.assert_expr(e); } /* * Invoke init after all expressions are asserted. @@ -91,10 +98,10 @@ namespace bv { lbool operator()(); void updt_params(params_ref const& p); - void collect_statistics(statistics & st) const { m_stats.collect_statistics(st); } - void reset_statistics() { m_stats.reset(); } + void collect_statistics(statistics& st) const { m_stats.collect_statistics(st); m_engine.collect_statistics(st); } + void reset_statistics() { m_stats.reset(); m_engine.reset_statistics(); } - sls_stats const& get_stats() const { return m_stats; } + unsigned get_num_moves() { return m_stats.m_moves + m_engine.get_stats().m_moves; } std::ostream& display(std::ostream& out); diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index 4b7bf954653..579bc736987 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -24,8 +24,8 @@ namespace bv { {} void sls_eval::init_eval(expr_ref_vector const& es, std::function const& eval) { - sort_assertions(es); - for (expr* e : m_todo) { + auto& terms = sort_assertions(es); + for (expr* e : terms) { if (!is_app(e)) continue; app* a = to_app(e); @@ -49,7 +49,7 @@ namespace bv { TRACE("sls", tout << "Unhandled expression " << mk_pp(e, m) << "\n"); } } - m_todo.reset(); + terms.reset(); } /** @@ -1698,7 +1698,7 @@ namespace bv { } if (bv.is_bv(e)) { auto& v = eval(to_app(e)); - // verbose_stream() << "committing: " << v << "\n"; + for (unsigned i = 0; i < v.nw; ++i) if (0 != (v.fixed[i] & (v.bits()[i] ^ v.eval[i]))) { v.bits().copy_to(v.nw, v.eval); diff --git a/src/ast/sls/bv_sls_fixed.cpp b/src/ast/sls/bv_sls_fixed.cpp index 91ce8e0e2a5..e2b29b1fe05 100644 --- a/src/ast/sls/bv_sls_fixed.cpp +++ b/src/ast/sls/bv_sls_fixed.cpp @@ -108,6 +108,13 @@ namespace bv { else if (bv.is_numeral(t, a)) init_range(s, -a, nullptr, rational(0), false); } + else if (sign && m.is_eq(e, s, t)) { + if (bv.is_numeral(s, a)) + // 1 <= t - a + init_range(nullptr, rational(1), t, -a, false); + else if (bv.is_numeral(t, a)) + init_range(nullptr, rational(1), s, -a, false); + } else if (bv.is_bit2bool(e, s, idx)) { auto& val = wval(s); val.try_set_bit(idx, !sign); @@ -157,7 +164,6 @@ namespace bv { else v.add_range(-b, -a); } - } void sls_fixed::get_offset(expr* e, expr*& x, rational& offset) { diff --git a/src/ast/sls/bv_sls_terms.cpp b/src/ast/sls/bv_sls_terms.cpp index 8702c3c480c..4624ab85c10 100644 --- a/src/ast/sls/bv_sls_terms.cpp +++ b/src/ast/sls/bv_sls_terms.cpp @@ -20,12 +20,14 @@ Module Name: #include "ast/ast_ll_pp.h" #include "ast/sls/bv_sls.h" +#include "ast/rewriter/th_rewriter.h" namespace bv { sls_terms::sls_terms(ast_manager& m): m(m), bv(m), + m_rewriter(m), m_assertions(m), m_pinned(m), m_translated(m), @@ -40,18 +42,20 @@ namespace bv { expr* top = e; m_pinned.push_back(e); m_todo.push_back(e); - expr_fast_mark1 mark; - for (unsigned i = 0; i < m_todo.size(); ++i) { - expr* e = m_todo[i]; - if (!is_app(e)) - continue; - if (m_translated.get(e->get_id(), nullptr)) - continue; - if (mark.is_marked(e)) - continue; - mark.mark(e); - for (auto arg : *to_app(e)) - m_todo.push_back(arg); + { + expr_fast_mark1 mark; + for (unsigned i = 0; i < m_todo.size(); ++i) { + expr* e = m_todo[i]; + if (!is_app(e)) + continue; + if (m_translated.get(e->get_id(), nullptr)) + continue; + if (mark.is_marked(e)) + continue; + mark.mark(e); + for (auto arg : *to_app(e)) + m_todo.push_back(arg); + } } std::stable_sort(m_todo.begin(), m_todo.end(), [&](expr* a, expr* b) { return get_depth(a) < get_depth(b); }); for (expr* e : m_todo) @@ -127,7 +131,7 @@ namespace bv { m_translated.setx(e->get_id(), r); } - expr* sls_terms::mk_sdiv(expr* x, expr* y) { + expr_ref sls_terms::mk_sdiv(expr* x, expr* y) { // d = udiv(abs(x), abs(y)) // y = 0, x >= 0 -> -1 // y = 0, x < 0 -> 1 @@ -141,17 +145,18 @@ namespace bv { expr_ref z(bv.mk_zero(sz), m); expr* signx = bv.mk_ule(bv.mk_numeral(N / 2, sz), x); expr* signy = bv.mk_ule(bv.mk_numeral(N / 2, sz), y); - expr* absx = m.mk_ite(signx, bv.mk_bv_sub(bv.mk_numeral(N - 1, sz), x), x); - expr* absy = m.mk_ite(signy, bv.mk_bv_sub(bv.mk_numeral(N - 1, sz), y), y); + expr* absx = m.mk_ite(signx, bv.mk_bv_neg(x), x); + expr* absy = m.mk_ite(signy, bv.mk_bv_neg(y), y); expr* d = bv.mk_bv_udiv(absx, absy); - expr* r = m.mk_ite(m.mk_eq(signx, signy), d, bv.mk_bv_neg(d)); + expr_ref r(m.mk_ite(m.mk_eq(signx, signy), d, bv.mk_bv_neg(d)), m); r = m.mk_ite(m.mk_eq(z, y), - m.mk_ite(signx, bv.mk_one(sz), bv.mk_numeral(N - 1, sz)), - m.mk_ite(m.mk_eq(x, z), z, r)); + m.mk_ite(signx, bv.mk_one(sz), bv.mk_numeral(N - 1, sz)), + m.mk_ite(m.mk_eq(x, z), z, r)); + m_rewriter(r); return r; } - expr* sls_terms::mk_smod(expr* x, expr* y) { + expr_ref sls_terms::mk_smod(expr* x, expr* y) { // u := umod(abs(x), abs(y)) // u = 0 -> 0 // y = 0 -> x @@ -164,21 +169,24 @@ namespace bv { expr_ref abs_x(m.mk_ite(bv.mk_sle(z, x), x, bv.mk_bv_neg(x)), m); expr_ref abs_y(m.mk_ite(bv.mk_sle(z, y), y, bv.mk_bv_neg(y)), m); expr_ref u(bv.mk_bv_urem(abs_x, abs_y), m); - return - m.mk_ite(m.mk_eq(u, z), z, + expr_ref r(m); + r = m.mk_ite(m.mk_eq(u, z), z, m.mk_ite(m.mk_eq(y, z), x, m.mk_ite(m.mk_and(bv.mk_sle(z, x), bv.mk_sle(z, x)), u, m.mk_ite(bv.mk_sle(z, x), bv.mk_bv_add(y, u), m.mk_ite(bv.mk_sle(z, y), bv.mk_bv_sub(y, u), bv.mk_bv_neg(u)))))); - + m_rewriter(r); + return r; } - expr* sls_terms::mk_srem(expr* x, expr* y) { + expr_ref sls_terms::mk_srem(expr* x, expr* y) { // y = 0 -> x // else x - sdiv(x, y) * y - return - m.mk_ite(m.mk_eq(y, bv.mk_zero(bv.get_bv_size(x))), + expr_ref r(m); + r = m.mk_ite(m.mk_eq(y, bv.mk_zero(bv.get_bv_size(x))), x, bv.mk_bv_sub(x, bv.mk_bv_mul(y, mk_sdiv(x, y)))); + m_rewriter(r); + return r; } @@ -209,4 +217,11 @@ namespace bv { m_assertion_set.insert(a->get_id()); } + void sls_terms::updt_params(params_ref const& p) { + params_ref q = p; + q.set_bool("flat", false); + m_rewriter.updt_params(q); + } + + } diff --git a/src/ast/sls/bv_sls_terms.h b/src/ast/sls/bv_sls_terms.h index 3baffc78e8a..a6294aa9c1e 100644 --- a/src/ast/sls/bv_sls_terms.h +++ b/src/ast/sls/bv_sls_terms.h @@ -21,6 +21,7 @@ Module Name: #include "util/scoped_ptr_vector.h" #include "util/uint_set.h" #include "ast/ast.h" +#include "ast/rewriter/th_rewriter.h" #include "ast/sls/sls_stats.h" #include "ast/sls/sls_powers.h" #include "ast/sls/sls_valuation.h" @@ -31,6 +32,7 @@ namespace bv { class sls_terms { ast_manager& m; bv_util bv; + th_rewriter m_rewriter; ptr_vector m_todo, m_args; expr_ref_vector m_assertions, m_pinned, m_translated; app_ref_vector m_terms; @@ -40,12 +42,14 @@ namespace bv { expr* ensure_binary(expr* e); void ensure_binary_core(expr* e); - expr* mk_sdiv(expr* x, expr* y); - expr* mk_smod(expr* x, expr* y); - expr* mk_srem(expr* x, expr* y); + expr_ref mk_sdiv(expr* x, expr* y); + expr_ref mk_smod(expr* x, expr* y); + expr_ref mk_srem(expr* x, expr* y); public: sls_terms(ast_manager& m); + + void updt_params(params_ref const& p); /** * Add constraints diff --git a/src/ast/sls/sls_engine.cpp b/src/ast/sls/sls_engine.cpp index 249c771ed32..a2ab861c0ed 100644 --- a/src/ast/sls/sls_engine.cpp +++ b/src/ast/sls/sls_engine.cpp @@ -421,6 +421,7 @@ lbool sls_engine::search() { // get candidate variables ptr_vector & to_evaluate = m_tracker.get_unsat_constants(m_assertions); + if (to_evaluate.empty()) { res = l_true; @@ -514,6 +515,12 @@ lbool sls_engine::operator()() { if (m_restart_init) m_tracker.randomize(m_assertions); + return search_loop(); +} + + +lbool sls_engine::search_loop() { + lbool res = l_undef; do { @@ -533,7 +540,6 @@ lbool sls_engine::operator()() { } while (res != l_true && m_stats.m_restarts++ < m_max_restarts); verbose_stream() << "(restarts: " << m_stats.m_restarts << " flips: " << m_stats.m_moves << " fps: " << (m_stats.m_moves / m_stats.m_stopwatch.get_current_seconds()) << ")" << std::endl; - return res; } diff --git a/src/ast/sls/sls_engine.h b/src/ast/sls/sls_engine.h index 614534f1a86..3e67aa49ca0 100644 --- a/src/ast/sls/sls_engine.h +++ b/src/ast/sls/sls_engine.h @@ -79,7 +79,11 @@ class sls_engine { void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); - lbool search(); + + + lbool search(); + + lbool search_loop(); lbool operator()(); diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 3160e5cf5d0..3f619df0567 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -106,10 +106,12 @@ namespace bv { bool sls_valuation::commit_eval() { for (unsigned i = 0; i < nw; ++i) - if (0 != (fixed[i] & (m_bits[i] ^ eval[i]))) - return false; - if (!in_range(eval)) + if (0 != (fixed[i] & (m_bits[i] ^ eval[i]))) + return false; + + if (!in_range(eval)) return false; + for (unsigned i = 0; i < nw; ++i) m_bits[i] = eval[i]; SASSERT(well_formed()); @@ -491,8 +493,8 @@ namespace bv { SASSERT(well_formed()); } - void sls_valuation::add_range(rational l, rational h) { - + void sls_valuation::add_range(rational l, rational h) { + l = mod(l, rational::power_of_two(bw)); h = mod(h, rational::power_of_two(bw)); if (h == l) @@ -509,21 +511,28 @@ namespace bv { auto old_lo = lo(); auto old_hi = hi(); if (old_lo < old_hi) { - if (old_lo < l && l < old_hi) + if (old_lo < l && l < old_hi && old_hi <= h) set_value(m_lo, l), old_lo = l; - if (old_hi < h && h < old_hi) + if (l <= old_lo && old_lo < h && h < old_hi) set_value(m_hi, h); } else { SASSERT(old_hi < old_lo); - if (old_lo < l || l < old_hi) - set_value(m_lo, l), - old_lo = l; - if (old_lo < h && h < old_hi) + if (h <= old_hi && old_lo <= l) { + set_value(m_lo, l); + set_value(m_hi, h); + } + else if (old_lo <= l && l <= h) { + set_value(m_lo, l); set_value(m_hi, h); - else if (old_hi < old_lo && (h < old_hi || old_lo < h)) + } + else if (old_lo + 1 == l) { + set_value(m_lo, l); + } + else if (old_hi == h + 1) { set_value(m_hi, h); + } } } @@ -552,8 +561,7 @@ namespace bv { // lo < hi, set most significant bits based on hi // void sls_valuation::tighten_range() { - - // verbose_stream() << "tighten " << *this << "\n"; + if (m_lo == m_hi) return; @@ -613,6 +621,9 @@ namespace bv { break; } + if (has_range() && !in_range(m_bits)) + m_bits = m_lo; + SASSERT(well_formed()); } diff --git a/src/params/sls_params.pyg b/src/params/sls_params.pyg index 05405ef2424..18b8d337146 100644 --- a/src/params/sls_params.pyg +++ b/src/params/sls_params.pyg @@ -3,6 +3,7 @@ def_module_params('sls', description='Experimental Stochastic Local Search Solver (for QFBV only).', params=(max_memory_param(), ('max_restarts', UINT, UINT_MAX, 'maximum number of restarts'), + ('max_repairs', UINT, 1000, 'maximum number of repairs before restart'), ('walksat', BOOL, 1, 'use walksat assertion selection (instead of gsat)'), ('walksat_ucb', BOOL, 1, 'use bandit heuristic for walksat assertion selection (instead of random)'), ('walksat_ucb_constant', DOUBLE, 20.0, 'the ucb constant c in the term score + c * f(touched)'), diff --git a/src/sat/smt/sls_solver.cpp b/src/sat/smt/sls_solver.cpp index 8feb9f83eac..ae8620e28f2 100644 --- a/src/sat/smt/sls_solver.cpp +++ b/src/sat/smt/sls_solver.cpp @@ -61,9 +61,10 @@ namespace sls { m_m = alloc(ast_manager, m); ast_translation tr(m, *m_m); + params_ref p; m_completed = false; m_result = l_undef; - m_bvsls = alloc(bv::sls, *m_m); + m_bvsls = alloc(bv::sls, *m_m, p); // walk clauses, add them // walk trail stack until search level, add units // encapsulate bvsls within the arguments of run-local-search. diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index 198204d90b0..520682d1bc8 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -134,7 +134,7 @@ class bv_sls_tactic : public tactic { bv_sls_tactic(ast_manager& _m, params_ref const& p) : m(_m), m_params(p) { - m_sls = alloc(bv::sls, m); + m_sls = alloc(bv::sls, m, p); } tactic* translate(ast_manager& m) override { @@ -172,12 +172,12 @@ class bv_sls_tactic : public tactic { m_sls->init_eval(false_eval); lbool res = m_sls->operator()(); - auto const& stats = m_sls->get_stats(); - report_tactic_progress("Number of flips:", stats.m_moves); - IF_VERBOSE(20, verbose_stream() << res << "\n"); - IF_VERBOSE(20, m_sls->display(verbose_stream())); m_st.reset(); m_sls->collect_statistics(m_st); + report_tactic_progress("Number of flips:", m_sls->get_num_moves()); + IF_VERBOSE(20, verbose_stream() << res << "\n"); + IF_VERBOSE(20, m_sls->display(verbose_stream())); + if (res == l_true) { if (g->models_enabled()) { model_ref mdl = m_sls->get_model(); @@ -207,7 +207,7 @@ class bv_sls_tactic : public tactic { void cleanup() override { - auto* d = alloc(bv::sls, m); + auto* d = alloc(bv::sls, m, m_params); std::swap(d, m_sls); dealloc(d); } @@ -235,12 +235,6 @@ tactic* mk_bv_sls_tactic(ast_manager& m, params_ref const& p) { static tactic * mk_preamble(ast_manager & m, params_ref const & p) { - params_ref main_p; - main_p.set_bool("elim_and", true); - // main_p.set_bool("pull_cheap_ite", true); - main_p.set_bool("push_ite_bv", true); - main_p.set_bool("blast_distinct", true); - main_p.set_bool("hi_div0", true); params_ref simp2_p = p; simp2_p.set_bool("som", true); @@ -249,18 +243,15 @@ static tactic * mk_preamble(ast_manager & m, params_ref const & p) { simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); - params_ref hoist_p; + params_ref hoist_p = p; hoist_p.set_bool("hoist_mul", true); hoist_p.set_bool("som", false); - params_ref gaussian_p; + params_ref gaussian_p = p; // conservative gaussian elimination. gaussian_p.set_uint("gaussian_max_occs", 2); - params_ref ctx_p; - ctx_p.set_uint("max_depth", 32); - ctx_p.set_uint("max_steps", 5000000); - return and_then(and_then(mk_simplify_tactic(m), + return and_then(and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m), using_params(mk_solve_eqs_tactic(m), gaussian_p), mk_elim_uncnstr_tactic(m), @@ -278,7 +269,9 @@ tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p) { } tactic* mk_qfbv_new_sls_tactic(ast_manager& m, params_ref const& p) { - tactic* t = and_then(mk_preamble(m, p), mk_bv_sls_tactic(m, p)); - t->updt_params(p); + params_ref q = p; + q.set_bool("elim_sign_ext", false); + tactic* t = and_then(mk_preamble(m, q), mk_bv_sls_tactic(m, q)); + t->updt_params(q); return t; } From 84092cbd96a1d4c65bea6db2370807b19519704e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Mar 2024 15:12:32 -0700 Subject: [PATCH 023/414] add engine-init to control model transfer Signed-off-by: Nikolaj Bjorner --- src/ast/sls/bv_sls.cpp | 24 +++++++++++++++++------- src/ast/sls/bv_sls.h | 1 + 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index 6a176016f97..12b11adf992 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -101,16 +101,24 @@ namespace bv { std::function eval = [&](expr* e, unsigned i) { unsigned id = e->get_id(); - bool keep = (m_rand() % 100 <= 50) || !m_to_repair.contains(id); - if (m.is_bool(e) && (m_eval.is_fixed0(e) || keep)) - return m_eval.bval0(e); + bool keep = !m_to_repair.contains(id); + if (m.is_bool(e)) { + if (m_eval.is_fixed0(e) || keep) + return m_eval.bval0(e); + if (m_engine_init) { + auto const& z = m_engine.get_value(e); + return rational(z).get_bit(0); + } + } else if (bv.is_bv(e)) { auto& w = m_eval.wval(e); if (w.fixed.get(i) || keep) return w.get_bit(i); - //auto const& z = m_engine.get_value(e); - //return rational(z).get_bit(i); - } + if (m_engine_init) { + auto const& z = m_engine.get_value(e); + return rational(z).get_bit(i); + } + } return m_rand() % 2 == 0; }; @@ -177,7 +185,8 @@ namespace bv { else if (m_stats.m_restarts % 1000 == 0) res = m_engine.search_loop(); if (res != l_undef) - m_engine_model = true; + m_engine_model = true; + m_engine_init = true; return res; } @@ -187,6 +196,7 @@ namespace bv { m_stats.reset(); m_stats.m_restarts = 0; m_engine_model = false; + m_engine_init = false; do { res = search1(); diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index ae7d7cb57ef..b1a7f51dfe7 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -52,6 +52,7 @@ namespace bv { config m_config; sls_engine m_engine; bool m_engine_model = false; + bool m_engine_init = false; std::pair next_to_repair(); From 918ac2b1760c7fcea3df80208cf554cc47e77a5d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 1 Apr 2024 17:25:50 +0100 Subject: [PATCH 024/414] =?UTF-8?q?fix=20#7196:=20make=20the=20code=20C++2?= =?UTF-8?q?3=20compatible=20Nikolaj=20is=20now=20more=20bleeding=20edge=20?= =?UTF-8?q?than=20I=20am...=20I=20must=20be=20getting=20old=3F=20(=CB=98?= =?UTF-8?q?=EF=BD=A5=5F=EF=BD=A5=CB=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/buffer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/buffer.h b/src/util/buffer.h index f40914638cb..3ca597af2e9 100644 --- a/src/util/buffer.h +++ b/src/util/buffer.h @@ -21,7 +21,7 @@ Revision History: --*/ #pragma once -#include +#include #include "util/memory_manager.h" template @@ -30,7 +30,7 @@ class buffer { T * m_buffer = reinterpret_cast(m_initial_buffer); unsigned m_pos = 0; unsigned m_capacity = INITIAL_SIZE; - typename std::aligned_storage::type m_initial_buffer[INITIAL_SIZE]; + alignas(T) std::byte m_initial_buffer[INITIAL_SIZE * sizeof(T)]; void free_memory() { if (m_buffer != reinterpret_cast(m_initial_buffer)) { From 2ce202db755d3e955ae036c9e21efaaed1143573 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 Apr 2024 21:08:59 -0700 Subject: [PATCH 025/414] add special handling of lshr, ashr --- src/ast/sls/bv_sls.cpp | 2 +- src/ast/sls/bv_sls_eval.cpp | 270 +++++++++++++++++++++++++++++++++- src/ast/sls/bv_sls_eval.h | 4 + src/ast/sls/sls_valuation.cpp | 107 +++++++++++--- src/ast/sls/sls_valuation.h | 20 ++- 5 files changed, 376 insertions(+), 27 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index 12b11adf992..b94d762cf44 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -203,7 +203,7 @@ namespace bv { if (res != l_undef) break; trace(); - res = search2(); + // res = search2(); if (res != l_undef) break; reinit_eval(); diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index 579bc736987..2e83f172980 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -84,10 +84,13 @@ namespace bv { return false; auto v = alloc_valuation(e); m_values.set(e->get_id(), v); - if (bv.is_sign_ext(e)) { - unsigned p = e->get_parameter(0).get_int(); - v->set_signed(p); - } + expr* x, * y; + rational val; + if (bv.is_sign_ext(e)) + v->set_signed(e->get_parameter(0).get_int()); + else if (bv.is_bv_ashr(e, x, y) && bv.is_numeral(y, val) && + val.is_unsigned() && val.get_unsigned() <= bv.get_bv_size(e)) + v->set_signed(val.get_unsigned()); return true; } @@ -1349,6 +1352,13 @@ namespace bv { } bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) { + if (true) { + if (i == 0) + return try_repair_ashr0(e, a, b); + else + return try_repair_ashr1(e, a, b); + } + if (i == 0) { unsigned sh = b.to_nat(b.bw); if (sh == 0) @@ -1382,7 +1392,259 @@ namespace bv { } bool sls_eval::try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i) { +#if 0 return try_repair_ashr(e, a, b, i); +#else + if (i == 0) + return try_repair_lshr0(e, a, b); + else + return try_repair_lshr1(e, a, b); +#endif + } + + /** + * strong: + * - e = (e << b) >> b -> a := e << b, upper b bits set random + * weak: + * - e = 0 -> a := random + * - e > 0 -> a := random with msb(a) >= msb(e) + */ + bool sls_eval::try_repair_lshr0(bvect const& e, bvval& a, bvval const& b) { + + auto& t = m_tmp; + // t := e << b + // t := t >> b + t.set_shift_left(e, b.bits()); + t.set_shift_right(t, b.bits()); + bool use_strong = m_rand(10) != 0; + if (t == e && use_strong) { + t.set_shift_left(e, b.bits()); + unsigned n = b.bits().to_nat(e.bw); + for (unsigned i = e.bw; i-- > e.bw - n;) + t.set(i, a.get_bit(i)); + a.clear_overflow_bits(t); + if (a.set_repair(random_bool(), t)) + return true; + } + + unsigned sh = b.to_nat(b.bw); + if (sh == 0 && a.try_set(e)) + return true; + else if (sh >= b.bw) + return true; + else if (sh < b.bw && m_rand(20) != 0) { + // e = a >> sh + // a[bw-1:sh] = e[bw-sh-1:0] + // a[sh-1:0] = a[sh-1:0] + for (unsigned i = sh; i < a.bw; ++i) + t.set(i, e.get(i - sh)); + for (unsigned i = 0; i < sh; ++i) + t.set(i, a.get_bit(i)); + a.clear_overflow_bits(t); + if (a.try_set(t)) + return true; + } + + //bool r = try_repair_ashr(e, a, const_cast(b), 0); + //verbose_stream() << "repair lshr0 " << e << " b: " << b << " a: " << a << "\n"; + //return r; + + a.get_variant(t, m_rand); + + unsigned msb = a.msb(e); + if (msb > a.msb(t)) { + unsigned num_flex = 0; + for (unsigned i = e.bw; i-- >= msb;) + if (!a.fixed.get(i)) + ++num_flex; + if (num_flex == 0) + return false; + unsigned n = m_rand(num_flex); + for (unsigned i = e.bw; i-- >= msb;) { + if (!a.fixed.get(i)) { + if (n == 0) { + t.set(i, true); + break; + } + else + n--; + } + } + } + return a.set_repair(random_bool(), t); + } + + /** + * strong: + * - clz(a) <= clz(e), e = 0 or (a >> (clz(e) - clz(a)) = e + * - e = 0 and a = 0: b := random + * - e = 0 and a != 0: b := random, such that shift <= b + * - e != 0: b := shift + * where shift := clz(e) - clz(a) + * + * weak: + * - e = 0: b := random + * - e > 0: b := random >= clz(e) + */ + bool sls_eval::try_repair_lshr1(bvect const& e, bvval const& a, bvval& b) { + + auto& t = m_tmp; + auto clza = a.clz(a.bits()); + auto clze = a.clz(e); + t.set_bw(a.bw); + + // strong + if (m_rand(10) != 0 && clza <= clze && (a.is_zero(e) || t.set_shift_right(a.bits(), clze - clza) == e)) { + if (a.is_zero(e) && a.is_zero()) + return true; + unsigned shift = clze - clza; + if (a.is_zero(e)) + shift = m_rand(a.bw + 1 - shift) + shift; + + b.set(t, shift); + if (b.try_set(t)) + return true; + } + + // no change + if (m_rand(10) != 0) { + if (a.is_zero(e)) + return true; + if (b.bits() <= clze) + return true; + } + + // weak + b.get_variant(t, m_rand); + if (a.is_zero(e)) + return b.set_repair(random_bool(), t); + else { + for (unsigned i = 0; i < 4; ++i) { + for (unsigned i = a.bw; !(t <= clze) && i-- > 0; ) + if (!b.fixed.get(i)) + t.set(i, false); + if (t <= clze && b.set_repair(random_bool(), t)) + return true; + b.get_variant(t, m_rand); + } + return false; + } + } + + /** + * strong: + * b < |b| => (e << b) >>a b = e) + * b >= |b| => (e = ones || e = 0) + * - if b < |b|: a := e << b + * - if b >= |b|: a[bw-1] := e = ones + * weak: + * + */ + bool sls_eval::try_repair_ashr0(bvect const& e, bvval& a, bvval const& b) { + auto& t = m_tmp; + t.set_bw(b.bw); + auto n = b.msb(b.bits()); + bool use_strong = m_rand(20) != 0; + if (use_strong && n < b.bw) { + t.set_shift_left(e, b.bits()); + bool sign = t.get(b.bw-1); + t.set_shift_right(t, b.bits()); + if (sign) { + for (unsigned i = b.bw; i-- > b.bw - n; ) + t.set(i, true); + } + use_strong &= t == e; + } + else { + use_strong &= a.is_zero(e) || a.is_ones(e); + } + if (use_strong) { + if (n < b.bw) { + t.set_shift_left(e, b.bits()); + for (unsigned i = 0; i < n; ++i) + t.set(i, a.get_bit(i)); + } + else { + for (unsigned i = 0; i < b.nw; ++i) + t[i] = a.bits()[i]; + t.set(b.bw - 1, a.is_ones(e)); + } + a.clear_overflow_bits(t); + if (a.set_repair(random_bool(), t)) + return true; + } + if (m_rand(10) != 0) { + if (n < b.bw) { + t.set_shift_left(e, b.bits()); + for (unsigned i = 0; i < n; ++i) + t.set(i, random_bool()); + } + else { + a.get_variant(t, m_rand); + t.set(b.bw - 1, a.is_ones(e)); + } + a.clear_overflow_bits(t); + if (a.set_repair(random_bool(), t)) + return true; + } + + a.get_variant(t, m_rand); + return a.set_repair(random_bool(), t); + } + + /* + * strong: + * - clz(a) <= clz(e), e = 0 or (a >>a (clz(e) - clz(a)) = e + * - e = 0 and a = 0: b := random + * - e = 0 and a != 0: b := random, such that shift <= b + * - e != 0: b := shift + * where shift := clz(e) - clz(a) + * + * weak: + * - e = 0: b := random + * - e > 0: b := random >= clz(e) + */ + + bool sls_eval::try_repair_ashr1(bvect const& e, bvval const& a, bvval& b) { + + auto& t = m_tmp; + auto clza = a.clz(a.bits()); + auto clze = a.clz(e); + t.set_bw(a.bw); + + // strong unsigned + if (!a.get_bit(a.bw - 1) && m_rand(10) != 0 && clza <= clze && (a.is_zero(e) || t.set_shift_right(a.bits(), clze - clza) == e)) { + if (a.is_zero(e) && a.is_zero()) + return true; + unsigned shift = clze - clza; + if (a.is_zero(e)) + shift = m_rand(a.bw + 1 - shift) + shift; + + b.set(t, shift); + if (b.try_set(t)) + return true; + } + // strong signed + if (a.get_bit(a.bw - 1) && m_rand(10) != 0 && clza >= clze) { + t.set_shift_right(a.bits(), clza - clze); + for (unsigned i = a.bw; i-- > a.bw - clza + clze; ) + t.set(i, true); + if (e == t) { + if (a.is_zero(e) && a.is_zero()) + return true; + unsigned shift = clze - clza; + if (a.is_zero(e)) + shift = m_rand(a.bw + 1 - shift) + shift; + + b.set(t, shift); + if (b.try_set(t)) + return true; + } + } + + // weak + b.get_variant(t, m_rand); + return b.set_repair(random_bool(), t); } bool sls_eval::try_repair_comp(bvect const& e, bvval& a, bvval& b, unsigned i) { diff --git a/src/ast/sls/bv_sls_eval.h b/src/ast/sls/bv_sls_eval.h index 5422d5b7c02..ebd36048184 100644 --- a/src/ast/sls/bv_sls_eval.h +++ b/src/ast/sls/bv_sls_eval.h @@ -93,6 +93,10 @@ namespace bv { bool try_repair_shl(bvect const& e, bvval& a, bvval& b, unsigned i); bool try_repair_ashr(bvect const& e, bvval& a, bvval& b, unsigned i); bool try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i); + bool try_repair_lshr0(bvect const& e, bvval& a, bvval const& b); + bool try_repair_lshr1(bvect const& e, bvval const& a, bvval& b); + bool try_repair_ashr0(bvect const& e, bvval& a, bvval const& b); + bool try_repair_ashr1(bvect const& e, bvval const& a, bvval& b); bool try_repair_bit2bool(bvval& a, unsigned idx); bool try_repair_udiv(bvect const& e, bvval& a, bvval& b, unsigned i); bool try_repair_urem(bvect const& e, bvval& a, bvval& b, unsigned i); diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 3f619df0567..c251d15ec33 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -56,6 +56,20 @@ namespace bv { return mpn_manager().compare(a.data(), a.nw, b.data(), a.nw) >= 0; } + bool operator<=(digit_t a, bvect const& b) { + for (unsigned i = 1; i < b.nw; ++i) + if (0 != b[i]) + return true; + return mpn_manager().compare(&a, 1, b.data(), 1) <= 0; + } + + bool operator<=(bvect const& a, digit_t b) { + for (unsigned i = 1; i < a.nw; ++i) + if (0 != a[i]) + return false; + return mpn_manager().compare(a.data(), 1, &b, 1) <= 0; + } + std::ostream& operator<<(std::ostream& out, bvect const& v) { out << std::hex; bool nz = false; @@ -83,6 +97,57 @@ namespace bv { return r; } + + unsigned bvect::to_nat(unsigned max_n) const { + SASSERT(max_n < UINT_MAX / 2); + unsigned p = 1; + unsigned value = 0; + for (unsigned i = 0; i < bw; ++i) { + if (p >= max_n) { + for (unsigned j = i; j < bw; ++j) + if (get(j)) + return max_n; + return value; + } + if (get(i)) + value += p; + p <<= 1; + } + return value; + } + + bvect& bvect::set_shift_right(bvect const& a, bvect const& b) { + SASSERT(a.bw == b.bw); + unsigned shift = b.to_nat(b.bw); + return set_shift_right(a, shift); + } + + bvect& bvect::set_shift_right(bvect const& a, unsigned shift) { + set_bw(a.bw); + if (shift == 0) + a.copy_to(a.nw, *this); + else if (shift >= a.bw) + set_zero(); + else + for (unsigned i = 0; i < bw; ++i) + set(i, i + shift < bw ? a.get(i + shift) : false); + return *this; + } + + bvect& bvect::set_shift_left(bvect const& a, bvect const& b) { + set_bw(a.bw); + SASSERT(a.bw == b.bw); + unsigned shift = b.to_nat(b.bw); + if (shift == 0) + a.copy_to(a.nw, *this); + else if (shift >= a.bw) + set_zero(); + else + for (unsigned i = bw; i-- > 0; ) + set(i, i >= shift ? a.get(i - shift) : false); + return *this; + } + sls_valuation::sls_valuation(unsigned bw) { set_bw(bw); m_lo.set_bw(bw); @@ -411,6 +476,16 @@ namespace bv { return bw; } + unsigned sls_valuation::clz(bvect const& src) const { + SASSERT(!has_overflow(src)); + unsigned i = bw; + for (; i-- > 0; ) + if (!src.get(i)) + return bw - 1 - i; + return bw; + } + + void sls_valuation::set_value(bvect& bits, rational const& n) { for (unsigned i = 0; i < bw; ++i) bits.set(i, n.get_bit(i)); @@ -438,11 +513,14 @@ namespace bv { void sls_valuation::repair_sign_bits(bvect& dst) const { if (m_signed_prefix == 0) return; - bool sign = dst.get(bw - 1); - for (unsigned i = bw; i-- >= bw - m_signed_prefix; ) { + bool sign = m_signed_prefix == bw ? dst.get(bw - 1) : dst.get(bw - m_signed_prefix - 1); + for (unsigned i = bw; i-- > bw - m_signed_prefix; ) { if (dst.get(i) != sign) { if (fixed.get(i)) { - for (unsigned i = bw; i-- >= bw - m_signed_prefix; ) + unsigned j = bw - m_signed_prefix; + if (j > 0 && !fixed.get(j - 1)) + dst.set(j - 1, !sign); + for (unsigned i = bw; i-- > bw - m_signed_prefix; ) if (!fixed.get(i)) dst.set(i, !sign); return; @@ -466,24 +544,11 @@ namespace bv { return in_range(new_bits); } - unsigned sls_valuation::to_nat(unsigned max_n) { + unsigned sls_valuation::to_nat(unsigned max_n) const { + bvect const& d = m_bits; SASSERT(!has_overflow(d)); - SASSERT(max_n < UINT_MAX / 2); - unsigned p = 1; - unsigned value = 0; - for (unsigned i = 0; i < bw; ++i) { - if (p >= max_n) { - for (unsigned j = i; j < bw; ++j) - if (d.get(j)) - return max_n; - return value; - } - if (d.get(i)) - value += p; - p <<= 1; - } - return value; + return d.to_nat(max_n); } void sls_valuation::shift_right(bvect& out, unsigned shift) const { @@ -493,7 +558,9 @@ namespace bv { SASSERT(well_formed()); } - void sls_valuation::add_range(rational l, rational h) { + void sls_valuation::add_range(rational l, rational h) { + + //return; l = mod(l, rational::power_of_two(bw)); h = mod(h, rational::power_of_two(bw)); diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index dcabf04c08a..1beba65a450 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -60,13 +60,27 @@ namespace bv { return bw; } + void set_zero() { + for (unsigned i = 0; i < nw; ++i) + (*this)[i] = 0; + } + + bvect& set_shift_right(bvect const& a, bvect const& b); + bvect& set_shift_right(bvect const& a, unsigned shift); + bvect& set_shift_left(bvect const& a, bvect const& b); + rational get_value(unsigned nw) const; + unsigned to_nat(unsigned max_n) const; + + friend bool operator==(bvect const& a, bvect const& b); friend bool operator<(bvect const& a, bvect const& b); friend bool operator>(bvect const& a, bvect const& b); friend bool operator<=(bvect const& a, bvect const& b); friend bool operator>=(bvect const& a, bvect const& b); + friend bool operator<=(digit_t a, bvect const& b); + friend bool operator<=(bvect const& a, digit_t b); friend std::ostream& operator<<(std::ostream& out, bvect const& v); private: @@ -198,6 +212,8 @@ namespace bv { // most significant bit or bw if src = 0 unsigned msb(bvect const& src) const; + unsigned clz(bvect const& src) const; + bool is_power_of2(bvect const& src) const; // retrieve largest number at or below (above) src which is feasible @@ -232,7 +248,7 @@ namespace bv { clear_overflow_bits(eval); } - void set_zero(bvect& out) const { + void set_zero(bvect& out) const { for (unsigned i = 0; i < nw; ++i) out[i] = 0; } @@ -288,7 +304,7 @@ namespace bv { dst[i] = src[i]; } - unsigned to_nat(unsigned max_n); + unsigned to_nat(unsigned max_n) const; std::ostream& display(std::ostream& out) const { out << m_bits; From d7c0e17f9617d641262bf3a5883d87556773cc5f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 Apr 2024 21:11:00 -0700 Subject: [PATCH 026/414] fixes to tighten-range --- src/ast/sls/sls_valuation.cpp | 99 +++++++++++++++-------------------- src/ast/sls/sls_valuation.h | 11 ++++ 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index c251d15ec33..6ce1c1df289 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -567,9 +567,6 @@ namespace bv { if (h == l) return; - //verbose_stream() << "[" << l << ", " << h << "[\n"; - //verbose_stream() << *this << "\n"; - if (m_lo == m_hi) { set_value(m_lo, l); set_value(m_hi, h); @@ -603,14 +600,11 @@ namespace bv { } } - - SASSERT(!has_overflow(m_lo)); SASSERT(!has_overflow(m_hi)); tighten_range(); SASSERT(well_formed()); - // verbose_stream() << *this << "\n"; } // @@ -632,60 +626,53 @@ namespace bv { if (m_lo == m_hi) return; - if (!in_range(m_bits)) { - // verbose_stream() << "not in range\n"; - bool compatible = true; - for (unsigned i = 0; i < nw && compatible; ++i) - compatible = 0 == (fixed[i] & (m_bits[i] ^ m_lo[i])); - //verbose_stream() << (fixed[0] & (m_bits[0] ^ m_lo[0])) << "\n"; - //verbose_stream() << bw << " " << m_lo[0] << " " << m_bits[0] << "\n"; - if (compatible) { - //verbose_stream() << "compatible\n"; - set(m_bits, m_lo); + bvect hi1(nw); + hi1.set_bw(bw); + m_hi.copy_to(nw, hi1); + sub1(hi1); + unsigned lo_index = 0, hi_index = 0; + for (unsigned i = nw; i-- > 0; ) { + auto lo_diff = (fixed[i] & (m_bits[i] ^ m_lo[i])); + if (lo_diff != 0 && lo_index == 0) + lo_index = 1 + i * 8 * sizeof(digit_t) + log2(lo_diff); + auto hi_diff = (fixed[i] & (m_bits[i] ^ hi1[i])); + if (hi_diff != 0 && hi_index == 0) + hi_index = 1 + i * 8 * sizeof(digit_t) + log2(hi_diff); + } + + if (lo_index != 0) { + lo_index--; + SASSERT(m_lo.get(lo_index) != m_bits.get(lo_index)); + SASSERT(fixed.get(lo_index)); + for (unsigned i = 0; i <= lo_index; ++i) { + if (!fixed.get(i)) + m_lo.set(i, false); + else if (fixed.get(i)) + m_lo.set(i, m_bits.get(i)); } - else { - bvect tmp(m_bits.nw); - tmp.set_bw(bw); - set(tmp, m_lo); - unsigned max_diff = bw; - for (unsigned i = 0; i < bw; ++i) { - if (fixed.get(i) && (m_bits.get(i) ^ m_lo.get(i))) - max_diff = i; - } - SASSERT(max_diff != bw); - - for (unsigned i = 0; i <= max_diff; ++i) - tmp.set(i, fixed.get(i) && m_bits.get(i)); - - bool found0 = false; - for (unsigned i = max_diff + 1; i < bw; ++i) { - if (found0 || m_lo.get(i) || fixed.get(i)) - tmp.set(i, m_lo.get(i) && fixed.get(i)); - else { - tmp.set(i, true); - found0 = true; - } + for (unsigned i = lo_index + 1; i < bw; ++i) + if (!fixed.get(i) && !m_lo.get(i)) { + m_lo.set(i, true); + break; } - set(m_bits, tmp); - } } - // update lo, hi to be feasible. - - for (unsigned i = bw; i-- > 0; ) { - if (!fixed.get(i)) - continue; - if (m_bits.get(i) == m_lo.get(i)) - continue; - if (m_bits.get(i)) { - m_lo.set(i, true); - for (unsigned j = i; j-- > 0; ) - m_lo.set(j, fixed.get(j) && m_bits.get(j)); + if (hi_index != 0) { + hi_index--; + SASSERT(hi1.get(hi_index) != m_bits.get(hi_index)); + SASSERT(fixed.get(hi_index)); + for (unsigned i = 0; i <= hi_index; ++i) { + if (!fixed.get(i)) + hi1.set(i, true); + else if (fixed.get(i)) + hi1.set(i, m_bits.get(i)); } - else { - for (unsigned j = bw; j-- > 0; ) - m_lo.set(j, fixed.get(j) && m_bits.get(j)); - } - break; + for (unsigned i = hi_index + 1; i < bw; ++i) + if (!fixed.get(i) && hi1.get(i)) { + hi1.set(i, false); + break; + } + add1(hi1); + hi1.copy_to(nw, m_hi); } if (has_range() && !in_range(m_bits)) diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index 1beba65a450..90794bfe2b1 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -274,6 +274,17 @@ namespace bv { } } + void add1(bvect& out) const { + for (unsigned i = 0; i < bw; ++i) { + if (!out.get(i)) { + out.set(i, true); + return; + } + else + out.set(i, false); + } + } + void set_sub(bvect& out, bvect const& a, bvect const& b) const; bool set_add(bvect& out, bvect const& a, bvect const& b) const; bool set_mul(bvect& out, bvect const& a, bvect const& b, bool check_overflow = true) const; From bab7ca2b70398e8ea95211691bb678b7bcc9a0c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 7 Apr 2024 14:24:13 -0700 Subject: [PATCH 027/414] fixes to bv-sls --- src/ast/sls/bv_sls.cpp | 31 +++-- src/ast/sls/bv_sls.h | 2 +- src/ast/sls/bv_sls_eval.cpp | 70 +++------- src/ast/sls/bv_sls_fixed.cpp | 180 ++++++++++++++++--------- src/ast/sls/bv_sls_fixed.h | 6 +- src/ast/sls/sls_valuation.cpp | 241 ++++++++++++++-------------------- src/ast/sls/sls_valuation.h | 7 +- src/tactic/sls/sls_tactic.cpp | 4 +- 8 files changed, 267 insertions(+), 274 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index b94d762cf44..f799d3cfee1 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -124,6 +124,7 @@ namespace bv { }; m_eval.init_eval(m_terms.assertions(), eval); init_repair(); + // m_engine_init = false; } std::pair sls::next_to_repair() { @@ -161,17 +162,18 @@ namespace bv { lbool sls::search1() { // init and init_eval were invoked unsigned n = 0; - for (; n++ < m_config.m_max_repairs && m.inc(); ) { + for (; n < m_config.m_max_repairs && m.inc(); ) { auto [down, e] = next_to_repair(); if (!e) return l_true; - trace_repair(down, e); - + IF_VERBOSE(20, trace_repair(down, e)); + ++m_stats.m_moves; - - if (down) + if (down) { try_repair_down(e); + ++n; + } else try_repair_up(e); } @@ -181,12 +183,13 @@ namespace bv { lbool sls::search2() { lbool res = l_undef; if (m_stats.m_restarts == 0) - res = m_engine(); + res = m_engine(), + m_engine_init = true; else if (m_stats.m_restarts % 1000 == 0) - res = m_engine.search_loop(); + res = m_engine.search_loop(), + m_engine_init = true; if (res != l_undef) m_engine_model = true; - m_engine_init = true; return res; } @@ -203,7 +206,7 @@ namespace bv { if (res != l_undef) break; trace(); - // res = search2(); + //res = search2(); if (res != l_undef) break; reinit_eval(); @@ -223,6 +226,7 @@ namespace bv { VERIFY(m_eval.wval(e).commit_eval()); } + IF_VERBOSE(3, verbose_stream() << "done\n"); for (auto p : m_terms.parents(e)) m_repair_up.insert(p->get_id()); @@ -256,6 +260,7 @@ namespace bv { } } } + IF_VERBOSE(3, verbose_stream() << "init-repair " << mk_bounded_pp(e, m) << "\n"); // repair was not successful, so reset the state to find a different way to repair init_repair(); } @@ -265,7 +270,7 @@ namespace bv { if (m_terms.is_assertion(e)) m_repair_roots.insert(e->get_id()); else if (!m_eval.repair_up(e)) { - //m_repair_roots.insert(e->get_id()); + IF_VERBOSE(2, verbose_stream() << "repair-up "; trace_repair(true, e)); init_repair(); } else { @@ -366,14 +371,14 @@ namespace bv { m_engine.updt_params(q); } - void sls::trace_repair(bool down, expr* e) { - IF_VERBOSE(20, + std::ostream& sls::trace_repair(bool down, expr* e) { verbose_stream() << (down ? "d #" : "u #") << e->get_id() << ": " << mk_bounded_pp(e, m, 1) << " "; if (bv.is_bv(e)) verbose_stream() << m_eval.wval(e) << " " << (m_eval.is_fixed0(e) ? "fixed " : " "); if (m.is_bool(e)) verbose_stream() << m_eval.bval0(e) << " "; - verbose_stream() << "\n"); + verbose_stream() << "\n"; + return verbose_stream(); } void sls::trace() { diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index b1a7f51dfe7..01ccbc41f5d 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -68,7 +68,7 @@ namespace bv { void reinit_eval(); void init_repair(); void trace(); - void trace_repair(bool down, expr* e); + std::ostream& trace_repair(bool down, expr* e); indexed_uint_set m_to_repair; void init_repair_candidates(); diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index 2e83f172980..b69cec867d0 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -1351,55 +1351,18 @@ namespace bv { return false; } - bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) { - if (true) { + bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) { if (i == 0) return try_repair_ashr0(e, a, b); else return try_repair_ashr1(e, a, b); - } - - if (i == 0) { - unsigned sh = b.to_nat(b.bw); - if (sh == 0) - return a.try_set(e); - else if (sh >= b.bw) { - if (e.get(a.bw - 1)) - return a.try_set_bit(a.bw - 1, true); - else - return a.try_set_bit(a.bw - 1, false); - } - else { - // e = a >> sh - // a[bw-1:sh] = e[bw-sh-1:0] - // a[sh-1:0] = a[sh-1:0] - // ignore sign - for (unsigned i = sh; i < a.bw; ++i) - m_tmp.set(i, e.get(i - sh)); - for (unsigned i = 0; i < sh; ++i) - m_tmp.set(i, a.get_bit(i)); - a.clear_overflow_bits(m_tmp); - return a.try_set(m_tmp); - } - } - else { - // NB. blind sub-range of possible values for b - SASSERT(i == 1); - unsigned sh = m_rand(a.bw + 1); - b.set(m_tmp, sh); - return b.try_set(m_tmp); - } } bool sls_eval::try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i) { -#if 0 - return try_repair_ashr(e, a, b, i); -#else if (i == 0) return try_repair_lshr0(e, a, b); else return try_repair_lshr1(e, a, b); -#endif } /** @@ -1427,22 +1390,25 @@ namespace bv { return true; } + unsigned sh = b.to_nat(b.bw); - if (sh == 0 && a.try_set(e)) - return true; - else if (sh >= b.bw) - return true; - else if (sh < b.bw && m_rand(20) != 0) { - // e = a >> sh - // a[bw-1:sh] = e[bw-sh-1:0] - // a[sh-1:0] = a[sh-1:0] - for (unsigned i = sh; i < a.bw; ++i) - t.set(i, e.get(i - sh)); - for (unsigned i = 0; i < sh; ++i) - t.set(i, a.get_bit(i)); - a.clear_overflow_bits(t); - if (a.try_set(t)) + if (m_rand(20) != 0) { + if (sh == 0 && a.try_set(e)) return true; + else if (sh >= b.bw) + return true; + else if (sh < b.bw && m_rand(20) != 0) { + // e = a >> sh + // a[bw-1:sh] = e[bw-sh-1:0] + // a[sh-1:0] = a[sh-1:0] + for (unsigned i = sh; i < a.bw; ++i) + t.set(i, e.get(i - sh)); + for (unsigned i = 0; i < sh; ++i) + t.set(i, a.get_bit(i)); + a.clear_overflow_bits(t); + if (a.try_set(t)) + return true; + } } //bool r = try_repair_ashr(e, a, const_cast(b), 0); diff --git a/src/ast/sls/bv_sls_fixed.cpp b/src/ast/sls/bv_sls_fixed.cpp index e2b29b1fe05..18518ed09bd 100644 --- a/src/ast/sls/bv_sls_fixed.cpp +++ b/src/ast/sls/bv_sls_fixed.cpp @@ -53,7 +53,7 @@ namespace bv { // s <=s t <=> s + K <= t + K, K = 2^{bw-1} - void sls_fixed::init_range(app* e, bool sign) { + bool sls_fixed::init_range(app* e, bool sign) { expr* s, * t, * x, * y; rational a, b; unsigned idx; @@ -64,63 +64,116 @@ namespace bv { if (bv.is_ule(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(x, a, y, b, sign); + return init_range(x, a, y, b, sign); } else if (bv.is_ult(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(y, b, x, a, !sign); + return init_range(y, b, x, a, !sign); } else if (bv.is_uge(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(y, b, x, a, sign); + return init_range(y, b, x, a, sign); } else if (bv.is_ugt(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(x, a, y, b, !sign); + return init_range(x, a, y, b, !sign); } else if (bv.is_sle(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(x, a + N(s), y, b + N(s), sign); + return init_range(x, a + N(s), y, b + N(s), sign); } else if (bv.is_slt(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(y, b + N(s), x, a + N(s), !sign); + return init_range(y, b + N(s), x, a + N(s), !sign); } else if (bv.is_sge(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(y, b + N(s), x, a + N(s), sign); + return init_range(y, b + N(s), x, a + N(s), sign); } else if (bv.is_sgt(e, s, t)) { get_offset(s, x, a); get_offset(t, y, b); - init_range(x, a + N(s), y, b + N(s), !sign); + return init_range(x, a + N(s), y, b + N(s), !sign); } - else if (!sign && m.is_eq(e, s, t)) { + else if (m.is_eq(e, s, t)) { if (bv.is_numeral(s, a)) - // t - a <= 0 - init_range(t, -a, nullptr, rational(0), false); + init_eq(t, a, sign); else if (bv.is_numeral(t, a)) - init_range(s, -a, nullptr, rational(0), false); - } - else if (sign && m.is_eq(e, s, t)) { - if (bv.is_numeral(s, a)) - // 1 <= t - a - init_range(nullptr, rational(1), t, -a, false); - else if (bv.is_numeral(t, a)) - init_range(nullptr, rational(1), s, -a, false); + init_eq(s, a, sign); + else + return false; + return true; } else if (bv.is_bit2bool(e, s, idx)) { auto& val = wval(s); val.try_set_bit(idx, !sign); val.fixed.set(idx, true); val.tighten_range(); + return true; + } + return false; + } + + bool sls_fixed::init_eq(expr* t, rational const& a, bool sign) { + unsigned lo, hi; + rational b(0); + // verbose_stream() << mk_bounded_pp(t, m) << " == " << a << "\n"; + expr* s = nullptr; + if (sign) + // 1 <= t - a + init_range(nullptr, rational(1), t, -a, false); + else + // t - a <= 0 + init_range(t, -a, nullptr, rational::zero(), false); + if (!sign && bv.is_bv_not(t, s)) { + for (unsigned i = 0; i < bv.get_bv_size(s); ++i) + if (!a.get_bit(i)) + b += rational::power_of_two(i); + init_eq(s, b, false); + return true; + } + if (!sign && bv.is_concat(t) && to_app(t)->get_num_args() == 2) { + auto x = to_app(t)->get_arg(0); + auto y = to_app(t)->get_arg(1); + auto sz = bv.get_bv_size(y); + auto k = rational::power_of_two(sz); + init_eq(y, mod(a, k), false); + init_eq(x, div(a + k - 1, k), false); + return true; + } + if (bv.is_extract(t, lo, hi, s)) { + if (hi == lo) { + sign = sign ? a == 1 : a == 0; + auto& val = wval(s); + if (val.try_set_bit(lo, !sign)) + val.fixed.set(lo, true); + val.tighten_range(); + } + else if (!sign) { + auto& val = wval(s); + for (unsigned i = lo; i <= hi; ++i) + if (val.try_set_bit(i, a.get_bit(i - lo))) + val.fixed.set(i, true); + val.tighten_range(); + // verbose_stream() << lo << " " << hi << " " << val << " := " << a << "\n"; + } + + if (!sign && hi + 1 == bv.get_bv_size(s)) { + // s < 2^lo * (a + 1) + rational b = rational::power_of_two(lo) * (a + 1) - 1; + rational offset; + get_offset(s, t, offset); + // t + offset <= b + init_range(t, offset, nullptr, b, false); + } } + return true; } // @@ -132,51 +185,66 @@ namespace bv { // a < x + b <=> ! (x + b <= a) <=> x not in [-a, b - a [ <=> x in [b - a, -a [ a != -1 // x + a < x + b <=> ! (x + b <= x + a) <=> x in [-a, -b [ a != b // - void sls_fixed::init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign) { + bool sls_fixed::init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign) { if (!x && !y) - return; - if (!x) { - // a <= y + b - if (a == 0) - return; - auto& v = wval(y); - if (!sign) - v.add_range(a - b, -b); - else - v.add_range(-b, a - b); - } - else if (!y) { + return false; + if (!x) + return add_range(y, a - b, -b, sign); + else if (!y) + return add_range(x, -a, b - a + 1, sign); + else if (x == y) + return add_range(x, -a, -b, sign); + return false; + } - if (mod(b + 1, rational::power_of_two(bv.get_bv_size(x))) == 0) - return; - auto& v = wval(x); - if (!sign) - v.add_range(-a, b - a + 1); - else - v.add_range(b - a + 1, -a); - } - else if (x == y) { - if (a == b) - return; - auto& v = wval(x); - if (!sign) - v.add_range(-a, -b); - else - v.add_range(-b, -a); + bool sls_fixed::add_range(expr* e, rational lo, rational hi, bool sign) { + auto& v = wval(e); + lo = mod(lo, rational::power_of_two(bv.get_bv_size(e))); + hi = mod(hi, rational::power_of_two(bv.get_bv_size(e))); + if (lo == hi) + return false; + if (sign) + std::swap(lo, hi); + v.add_range(lo, hi); + if (v.lo() == 0 && bv.is_concat(e) && to_app(e)->get_num_args() == 2) { + auto x = to_app(e)->get_arg(0); + auto y = to_app(e)->get_arg(1); + auto sz = bv.get_bv_size(y); + auto k = rational::power_of_two(sz); + lo = v.lo(); + hi = v.hi(); + if (hi <= k) { + add_range(y, lo, hi, false); + init_eq(x, lo, false); + } + else { + hi = div(hi + k - 1, k); + add_range(x, lo, hi, false); + } } + return true; } void sls_fixed::get_offset(expr* e, expr*& x, rational& offset) { expr* s, * t; x = e; offset = 0; - if (bv.is_bv_add(e, s, t)) { - if (bv.is_numeral(s, offset)) + rational n; + while (true) { + if (bv.is_bv_add(x, s, t) && bv.is_numeral(s, n)) { x = t; - else if (bv.is_numeral(t, offset)) + offset += n; + continue; + } + if (bv.is_bv_add(x, s, t) && bv.is_numeral(t, n)) { x = s; + offset += n; + continue; + } + break; } - else if (bv.is_numeral(e, offset)) + if (bv.is_numeral(e, n)) + offset += n, x = nullptr; } @@ -264,11 +332,6 @@ namespace bv { case OP_BADD: { auto& a = wval(e->get_arg(0)); auto& b = wval(e->get_arg(1)); - rational r; - if (bv.is_numeral(e->get_arg(0), r) && b.has_range()) - v.add_range(r + b.lo(), r + b.hi()); - else if (bv.is_numeral(e->get_arg(1), r) && a.has_range()) - v.add_range(r + a.lo(), r + a.hi()); bool pfixed = true; for (unsigned i = 0; i < v.bw; ++i) { if (pfixed && a.fixed.get(i) && b.fixed.get(i)) @@ -283,7 +346,6 @@ namespace bv { v.fixed.set(i, false); } } - break; } case OP_BMUL: { diff --git a/src/ast/sls/bv_sls_fixed.h b/src/ast/sls/bv_sls_fixed.h index 14970c20c69..73dd5a52c33 100644 --- a/src/ast/sls/bv_sls_fixed.h +++ b/src/ast/sls/bv_sls_fixed.h @@ -30,9 +30,11 @@ namespace bv { bv_util& bv; void init_ranges(expr_ref_vector const& es); - void init_range(app* e, bool sign); - void init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign); + bool init_range(app* e, bool sign); + bool init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign); void get_offset(expr* e, expr*& x, rational& offset); + bool init_eq(expr* e, rational const& v, bool sign); + bool add_range(expr* e, rational lo, rational hi, bool sign); void init_fixed_basic(app* e); void init_fixed_bv(app* e); diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 6ce1c1df289..1f3674e9329 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -204,139 +204,69 @@ namespace bv { // // largest dst <= src and dst is feasible - // set dst := src & (~fixed | bits) - // - // increment dst if dst < src by setting bits below msb(src & ~dst) to 1 - // - // if dst < lo < hi: - // return false - // if lo < hi <= dst: - // set dst := hi - 1 - // if hi <= dst < lo - // set dst := hi - 1 - // + // bool sls_valuation::get_at_most(bvect const& src, bvect& dst) const { SASSERT(!has_overflow(src)); - for (unsigned i = 0; i < nw; ++i) - dst[i] = src[i] & (~fixed[i] | m_bits[i]); - - // - // If dst < src, then find the most significant - // bit where src[idx] = 1, dst[idx] = 0 - // set dst[j] = bits_j | ~fixed_j for j < idx - // - for (unsigned i = nw; i-- > 0; ) { - if (0 != (~dst[i] & src[i])) { - auto idx = log2(~dst[i] & src[i]); - auto mask = (1 << idx) - 1; - dst[i] = (~fixed[i] & mask) | dst[i]; - for (unsigned j = i; j-- > 0; ) - dst[j] = (~fixed[j] | m_bits[j]); - break; - } - } - SASSERT(!has_overflow(dst)); - return round_down(dst); + src.copy_to(nw, dst); + sup_feasible(dst); + if (in_range(dst)) { + SASSERT(can_set(dst)); + return true; + } + if (dst < m_lo && m_lo < m_hi) // dst < lo < hi + return false; + if (is_zero(m_hi)) + return false; + m_hi.copy_to(nw, dst); // hi <= dst < lo or lo < hi <= dst + sub1(dst); + SASSERT(can_set(dst)); + return true; } // // smallest dst >= src and dst is feasible with respect to this. - // set dst := (src & ~fixed) | (fixed & bits) - // - // decrement dst if dst > src by setting bits below msb to 0 unless fixed - // - // if lo < hi <= dst - // return false - // if dst < lo < hi: - // set dst := lo - // if hi <= dst < lo - // set dst := lo - // bool sls_valuation::get_at_least(bvect const& src, bvect& dst) const { SASSERT(!has_overflow(src)); - for (unsigned i = 0; i < nw; ++i) - dst[i] = (~fixed[i] & src[i]) | (fixed[i] & m_bits[i]); - - // - // If dst > src, then find the most significant - // bit where src[idx] = 0, dst[idx] = 1 - // set dst[j] = dst[j] & fixed_j for j < idx - // - for (unsigned i = nw; i-- > 0; ) { - if (0 != (dst[i] & ~src[i])) { - auto idx = log2(dst[i] & ~src[i]); - auto mask = (1 << idx); - dst[i] = dst[i] & (fixed[i] | mask); - for (unsigned j = i; j-- > 0; ) - dst[j] = dst[j] & fixed[j]; - break; - } - } - SASSERT(!has_overflow(dst)); - return round_up(dst); - } - - bool sls_valuation::round_up(bvect& dst) const { - if (m_lo < m_hi) { - if (m_hi <= dst) - return false; - if (m_lo > dst) - set(dst, m_lo); + src.copy_to(nw, dst); + dst.set_bw(bw); + inf_feasible(dst); + if (in_range(dst)) { + SASSERT(can_set(dst)); + return true; } - else if (m_hi <= dst && m_lo > dst) - set(dst, m_lo); - SASSERT(!has_overflow(dst)); - return true; - } - bool sls_valuation::round_down(bvect& dst) const { - if (m_lo < m_hi) { - if (m_lo > dst) - return false; - if (m_hi <= dst) { - set(dst, m_hi); - sub1(dst); - } - } - else if (m_hi <= dst && m_lo > dst) { - set(dst, m_hi); - sub1(dst); - } - SASSERT(well_formed()); + if (dst > m_lo) + return false; + m_lo.copy_to(nw, dst); + SASSERT(can_set(dst)); return true; } bool sls_valuation::set_random_at_most(bvect const& src, bvect& tmp, random_gen& r) { if (!get_at_most(src, tmp)) return false; - if (is_zero(tmp) || (0 == r() % 2)) - return try_set(tmp); - set_random_below(tmp, r); - // random value below tmp - - if (m_lo == m_hi || is_zero(m_lo) || m_lo <= tmp) + if (is_zero(tmp) || (0 != r(10))) return try_set(tmp); - // for simplicity, bail out if we were not lucky - return get_at_most(src, tmp) && try_set(tmp); + // random value below tmp + set_random_below(tmp, r); + + return (can_set(tmp) || get_at_most(src, tmp)) && try_set(tmp); } bool sls_valuation::set_random_at_least(bvect const& src, bvect& tmp, random_gen& r) { if (!get_at_least(src, tmp)) return false; - if (is_ones(tmp) || (0 == r() % 2)) + + if (is_ones(tmp) || (0 != r(10))) return try_set(tmp); // random value at least tmp set_random_above(tmp, r); - - if (m_lo == m_hi || is_zero(m_hi) || m_hi > tmp) - return try_set(tmp); - // for simplicity, bail out if we were not lucky - return get_at_least(src, tmp) && try_set(tmp); + return (can_set(tmp) || get_at_least(src, tmp)) && try_set(tmp); } bool sls_valuation::set_random_in_range(bvect const& lo, bvect const& hi, bvect& tmp, random_gen& r) { @@ -533,7 +463,7 @@ namespace bv { // // new_bits != bits => ~fixed - // 0 = (new_bits ^ bits) & fixed + // 0 = (new_bits ^ bits) & fixedf // also check that new_bits are in range // bool sls_valuation::can_set(bvect const& new_bits) const { @@ -559,14 +489,16 @@ namespace bv { } void sls_valuation::add_range(rational l, rational h) { - - //return; + return; + //verbose_stream() << *this << " " << l << " " << h << " --> \n"; l = mod(l, rational::power_of_two(bw)); h = mod(h, rational::power_of_two(bw)); if (h == l) return; +// verbose_stream() << *this << " " << l << " " << h << " --> "; + if (m_lo == m_hi) { set_value(m_lo, l); set_value(m_hi, h); @@ -591,19 +523,25 @@ namespace bv { set_value(m_lo, l); set_value(m_hi, h); } - else if (old_lo + 1 == l) { + else if (old_lo + 1 == l) + set_value(m_lo, l); + else if (old_hi == h + 1) + set_value(m_hi, h); + else if (old_hi == h && old_lo < l) set_value(m_lo, l); - } - else if (old_hi == h + 1) { + else if (old_lo == l && h < old_hi) set_value(m_hi, h); - } } } SASSERT(!has_overflow(m_lo)); SASSERT(!has_overflow(m_hi)); + //verbose_stream() << *this << " --> "; + tighten_range(); + + //verbose_stream() << *this << "\n"; SASSERT(well_formed()); } @@ -621,59 +559,76 @@ namespace bv { // lo + 1 = hi -> set bits = lo // lo < hi, set most significant bits based on hi // - void sls_valuation::tighten_range() { - - if (m_lo == m_hi) - return; - bvect hi1(nw); - hi1.set_bw(bw); - m_hi.copy_to(nw, hi1); - sub1(hi1); - unsigned lo_index = 0, hi_index = 0; + unsigned sls_valuation::diff_index(bvect const& a) const { + unsigned index = 0; for (unsigned i = nw; i-- > 0; ) { - auto lo_diff = (fixed[i] & (m_bits[i] ^ m_lo[i])); - if (lo_diff != 0 && lo_index == 0) - lo_index = 1 + i * 8 * sizeof(digit_t) + log2(lo_diff); - auto hi_diff = (fixed[i] & (m_bits[i] ^ hi1[i])); - if (hi_diff != 0 && hi_index == 0) - hi_index = 1 + i * 8 * sizeof(digit_t) + log2(hi_diff); + auto diff = fixed[i] & (m_bits[i] ^ a[i]); + if (diff != 0 && index == 0) + index = 1 + i * 8 * sizeof(digit_t) + log2(diff); } + return index; + } + void sls_valuation::inf_feasible(bvect& a) const { + unsigned lo_index = diff_index(a); + if (lo_index != 0) { lo_index--; - SASSERT(m_lo.get(lo_index) != m_bits.get(lo_index)); + SASSERT(a.get(lo_index) != m_bits.get(lo_index)); SASSERT(fixed.get(lo_index)); for (unsigned i = 0; i <= lo_index; ++i) { if (!fixed.get(i)) - m_lo.set(i, false); + a.set(i, false); else if (fixed.get(i)) - m_lo.set(i, m_bits.get(i)); + a.set(i, m_bits.get(i)); + } + if (!a.get(lo_index)) { + for (unsigned i = lo_index + 1; i < bw; ++i) + if (!fixed.get(i) && !a.get(i)) { + a.set(i, true); + break; + } } - for (unsigned i = lo_index + 1; i < bw; ++i) - if (!fixed.get(i) && !m_lo.get(i)) { - m_lo.set(i, true); - break; - } } + } + + void sls_valuation::sup_feasible(bvect& a) const { + unsigned hi_index = diff_index(a); if (hi_index != 0) { hi_index--; - SASSERT(hi1.get(hi_index) != m_bits.get(hi_index)); + SASSERT(a.get(hi_index) != m_bits.get(hi_index)); SASSERT(fixed.get(hi_index)); for (unsigned i = 0; i <= hi_index; ++i) { if (!fixed.get(i)) - hi1.set(i, true); + a.set(i, true); else if (fixed.get(i)) - hi1.set(i, m_bits.get(i)); + a.set(i, m_bits.get(i)); + } + if (a.get(hi_index)) { + for (unsigned i = hi_index + 1; i < bw; ++i) + if (!fixed.get(i) && a.get(i)) { + a.set(i, false); + break; + } } - for (unsigned i = hi_index + 1; i < bw; ++i) - if (!fixed.get(i) && hi1.get(i)) { - hi1.set(i, false); - break; - } - add1(hi1); - hi1.copy_to(nw, m_hi); } + } + + void sls_valuation::tighten_range() { + + if (m_lo == m_hi) + return; + + inf_feasible(m_lo); + + bvect hi1(nw); + hi1.set_bw(bw); + m_hi.copy_to(nw, hi1); + sub1(hi1); + sup_feasible(hi1); + add1(hi1); + hi1.copy_to(nw, m_hi); if (has_range() && !in_range(m_bits)) m_bits = m_lo; diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index 90794bfe2b1..04efec24a5c 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -113,8 +113,6 @@ namespace bv { unsigned m_signed_prefix = 0; unsigned mask; - bool round_up(bvect& dst) const; - bool round_down(bvect& dst) const; void repair_sign_bits(bvect& dst) const; @@ -141,9 +139,11 @@ namespace bv { SASSERT(in_range(m_bits)); if (fixed.get(i) && get_bit(i) != b) return false; + m_bits.set(i, b); eval.set(i, b); if (in_range(m_bits)) return true; + m_bits.set(i, !b); eval.set(i, !b); return false; } @@ -155,6 +155,9 @@ namespace bv { rational lo() const { return m_lo.get_value(nw); } rational hi() const { return m_hi.get_value(nw); } + unsigned diff_index(bvect const& a) const; + void inf_feasible(bvect& a) const; + void sup_feasible(bvect& a) const; void get(bvect& dst) const; void add_range(rational lo, rational hi); diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index 520682d1bc8..f484372c8bd 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -175,8 +175,8 @@ class bv_sls_tactic : public tactic { m_st.reset(); m_sls->collect_statistics(m_st); report_tactic_progress("Number of flips:", m_sls->get_num_moves()); - IF_VERBOSE(20, verbose_stream() << res << "\n"); - IF_VERBOSE(20, m_sls->display(verbose_stream())); + IF_VERBOSE(10, verbose_stream() << res << "\n"); + IF_VERBOSE(10, m_sls->display(verbose_stream())); if (res == l_true) { if (g->models_enabled()) { From 9a681b1a37afaaceb4e3b70e94ec3c40f85a07d1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 Apr 2024 10:44:53 -0700 Subject: [PATCH 028/414] reorg sls Signed-off-by: Nikolaj Bjorner --- src/ast/bv_decl_plugin.h | 1 + src/ast/sls/bv_sls.cpp | 92 ++++++----------------- src/ast/sls/bv_sls.h | 2 - src/ast/sls/bv_sls_eval.cpp | 135 +++++++++++++++++++++++----------- src/ast/sls/bv_sls_eval.h | 14 ++++ src/ast/sls/bv_sls_fixed.cpp | 51 +++++++++++-- src/ast/sls/bv_sls_fixed.h | 1 + src/ast/sls/sls_valuation.cpp | 10 ++- src/ast/sls/sls_valuation.h | 4 + src/math/lp/nla_core.cpp | 22 ++++-- 10 files changed, 207 insertions(+), 125 deletions(-) diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 137dc754f59..58445afda47 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -438,6 +438,7 @@ class bv_recognizers { MATCH_BINARY(is_bv_xor); MATCH_BINARY(is_bv_nand); MATCH_BINARY(is_bv_nor); + MATCH_BINARY(is_concat); MATCH_BINARY(is_bv_uremi); diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index f799d3cfee1..df741dac34b 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -56,18 +56,13 @@ namespace bv { } } for (auto* t : m_terms.terms()) { - if (t && !re_eval_is_correct(t)) + if (t && !m_eval.re_eval_is_correct(t)) m_repair_roots.insert(t->get_id()); } } void sls::init_repair_goal(app* t) { - if (m.is_bool(t)) - m_eval.set(t, m_eval.bval1(t)); - else if (bv.is_bv(t)) { - auto& v = m_eval.wval(t); - v.bits().copy_to(v.nw, v.eval); - } + m_eval.init_eval(t); } void sls::init_repair_candidates() { @@ -149,7 +144,7 @@ namespace bv { SASSERT(m_eval.bval0(e)); return { true, e }; } - if (!re_eval_is_correct(e)) { + if (!m_eval.re_eval_is_correct(e)) { init_repair_goal(e); return { true, e }; } @@ -162,7 +157,7 @@ namespace bv { lbool sls::search1() { // init and init_eval were invoked unsigned n = 0; - for (; n < m_config.m_max_repairs && m.inc(); ) { + for (; n < m_config.m_max_repairs && m.inc(); ++n) { auto [down, e] = next_to_repair(); if (!e) return l_true; @@ -170,10 +165,8 @@ namespace bv { IF_VERBOSE(20, trace_repair(down, e)); ++m_stats.m_moves; - if (down) { - try_repair_down(e); - ++n; - } + if (down) + try_repair_down(e); else try_repair_up(e); } @@ -219,12 +212,7 @@ namespace bv { void sls::try_repair_down(app* e) { unsigned n = e->get_num_args(); if (n == 0) { - if (m.is_bool(e)) { - m_eval.set(e, m_eval.bval1(e)); - } - else { - VERIFY(m_eval.wval(e).commit_eval()); - } + m_eval.commit_eval(e); IF_VERBOSE(3, verbose_stream() << "done\n"); for (auto p : m_terms.parents(e)) @@ -271,44 +259,24 @@ namespace bv { m_repair_roots.insert(e->get_id()); else if (!m_eval.repair_up(e)) { IF_VERBOSE(2, verbose_stream() << "repair-up "; trace_repair(true, e)); - init_repair(); + if (m_rand(10) != 0) { + m_eval.set_random(e); + + m_repair_roots.insert(e->get_id()); + } + else + init_repair(); } else { - if (!eval_is_correct(e)) { + if (!m_eval.eval_is_correct(e)) { verbose_stream() << "incorrect eval #" << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; } - SASSERT(eval_is_correct(e)); + SASSERT(m_eval.eval_is_correct(e)); for (auto p : m_terms.parents(e)) m_repair_up.insert(p->get_id()); } } - bool sls::eval_is_correct(app* e) { - if (!m_eval.can_eval1(e)) - return false; - if (m.is_bool(e)) - return m_eval.bval0(e) == m_eval.bval1(e); - if (bv.is_bv(e)) { - auto const& v = m_eval.wval(e); - return v.eval == v.bits(); - } - UNREACHABLE(); - return false; - } - - - bool sls::re_eval_is_correct(app* e) { - if (!m_eval.can_eval1(e)) - return false; - if (m.is_bool(e)) - return m_eval.bval0(e) == m_eval.bval1(e); - if (bv.is_bv(e)) { - auto const& v = m_eval.eval(e); - return v.eval == v.bits(); - } - UNREACHABLE(); - return false; - } model_ref sls::get_model() { if (m_engine_model) @@ -317,24 +285,17 @@ namespace bv { model_ref mdl = alloc(model, m); auto& terms = m_eval.sort_assertions(m_terms.assertions()); for (expr* e : terms) { - if (!re_eval_is_correct(to_app(e))) { + if (!m_eval.re_eval_is_correct(to_app(e))) { verbose_stream() << "missed evaluation #" << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; - if (bv.is_bv(e)) { - auto const& v = m_eval.wval(e); - verbose_stream() << v << "\n" << v.eval << "\n"; - } + m_eval.display_value(verbose_stream(), e) << "\n"; } if (!is_uninterp_const(e)) continue; auto f = to_app(e)->get_decl(); - if (m.is_bool(e)) - mdl->register_decl(f, m.mk_bool_val(m_eval.bval0(e))); - else if (bv.is_bv(e)) { - auto const& v = m_eval.wval(e); - rational n = v.get_value(); - mdl->register_decl(f, bv.mk_numeral(n, v.bw)); - } + auto v = m_eval.get_value(to_app(e)); + if (v) + mdl->register_decl(f, v); } terms.reset(); return mdl; @@ -350,10 +311,7 @@ namespace bv { out << "u "; if (m_repair_roots.contains(e->get_id())) out << "r "; - if (bv.is_bv(e)) - out << m_eval.wval(e); - else if (m.is_bool(e)) - out << (m_eval.bval0(e)?"T":"F"); + m_eval.display_value(out, e); out << "\n"; } terms.reset(); @@ -372,12 +330,10 @@ namespace bv { } std::ostream& sls::trace_repair(bool down, expr* e) { - verbose_stream() << (down ? "d #" : "u #") + verbose_stream() << (down ? "d #" : "u #") << e->get_id() << ": " << mk_bounded_pp(e, m, 1) << " "; - if (bv.is_bv(e)) verbose_stream() << m_eval.wval(e) << " " << (m_eval.is_fixed0(e) ? "fixed " : " "); - if (m.is_bool(e)) verbose_stream() << m_eval.bval0(e) << " "; - verbose_stream() << "\n"; + m_eval.display_value(verbose_stream(), e) << "\n"; return verbose_stream(); } diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index 01ccbc41f5d..fe31125a790 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -56,8 +56,6 @@ namespace bv { std::pair next_to_repair(); - bool eval_is_correct(app* e); - bool re_eval_is_correct(app* e); void init_repair_goal(app* e); void try_repair_down(app* e); void try_repair_up(app* e); diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index b69cec867d0..486b3344565 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -914,15 +914,14 @@ namespace bv { bool sls_eval::try_repair_eq(bool is_true, bvval& a, bvval const& b) { if (is_true) { - if (m_rand() % 20 != 0) + if (m_rand(20) != 0) if (a.try_set(b.bits())) return true; - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); + return a.set_random(m_rand); } else { - bool try_above = m_rand() % 2 == 0; + bool try_above = m_rand(2) == 0; if (try_above) { a.set_add(m_tmp, b.bits(), m_one); if (!a.is_zero(m_tmp) && a.set_random_at_least(m_tmp, m_tmp2, m_rand)) @@ -1018,17 +1017,16 @@ namespace bv { // If this fails, set a to a random value // bool sls_eval::try_repair_add(bvect const& e, bvval& a, bvval const& b) { - if (m_rand() % 20 != 0) { + if (m_rand(20) != 0) { a.set_sub(m_tmp, e, b.bits()); if (a.try_set(m_tmp)) return true; } - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); + return a.set_random(m_rand); } bool sls_eval::try_repair_sub(bvect const& e, bvval& a, bvval & b, unsigned i) { - if (m_rand() % 20 != 0) { + if (m_rand(20) != 0) { if (i == 0) // e = a - b -> a := e + b a.set_add(m_tmp, e, b.bits()); @@ -1039,8 +1037,7 @@ namespace bv { return true; } // fall back to a random value - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); + return a.set_random(m_rand); } /** @@ -1058,15 +1055,11 @@ namespace bv { return a.set_repair(random_bool(), m_tmp); } - if (b.is_zero()) { - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); - } - - if (m_rand() % 20 == 0) { - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); - } + if (b.is_zero()) + return a.set_random(m_rand); + + if (m_rand(20) == 0) + return a.set_random(m_rand); #if 0 verbose_stream() << "solve for " << e << "\n"; @@ -1096,7 +1089,7 @@ namespace bv { b.shift_right(y, parity_b); #if 0 for (unsigned i = parity_b; i < b.bw; ++i) - y.set(i, m_rand() % 2 == 0); + y.set(i, m_rand(2) == 0); #endif } @@ -1151,8 +1144,7 @@ namespace bv { if (a.set_repair(random_bool(), m_tmp)) return true; - a.get_variant(m_tmp, m_rand); - return a.set_repair(random_bool(), m_tmp); + return a.set_random(m_rand); } bool sls_eval::try_repair_bnot(bvect const& e, bvval& a) { @@ -1236,7 +1228,7 @@ namespace bv { bool sls_eval::try_repair_sle(bvval& a, bvect const& b, bvect const& p2) { bool r = false; if (b < p2) { - bool coin = m_rand() % 2 == 0; + bool coin = m_rand(2) == 0; if (coin) r = a.set_random_at_least(p2, m_tmp3, m_rand); if (!r) @@ -1268,7 +1260,7 @@ namespace bv { r = a.set_random_in_range(b, p2_1, m_tmp3, m_rand); else { // random b <= x or x < p2 - bool coin = m_rand() % 2 == 0; + bool coin = m_rand(2) == 0; if (coin) r = a.set_random_at_most(p2_1, m_tmp3, m_rand); if (!r) @@ -1657,12 +1649,9 @@ namespace bv { return a.set_repair(true, m_tmp3); } else { - if (a.is_one(e) && a.is_zero()) { - for (unsigned i = 0; i < a.nw; ++i) - m_tmp[i] = random_bits(); - a.clear_overflow_bits(m_tmp); - return b.set_repair(true, m_tmp); - } + if (a.is_one(e) && a.is_zero()) + return b.set_random(m_rand); + if (a.is_one(e)) { a.set(m_tmp, a.bits()); return b.set_repair(true, m_tmp); @@ -1858,8 +1847,7 @@ namespace bv { m_tmp.set(i, e.get(i)); b.clear_overflow_bits(m_tmp); r = b.try_set(m_tmp); - } - //verbose_stream() << e << " := " << a << " " << b << "\n"; + } return r; } @@ -1869,15 +1857,15 @@ namespace bv { // set a outside of [hi:lo] to random values with preference to 0 or 1 bits // bool sls_eval::try_repair_extract(bvect const& e, bvval& a, unsigned lo) { - if (m_rand() % m_config.m_prob_randomize_extract <= 100) { + if (m_rand(m_config.m_prob_randomize_extract) <= 100) { a.get_variant(m_tmp, m_rand); - if (0 == (m_rand() % 2)) { - auto bit = 0 == (m_rand() % 2); + if (0 == (m_rand(2))) { + auto bit = 0 == (m_rand(2)); if (!a.try_set_range(m_tmp, 0, lo, bit)) a.try_set_range(m_tmp, 0, lo, !bit); } - if (0 == (m_rand() % 2)) { - auto bit = 0 == (m_rand() % 2); + if (0 == (m_rand(2))) { + auto bit = 0 == (m_rand(2)); if (!a.try_set_range(m_tmp, lo + e.bw, a.bw, bit)) a.try_set_range(m_tmp, lo + e.bw, a.bw, !bit); } @@ -1888,10 +1876,7 @@ namespace bv { m_tmp.set(i + lo, e.get(i)); if (a.try_set(m_tmp)) return true; - a.get_variant(m_tmp, m_rand); - bool res = a.set_repair(random_bool(), m_tmp); - // verbose_stream() << "try set " << res << " " << m_tmp[0] << " " << a << "\n"; - return res; + return a.set_random(m_rand); } void sls_eval::set_div(bvect const& a, bvect const& b, unsigned bw, @@ -1945,6 +1930,66 @@ namespace bv { return *m_values[e->get_id()]; } + void sls_eval::init_eval(app* t) { + if (m.is_bool(t)) + set(t, bval1(t)); + else if (bv.is_bv(t)) { + auto& v = wval(t); + v.bits().copy_to(v.nw, v.eval); + } + } + + void sls_eval::commit_eval(app* e) { + if (m.is_bool(e)) { + set(e, bval1(e)); + } + else { + VERIFY(wval(e).commit_eval()); + } + } + + void sls_eval::set_random(app* e) { + if (bv.is_bv(e)) + eval(e).set_random(m_rand); + } + + bool sls_eval::eval_is_correct(app* e) { + if (!can_eval1(e)) + return false; + if (m.is_bool(e)) + return bval0(e) == bval1(e); + if (bv.is_bv(e)) { + auto const& v = wval(e); + return v.eval == v.bits(); + } + UNREACHABLE(); + return false; + } + + bool sls_eval::re_eval_is_correct(app* e) { + if (!can_eval1(e)) + return false; + if (m.is_bool(e)) + return bval0(e) ==bval1(e); + if (bv.is_bv(e)) { + auto const& v = eval(e); + return v.eval == v.bits(); + } + UNREACHABLE(); + return false; + } + + expr_ref sls_eval::get_value(app* e) { + if (m.is_bool(e)) + return expr_ref(m.mk_bool_val(bval0(e)), m); + else if (bv.is_bv(e)) { + auto const& v = wval(e); + rational n = v.get_value(); + return expr_ref(bv.mk_numeral(n, v.bw), m); + } + return expr_ref(m); + } + std::ostream& sls_eval::display(std::ostream& out, expr_ref_vector const& es) { auto& terms = sort_assertions(es); for (expr* e : terms) { @@ -1960,4 +2005,12 @@ namespace bv { terms.reset(); return out; } + + std::ostream& sls_eval::display_value(std::ostream& out, expr* e) { + if (bv.is_bv(e)) + return out << wval(e); + if (m.is_bool(e)) + return out << (bval0(e)?"T":"F"); + return out << "?"; + } } diff --git a/src/ast/sls/bv_sls_eval.h b/src/ast/sls/bv_sls_eval.h index ebd36048184..89cd3f4cf03 100644 --- a/src/ast/sls/bv_sls_eval.h +++ b/src/ast/sls/bv_sls_eval.h @@ -157,6 +157,18 @@ namespace bv { sls_valuation& eval(app* e) const; + void commit_eval(app* e); + + void init_eval(app* e); + + void set_random(app* e); + + bool eval_is_correct(app* e); + + bool re_eval_is_correct(app* e); + + expr_ref get_value(app* e); + /** * Override evaluaton. */ @@ -178,5 +190,7 @@ namespace bv { std::ostream& display(std::ostream& out, expr_ref_vector const& es); + + std::ostream& display_value(std::ostream& out, expr* e); }; } diff --git a/src/ast/sls/bv_sls_fixed.cpp b/src/ast/sls/bv_sls_fixed.cpp index 18518ed09bd..be587a8bda9 100644 --- a/src/ast/sls/bv_sls_fixed.cpp +++ b/src/ast/sls/bv_sls_fixed.cpp @@ -38,8 +38,8 @@ namespace bv { else ; } - ev.m_todo.reset(); init_ranges(es); + ev.m_todo.reset(); } @@ -49,6 +49,44 @@ namespace bv { if (is_app(e)) init_range(to_app(e), sign); } + + for (expr* e : ev.m_todo) + propagate_range_up(e); + } + + void sls_fixed::propagate_range_up(expr* e) { + expr* t, * s; + rational v; + if (bv.is_concat(e, t, s)) { + auto& val = wval(s); + if (val.lo() != val.hi() && (val.lo() < val.hi() || val.hi() == 0)) + // lo <= e + add_range(e, val.lo(), rational::zero(), false); + auto valt = wval(t); +#if 0 + if (val.lo() < val.hi()) + // e < (2^|s|) * hi + add_range(e, rational::zero(), val.hi() * rational::power_of_two(bv.get_bv_size(s)), false); +#endif + } + else if (bv.is_bv_add(e, s, t) && bv.is_numeral(s, v)) { + auto& val = wval(t); + if (val.lo() != val.hi()) + add_range(e, v + val.lo(), v + val.hi(), false); + } + else if (bv.is_bv_add(e, t, s) && bv.is_numeral(s, v)) { + auto& val = wval(t); + if (val.lo() != val.hi()) + add_range(e, v + val.lo(), v + val.hi(), false); + } + // x in [1, 4[ => -x in [-3, 0[ + // x in [lo, hi[ => -x in [-hi + 1, -lo + 1[ + else if (bv.is_bv_mul(e, s, t) && bv.is_numeral(s, v) && + v + 1 == rational::power_of_two(bv.get_bv_size(e))) { + auto& val = wval(t); + if (val.lo() != val.hi()) + add_range(e, -val.hi() + 1, - val.lo() + 1, false); + } } // s <=s t <=> s + K <= t + K, K = 2^{bw-1} @@ -117,6 +155,7 @@ namespace bv { val.tighten_range(); return true; } + return false; } @@ -138,9 +177,8 @@ namespace bv { init_eq(s, b, false); return true; } - if (!sign && bv.is_concat(t) && to_app(t)->get_num_args() == 2) { - auto x = to_app(t)->get_arg(0); - auto y = to_app(t)->get_arg(1); + expr* x, * y; + if (!sign && bv.is_concat(t, x, y)) { auto sz = bv.get_bv_size(y); auto k = rational::power_of_two(sz); init_eq(y, mod(a, k), false); @@ -206,9 +244,8 @@ namespace bv { if (sign) std::swap(lo, hi); v.add_range(lo, hi); - if (v.lo() == 0 && bv.is_concat(e) && to_app(e)->get_num_args() == 2) { - auto x = to_app(e)->get_arg(0); - auto y = to_app(e)->get_arg(1); + expr* x, * y; + if (v.lo() == 0 && bv.is_concat(e, x, y)) { auto sz = bv.get_bv_size(y); auto k = rational::power_of_two(sz); lo = v.lo(); diff --git a/src/ast/sls/bv_sls_fixed.h b/src/ast/sls/bv_sls_fixed.h index 73dd5a52c33..2e88484c55d 100644 --- a/src/ast/sls/bv_sls_fixed.h +++ b/src/ast/sls/bv_sls_fixed.h @@ -31,6 +31,7 @@ namespace bv { void init_ranges(expr_ref_vector const& es); bool init_range(app* e, bool sign); + void propagate_range_up(expr* e); bool init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign); void get_offset(expr* e, expr*& x, rational& offset); bool init_eq(expr* e, rational const& v, bool sign); diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 1f3674e9329..68143e7b75a 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -153,6 +153,7 @@ namespace bv { m_lo.set_bw(bw); m_hi.set_bw(bw); m_bits.set_bw(bw); + m_tmp.set_bw(bw); fixed.set_bw(bw); eval.set_bw(bw); // have lo, hi bits, fixed point to memory allocated within this of size num_bytes each allocated @@ -440,6 +441,11 @@ namespace bv { clear_overflow_bits(dst); } + bool sls_valuation::set_random(random_gen& r) { + get_variant(m_tmp, r); + return set_repair(r(2) == 0, m_tmp); + } + void sls_valuation::repair_sign_bits(bvect& dst) const { if (m_signed_prefix == 0) return; @@ -489,7 +495,7 @@ namespace bv { } void sls_valuation::add_range(rational l, rational h) { - return; + //return; //verbose_stream() << *this << " " << l << " " << h << " --> \n"; l = mod(l, rational::power_of_two(bw)); @@ -622,7 +628,7 @@ namespace bv { inf_feasible(m_lo); - bvect hi1(nw); + bvect& hi1 = m_tmp; hi1.set_bw(bw); m_hi.copy_to(nw, hi1); sub1(hi1); diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index 04efec24a5c..e4828302e0d 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -110,6 +110,7 @@ namespace bv { protected: bvect m_bits; bvect m_lo, m_hi; // range assignment to bit-vector, as wrap-around interval + bvect m_tmp; unsigned m_signed_prefix = 0; unsigned mask; @@ -123,6 +124,7 @@ namespace bv { bvect fixed; // bit assignment and don't care bit bvect eval; // current evaluation + sls_valuation(unsigned bw); void set_bw(unsigned bw); @@ -231,12 +233,14 @@ namespace bv { bool set_repair(bool try_down, bvect& dst); void set_random_above(bvect& dst, random_gen& r); void set_random_below(bvect& dst, random_gen& r); + bool set_random(random_gen& r); void round_down(bvect& dst, std::function const& is_feasible); void round_up(bvect& dst, std::function const& is_feasible); static digit_t random_bits(random_gen& r); void get_variant(bvect& dst, random_gen& r) const; + bool try_set(bvect const& src) { if (!can_set(src)) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 675bf502231..44f56efcd77 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1062,6 +1062,8 @@ new_lemma::~new_lemma() { if (current().is_conflict()) { c.m_conflicts++; } + IF_VERBOSE(4, verbose_stream() << name << "\n"); + IF_VERBOSE(4, verbose_stream() << *this << "\n"); TRACE("nla_solver", tout << name << " " << (++i) << "\n" << *this; ); } @@ -1519,6 +1521,12 @@ lbool core::check() { if (!m_lemmas.empty() || !m_literals.empty() || m_check_feasible) return l_false; } + + if (no_effect() && params().arith_nl_nra()) { + ret = m_nra.check(); + lp_settings().stats().m_nra_calls++; + } + if (no_effect() && should_run_bounded_nlsat()) ret = bounded_nlsat(); @@ -1530,12 +1538,16 @@ lbool core::check() { m_basics.basic_lemma(false); if (no_effect()) - m_divisions.check(); + m_divisions.check(); + - if (no_effect()) { - std::function check1 = [&]() { m_order.order_lemma(); }; - std::function check2 = [&]() { m_monotone.monotonicity_lemma(); }; - std::function check3 = [&]() { m_tangents.tangent_lemma(); }; + if (false && no_effect()) { + std::function check1 = [&]() { m_order.order_lemma(); + }; + std::function check2 = [&]() { m_monotone.monotonicity_lemma(); + }; + std::function check3 = [&]() { m_tangents.tangent_lemma(); + }; std::pair> checks[] = { { 6, check1 }, From 8d0e66b3e371b2e0d54070e98813033c54bacb24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 Apr 2024 11:16:34 -0700 Subject: [PATCH 029/414] fix regression introduced when testing Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 44f56efcd77..216e0358319 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1522,11 +1522,6 @@ lbool core::check() { return l_false; } - if (no_effect() && params().arith_nl_nra()) { - ret = m_nra.check(); - lp_settings().stats().m_nra_calls++; - } - if (no_effect() && should_run_bounded_nlsat()) ret = bounded_nlsat(); From 7b8980f82d95471bddd6d5fe4a0ba8520956964e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 Apr 2024 11:17:03 -0700 Subject: [PATCH 030/414] fix regression introduced when testing Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 216e0358319..449ecad4c33 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1536,7 +1536,7 @@ lbool core::check() { m_divisions.check(); - if (false && no_effect()) { + if (no_effect()) { std::function check1 = [&]() { m_order.order_lemma(); }; std::function check2 = [&]() { m_monotone.monotonicity_lemma(); From 974ea7b68d925e5404e16402c71bc4ca6832b720 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 10 Apr 2024 17:57:14 -0700 Subject: [PATCH 031/414] maintain ownership of dependency --- src/ast/simplifiers/extract_eqs.h | 5 +++-- src/ast/sls/bv_sls_eval.cpp | 6 +----- src/math/lp/lar_solver.h | 1 + src/tactic/portfolio/smt_strategic_solver.cpp | 10 ++++++++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ast/simplifiers/extract_eqs.h b/src/ast/simplifiers/extract_eqs.h index 724425d6afd..b88a3e8a5de 100644 --- a/src/ast/simplifiers/extract_eqs.h +++ b/src/ast/simplifiers/extract_eqs.h @@ -31,8 +31,9 @@ namespace euf { expr* orig; // original expression that encoded equation app* var; // isolated variable expr_ref term; // defined term - expr_dependency* dep; - dependent_eq(expr* orig, app* var, expr_ref const& term, expr_dependency* d) : orig(orig), var(var), term(term), dep(d) {} + expr_dependency_ref dep; + dependent_eq(expr* orig, app* var, expr_ref const& term, expr_dependency* d) : + orig(orig), var(var), term(term), dep(d, term.get_manager()) {} }; typedef vector dep_eq_vector; diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index 486b3344565..d7ce8e70338 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -1996,11 +1996,7 @@ namespace bv { out << e->get_id() << ": " << mk_bounded_pp(e, m, 1) << " "; if (is_fixed0(e)) out << "f "; - if (bv.is_bv(e)) - out << wval(e); - else if (m.is_bool(e)) - out << (bval0(e) ? "T" : "F"); - out << "\n"; + display_value(out, e) << "\n"; } terms.reset(); return out; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index ec40fcb2409..f223b5cc5a4 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -393,6 +393,7 @@ class lar_solver : public column_namer { bool external_is_used(unsigned) const; void pop(unsigned k); unsigned num_scopes() const { return m_trail.get_num_scopes(); } + trail_stack& trail() { return m_trail; } bool compare_values(lpvar j, lconstraint_kind kind, const mpq& right_side); lpvar add_term(const vector>& coeffs, unsigned ext_i); void register_existing_terms(); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 138c6e6465a..09e7aa047b8 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -49,8 +49,18 @@ Module Name: #include "parsers/smt2/smt2parser.h" #include "sat/sat_params.hpp" +tactic* mk_tactic_for_logic(ast_manager& m, params_ref const& p, symbol const& logic); +class smt_nested_solver_factory : public solver_factory { +public: + solver* operator()(ast_manager& m, params_ref const& p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const& logic) override { + auto t = mk_tactic_for_logic(m, p, logic); + auto s = mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); + return s; + } +}; + tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { if (logic=="QF_UF") return mk_qfuf_tactic(m, p); From 510534dbd40bf0bbb6c2f37eaf85ebc9b49bf499 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 10 Apr 2024 19:09:30 -0700 Subject: [PATCH 032/414] cleanup --- src/ast/sls/bv_sls_eval.cpp | 42 ++++++++++++++--------------------- src/ast/sls/sls_valuation.cpp | 33 +++++++++++++-------------- src/ast/sls/sls_valuation.h | 6 ++--- 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index d7ce8e70338..d61a831d375 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -924,15 +924,15 @@ namespace bv { bool try_above = m_rand(2) == 0; if (try_above) { a.set_add(m_tmp, b.bits(), m_one); - if (!a.is_zero(m_tmp) && a.set_random_at_least(m_tmp, m_tmp2, m_rand)) + if (!a.is_zero(m_tmp) && a.set_random_at_least(m_tmp, m_rand)) return true; } a.set_sub(m_tmp, b.bits(), m_one); - if (!a.is_zero(m_tmp) && a.set_random_at_most(m_tmp, m_tmp2, m_rand)) + if (!a.is_zero(m_tmp) && a.set_random_at_most(m_tmp, m_rand)) return true; if (!try_above) { a.set_add(m_tmp, b.bits(), m_one); - if (!a.is_zero(m_tmp) && a.set_random_at_least(m_tmp, m_tmp2, m_rand)) + if (!a.is_zero(m_tmp) && a.set_random_at_least(m_tmp, m_rand)) return true; } return false; @@ -1007,7 +1007,6 @@ namespace bv { bool sls_eval::try_repair_bxor(bvect const& e, bvval& a, bvval const& b) { for (unsigned i = 0; i < a.nw; ++i) m_tmp[i] = e[i] ^ b.bits()[i]; - a.clear_overflow_bits(m_tmp); return a.set_repair(random_bool(), m_tmp); } @@ -1149,7 +1148,7 @@ namespace bv { bool sls_eval::try_repair_bnot(bvect const& e, bvval& a) { for (unsigned i = 0; i < a.nw; ++i) - m_tmp[i] = ~e[i]; + m_tmp[i] = ~e[i]; a.clear_overflow_bits(m_tmp); return a.try_set(m_tmp); } @@ -1230,14 +1229,14 @@ namespace bv { if (b < p2) { bool coin = m_rand(2) == 0; if (coin) - r = a.set_random_at_least(p2, m_tmp3, m_rand); + r = a.set_random_at_least(p2, m_rand); if (!r) - r = a.set_random_at_most(b, m_tmp3, m_rand); + r = a.set_random_at_most(b, m_rand); if (!coin && !r) - r = a.set_random_at_least(p2, m_tmp3, m_rand); + r = a.set_random_at_least(p2, m_rand); } else - r = a.set_random_in_range(p2, b, m_tmp3, m_rand); + r = a.set_random_in_range(p2, b, m_rand); return r; } @@ -1257,16 +1256,16 @@ namespace bv { bool r = false; if (p2 < b) // random b <= x < p2 - r = a.set_random_in_range(b, p2_1, m_tmp3, m_rand); + r = a.set_random_in_range(b, p2_1, m_rand); else { // random b <= x or x < p2 bool coin = m_rand(2) == 0; if (coin) - r = a.set_random_at_most(p2_1, m_tmp3, m_rand); + r = a.set_random_at_most(p2_1,m_rand); if (!r) - r = a.set_random_at_least(b, m_tmp3, m_rand); + r = a.set_random_at_least(b, m_rand); if (!r && !coin) - r = a.set_random_at_most(p2_1, m_tmp3, m_rand); + r = a.set_random_at_most(p2_1, m_rand); } p2_1.set_bw(0); return r; @@ -1282,28 +1281,28 @@ namespace bv { bool sls_eval::try_repair_ule(bool e, bvval& a, bvval const& b) { if (e) { // a <= t - return a.set_random_at_most(b.bits(), m_tmp, m_rand); + return a.set_random_at_most(b.bits(), m_rand); } else { // a > t a.set_add(m_tmp, b.bits(), m_one); if (a.is_zero(m_tmp)) return false; - return a.set_random_at_least(m_tmp, m_tmp2, m_rand); + return a.set_random_at_least(m_tmp, m_rand); } } bool sls_eval::try_repair_uge(bool e, bvval& a, bvval const& b) { if (e) { // a >= t - return a.set_random_at_least(b.bits(), m_tmp, m_rand); + return a.set_random_at_least(b.bits(), m_rand); } else { // a < t if (b.is_zero()) return false; a.set_sub(m_tmp, b.bits(), m_one); - return a.set_random_at_most(m_tmp, m_tmp2, m_rand); + return a.set_random_at_most(m_tmp, m_rand); } } @@ -1377,7 +1376,6 @@ namespace bv { unsigned n = b.bits().to_nat(e.bw); for (unsigned i = e.bw; i-- > e.bw - n;) t.set(i, a.get_bit(i)); - a.clear_overflow_bits(t); if (a.set_repair(random_bool(), t)) return true; } @@ -1527,7 +1525,6 @@ namespace bv { t[i] = a.bits()[i]; t.set(b.bw - 1, a.is_ones(e)); } - a.clear_overflow_bits(t); if (a.set_repair(random_bool(), t)) return true; } @@ -1541,13 +1538,10 @@ namespace bv { a.get_variant(t, m_rand); t.set(b.bw - 1, a.is_ones(e)); } - a.clear_overflow_bits(t); if (a.set_repair(random_bool(), t)) return true; } - - a.get_variant(t, m_rand); - return a.set_repair(random_bool(), t); + return a.set_random(m_rand); } /* @@ -1645,7 +1639,6 @@ namespace bv { m_tmp2.set(b.msb(m_tmp2), false); while (a.set_add(m_tmp3, m_tmp, m_tmp2)) m_tmp2.set(b.msb(m_tmp2), false); - a.clear_overflow_bits(m_tmp3); return a.set_repair(true, m_tmp3); } else { @@ -1723,7 +1716,6 @@ namespace bv { m_tmp[i] = random_bits(); a.set_sub(m_tmp2, a.bits(), e); set_div(m_tmp2, m_tmp, a.bw, m_tmp3, m_tmp4); - a.clear_overflow_bits(m_tmp3); return b.set_repair(random_bool(), m_tmp3); } } diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 68143e7b75a..c46b2ef77f3 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -244,34 +244,35 @@ namespace bv { return true; } - bool sls_valuation::set_random_at_most(bvect const& src, bvect& tmp, random_gen& r) { - if (!get_at_most(src, tmp)) + bool sls_valuation::set_random_at_most(bvect const& src, random_gen& r) { + if (!get_at_most(src, m_tmp)) return false; - if (is_zero(tmp) || (0 != r(10))) - return try_set(tmp); + if (is_zero(m_tmp) || (0 != r(10))) + return try_set(m_tmp); // random value below tmp - set_random_below(tmp, r); + set_random_below(m_tmp, r); - return (can_set(tmp) || get_at_most(src, tmp)) && try_set(tmp); + return (can_set(m_tmp) || get_at_most(src, m_tmp)) && try_set(m_tmp); } - bool sls_valuation::set_random_at_least(bvect const& src, bvect& tmp, random_gen& r) { - if (!get_at_least(src, tmp)) + bool sls_valuation::set_random_at_least(bvect const& src, random_gen& r) { + if (!get_at_least(src, m_tmp)) return false; - if (is_ones(tmp) || (0 != r(10))) - return try_set(tmp); + if (is_ones(m_tmp) || (0 != r(10))) + return try_set(m_tmp); // random value at least tmp - set_random_above(tmp, r); + set_random_above(m_tmp, r); - return (can_set(tmp) || get_at_least(src, tmp)) && try_set(tmp); + return (can_set(m_tmp) || get_at_least(src, m_tmp)) && try_set(m_tmp); } - bool sls_valuation::set_random_in_range(bvect const& lo, bvect const& hi, bvect& tmp, random_gen& r) { - if (0 == r() % 2) { + bool sls_valuation::set_random_in_range(bvect const& lo, bvect const& hi, random_gen& r) { + bvect& tmp = m_tmp; + if (0 == r(2)) { if (!get_at_least(lo, tmp)) return false; SASSERT(in_range(tmp)); @@ -342,7 +343,7 @@ namespace bv { bool sls_valuation::set_repair(bool try_down, bvect& dst) { for (unsigned i = 0; i < nw; ++i) dst[i] = (~fixed[i] & dst[i]) | (fixed[i] & m_bits[i]); - + clear_overflow_bits(dst); repair_sign_bits(dst); if (in_range(dst)) { set(eval, dst); @@ -674,6 +675,4 @@ namespace bv { c += get_num_1bits(src[i]); return c == 1; } - - } diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index e4828302e0d..66d7e0281cc 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -226,9 +226,9 @@ namespace bv { bool get_at_most(bvect const& src, bvect& dst) const; bool get_at_least(bvect const& src, bvect& dst) const; - bool set_random_at_most(bvect const& src, bvect& tmp, random_gen& r); - bool set_random_at_least(bvect const& src, bvect& tmp, random_gen& r); - bool set_random_in_range(bvect const& lo, bvect const& hi, bvect& tmp, random_gen& r); + bool set_random_at_most(bvect const& src, random_gen& r); + bool set_random_at_least(bvect const& src, random_gen& r); + bool set_random_in_range(bvect const& lo, bvect const& hi, random_gen& r); bool set_repair(bool try_down, bvect& dst); void set_random_above(bvect& dst, random_gen& r); From c0bdc7cdd6bd22ea7b15bd37882f42555cc0b77d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 Apr 2024 10:49:30 +0200 Subject: [PATCH 033/414] enable concurrent sls with new solver core allow using sls engine (for bit-vectors) with the new core. Examples z3 sat.smt=true tactic.default_tactic=smt /v:1 smt.sls.enable=true smt.bv.solver=0 /st C:\QF_BV_SAT\bench_10.smt2 z3 sat.smt=true tactic.default_tactic=smt /v:1 smt.sls.enable=true smt.bv.solver=2 /st C:\QF_BV_SAT\bench_10.smt2 z3 C:\QF_BV_SAT\bench_11100.smt2 sat.smt=true tactic.default_tactic=smt /v:1 smt.sls.enable=true smt.bv.solver=2 /st --- src/ast/sls/bv_sls.cpp | 16 ++- src/ast/sls/bv_sls.h | 7 +- src/ast/sls/bv_sls_fixed.cpp | 16 +-- src/ast/sls/bv_sls_terms.cpp | 2 + src/ast/sls/sls_valuation.cpp | 9 ++ src/sat/sat_solver.cpp | 3 +- src/sat/sat_solver.h | 2 + src/sat/sat_solver/sat_smt_solver.cpp | 14 ++- src/sat/smt/euf_internalize.cpp | 4 + src/sat/smt/euf_model.cpp | 9 ++ src/sat/smt/euf_solver.cpp | 13 ++- src/sat/smt/euf_solver.h | 6 ++ src/sat/smt/sat_th.h | 3 + src/sat/smt/sls_solver.cpp | 149 +++++++++++++++----------- src/sat/smt/sls_solver.h | 23 +++- src/sat/tactic/goal2sat.cpp | 7 ++ src/smt/params/smt_params.cpp | 2 + src/smt/params/smt_params.h | 1 + src/smt/params/smt_params_helper.pyg | 1 + 19 files changed, 205 insertions(+), 82 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index df741dac34b..9af87d3c5de 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -93,6 +93,18 @@ namespace bv { if (m_to_repair.empty()) return; + + // add fresh units, if any + bool new_assertion = false; + while (m_get_unit) { + auto e = m_get_unit(); + if (!e) + break; + new_assertion = true; + assert_expr(e); + } + if (new_assertion) + init(); std::function eval = [&](expr* e, unsigned i) { unsigned id = e->get_id(); @@ -212,9 +224,7 @@ namespace bv { void sls::try_repair_down(app* e) { unsigned n = e->get_num_args(); if (n == 0) { - m_eval.commit_eval(e); - - IF_VERBOSE(3, verbose_stream() << "done\n"); + m_eval.commit_eval(e); for (auto p : m_terms.parents(e)) m_repair_up.insert(p->get_id()); diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index fe31125a790..690b618bff4 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -53,6 +53,7 @@ namespace bv { sls_engine m_engine; bool m_engine_model = false; bool m_engine_init = false; + std::function m_get_unit; std::pair next_to_repair(); @@ -81,7 +82,6 @@ namespace bv { /* * Invoke init after all expressions are asserted. - * No other expressions can be asserted after init. */ void init(); @@ -91,6 +91,11 @@ namespace bv { */ void init_eval(std::function& eval); + /** + * add callback to retrieve new units + */ + void init_unit(std::function get_unit) { m_get_unit = get_unit; } + /** * Run (bounded) local search to find feasible assignments. */ diff --git a/src/ast/sls/bv_sls_fixed.cpp b/src/ast/sls/bv_sls_fixed.cpp index be587a8bda9..9f897a7bdca 100644 --- a/src/ast/sls/bv_sls_fixed.cpp +++ b/src/ast/sls/bv_sls_fixed.cpp @@ -58,16 +58,16 @@ namespace bv { expr* t, * s; rational v; if (bv.is_concat(e, t, s)) { - auto& val = wval(s); - if (val.lo() != val.hi() && (val.lo() < val.hi() || val.hi() == 0)) + auto& vals = wval(s); + if (vals.lo() != vals.hi() && (vals.lo() < vals.hi() || vals.hi() == 0)) // lo <= e - add_range(e, val.lo(), rational::zero(), false); + add_range(e, vals.lo(), rational::zero(), false); auto valt = wval(t); -#if 0 - if (val.lo() < val.hi()) - // e < (2^|s|) * hi - add_range(e, rational::zero(), val.hi() * rational::power_of_two(bv.get_bv_size(s)), false); -#endif + if (valt.lo() != valt.hi() && (valt.lo() < valt.hi() || valt.hi() == 0)) { + // (2^|s|) * lo <= e < (2^|s|) * hi + auto p = rational::power_of_two(bv.get_bv_size(s)); + add_range(e, valt.lo() * p, valt.hi() * p, false); + } } else if (bv.is_bv_add(e, s, t) && bv.is_numeral(s, v)) { auto& val = wval(t); diff --git a/src/ast/sls/bv_sls_terms.cpp b/src/ast/sls/bv_sls_terms.cpp index 4624ab85c10..ed1bf2396bc 100644 --- a/src/ast/sls/bv_sls_terms.cpp +++ b/src/ast/sls/bv_sls_terms.cpp @@ -206,6 +206,7 @@ namespace bv { m_todo.push_back(arg); } // populate parents + m_parents.reset(); m_parents.reserve(m_terms.size()); for (expr* e : m_terms) { if (!e || !is_app(e)) @@ -213,6 +214,7 @@ namespace bv { for (expr* arg : *to_app(e)) m_parents[arg->get_id()].push_back(e); } + m_assertion_set.reset(); for (auto a : m_assertions) m_assertion_set.insert(a->get_id()); } diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index c46b2ef77f3..99b3921f3bf 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -245,6 +245,7 @@ namespace bv { } bool sls_valuation::set_random_at_most(bvect const& src, random_gen& r) { + m_tmp.set_bw(bw); if (!get_at_most(src, m_tmp)) return false; @@ -639,6 +640,14 @@ namespace bv { if (has_range() && !in_range(m_bits)) m_bits = m_lo; + + if (mod(lo() + 1, rational::power_of_two(bw)) == hi()) + for (unsigned i = 0; i < nw; ++i) + fixed[i] = ~0; + if (lo() < hi() && hi() < rational::power_of_two(bw - 1)) + for (unsigned i = 0; i < bw; ++i) + if (hi() < rational::power_of_two(i)) + fixed.set(i, true); SASSERT(well_formed()); } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 2d296294085..5829b18d11f 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1314,7 +1314,7 @@ namespace sat { } bool solver::should_cancel() { - if (limit_reached() || memory_exceeded()) { + if (limit_reached() || memory_exceeded() || m_solver_canceled) { return true; } if (m_config.m_restart_max <= m_restarts) { @@ -1959,6 +1959,7 @@ namespace sat { void solver::init_search() { m_model_is_current = false; + m_solver_canceled = false; m_phase_counter = 0; m_search_state = s_unsat; m_search_unsat_conflicts = m_config.m_search_unsat_conflicts; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 0361fc157b9..57477f686cf 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -177,6 +177,7 @@ namespace sat { clause_wrapper_vector m_clauses_to_reinit; std::string m_reason_unknown; bool m_trim = false; + bool m_solver_canceled = false; visit_helper m_visited; @@ -287,6 +288,7 @@ namespace sat { random_gen& rand() { return m_rand; } void set_trim() { m_trim = true; } + void set_canceled() { m_solver_canceled = true; } protected: void reset_var(bool_var v, bool ext, bool dvar); diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index ab0e71cc311..19b10eb3e91 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -197,10 +197,16 @@ class sat_smt_solver : public solver { case l_false: extract_core(); break; - default: + default: { + auto* ext = get_euf(); + if (ext && ext->get_sls_model()) { + r = l_true; + break; + } set_reason_unknown(m_solver.get_reason_unknown()); break; } + } return r; } @@ -576,6 +582,7 @@ class sat_smt_solver : public solver { void add_assumption(expr* a) { init_goal2sat(); m_dep.insert(a, m_goal2sat.internalize(a)); + get_euf()->add_assertion(a); } void internalize_assumptions(expr_ref_vector const& asms) { @@ -632,6 +639,11 @@ class sat_smt_solver : public solver { void get_model_core(model_ref & mdl) override { TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); mdl = nullptr; + auto ext = get_euf(); + if (ext) + mdl = ext->get_sls_model(); + if (mdl) + return; if (!m_solver.model_is_current()) return; if (m_fmls.size() > m_qhead) diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index f750f186d56..ebb6e4b85fb 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -525,4 +525,8 @@ namespace euf { return n; } + void solver::add_assertion(expr* f) { + m_assertions.push_back(f); + m_trail.push(push_back_vector(m_assertions)); + } } diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index 2035e16b643..ac7ef1522a2 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -18,6 +18,7 @@ Module Name: #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "sat/smt/euf_solver.h" +#include "sat/smt/sls_solver.h" #include "model/value_factory.h" namespace euf { @@ -67,6 +68,14 @@ namespace euf { m_qmodel = mdl; } + model_ref solver::get_sls_model() { + model_ref mdl; + auto s = get_solver(m.mk_family_id("sls"), nullptr); + if (s) + mdl = dynamic_cast(s)->get_model(); + return mdl; + } + void solver::update_model(model_ref& mdl, bool validate) { TRACE("model", tout << "create model\n";); if (m_qmodel) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index b108430d8a5..efd091f6653 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -28,6 +28,7 @@ Module Name: #include "sat/smt/q_solver.h" #include "sat/smt/fpa_solver.h" #include "sat/smt/dt_solver.h" +#include "sat/smt/sls_solver.h" #include "sat/smt/recfun_solver.h" #include "sat/smt/specrel_solver.h" @@ -54,6 +55,7 @@ namespace euf { m_smt_proof_checker(m, p), m_clause(m), m_expr_args(m), + m_assertions(m), m_values(m) { updt_params(p); @@ -77,6 +79,7 @@ namespace euf { }; m_egraph.set_on_merge(on_merge); } + } void solver::updt_params(params_ref const& p) { @@ -185,7 +188,9 @@ namespace euf { IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n"); } - void solver::init_search() { + void solver::init_search() { + if (get_config().m_sls_enable) + add_solver(alloc(sls::solver, *this)); TRACE("before_search", s().display(tout);); m_reason_unknown.clear(); for (auto* s : m_solvers) @@ -217,7 +222,7 @@ namespace euf { mark_relevant(lit); s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx)); } - + lbool solver::resolve_conflict() { for (auto* s : m_solvers) { lbool r = s->resolve_conflict(); @@ -664,7 +669,9 @@ namespace euf { if (give_up) return sat::check_result::CR_GIVEUP; if (m_qsolver && m_config.m_arith_ignore_int) - return sat::check_result::CR_GIVEUP; + return sat::check_result::CR_GIVEUP; + for (auto s : m_solvers) + s->finalize(); return sat::check_result::CR_DONE; } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 7d2d01473ea..ec89667d5bf 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -154,6 +154,7 @@ namespace euf { svector m_scopes; scoped_ptr_vector m_solvers; ptr_vector m_id2solver; + constraint* m_conflict = nullptr; constraint* m_eq = nullptr; @@ -173,6 +174,7 @@ namespace euf { symbol m_smt = symbol("smt"); expr_ref_vector m_clause; expr_ref_vector m_expr_args; + expr_ref_vector m_assertions; // internalization @@ -482,6 +484,10 @@ namespace euf { bool enable_ackerman_axioms(expr* n) const; bool is_fixed(euf::enode* n, expr_ref& val, sat::literal_vector& explain); + void add_assertion(expr* f); + expr_ref_vector const& get_assertions() { return m_assertions; } + model_ref get_sls_model(); + // relevancy bool relevancy_enabled() const { return m_relevancy.enabled(); } diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index e226566b8f8..cdd1292a330 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -148,6 +148,8 @@ namespace euf { virtual void set_bounds(enode* n) {} + virtual void finalize() {} + }; class th_proof_hint : public sat::proof_hint { @@ -225,6 +227,7 @@ namespace euf { void push() override { m_num_scopes++; } void pop(unsigned n) override; + unsigned random(); }; diff --git a/src/sat/smt/sls_solver.cpp b/src/sat/smt/sls_solver.cpp index ae8620e28f2..e12ff5ba7d6 100644 --- a/src/sat/smt/sls_solver.cpp +++ b/src/sat/smt/sls_solver.cpp @@ -23,38 +23,79 @@ Module Name: namespace sls { solver::solver(euf::solver& ctx): - th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls")) {} + th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls")), + m_units(m) {} solver::~solver() { - if (m_bvsls) { - m_bvsls->cancel(); + finalize(); + } + + void solver::finalize() { + if (!m_completed && m_bvsls) { + m_bvsls->cancel(); m_thread.join(); + m_bvsls->collect_statistics(m_st); + m_bvsls = nullptr; } } - void solver::push_core() { - if (s().scope_lvl() == s().search_lvl() + 1) - init_local_search(); + sat::check_result solver::check() { + + return sat::check_result::CR_DONE; } - - void solver::pop_core(unsigned n) { - if (s().scope_lvl() - n <= s().search_lvl()) - sample_local_search(); + + void solver::simplify() { } - void solver::simplify() { - + bool solver::unit_propagate() { + force_push(); + sample_local_search(); + return false; + } + + bool solver::is_unit(expr* e) { + if (!e) + return false; + m.is_not(e, e); + if (is_uninterp_const(e)) + return true; + bv_util bu(m); + expr* s; + if (bu.is_bit2bool(e, s)) + return is_uninterp_const(s); + return false; + } + + void solver::push_core() { + + } + + void solver::pop_core(unsigned n) { + for (; m_trail_lim < s().init_trail_size(); ++m_trail_lim) { + auto lit = s().trail_literal(m_trail_lim); + auto e = ctx.literal2expr(lit); + if (is_unit(e)) { + // IF_VERBOSE(1, verbose_stream() << "add unit " << mk_pp(e, m) << "\n"); + std::lock_guard lock(m_mutex); + m_units.push_back(e); + m_has_units = true; + } + } } + void solver::init_search() { + init_local_search(); + } void solver::init_local_search() { if (m_bvsls) { m_bvsls->cancel(); m_thread.join(); - if (m_result == l_true) { - verbose_stream() << "Found model using local search - INIT\n"; - exit(1); - } + m_result = l_undef; + m_completed = false; + m_has_units = false; + m_model = nullptr; + m_units.reset(); } // set up state for local search solver here @@ -64,43 +105,12 @@ namespace sls { params_ref p; m_completed = false; m_result = l_undef; + m_model = nullptr; m_bvsls = alloc(bv::sls, *m_m, p); - // walk clauses, add them - // walk trail stack until search level, add units - // encapsulate bvsls within the arguments of run-local-search. - // ensure bvsls does not touch ast-manager. - - unsigned trail_sz = s().trail_size(); - for (unsigned i = 0; i < trail_sz; ++i) { - auto lit = s().trail_literal(i); - if (s().lvl(lit) > s().search_lvl()) - break; - expr_ref fml = literal2expr(lit); - m_bvsls->assert_expr(tr(fml.get())); - } - unsigned num_vars = s().num_vars(); - for (unsigned i = 0; i < 2*num_vars; ++i) { - auto l1 = ~sat::to_literal(i); - auto const& wlist = s().get_wlist(l1); - for (sat::watched const& w : wlist) { - if (!w.is_binary_non_learned_clause()) - continue; - sat::literal l2 = w.get_literal(); - if (l1.index() > l2.index()) - continue; - expr_ref fml(m.mk_or(literal2expr(l1), literal2expr(l2)), m); - m_bvsls->assert_expr(tr(fml.get())); - } - } - for (auto clause : s().clauses()) { - expr_ref_vector cls(m); - for (auto lit : *clause) - cls.push_back(literal2expr(lit)); - expr_ref fml(m.mk_or(cls), m); - m_bvsls->assert_expr(tr(fml.get())); - } + + for (expr* a : ctx.get_assertions()) + m_bvsls->assert_expr(tr(a)); - // use phase assignment from literals? std::function eval = [&](expr* e, unsigned r) { return false; }; @@ -108,23 +118,42 @@ namespace sls { m_bvsls->init(); m_bvsls->init_eval(eval); m_bvsls->updt_params(s().params()); - + m_bvsls->init_unit([&]() { + if (!m_has_units) + return expr_ref(*m_m); + expr_ref e(m); + { + std::lock_guard lock(m_mutex); + if (m_units.empty()) + return expr_ref(*m_m); + e = m_units.back(); + m_units.pop_back(); + } + ast_translation tr(m, *m_m); + return expr_ref(tr(e.get()), *m_m); + }); + m_thread = std::thread([this]() { run_local_search(); }); } void solver::sample_local_search() { - if (m_completed) { - m_thread.join(); - if (m_result == l_true) { - verbose_stream() << "Found model using local search\n"; - exit(1); - } + if (!m_completed) + return; + m_thread.join(); + m_completed = false; + m_bvsls->collect_statistics(m_st); + if (m_result == l_true) { + IF_VERBOSE(2, verbose_stream() << "(sat.sls :model-completed)\n";); + auto mdl = m_bvsls->get_model(); + ast_translation tr(*m_m, m); + m_model = mdl->translate(tr); + s().set_canceled(); } + m_bvsls = nullptr; } void solver::run_local_search() { - lbool r = (*m_bvsls)(); - m_result = r; + m_result = (*m_bvsls)(); m_completed = true; } diff --git a/src/sat/smt/sls_solver.h b/src/sat/smt/sls_solver.h index c473264acca..55e98fac7d9 100644 --- a/src/sat/smt/sls_solver.h +++ b/src/sat/smt/sls_solver.h @@ -30,30 +30,43 @@ namespace sls { class solver : public euf::th_euf_solver { std::atomic m_result; - std::atomic m_completed; + std::atomic m_completed, m_has_units; std::thread m_thread; + std::mutex m_mutex; scoped_ptr m_m; scoped_ptr m_bvsls; + model_ref m_model; + unsigned m_trail_lim = 0; + expr_ref_vector m_units; + statistics m_st; void run_local_search(); void init_local_search(); void sample_local_search(); + + bool is_unit(expr*); public: solver(euf::solver& ctx); ~solver(); + void simplify() override; + void init_search() override; + void push_core() override; void pop_core(unsigned n) override; - void simplify() override; sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; } void internalize(expr* e) override { UNREACHABLE(); } th_solver* clone(euf::solver& ctx) override { return alloc(solver, ctx); } - + void collect_statistics(statistics& st) const override { st.copy(m_st); } + + model_ref get_model() { return m_model; } + + void finalize() override; - bool unit_propagate() override { return false; } + bool unit_propagate() override; void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override { UNREACHABLE(); } - sat::check_result check() override { return sat::check_result::CR_DONE; } + sat::check_result check() override; std::ostream & display(std::ostream & out) const override { return out; } std::ostream & display_justification(std::ostream & out, sat::ext_justification_idx idx) const override { UNREACHABLE(); return out; } std::ostream & display_constraint(std::ostream & out, sat::ext_constraint_idx idx) const override { UNREACHABLE(); return out; } diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 2678888044c..57e3a89b5be 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -895,6 +895,7 @@ struct goal2sat::imp : public sat::sat_internalizer { process(n, true); CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";); SASSERT(m_result_stack.empty()); + add_assertion(n); } void insert_dep(expr* dep0, expr* dep, bool sign) { @@ -989,6 +990,12 @@ struct goal2sat::imp : public sat::sat_internalizer { } } + void add_assertion(expr* f) { + auto* ext = dynamic_cast(m_solver.get_extension()); + if (ext) + ext->add_assertion(f); + } + void update_model(model_ref& mdl) { auto* ext = dynamic_cast(m_solver.get_extension()); if (ext) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index ef617f724fd..02919b28775 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -49,6 +49,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_threads_max_conflicts = p.threads_max_conflicts(); m_threads_cube_frequency = p.threads_cube_frequency(); m_core_validate = p.core_validate(); + m_sls_enable = p.sls_enable(); m_logic = _p.get_sym("logic", m_logic); m_string_solver = p.string_solver(); m_up_persist_clauses = p.up_persist_clauses(); @@ -66,6 +67,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_lemmas2console = sp.lemmas2console(); m_instantiations2console = sp.instantiations2console(); m_proof_log = sp.proof_log(); + } void smt_params::updt_params(params_ref const & p) { diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index c678b75369e..0ef063e4a2e 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -114,6 +114,7 @@ struct smt_params : public preprocessor_params, bool m_induction = false; bool m_clause_proof = false; symbol m_proof_log; + bool m_sls_enable = false; // ----------------------------------- // diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 708fa87d899..4e498b2c461 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -135,6 +135,7 @@ def_module_params(module_name='smt', ('str.regex_automata_length_attempt_threshold', UINT, 10, 'number of length/path constraint attempts before checking unsatisfiability of regex terms'), ('str.fixed_length_refinement', BOOL, False, 'use abstraction refinement in fixed-length equation solver (Z3str3 only)'), ('str.fixed_length_naive_cex', BOOL, True, 'construct naive counterexamples when fixed-length model construction fails for a given length assignment (Z3str3 only)'), + ('sls.enable', BOOL, False, 'enable sls co-processor with SMT engine'), ('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'), From 43dd6a543640ff7fe9b64954912643e9ff53639f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 Apr 2024 18:19:58 +0200 Subject: [PATCH 034/414] include mutex Signed-off-by: Nikolaj Bjorner --- src/sat/smt/sls_solver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sat/smt/sls_solver.h b/src/sat/smt/sls_solver.h index 55e98fac7d9..5a6c9950bbf 100644 --- a/src/sat/smt/sls_solver.h +++ b/src/sat/smt/sls_solver.h @@ -17,6 +17,7 @@ Module Name: #pragma once #include +#include #include "util/rlimit.h" #include "ast/sls/bv_sls.h" #include "sat/smt/sat_th.h" From 2682c2ef2b3f31f065cc54b83e91f6d42c60db2f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 13 Apr 2024 16:42:26 +0200 Subject: [PATCH 035/414] sls updates - add SINGLE_THREAD mode - add interface to retrieve "best" model so far --- src/ast/sls/bv_sls.cpp | 18 +++++- src/ast/sls/bv_sls.h | 8 +++ src/sat/smt/intblast_solver.cpp | 2 +- src/sat/smt/sls_solver.cpp | 98 +++++++++++++++++---------------- src/sat/smt/sls_solver.h | 68 +++++++++++++++++------ 5 files changed, 128 insertions(+), 66 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index 9af87d3c5de..c0972349b18 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -61,6 +61,17 @@ namespace bv { } } + + void sls::set_model() { + if (!m_set_model) + return; + if (m_repair_roots.size() >= m_min_repair_size) + return; + m_min_repair_size = m_repair_roots.size(); + IF_VERBOSE(2, verbose_stream() << "(sls-update-model :num-unsat " << m_min_repair_size << ")\n"); + m_set_model(*get_model()); + } + void sls::init_repair_goal(app* t) { m_eval.init_eval(t); } @@ -94,6 +105,9 @@ namespace bv { if (m_to_repair.empty()) return; + // refresh the best model so far to a callback + set_model(); + // add fresh units, if any bool new_assertion = false; while (m_get_unit) { @@ -130,7 +144,7 @@ namespace bv { return m_rand() % 2 == 0; }; m_eval.init_eval(m_terms.assertions(), eval); - init_repair(); + init_repair(); // m_engine_init = false; } @@ -295,10 +309,12 @@ namespace bv { model_ref mdl = alloc(model, m); auto& terms = m_eval.sort_assertions(m_terms.assertions()); for (expr* e : terms) { +#if 0 if (!m_eval.re_eval_is_correct(to_app(e))) { verbose_stream() << "missed evaluation #" << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; m_eval.display_value(verbose_stream(), e) << "\n"; } +#endif if (!is_uninterp_const(e)) continue; diff --git a/src/ast/sls/bv_sls.h b/src/ast/sls/bv_sls.h index 690b618bff4..987cebcdbfc 100644 --- a/src/ast/sls/bv_sls.h +++ b/src/ast/sls/bv_sls.h @@ -54,10 +54,13 @@ namespace bv { bool m_engine_model = false; bool m_engine_init = false; std::function m_get_unit; + std::function m_set_model; + unsigned m_min_repair_size = UINT_MAX; std::pair next_to_repair(); void init_repair_goal(app* e); + void set_model(); void try_repair_down(app* e); void try_repair_up(app* e); void set_repair_down(expr* e) { m_repair_down = e->get_id(); } @@ -96,6 +99,11 @@ namespace bv { */ void init_unit(std::function get_unit) { m_get_unit = get_unit; } + /** + * Add callback to set model + */ + void set_model(std::function f) { m_set_model = f; } + /** * Run (bounded) local search to find feasible assignments. */ diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index f4491896bcf..459b263390c 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -1069,7 +1069,7 @@ namespace intblast { if (e->get_family_id() != bv.get_family_id()) return false; for (euf::enode* arg : euf::enode_args(n)) - dep.add(n, arg->get_root()); + dep.add(n, arg); return true; } diff --git a/src/sat/smt/sls_solver.cpp b/src/sat/smt/sls_solver.cpp index e12ff5ba7d6..a507619ee32 100644 --- a/src/sat/smt/sls_solver.cpp +++ b/src/sat/smt/sls_solver.cpp @@ -22,31 +22,37 @@ Module Name: namespace sls { +#ifdef SINGLE_THREAD + + solver::solver(euf::solver& ctx) : + th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls")) + {} + +#else solver::solver(euf::solver& ctx): - th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls")), - m_units(m) {} + th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls")) + {} solver::~solver() { finalize(); } void solver::finalize() { - if (!m_completed && m_bvsls) { - m_bvsls->cancel(); + if (!m_completed && m_sls) { + m_sls->cancel(); m_thread.join(); - m_bvsls->collect_statistics(m_st); - m_bvsls = nullptr; + m_sls->collect_statistics(m_st); + m_sls = nullptr; + m_shared = nullptr; + m_slsm = nullptr; + m_units = nullptr; } } sat::check_result solver::check() { - return sat::check_result::CR_DONE; } - void solver::simplify() { - } - bool solver::unit_propagate() { force_push(); sample_local_search(); @@ -66,10 +72,6 @@ namespace sls { return false; } - void solver::push_core() { - - } - void solver::pop_core(unsigned n) { for (; m_trail_lim < s().init_trail_size(); ++m_trail_lim) { auto lit = s().trail_literal(m_trail_lim); @@ -77,60 +79,63 @@ namespace sls { if (is_unit(e)) { // IF_VERBOSE(1, verbose_stream() << "add unit " << mk_pp(e, m) << "\n"); std::lock_guard lock(m_mutex); - m_units.push_back(e); + ast_translation tr(m, *m_shared); + m_units->push_back(tr(e.get())); m_has_units = true; } } - } - - void solver::init_search() { - init_local_search(); - } + } - void solver::init_local_search() { - if (m_bvsls) { - m_bvsls->cancel(); + void solver::init_search() { + if (m_sls) { + m_sls->cancel(); m_thread.join(); m_result = l_undef; m_completed = false; m_has_units = false; m_model = nullptr; - m_units.reset(); + m_units = nullptr; } // set up state for local search solver here - m_m = alloc(ast_manager, m); - ast_translation tr(m, *m_m); + m_shared = alloc(ast_manager); + m_slsm = alloc(ast_manager); + m_units = alloc(expr_ref_vector, *m_shared); + ast_translation tr(m, *m_slsm); - params_ref p; m_completed = false; m_result = l_undef; m_model = nullptr; - m_bvsls = alloc(bv::sls, *m_m, p); + m_sls = alloc(bv::sls, *m_slsm, s().params()); for (expr* a : ctx.get_assertions()) - m_bvsls->assert_expr(tr(a)); + m_sls->assert_expr(tr(a)); std::function eval = [&](expr* e, unsigned r) { return false; }; - m_bvsls->init(); - m_bvsls->init_eval(eval); - m_bvsls->updt_params(s().params()); - m_bvsls->init_unit([&]() { + m_sls->init(); + m_sls->init_eval(eval); + m_sls->updt_params(s().params()); + m_sls->init_unit([&]() { if (!m_has_units) - return expr_ref(*m_m); - expr_ref e(m); + return expr_ref(*m_slsm); + expr_ref e(*m_slsm); { std::lock_guard lock(m_mutex); - if (m_units.empty()) - return expr_ref(*m_m); - e = m_units.back(); - m_units.pop_back(); + if (m_units->empty()) + return expr_ref(*m_slsm); + ast_translation tr(*m_shared, *m_slsm); + e = tr(m_units->back()); + m_units->pop_back(); } - ast_translation tr(m, *m_m); - return expr_ref(tr(e.get()), *m_m); + return e; + }); + m_sls->set_model([&](model& mdl) { + std::lock_guard lock(m_mutex); + ast_translation tr(*m_shared, m); + m_model = mdl.translate(tr); }); m_thread = std::thread([this]() { run_local_search(); }); @@ -141,20 +146,21 @@ namespace sls { return; m_thread.join(); m_completed = false; - m_bvsls->collect_statistics(m_st); + m_sls->collect_statistics(m_st); if (m_result == l_true) { IF_VERBOSE(2, verbose_stream() << "(sat.sls :model-completed)\n";); - auto mdl = m_bvsls->get_model(); - ast_translation tr(*m_m, m); + auto mdl = m_sls->get_model(); + ast_translation tr(*m_slsm, m); m_model = mdl->translate(tr); s().set_canceled(); } - m_bvsls = nullptr; + m_sls = nullptr; } void solver::run_local_search() { - m_result = (*m_bvsls)(); + m_result = (*m_sls)(); m_completed = true; } +#endif } diff --git a/src/sat/smt/sls_solver.h b/src/sat/smt/sls_solver.h index 5a6c9950bbf..e1d8a95b51c 100644 --- a/src/sat/smt/sls_solver.h +++ b/src/sat/smt/sls_solver.h @@ -16,13 +16,45 @@ Module Name: --*/ #pragma once -#include -#include + #include "util/rlimit.h" #include "ast/sls/bv_sls.h" #include "sat/smt/sat_th.h" +#ifdef SINGLE_THREAD + + +namespace euf { + class solver; +} + +namespace sls { + + class solver : public euf::th_euf_solver { + public: + solver(euf::solver& ctx); + + sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; } + void internalize(expr* e) override { UNREACHABLE(); } + th_solver* clone(euf::solver& ctx) override { return alloc(solver, ctx); } + + model_ref get_model() { return model_ref(nullptr); } + bool unit_propagate() override { return false; } + void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing) override { UNREACHABLE(); } + sat::check_result check() override { return sat::check_result::CR_DONE;} + std::ostream& display(std::ostream& out) const override { return out; } + std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override { UNREACHABLE(); return out; } + std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override { UNREACHABLE(); return out; } + + }; +} + +#else + +#include +#include + namespace euf { class solver; } @@ -34,38 +66,36 @@ namespace sls { std::atomic m_completed, m_has_units; std::thread m_thread; std::mutex m_mutex; - scoped_ptr m_m; - scoped_ptr m_bvsls; + // m is accessed by the main thread + // m_slsm is accessed by the sls thread + // m_shared is only accessed at synchronization points + scoped_ptr m_shared, m_slsm; + scoped_ptr m_sls; + scoped_ptr m_units; model_ref m_model; unsigned m_trail_lim = 0; - expr_ref_vector m_units; statistics m_st; void run_local_search(); - void init_local_search(); void sample_local_search(); - bool is_unit(expr*); + public: solver(euf::solver& ctx); ~solver(); - void simplify() override; - void init_search() override; + model_ref get_model() { return m_model; } - void push_core() override; + void init_search() override; + void push_core() override {} void pop_core(unsigned n) override; - - sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; } - void internalize(expr* e) override { UNREACHABLE(); } th_solver* clone(euf::solver& ctx) override { return alloc(solver, ctx); } - void collect_statistics(statistics& st) const override { st.copy(m_st); } - - model_ref get_model() { return m_model; } - + void collect_statistics(statistics& st) const override { st.copy(m_st); } void finalize() override; - bool unit_propagate() override; + + sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; } + void internalize(expr* e) override { UNREACHABLE(); } void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override { UNREACHABLE(); } sat::check_result check() override; std::ostream & display(std::ostream & out) const override { return out; } @@ -75,3 +105,5 @@ namespace sls { }; } + +#endif \ No newline at end of file From 0368b5271657f9b10ecb7a3ac4d24a5a5a977c5f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 17 Apr 2024 15:16:11 +0200 Subject: [PATCH 036/414] add missing expr Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_model.cpp | 2 +- src/sat/smt/intblast_solver.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index ac7ef1522a2..9739249db51 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -327,7 +327,7 @@ namespace euf { out << mdl << "\n"; } - void solver::validate_model(model& mdl) { + void solver::validate_model(model& mdl) { if (!m_unhandled_functions.empty()) return; if (get_config().m_arith_ignore_int) diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 459b263390c..472fa4b2bb5 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -726,6 +726,7 @@ namespace intblast { r = a.mk_le(smod(bv_expr, 0), smod(bv_expr, 1)); break; case OP_SGEQ: + bv_expr = e->get_arg(0); r = a.mk_ge(smod(bv_expr, 0), smod(bv_expr, 1)); break; case OP_SLT: From e184a9a7111b5e2d20cb7923571ddeb760c97e56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 20 Apr 2024 07:32:52 -0400 Subject: [PATCH 037/414] fix translation of bvudiv --- src/sat/smt/intblast_solver.cpp | 77 +++++++++++++++++++++++---------- src/sat/smt/intblast_solver.h | 2 + 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 472fa4b2bb5..33c9980411f 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -49,6 +49,7 @@ namespace intblast { sat::literal lit = expr2literal(e); if (sign) lit.neg(); + TRACE("bv", tout << mk_pp(e, m) << " -> " << literal2expr(lit) << "\n"); return lit; } @@ -101,6 +102,7 @@ namespace intblast { set_translated(e, m.mk_eq(umod(x, 0), a.mk_int(0))); } m_preds.push_back(e); + TRACE("bv", tout << mk_pp(e, m) << " " << mk_pp(translated(e), m) << "\n"); ctx.push(push_back_vector(m_preds)); } @@ -456,10 +458,10 @@ namespace intblast { auto nBv2int = ctx.get_enode(bv2int); auto nxModN = ctx.get_enode(xModN); if (nBv2int->get_root() != nxModN->get_root()) { - auto a = eq_internalize(nBv2int, nxModN); - ctx.mark_relevant(a); - add_unit(a); - return sat::check_result::CR_CONTINUE; + auto a = eq_internalize(nBv2int, nxModN); + ctx.mark_relevant(a); + add_unit(a); + return sat::check_result::CR_CONTINUE; } } return sat::check_result::CR_DONE; @@ -585,12 +587,12 @@ namespace intblast { } void solver::translate_quantifier(quantifier* q) { - if (is_lambda(q)) - throw default_exception("lambdas are not supported in intblaster"); if (m_is_plugin) { set_translated(q, q); return; } + if (is_lambda(q)) + throw default_exception("lambdas are not supported in intblaster"); expr* b = q->get_expr(); unsigned nd = q->get_num_decls(); ptr_vector sorts; @@ -601,7 +603,6 @@ namespace intblast { sorts.push_back(a.mk_int()); } else - sorts.push_back(s); } b = translated(b); @@ -775,13 +776,13 @@ namespace intblast { case OP_BUREM: case OP_BUREM_I: { expr* x = umod(e, 0), * y = umod(e, 1); - r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), x, a.mk_mod(x, y)); + r = if_eq(y, 0, x, a.mk_mod(x, y)); break; } case OP_BUDIV: case OP_BUDIV_I: { - expr* x = arg(0), * y = umod(e, 1); - r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(-1), a.mk_idiv(x, y)); + expr* x = umod(e, 0), * y = umod(e, 1); + r = if_eq(y, 0, a.mk_int(-1), a.mk_idiv(x, y)); break; } case OP_BUMUL_NO_OVFL: { @@ -797,7 +798,7 @@ namespace intblast { r = a.mk_int(0); IF_VERBOSE(2, verbose_stream() << "shl " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n"); for (unsigned i = 0; i < bv.get_bv_size(e); ++i) - r = m.mk_ite(m.mk_eq(y, a.mk_int(i)), mul(x, a.mk_int(rational::power_of_two(i))), r); + r = if_eq(y, i, mul(x, a.mk_int(rational::power_of_two(i))), r); } break; } @@ -812,7 +813,7 @@ namespace intblast { r = a.mk_int(0); IF_VERBOSE(2, verbose_stream() << "lshr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n"); for (unsigned i = 0; i < bv.get_bv_size(e); ++i) - r = m.mk_ite(m.mk_eq(y, a.mk_int(i)), a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r); + r = if_eq(y, i, a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r); } break; case OP_BASHR: @@ -833,20 +834,19 @@ namespace intblast { IF_VERBOSE(1, verbose_stream() << "ashr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n"); for (unsigned i = 0; i < sz; ++i) { expr* d = a.mk_idiv(x, a.mk_int(rational::power_of_two(i))); - r = m.mk_ite(m.mk_eq(y, a.mk_int(i)), + r = if_eq(y, i, m.mk_ite(signx, add(d, a.mk_int(- rational::power_of_two(sz-i))), d), r); } } break; - case OP_BOR: { + case OP_BOR: // p | q := (p + q) - band(p, q) IF_VERBOSE(2, verbose_stream() << "bor " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n"); r = arg(0); for (unsigned i = 1; i < args.size(); ++i) r = a.mk_sub(add(r, arg(i)), a.mk_band(bv.get_bv_size(e), r, arg(i))); - break; - } + break; case OP_BNAND: r = bnot(band(args)); break; @@ -916,8 +916,8 @@ namespace intblast { r = m.mk_ite(m.mk_and(m.mk_not(signx), signy), add(u, y), r); r = m.mk_ite(m.mk_and(signx, m.mk_not(signy)), a.mk_sub(y, u), r); r = m.mk_ite(m.mk_and(m.mk_not(signx), m.mk_not(signy)), u, r); - r = m.mk_ite(m.mk_eq(u, a.mk_int(0)), a.mk_int(0), r); - r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), x, r); + r = if_eq(u, 0, a.mk_int(0), r); + r = if_eq(y, 0, x, r); break; } case OP_BSDIV_I: @@ -938,7 +938,7 @@ namespace intblast { y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y); expr* d = a.mk_idiv(x, y); r = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d)); - r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), m.mk_ite(signx, a.mk_int(1), a.mk_int(-1)), r); + r = if_eq(y, 0, m.mk_ite(signx, a.mk_int(1), a.mk_int(-1)), r); break; } case OP_BSREM_I: @@ -954,7 +954,7 @@ namespace intblast { expr* d = a.mk_idiv(absx, absy); d = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d)); r = a.mk_sub(x, mul(d, y)); - r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), x, r); + r = if_eq(y, 0, x, r); break; } case OP_ROTATE_LEFT: { @@ -973,7 +973,7 @@ namespace intblast { expr* y = umod(e, 1); r = a.mk_int(0); for (unsigned i = 0; i < sz; ++i) - r = m.mk_ite(m.mk_eq(a.mk_int(i), y), rotate_left(i), r); + r = if_eq(y, i, rotate_left(i), r); break; } case OP_EXT_ROTATE_RIGHT: { @@ -981,7 +981,7 @@ namespace intblast { expr* y = umod(e, 1); r = a.mk_int(0); for (unsigned i = 0; i < sz; ++i) - r = m.mk_ite(m.mk_eq(a.mk_int(i), y), rotate_left(sz - i), r); + r = if_eq(y, i, rotate_left(sz - i), r); break; } case OP_REPEAT: { @@ -1012,6 +1012,18 @@ namespace intblast { set_translated(e, r); } + expr_ref solver::if_eq(expr* n, unsigned k, expr* th, expr* el) { + rational r; + expr_ref _th(th, m), _el(el, m); + if (bv.is_numeral(n, r)) { + if (r == k) + return expr_ref(th, m); + else + return expr_ref(el, m); + } + return expr_ref(m.mk_ite(m.mk_eq(n, a.mk_int(k)), th, el), m); + } + void solver::translate_basic(app* e) { if (m.is_eq(e)) { bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); }); @@ -1125,6 +1137,27 @@ namespace intblast { TRACE("model", tout << "add_value " << ctx.bpp(n) << " := " << value << "\n"); } + void solver::finalize_model(model& mdl) { + return; + for (auto n : ctx.get_egraph().nodes()) { + auto e = n->get_expr(); + if (!is_translated(e)) + continue; + if (!bv.is_bv(e)) + continue; + auto t = translated(e); + + expr_ref ei(bv.mk_bv2int(e), m); + expr_ref ti(a.mk_mod(t, a.mk_int(rational::power_of_two(bv.get_bv_size(e)))), m); + auto ev = mdl(ei); + auto tv = mdl(ti); + if (ev != tv) { + IF_VERBOSE(0, verbose_stream() << mk_pp(e, m) << " <- " << ev << "\n"); + IF_VERBOSE(0, verbose_stream() << mk_pp(t, m) << " <- " << tv << "\n"); + } + } + } + sat::literal_vector const& solver::unsat_core() { return m_core; } diff --git a/src/sat/smt/intblast_solver.h b/src/sat/smt/intblast_solver.h index 0aceb8b2bb6..34f876be6a6 100644 --- a/src/sat/smt/intblast_solver.h +++ b/src/sat/smt/intblast_solver.h @@ -77,6 +77,7 @@ namespace intblast { bool is_non_negative(expr* bv_expr, expr* e); expr_ref mul(expr* x, expr* y); expr_ref add(expr* x, expr* y); + expr_ref if_eq(expr* n, unsigned k, expr* th, expr* el); expr* amod(expr* bv_expr, expr* x, rational const& N); rational bv_size(expr* bv_expr); @@ -147,6 +148,7 @@ namespace intblast { rational get_value(expr* e) const; + void finalize_model(model& mdl) override; }; } From cbef183ae50181ce8f3b962cab9d97f51669d089 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 20 Apr 2024 14:57:04 -0400 Subject: [PATCH 038/414] type check equality injectivity axiom Signed-off-by: Nikolaj Bjorner --- src/sat/smt/intblast_solver.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 33c9980411f..421966377c0 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -437,10 +437,12 @@ namespace intblast { continue; if (sib->get_arg(0)->get_root() == r1) continue; - auto a = eq_internalize(n, sib); - auto b = eq_internalize(sib->get_arg(0), n->get_arg(0)); - ctx.mark_relevant(a); - ctx.mark_relevant(b); + if (bv.get_bv_size(r1->get_expr()) != bv.get_bv_size(sib->get_arg(0)->get_expr())) + continue; + auto a = eq_internalize(n, sib); + auto b = eq_internalize(sib->get_arg(0), n->get_arg(0)); + ctx.mark_relevant(a); + ctx.mark_relevant(b); add_clause(~a, b, nullptr); return sat::check_result::CR_CONTINUE; } From 2a4f0e785b2b48d611ba4ae8d4c515c1b81890ae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 20 Apr 2024 18:04:10 -0400 Subject: [PATCH 039/414] tidy --- src/ast/sls/bv_sls.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ast/sls/bv_sls.cpp b/src/ast/sls/bv_sls.cpp index c0972349b18..f1b2a9f4f27 100644 --- a/src/ast/sls/bv_sls.cpp +++ b/src/ast/sls/bv_sls.cpp @@ -144,7 +144,7 @@ namespace bv { return m_rand() % 2 == 0; }; m_eval.init_eval(m_terms.assertions(), eval); - init_repair(); + init_repair(); // m_engine_init = false; } @@ -284,8 +284,7 @@ namespace bv { else if (!m_eval.repair_up(e)) { IF_VERBOSE(2, verbose_stream() << "repair-up "; trace_repair(true, e)); if (m_rand(10) != 0) { - m_eval.set_random(e); - + m_eval.set_random(e); m_repair_roots.insert(e->get_id()); } else @@ -309,15 +308,8 @@ namespace bv { model_ref mdl = alloc(model, m); auto& terms = m_eval.sort_assertions(m_terms.assertions()); for (expr* e : terms) { -#if 0 - if (!m_eval.re_eval_is_correct(to_app(e))) { - verbose_stream() << "missed evaluation #" << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; - m_eval.display_value(verbose_stream(), e) << "\n"; - } -#endif if (!is_uninterp_const(e)) continue; - auto f = to_app(e)->get_decl(); auto v = m_eval.get_value(to_app(e)); if (v) From bebcd9470327759739cd598d0be85c73d7a22e42 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 25 Apr 2024 10:29:34 -0400 Subject: [PATCH 040/414] enable logging nla lemmas Signed-off-by: Nikolaj Bjorner --- src/ast/sls/bv_sls_eval.h | 8 ++++++++ src/math/lp/nra_solver.cpp | 22 ++++++++++++++++++++++ src/nlsat/nlsat_solver.cpp | 4 ++++ src/nlsat/nlsat_solver.h | 2 ++ src/smt/params/smt_params_helper.pyg | 1 + 5 files changed, 37 insertions(+) diff --git a/src/ast/sls/bv_sls_eval.h b/src/ast/sls/bv_sls_eval.h index 89cd3f4cf03..4384660e79a 100644 --- a/src/ast/sls/bv_sls_eval.h +++ b/src/ast/sls/bv_sls_eval.h @@ -25,6 +25,12 @@ namespace bv { class sls_fixed; + class sls_eval_plugin { + public: + virtual ~sls_eval_plugin() {} + + }; + class sls_eval { struct config { unsigned m_prob_randomize_extract = 50; @@ -40,6 +46,8 @@ namespace bv { random_gen m_rand; config m_config; + scoped_ptr_vector m_plugins; + scoped_ptr_vector m_values; // expr-id -> bv valuation diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 9c9db4e4114..4c1b2b3eef8 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -3,6 +3,10 @@ Author: Nikolaj Bjorner, Lev Nachmanson */ +#ifndef SINGLE_THREAD +#include +#endif +#include #include "math/lp/lar_solver.h" #include "math/lp/nra_solver.h" #include "nlsat/nlsat_solver.h" @@ -11,6 +15,7 @@ #include "util/map.h" #include "util/uint_set.h" #include "math/lp/nla_core.h" +#include "smt/params/smt_params_helper.hpp" namespace nra { @@ -157,6 +162,23 @@ struct solver::imp { TRACE("nra", m_nlsat->display(tout)); + smt_params_helper p(m_params); + if (p.arith_nl_log()) { + static unsigned id = 0; + std::stringstream strm; + +#ifndef SINGLE_THREAD + std::thread::id this_id = std::this_thread::get_id(); + strm << "nla_" << this_id << "." << (++id) << ".smt2"; +#else + strm << "nla_" << (++id) << ".smt2"; +#endif + std::ofstream out(strm.str()); + m_nlsat->display_smt2(out); + out << "(check-sat)\n"; + out.close(); + } + lbool r = l_undef; try { r = m_nlsat->check(); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index d99603dcddc..7a63f4e636b 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -3863,6 +3863,10 @@ namespace nlsat { return out; } + std::ostream& solver::display_smt2(std::ostream & out) const { + return m_imp->display_smt2(out); + } + std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { return display_smt2(out, ls.size(), ls.data()); } diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index c65a2b4ff79..15764150d7d 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -254,6 +254,8 @@ namespace nlsat { std::ostream& display_smt2(std::ostream & out, literal_vector const& ls) const; + std::ostream& display_smt2(std::ostream & out) const; + /** \brief Display variable diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 4e498b2c461..1ef795e4b7d 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -83,6 +83,7 @@ def_module_params(module_name='smt', ('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'), ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), ('arith.nl.cross_nested', BOOL, True, 'enable cross-nested consistency checking'), + ('arith.nl.log', BOOL, False, 'Log lemmas sent to nra solver'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), ('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), From 2ad9f220f281b82b601b3e2371fd02bfc9b2dc73 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 16:57:59 -0700 Subject: [PATCH 041/414] add logging --- src/ast/simplifiers/bound_simplifier.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ast/simplifiers/bound_simplifier.cpp b/src/ast/simplifiers/bound_simplifier.cpp index 1a5d4c10107..2d5094c4ff6 100644 --- a/src/ast/simplifiers/bound_simplifier.cpp +++ b/src/ast/simplifiers/bound_simplifier.cpp @@ -288,7 +288,8 @@ void bound_simplifier::tighten_bound(dependent_expr const& de) { void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) { scoped_mpq c(nm); - nm.set(c, n.to_mpq()); + nm.set(c, n.to_mpq()); + TRACE("propagate-ineqs", tout << to_var(x) << ": " << mk_pp(x, m) << (strict ? " < " : " <= ") << n << "\n"); bp.assert_upper(to_var(x), c, strict); } @@ -296,6 +297,7 @@ void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) { void bound_simplifier::assert_lower(expr* x, rational const& n, bool strict) { scoped_mpq c(nm); nm.set(c, n.to_mpq()); + TRACE("propagate-ineqs", tout << to_var(x) << ": " << mk_pp(x, m) << (strict ? " > " : " >= ") << n << "\n"); bp.assert_lower(to_var(x), c, strict); } @@ -306,6 +308,7 @@ bool bound_simplifier::has_lower(expr* x, rational& n, bool& strict) { return false; strict = m_interval.lower_is_open(i); n = m_interval.lower(i); + TRACE("propagate-ineqs", tout << to_var(x) << ": " << mk_pp(x, m) << (strict ? " > " : " >= ") << n << "\n"); return true; } @@ -316,6 +319,7 @@ bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) { return false; strict = m_interval.upper_is_open(i); n = m_interval.upper(i); + TRACE("propagate-ineqs", tout << to_var(x) << ": " << mk_pp(x, m) << (strict ? " < " : " <= ") << n << "\n"); return true; } From bc577b93ae3e88e2e34443b68aca66491f5efe03 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 16:58:22 -0700 Subject: [PATCH 042/414] refine precision before taking closest integral value. --- src/math/polynomial/algebraic_numbers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 06b4465d77e..a10e0dc980a 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2599,6 +2599,7 @@ namespace algebraic_numbers { qm().dec(v); } else { + refine_until_prec(const_cast(a), 1); bqm().floor(qm(), lower(a.to_algebraic()), v); } m_wrapper.set(b, v); @@ -2611,6 +2612,7 @@ namespace algebraic_numbers { qm().inc(v); } else { + refine_until_prec(const_cast(a), 1); bqm().ceil(qm(), upper(a.to_algebraic()), v); } m_wrapper.set(b, v); From 39dc8861eec43f91250bc0ad40943a8d457a2dc0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 16:59:50 -0700 Subject: [PATCH 043/414] expose comparisons with polynomials, incremental way to extract variables --- src/math/polynomial/polynomial.cpp | 89 +++++++++++++++++++++++++++--- src/math/polynomial/polynomial.h | 19 +++++-- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index da6bc7b3925..44a5c8864ac 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -190,6 +190,10 @@ namespace polynomial { } }; + bool operator==(monomial const& other) const { + return eq_proc()(this, &other); + } + static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz * sizeof(power); } monomial(unsigned id, unsigned sz, power const * pws, unsigned h): @@ -3221,9 +3225,16 @@ namespace polynomial { }; bool_vector m_found_vars; - void vars(polynomial const * p, var_vector & xs) { - xs.reset(); + void begin_vars_incremental() { m_found_vars.reserve(num_vars(), false); + } + void end_vars_incremental(var_vector& xs) { + // reset m_found_vars + unsigned sz = xs.size(); + for (unsigned i = 0; i < sz; i++) + m_found_vars[xs[i]] = false; + } + void vars(polynomial const * p, var_vector & xs) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); @@ -3236,10 +3247,6 @@ namespace polynomial { } } } - // reset m_found_vars - sz = xs.size(); - for (unsigned i = 0; i < sz; i++) - m_found_vars[xs[i]] = false; } typedef sbuffer power_buffer; @@ -6045,6 +6052,47 @@ namespace polynomial { } return true; } + + bool ge(polynomial const* p, polynomial const* q) { + unsigned sz1 = p->size(); + unsigned sz2 = q->size(); + unsigned i = 0, j = 0; + while (i < sz1 || j < sz2) { + auto * m1 = i < sz1 ? p->m(i) : q->m(j); + auto & a1 = i < sz1 ? p->a(i) : q->a(j); + auto * m2 = j < sz2 ? q->m(j) : p->m(i); + auto & a2 = j < sz2 ? q->a(j) : p->a(i); + + if (i < sz1 && j == sz2 && m1->is_unit()) { + if (!m_manager.is_pos(a1)) + return false; + ++i; + continue; + } + + if (i == sz1 && j < sz2 && m2->is_unit()) { + if (!m_manager.is_neg(a2)) + return false; + ++j; + continue; + } + + if (i == sz1 || j == sz2) + break; + + if (!(*m1 == *m2)) { + if (m_manager.is_pos(a1) && m1->is_square()) { + ++i; + continue; + } + return false; + } + if (!m_manager.ge(a1, a2)) + return false; + ++i, ++j; + } + return i == sz1 && j == sz2; + } // Functor used to compute the maximal degree of each variable in a polynomial p. class var_max_degree { @@ -7228,14 +7276,34 @@ namespace polynomial { return m_imp->is_nonneg(p); } + bool manager::ge(polynomial const* p, polynomial const* q) { + return m_imp->ge(p, q); + } + + void manager::rename(unsigned sz, var const * xs) { return m_imp->rename(sz, xs); } void manager::vars(polynomial const * p, var_vector & xs) { + xs.reset(); + m_imp->begin_vars_incremental(); m_imp->vars(p, xs); + m_imp->end_vars_incremental(xs); } + void manager::vars_incremental(polynomial const * p, var_vector & xs) { + m_imp->vars(p, xs); + } + void manager::begin_vars_incremental() { + m_imp->begin_vars_incremental(); + } + + void manager::end_vars_incremental(var_vector & xs) { + m_imp->end_vars_incremental(xs); + } + + polynomial * manager::substitute(polynomial const * p, var2mpq const & x2v) { return m_imp->substitute(p, x2v); } @@ -7293,17 +7361,20 @@ namespace polynomial { return m_imp->eval(p, x2v, r); } - void manager::display(std::ostream & out, monomial const * m, display_var_proc const & proc, bool user_star) const { + std::ostream& manager::display(std::ostream & out, monomial const * m, display_var_proc const & proc, bool user_star) const { m->display(out, proc, user_star); + return out; } - void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { + std::ostream& manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { SASSERT(m_imp->consistent_coeffs(p)); p->display(out, m_imp->m_manager, proc, use_star); + return out; } - void manager::display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc) const { + std::ostream& manager::display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc) const { p->display_smt2(out, m_imp->m_manager, proc); + return out; } }; diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 416422f6435..48fe5ffeb06 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -921,6 +921,13 @@ namespace polynomial { */ bool is_nonneg(polynomial const * p); + + /** + \brief Return true if p is always greater or equal to q. + This is an incomplete check + */ + bool ge(polynomial const* p, polynomial const* q); + /** \brief Make sure the monomials in p are sorted using lexicographical order. Remark: the maximal monomial is at position 0. @@ -931,6 +938,9 @@ namespace polynomial { \brief Collect variables that occur in p into xs */ void vars(polynomial const * p, var_vector & xs); + void vars_incremental(polynomial const * p, var_vector & xs); + void begin_vars_incremental(); + void end_vars_incremental(var_vector & xs); /** \brief Evaluate polynomial p using the assignment [x_1 -> v_1, ..., x_n -> v_n]. @@ -1019,15 +1029,14 @@ namespace polynomial { */ void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R); - void display(std::ostream & out, monomial const * m, display_var_proc const & proc = display_var_proc(), bool use_star = true) const; + std::ostream& display(std::ostream & out, monomial const * m, display_var_proc const & proc = display_var_proc(), bool use_star = true) const; - void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; + std::ostream& display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; - void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; + std::ostream& display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) { - p.m().display(out, p); - return out; + return p.m().display(out, p); } }; From 4c070f9e76fc11893c705e67ef675aa58f7bff45 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 17:00:05 -0700 Subject: [PATCH 044/414] add extra fields to nlsat-clause --- src/nlsat/nlsat_clause.cpp | 5 ++++- src/nlsat/nlsat_clause.h | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/nlsat/nlsat_clause.cpp b/src/nlsat/nlsat_clause.cpp index a64ec2856ab..268086c2437 100644 --- a/src/nlsat/nlsat_clause.cpp +++ b/src/nlsat/nlsat_clause.cpp @@ -25,7 +25,10 @@ namespace nlsat { m_size(sz), m_capacity(sz), m_learned(learned), - m_activity(0), + m_active(false), + m_removed(false), + m_marked(false), + m_var_hash(0), m_assumptions(as) { for (unsigned i = 0; i < sz; i++) { m_lits[i] = lits[i]; diff --git a/src/nlsat/nlsat_clause.h b/src/nlsat/nlsat_clause.h index 7c3416febbf..91467303cd3 100644 --- a/src/nlsat/nlsat_clause.h +++ b/src/nlsat/nlsat_clause.h @@ -29,7 +29,10 @@ namespace nlsat { unsigned m_size; unsigned m_capacity:31; unsigned m_learned:1; - unsigned m_activity; + unsigned m_active:1; + unsigned m_removed:1; + unsigned m_marked:1; + unsigned m_var_hash; assumption_set m_assumptions; literal m_lits[0]; static size_t get_obj_size(unsigned num_lits) { return sizeof(clause) + num_lits * sizeof(literal); } @@ -46,9 +49,15 @@ namespace nlsat { literal const * begin() const { return m_lits; } literal const * end() const { return m_lits + m_size; } literal const * data() const { return m_lits; } - void inc_activity() { m_activity++; } - void set_activity(unsigned v) { m_activity = v; } - unsigned get_activity() const { return m_activity; } + void set_active(bool b) { m_active = b; } + bool is_active() const { return m_active; } + void set_removed() { m_removed = true; } + bool is_removed() const { return m_removed; } + unsigned var_hash() const { return m_var_hash; } + void set_var_hash(unsigned h) { m_var_hash = h; } + bool is_marked() const { return m_marked; } + void mark() { m_marked = true; } + void unmark() { m_marked = false; } bool contains(literal l) const; bool contains(bool_var v) const; void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; } } From b0222cbdaa1ad345ac39b3b904ba55f127448be0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 17:00:49 -0700 Subject: [PATCH 045/414] temper warning messages from uninitalized pointers --- src/util/hashtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/hashtable.h b/src/util/hashtable.h index b2830326bc4..b59a2e7d835 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -472,7 +472,7 @@ class core_hashtable : private HashProc, private EqProc { that was already in the table. */ data const & insert_if_not_there(data const & e) { - entry * et; + entry * et = nullptr; insert_if_not_there_core(e, et); return et->get_data(); } @@ -482,7 +482,7 @@ class core_hashtable : private HashProc, private EqProc { Return the entry that contains e. */ entry * insert_if_not_there2(data const & e) { - entry * et; + entry * et = nullptr; insert_if_not_there_core(e, et); return et; } From 29e724f78737949d4da73bfaa46fac77a4a43b8e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 17:05:21 -0700 Subject: [PATCH 046/414] add gc to lemmas, convert bounds constraints to lemmas, add simplification pre-processing beyond equality extraction --- src/nlsat/nlsat_solver.cpp | 829 +++++++++++++++++++++++++++++++++---- 1 file changed, 745 insertions(+), 84 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 7a63f4e636b..8d9e563f882 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -85,6 +85,16 @@ namespace nlsat { typedef polynomial::cache cache; typedef ptr_vector interval_set_vector; + struct bound_constraint { + var x; + polynomial_ref A, B; + bool is_strict; + clause* c; + bound_constraint(var x, polynomial_ref& A, polynomial_ref& B, bool is_strict, clause* c): + x(x), A(A), B(B), is_strict(is_strict), c(c) {} + }; + + ctx& m_ctx; solver& m_solver; reslimit& m_rlimit; @@ -95,13 +105,13 @@ namespace nlsat { cache m_cache; anum_manager& m_am; mutable assumption_manager m_asm; - assignment m_assignment; // partial interpretation + assignment m_assignment, m_lo, m_hi; // partial interpretation evaluator m_evaluator; interval_set_manager & m_ism; ineq_atom_table m_ineq_atoms; root_atom_table m_root_atoms; - svector m_patch_var; - polynomial_ref_vector m_patch_num, m_patch_denom; + + vector m_bounds; id_gen m_cid_gen; clause_vector m_clauses; // set of clauses @@ -228,11 +238,9 @@ namespace nlsat { m_cache(m_pm), m_am(c.m_am), m_asm(*this, m_allocator), - m_assignment(m_am), + m_assignment(m_am), m_lo(m_am), m_hi(m_am), m_evaluator(s, m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), - m_patch_num(m_pm), - m_patch_denom(m_pm), m_num_bool_vars(0), m_display_var(m_perm), m_display_assumption(nullptr), @@ -288,6 +296,8 @@ namespace nlsat { del_unref_atoms(); m_cache.reset(); m_assignment.reset(); + m_lo.reset(); + m_hi.reset(); } void clear() { @@ -1512,8 +1522,6 @@ namespace nlsat { TRACE("nlsat", display_smt2(tout);); m_bk = 0; m_xk = null_var; - m_conflicts = 0; - m_next_conflict = 100; while (true) { CASSERT("nlsat", check_satisfied()); @@ -1565,6 +1573,26 @@ namespace nlsat { } } + void gc() { + if (m_learned.size() <= 2*m_clauses.size()) + return; + reset_watches(); + reinit_cache(); + unsigned j = 0; + for (unsigned i = 0; i < m_learned.size(); ++i) { + auto cls = m_learned[i]; + if (i - j < m_clauses.size() && cls->size() > 1 && !cls->is_active()) + del_clause(cls); + else { + m_learned[j++] = cls; + cls->set_active(false); + } + } + m_learned.shrink(j); + reattach_arith_clauses(m_clauses); + reattach_arith_clauses(m_learned); + } + unsigned m_next_conflict = 100; void log() { if (m_conflicts < m_next_conflict) @@ -1576,6 +1604,8 @@ namespace nlsat { lbool search_check() { lbool r = l_undef; + m_conflicts = 0; + m_next_conflict = 100; while (true) { r = search(); if (r != l_true) break; @@ -1593,14 +1623,19 @@ namespace nlsat { // derive tight bounds. while (true) { lo++; - if (!m_am.gt(v, lo.to_mpq())) { lo--; break; } + if (!m_am.gt(v, lo.to_mpq())) { + lo--; + break; + } } bounds.push_back(std::make_pair(x, lo)); } } if (bounds.empty()) break; - init_search(); + gc(); + init_search(); + IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_conflicts << " :decisions " << m_decisions << " :propagations " << m_propagations << " :clauses " << m_clauses.size() << " :learned " << m_learned.size() << ")\n"); for (auto const& b : bounds) { var x = b.first; rational lo = b.second; @@ -1617,7 +1652,7 @@ namespace nlsat { m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); // perform branch and bound - clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), false, nullptr); + clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); if (cls) { TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); } @@ -1826,8 +1861,9 @@ namespace nlsat { } } - void resolve_clause(bool_var b, clause const & c) { + void resolve_clause(bool_var b, clause & c) { TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); + c.set_active(true); resolve_clause(b, c.size(), c.data()); m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); } @@ -2009,8 +2045,8 @@ namespace nlsat { /** \brief Return true if the conflict was solved. */ - bool resolve(clause const & conflict) { - clause const * conflict_clause = &conflict; + bool resolve(clause & conflict) { + clause * conflict_clause = &conflict; m_lemma_assumptions = nullptr; start: SASSERT(check_marks()); @@ -2405,7 +2441,7 @@ namespace nlsat { } bool can_reorder() const { - return m_patch_var.empty() + return m_bounds.empty() && all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); } @@ -2679,6 +2715,10 @@ namespace nlsat { // solve simple equalities // TBD WU-Reit decomposition? + // - elim_unconstrained + // - solve_eqs + // - fm + /** \brief isolate variables in unit equalities. Assume a clause is c == v*p + q @@ -2694,7 +2734,503 @@ namespace nlsat { The method ignores lemmas and assumes constraints don't use roots. */ + vector> m_var_occurs; + bool simplify() { + unsigned sz = m_clauses.size(); + while (true) { + + while (elim_uncnstr()) + ; + + while (fm()) + ; + + if (!solve_eqs()) + return false; + + subsumption_simplify(); + if (m_clauses.size() >= sz) + break; + sz = m_clauses.size(); + } + + IF_VERBOSE(3, display(verbose_stream())); + + return true; + } + + // + // + bool elim_uncnstr() { + // compute variable occurrences + if (any_of(m_clauses, [&](clause* c) { return has_root_atom(*c); })) + return false; + compute_occurs(); + // for each variable occurrence, figure out if it is unconstrained. + ptr_vector to_delete; + for (unsigned v = m_var_occurs.size(); v-- > 0; ) { + auto& clauses = m_var_occurs[v]; + if (clauses.size() != 1) + continue; + auto& c = *clauses[0]; + if (c.is_removed()) + continue; + if (!is_unconstrained(v, c)) + continue; + c.set_removed(); + to_delete.push_back(&c); + } + for (auto* c : to_delete) + del_clause(c, m_clauses); + + return !to_delete.empty(); + } + + void compute_occurs(clause& c) { + var_vector vars; + m_pm.begin_vars_incremental(); + for (auto lit : c) { + bool_var b = lit.var(); + atom* a = m_atoms[b]; + if (!a) + continue; + if (a->is_ineq_atom()) { + auto sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; ++i) { + auto* p = to_ineq_atom(a)->p(i); + m_pm.vars_incremental(p, vars); + } + } + } + m_pm.end_vars_incremental(vars); + unsigned h = 0; + for (auto v : vars) { + m_var_occurs.reserve(v + 1); + m_var_occurs[v].push_back(&c); + h |= (1ul << (v % 32ul)); + } + c.set_var_hash(h); + } + + void compute_occurs() { + m_var_occurs.reset(); + for (auto c : m_clauses) + compute_occurs(*c); + } + + bool is_unconstrained(var x, clause& c) { + poly* p; + polynomial_ref A(m_pm), B(m_pm); + for (auto lit : c) { + bool_var b = lit.var(); + if (!m_atoms[b]) + continue; + auto& a = *to_ineq_atom(m_atoms[b]); + if (!is_single_poly(a, p)) + continue; + + if (1 != m_pm.degree(p, x)) + continue; + + A = m_pm.coeff(p, x, 1, B); + + if (a.is_eq() && !lit.sign()) { + // A*x + B = 0 + if (is_int(x) && is_unit(A)) { + m_bounds.push_back(bound_constraint(x, A, B, false, nullptr)); + return true; + } + + if (!is_int(x) && m_pm.is_const(A)) { + m_bounds.push_back(bound_constraint(x, A, B, false, nullptr)); + return true; + } + } + // TODO: add other cases for LT and GT atoms + } + return false; + } + + bool cleanup_removed() { + unsigned j = 0, sz = m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + auto c = m_clauses[i]; + if (c->is_removed()) + del_clause(c); + else + m_clauses[j++] = c; + } + m_clauses.shrink(j); + return j < sz; + } + + // + // Fourier Motzkin elimination + // + + bool fm() { + if (any_of(m_clauses, [&](clause* c) { return has_root_atom(*c); })) + return false; + compute_occurs(); + + for (unsigned v = m_var_occurs.size(); v-- > 0; ) + apply_fm(v, m_var_occurs[v]); + + return cleanup_removed(); + } + + // progression of features + // unit literals + // single occurrence of x in C + // (x <= t or x <= s or C) == (x <= max(s, t) or C) + + + bool is_invertible(var x, polynomial_ref & A) { + if (!m_pm.is_const(A)) + return false; + if (is_int(x) && !is_unit(A)) + return false; + return true; + } + + bool apply_fm(var x, ptr_vector& clauses) { + polynomial_ref A(m_pm), B(m_pm); + vector lo, hi; + poly* p = nullptr; + bool all_solved = true; + for (auto c : clauses) { + if (c->is_removed()) + continue; + if (c->size() != 1) { + all_solved = false; + continue; + } + literal lit = (*c)[0]; + bool sign = lit.sign(); + ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); + if (sign && a.is_eq()) { + all_solved = false; + continue; + } + if (!is_single_poly(a, p)) { + all_solved = false; + continue; + } + if (1 != m_pm.degree(p, x)) { + all_solved = false; + continue; + } + A = m_pm.coeff(p, x, 1, B); + if (!is_invertible(x, A)) { + all_solved = false; + continue; + } + auto const& A_value = m_pm.coeff(A, 0); + bool is_pos = m_pm.m().is_pos(A_value); + bool is_strict = false; + switch (a.get_kind()) { + case atom::LT: + // !(Ax + B < 0) == Ax + B >= 0 + if (sign) + is_strict = false; + else { + // Ax + B < 0 == -Ax - B > 0 + A = -A; + B = -B; + is_pos = !is_pos; + if (is_int(x)) { + // Ax + B > 0 == Ax + B - |A| >= 0 + if (is_pos) + B = m_pm.add(B, A); + else + B = m_pm.sub(B, A); + is_strict = false; + } + else + is_strict = true; + } + break; + case atom::GT: + // !(Ax + B > 0) == -Ax + -B >= 0 + if (sign) { + A = -A; + B = -B; + is_pos = !is_pos; + is_strict = false; + } + else { + // Ax + B > 0 + if (is_int(x)) { + // Ax + B - |A| >= 0 + if (is_pos) + B = m_pm.sub(B, A); + else + B = m_pm.add(B, A); + is_strict = false; + } + else + is_strict = true; + } + break; + case atom::EQ: { + all_solved = false; + continue; + // unsound: + m_display_var(verbose_stream(), x); + display(verbose_stream() << " ", *c) << "\n"; + bound_constraint l(x, A, B, false, c); + bound_constraint h(x, -A, -B, false, c); + apply_fm_equality(x, clauses, l, h); + return true; + } + default: + UNREACHABLE(); + break; + } + auto& set = is_pos ? hi : lo; + bool found = false; + for (auto const& bound : set) { + if (is_strict == bound.is_strict && m_pm.eq(A, bound.A) && m_pm.eq(B, bound.B)) + found = true; + } + if (found) + continue; + + set.push_back(bound_constraint(x, A, B, is_strict, c)); + + } + + if (lo.empty() && hi.empty()) + return false; + + IF_VERBOSE(3, + verbose_stream() << "x" << x << " lo " << lo.size() << " hi " << hi.size() << "\n"; + for (auto c : clauses) + if (!c->is_removed()) + display(verbose_stream(), *c) << "\n"; + ); + + if (apply_fm_equality(x, clauses, lo, hi)) + return true; + + if (!all_solved) + return false; + + auto num_lo = lo.size(), num_hi = hi.size(); + if (num_lo >= 2 && num_hi >= 2 && (num_lo > 2 || num_hi > 2)) + return false; + + apply_fm_inequality(x, clauses, lo, hi); + + return true; + } + + void apply_fm_inequality( + var x, ptr_vector& clauses, + vector& lo, vector& hi) { + + polynomial_ref C(m_pm); + for (auto c : clauses) + c->set_removed(); + + for (auto const& l : lo) { + // l.A * x + l.B, l.is_strict;, l.A < 0 + for (auto const& h : hi) { + // h.A * x + h.B, h.is_strict; h.A > 0 + // (l.A x + l.B)*h.A + (h.A x + h.B)*|l.A| >= 0 + C = m_pm.mul(l.B, h.A); + C = m_pm.sub(C, m_pm.mul(h.B, l.A)); + poly* p = C.get(); + bool is_even = false; + m_lemma.reset(); + if (l.is_strict || h.is_strict) + m_lemma.push_back(mk_ineq_literal(atom::GT, 1, &p, &is_even)); + else + m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p, &is_even)); + if (m_lemma[0] == true_literal) + continue; + auto a1 = static_cast<_assumption_set>(l.c->assumptions()); + auto a2 = static_cast<_assumption_set>(h.c->assumptions()); + auto cls = mk_clause(m_lemma.size(), m_lemma.data(), false, m_asm.mk_join(a1, a2)); + if (cls) + compute_occurs(*cls); + IF_VERBOSE(3, display(verbose_stream() << "add resolvent ", *cls) << "\n"); + } + } + + // track updates for model reconstruction + for (auto const& l : lo) + m_bounds.push_back(l); + for (auto const& h : hi) + m_bounds.push_back(h); + } + + bool apply_fm_equality( + var x, ptr_vector& clauses, + vector& lo, vector& hi) { + for (auto& l : lo) { + if (l.is_strict) + continue; + l.A = -l.A; + l.B = -l.B; + for (auto& h : hi) { + if (h.is_strict) + continue; + if (!m_pm.eq(l.B, h.B)) + continue; + if (!m_pm.eq(l.A, h.A)) + continue; + l.A = -l.A; + l.B = -l.B; + apply_fm_equality(x, clauses, l, h); + return true; + } + l.A = -l.A; + l.B = -l.B; + } + return false; + } + + void apply_fm_equality( + var x, ptr_vector& clauses, + bound_constraint& l, bound_constraint& h) { + auto a1 = static_cast<_assumption_set>(l.c->assumptions()); + auto a2 = static_cast<_assumption_set>(h.c->assumptions()); + a1 = m_asm.mk_join(a1, a2); + + // TODO: this can also replace solve_eqs + for (auto c : clauses) { + if (c->is_removed()) + continue; + c->set_removed(); + if (c == l.c || c == h.c) + continue; + m_lemma.reset(); + bool is_tautology = false; + for (literal lit : *c) { + lit = substitute_var(x, l.A, l.B, lit); + m_lemma.push_back(lit); + if (lit == true_literal) + is_tautology = true; + } + if (is_tautology) + continue; + a2 = static_cast<_assumption_set>(c->assumptions()); + auto cls = mk_clause(m_lemma.size(), m_lemma.data(), false, m_asm.mk_join(a1, a2)); + + IF_VERBOSE(3, + if (cls) { + verbose_stream() << "x" << x << " * " << l.A << " = " << l.B << "\n"; + display(verbose_stream(), *c) << " -> "; + display(verbose_stream(), *cls) << "\n"; + }); + if (cls) + compute_occurs(*cls); + } + // track updates for model reconstruction + m_bounds.push_back(l); + m_bounds.push_back(h); + } + + // + // Subsumption simplification + // + void subsumption_simplify() { + compute_occurs(); + for (unsigned v = m_var_occurs.size(); v-- > 0; ) { + auto& clauses = m_var_occurs[v]; + for (auto c : clauses) { + if (c->is_marked() || c->is_removed()) + continue; + c->mark(); + for (auto c2 : clauses) { + if (c == c2 || c2->is_removed()) + continue; + if (subsumes(*c, *c2)) { + IF_VERBOSE(3, display(verbose_stream() << "subsumes ", *c); + display(verbose_stream() << " ", *c2) << "\n"); + c2->set_removed(); + } + } + } + } + for (auto c : m_clauses) + c->unmark(); + + cleanup_removed(); + } + + // does c1 subsume c2? + bool subsumes(clause const& c1, clause const& c2) { + if (c1.size() > c2.size()) + return false; + if ((c1.var_hash() & c2.var_hash()) != c1.var_hash()) + return false; + for (auto lit1 : c1) { + if (!any_of(c2, [&](auto lit2) { return subsumes(lit1, lit2); })) + return false; + } + return true; + } + + bool subsumes(literal lit1, literal lit2) { + if (lit1 == lit2) + return true; + + atom* a1 = m_atoms[lit1.var()]; + atom* a2 = m_atoms[lit2.var()]; + if (!a1 || !a2) + return false; + + // use m_pm.ge(p1, p2) + // whenever lit1 = p1 < 0, lit2 = p2 < 0 + // or lit1 = p1 < 0, lit2 = !(p2 > 0) + // or lit1 = !(p1 > 0), lit2 = !(p2 > 0) + // use m_pm.ge(p2, p1) + // whenever lit1 = p1 > 0, lit2 = p2 > 0 + // or lit1 = !(p1 < 0), lit2 = !(p2 < 0) + // or lit1 = p1 > 0, lit2 = !(p2 < 0) + // or lit1 = !(p1 > 0), lit2 = p2 < 0 + // + if (a1->is_ineq_atom() && a2->is_ineq_atom()) { + auto& i1 = *to_ineq_atom(a1); + auto& i2 = *to_ineq_atom(a2); + auto is_lt1 = !lit1.sign() && a1->get_kind() == atom::kind::LT; + auto is_le1 = lit1.sign() && a1->get_kind() == atom::kind::GT; + auto is_gt1 = !lit1.sign() && a1->get_kind() == atom::kind::GT; + auto is_ge1 = lit1.sign() && a1->get_kind() == atom::kind::LT; + + auto is_lt2 = !lit2.sign() && a2->get_kind() == atom::kind::LT; + auto is_le2 = lit2.sign() && a2->get_kind() == atom::kind::GT; + auto is_gt2 = !lit2.sign() && a2->get_kind() == atom::kind::GT; + auto is_ge2 = lit2.sign() && a2->get_kind() == atom::kind::LT; + + auto check_ge = (is_lt1 && (is_lt2 || is_le2)) || (is_le1 && is_le2); + auto check_le = (is_gt1 && (is_gt2 || is_ge2)) || (is_ge1 && is_ge2); + + if (i1.size() != i2.size()) + ; + else if (check_ge) { + for (unsigned i = 0; i < i1.size(); ++i) + if (!m_pm.ge(i1.p(i), i2.p(i))) + return false; + return true; + } + else if (check_le) { + for (unsigned i = 0; i < i1.size(); ++i) + if (!m_pm.ge(i2.p(i), i1.p(i))) + return false; + return true; + } + } + return false; + } + + // + // Equality simplificadtion (TODO, this should is deprecated by fm) + // + bool solve_eqs() { polynomial_ref p(m_pm), q(m_pm); var v; init_var_signs(); @@ -2704,14 +3240,26 @@ namespace nlsat { change = false; for (clause* c : m_clauses) { if (solve_var(*c, v, p, q)) { - q = -q; + if (!m_pm.is_const(p)) + continue; + // optional throttles to restrict where solved variables are used + if (false && !m_pm.is_linear(q)) + continue; + if (false && !m_pm.is_univariate(q)) + continue; + bool is_small = true; + for (unsigned i = 0; i < m_pm.size(q) && is_small ; ++i) { + auto const& c = m_pm.coeff(q, i); + is_small &= m_pm.m().is_small(c); + } + if (!is_small && false) + continue; TRACE("nlsat", tout << "p: " << p << "\nq: " << q << "\n x" << v << "\n";); - m_patch_var.push_back(v); - m_patch_num.push_back(q); - m_patch_denom.push_back(p); - del_clause(c, m_clauses); - if (!substitute_var(v, p, q)) + m_bounds.push_back(bound_constraint(v, p, q, false, nullptr)); + + if (!substitute_var(v, p, q, *c)) return false; + del_clause(c, m_clauses); TRACE("nlsat", display(tout << "simplified\n");); change = true; break; @@ -2721,84 +3269,185 @@ namespace nlsat { return true; } + // Eliminated variables are tracked in m_bounds. + // Each element in m_bounds tracks the eliminated variable and an upper or lower bound + // that has to be satisfied. Variables that are eliminated through equalities are tracked + // by non-strict bounds. A satisfiable solution is required to provide an evaluation that + // is consistent with the bounds. For equalities, the non-strict lower or upper bound can + // always be assigned as a value to the variable. + void fix_patch() { - for (unsigned i = m_patch_var.size(); i-- > 0; ) { - var v = m_patch_var[i]; - poly* q = m_patch_num.get(i); - poly* p = m_patch_denom.get(i); - scoped_anum pv(m_am), qv(m_am), val(m_am); - m_pm.eval(p, m_assignment, pv); - m_pm.eval(q, m_assignment, qv); - SASSERT(!m_am.is_zero(pv)); - val = qv / pv; - TRACE("nlsat", - m_display_var(tout << "patch v" << v << " ", v) << "\n"; - if (m_assignment.is_assigned(v)) m_am.display(tout << "previous value: ", m_assignment.value(v)); tout << "\n"; - m_am.display(tout << "updated value: ", val); tout << "\n"; - ); - m_assignment.set_core(v, val); + m_lo.reset(); m_hi.reset(); + for (auto& b : m_bounds) + m_assignment.reset(b.x); + for (unsigned i = m_bounds.size(); i-- > 0; ) + fix_patch(m_bounds[i]); + } + + // x is unassigned, lo < x -> x <- lo + 1 + // x is unassigned, x < hi -> x <- hi - 1 + // x is unassigned, lo <= x -> x <- lo + // x is unassigned, x <= hi -> x <- hi + // x is assigned above hi, lo is strict lo < x < hi -> set x <- (lo + hi)/2 + // x is assigned below hi, above lo -> no-op + // x is assigned below lo, hi is strict lo < x < hi -> set x <-> (lo + hi)/2 + // x is assigned above hi, x <= hi -> x <- hi + // x is assigned blow lo, lo <= x -> x <- lo + void fix_patch(bound_constraint& b) { + var x = b.x; + scoped_anum Av(m_am), Bv(m_am), val(m_am); + m_pm.eval(b.A, m_assignment, Av); + m_pm.eval(b.B, m_assignment, Bv); + m_am.neg(Bv); + val = Bv / Av; + // Ax >= B + // is-lower : A > 0 + // is-upper: A < 0 + // x <- B / A + bool is_lower = m_am.is_pos(Av); + TRACE("nlsat", + m_display_var(tout << "patch v" << x << " ", x) << "\n"; + if (m_assignment.is_assigned(x)) m_am.display(tout << "previous value: ", m_assignment.value(x)); tout << "\n"; + m_am.display(tout << "updated value: ", val); tout << "\n"; + ); + + if (!m_assignment.is_assigned(x)) { + if (!b.is_strict) + m_assignment.set_core(x, val); + else if (is_lower) + m_assignment.set_core(x, val + 1); + else + m_assignment.set_core(x, val - 1); + } + else { + auto& aval = m_assignment.value(x); + if (is_lower) { + // lo < value(x), lo < x -> x is unchanged + if (b.is_strict && m_am.lt(val, aval)) + ; + else if (!b.is_strict && m_am.le(val, aval)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + // aval < lo < x, hi is unassigned: x <- lo + 1 + else if (!m_hi.is_assigned(x)) + m_assignment.set_core(x, val + 1); + // aval < lo < x, hi is assigned: x <- (lo + hi) / 2 + else { + scoped_anum mid(m_am); + m_am.add(m_hi.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + else { + // dual to lower bounds + if (b.is_strict && m_am.lt(aval, val)) + ; + else if (!b.is_strict && m_am.le(aval, val)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + else if (!m_lo.is_assigned(x)) + m_assignment.set_core(x, val - 1); + else { + scoped_anum mid(m_am); + m_am.add(m_lo.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + } + + if (is_lower) { + if (!m_lo.is_assigned(x) || m_am.lt(m_lo.value(x), val)) + m_lo.set_core(x, val); } + else { + if (!m_hi.is_assigned(x) || m_am.gt(m_hi.value(x), val)) + m_hi.set_core(x, val); + } } - bool substitute_var(var x, poly* p, poly* q) { - bool is_sat = true; - polynomial_ref pr(m_pm); + literal substitute_var(var x, poly* p, poly* q, literal lit) { + auto b = lit.var(); + auto a = m_atoms[b]; + if (!a) + return lit; + SASSERT(a->is_ineq_atom()); + auto& a1 = *to_ineq_atom(a); + auto r = substitute_var(x, p, q, a1); + if (r == null_literal) + r = lit; + else if (lit.sign()) + r.neg(); + return r; + } + + literal substitute_var(var x, poly* p, poly* q, ineq_atom const& a) { + unsigned sz = a.size(); + bool_vector even; + polynomial_ref pr(m_pm), qq(q, m_pm); + qq = -qq; polynomial_ref_vector ps(m_pm); + bool change = false; + auto k = a.get_kind(); + for (unsigned i = 0; i < sz; ++i) { + poly* po = a.p(i); + m_pm.substitute(po, x, qq, p, pr); + change |= pr != po; + TRACE("nlsat", tout << pr << "\n";); + if (m_pm.is_zero(pr)) { + ps.reset(); + even.reset(); + ps.push_back(pr); + even.push_back(false); + break; + } + if (m_pm.is_const(pr)) { + if (!a.is_even(i) && m_pm.m().is_neg(m_pm.coeff(pr, 0))) + k = atom::flip(k); + continue; + } + ps.push_back(pr); + even.push_back(a.is_even(i)); + } + if (!change) + return null_literal; + return mk_ineq_literal(k, ps.size(), ps.data(), even.data()); + } + bool substitute_var(var x, poly* p, poly* q, clause& src) { u_map b2l; scoped_literal_vector lits(m_solver); - bool_vector even; unsigned num_atoms = m_atoms.size(); for (unsigned j = 0; j < num_atoms; ++j) { atom* a = m_atoms[j]; if (a && a->is_ineq_atom()) { ineq_atom const& a1 = *to_ineq_atom(a); - unsigned sz = a1.size(); - ps.reset(); - even.reset(); - bool change = false; - auto k = a1.get_kind(); - for (unsigned i = 0; i < sz; ++i) { - poly * po = a1.p(i); - m_pm.substitute(po, x, q, p, pr); - change |= pr != po; - TRACE("nlsat", tout << pr << "\n";); - if (m_pm.is_zero(pr)) { - ps.reset(); - even.reset(); - ps.push_back(pr); - even.push_back(false); - break; - } - if (m_pm.is_const(pr)) { - if (!a1.is_even(i) && m_pm.m().is_neg(m_pm.coeff(pr, 0))) { - k = atom::flip(k); - } - continue; - } - ps.push_back(pr); - even.push_back(a1.is_even(i)); - } - if (!change) continue; - literal l = mk_ineq_literal(k, ps.size(), ps.data(), even.data()); + literal l = substitute_var(x, p, q, a1); + if (l == null_literal) + continue; lits.push_back(l); if (a1.m_bool_var != l.var()) { b2l.insert(a1.m_bool_var, l); } } } - is_sat = update_clauses(b2l); - return is_sat; + return update_clauses(b2l, src); } - bool update_clauses(u_map const& b2l) { + bool update_clauses(u_map const& b2l, clause& src) { bool is_sat = true; literal_vector lits; clause_vector to_delete; unsigned n = m_clauses.size(); + auto a1 = static_cast<_assumption_set>(src.assumptions()); for (unsigned i = 0; i < n; ++i) { clause* c = m_clauses[i]; + if (c == &src) + continue; lits.reset(); bool changed = false; bool is_tautology = false; @@ -2827,7 +3476,9 @@ namespace nlsat { is_sat = false; } else { - mk_clause(lits.size(), lits.data(), c->is_learned(), static_cast<_assumption_set>(c->assumptions())); + auto a2 = static_cast<_assumption_set>(c->assumptions()); + auto a = m_asm.mk_join(a1, a2); + mk_clause(lits.size(), lits.data(), c->is_learned(), a); } } } @@ -2855,12 +3506,14 @@ namespace nlsat { \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. */ lbool is_cmp0(clause const& c, var& v) { - if (!is_unit_ineq(c)) return l_undef; + if (!is_unit_ineq(c)) + return l_undef; literal lit = c[0]; ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); bool sign = lit.sign(); poly * p0; - if (!is_single_poly(a, p0)) return l_undef; + if (!is_single_poly(a, p0)) + return l_undef; if (m_pm.is_var(p0, v)) { if (!sign && a.get_kind() == atom::GT) { return l_true; @@ -2924,18 +3577,19 @@ namespace nlsat { */ bool solve_var(clause& c, var& v, polynomial_ref& p, polynomial_ref& q) { poly* p0; - if (!is_unit_eq(c)) return false; + if (!is_unit_eq(c)) + return false; ineq_atom & a = *to_ineq_atom(m_atoms[c[0].var()]); - if (!is_single_poly(a, p0)) return false; + if (!is_single_poly(a, p0)) + return false; var mx = max_var(p0); - if (mx >= m_is_int.size()) return false; + if (mx >= m_is_int.size()) + return false; for (var x = 0; x <= mx; ++x) { - if (is_int(x)) - continue; if (1 == m_pm.degree(p0, x)) { p = m_pm.coeff(p0, x, 1, q); - if (!m_pm.is_const(p)) - break; + if (!is_invertible(x, p)) + continue; switch (m_pm.sign(p, m_var_signs)) { case l_true: v = x; @@ -2951,7 +3605,15 @@ namespace nlsat { } } return false; - } + } + + + bool is_unit(polynomial_ref const& p) { + if (!m_pm.is_const(p)) + return false; + auto const& c = m_pm.coeff(p, 0); + return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); + } // ----------------------- // @@ -3090,8 +3752,7 @@ namespace nlsat { } std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { - m_pm.display_smt2(out, p, proc); - return out; + return m_pm.display_smt2(out, p, proc); } std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { From aa1a59639445739539003d5b7a0f0ef5502cae08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 17:05:40 -0700 Subject: [PATCH 047/414] add doc-string --- src/nlsat/nlsat_solver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 8d9e563f882..a1b653a8988 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -2761,6 +2761,7 @@ namespace nlsat { } // + // Remove unconstrained assertions. // bool elim_uncnstr() { // compute variable occurrences From 1ef43540807559decd41329b8993341ed55fe124 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2024 17:52:00 -0700 Subject: [PATCH 048/414] fix build Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_solver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index a1b653a8988..b155a0f376c 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -2981,7 +2981,9 @@ namespace nlsat { m_display_var(verbose_stream(), x); display(verbose_stream() << " ", *c) << "\n"; bound_constraint l(x, A, B, false, c); - bound_constraint h(x, -A, -B, false, c); + A = -A; + B = -B; + bound_constraint h(x, A, B, false, c); apply_fm_equality(x, clauses, l, h); return true; } From 869643a551d8edcd707c9d789d8f76f314f0c3e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 10:07:37 -0700 Subject: [PATCH 049/414] fix memory leak --- src/nlsat/nlsat_solver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index b155a0f376c..98ee5233ec7 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -3033,7 +3033,7 @@ namespace nlsat { var x, ptr_vector& clauses, vector& lo, vector& hi) { - polynomial_ref C(m_pm); + polynomial_ref C(m_pm), D(m_pm); for (auto c : clauses) c->set_removed(); @@ -3043,7 +3043,8 @@ namespace nlsat { // h.A * x + h.B, h.is_strict; h.A > 0 // (l.A x + l.B)*h.A + (h.A x + h.B)*|l.A| >= 0 C = m_pm.mul(l.B, h.A); - C = m_pm.sub(C, m_pm.mul(h.B, l.A)); + D = m_pm.mul(h.B, l.A); + C = m_pm.sub(C, D); poly* p = C.get(); bool is_even = false; m_lemma.reset(); @@ -3101,6 +3102,7 @@ namespace nlsat { auto a1 = static_cast<_assumption_set>(l.c->assumptions()); auto a2 = static_cast<_assumption_set>(h.c->assumptions()); a1 = m_asm.mk_join(a1, a2); + m_lemma_assumptions = a1; // TODO: this can also replace solve_eqs for (auto c : clauses) { From 04c55c34e523c83ba4fd3b21ab927b46b77403ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 14:45:15 -0700 Subject: [PATCH 050/414] fix unsoundness bug Signed-off-by: Nikolaj Bjorner --- src/math/polynomial/polynomial.cpp | 21 +++++++++++++++++++++ src/nlsat/nlsat_solver.cpp | 15 +++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 44a5c8864ac..97f0e25e5f5 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -6317,6 +6317,27 @@ namespace polynomial { return R.mk(); } + // x*q = p + // + // md = degree of x in p + // P = m0 + ... + // m0 = x^dm*m1 + // m1 * p^dm * q^{md - dm} + // P' = m1 + ... + // property would be that x*q = p => P > 0 <=> P' > 0 + // requires that q > 0 + // Reasoning: + // P > 0 + // <=> { since q > 0 } + // q^md * P > 0 + // <=> + // q^md*x^dm*m0 + .. > 0 + // <=> + // q^{md-dm}*(xq)^dm*m0 + ... > 0 + // <=> + // q^{md-dm}*p^dm + .. > 0 + // <=> + // P' > 0 void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) { unsigned md = degree(r, x); if (md == 0) { diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 98ee5233ec7..d986f967a55 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -2742,10 +2742,8 @@ namespace nlsat { while (elim_uncnstr()) ; - while (fm()) ; - if (!solve_eqs()) return false; @@ -3017,6 +3015,8 @@ namespace nlsat { if (apply_fm_equality(x, clauses, lo, hi)) return true; + return false; + if (!all_solved) return false; @@ -3104,6 +3104,13 @@ namespace nlsat { a1 = m_asm.mk_join(a1, a2); m_lemma_assumptions = a1; + polynomial_ref A(l.A), B(l.B); + + if (m_pm.is_neg(l.A)) { + A = -A; + B = -B; + } + // TODO: this can also replace solve_eqs for (auto c : clauses) { if (c->is_removed()) @@ -3114,7 +3121,7 @@ namespace nlsat { m_lemma.reset(); bool is_tautology = false; for (literal lit : *c) { - lit = substitute_var(x, l.A, l.B, lit); + lit = substitute_var(x, A, B, lit); m_lemma.push_back(lit); if (lit == true_literal) is_tautology = true; @@ -3126,7 +3133,7 @@ namespace nlsat { IF_VERBOSE(3, if (cls) { - verbose_stream() << "x" << x << " * " << l.A << " = " << l.B << "\n"; + m_display_var(verbose_stream(), x) << " * " << l.A << " = " << l.B << "\n"; display(verbose_stream(), *c) << " -> "; display(verbose_stream(), *cls) << "\n"; }); From 231a985bf9beb6aab9719324bd1f2e74f90c3d14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 16:17:06 -0700 Subject: [PATCH 051/414] add virtual destructor to z3::object class Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index cb1446a08c3..5d0d9425e52 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -470,6 +470,7 @@ namespace z3 { context * m_ctx; public: object(context & c):m_ctx(&c) {} + virtual ~object() = default; context & ctx() const { return *m_ctx; } Z3_error_code check_error() const { return m_ctx->check_error(); } friend void check_context(object const & a, object const & b); From 19eb7225ea6bc4fab1bc9aa742e203e5344b38f0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 16:20:05 -0700 Subject: [PATCH 052/414] add virtual destructor to z3::object class Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 5d0d9425e52..4025be4fdbf 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -509,7 +509,7 @@ namespace z3 { object::operator=(o); return *this; } - ~param_descrs() { Z3_param_descrs_dec_ref(ctx(), m_descrs); } + ~param_descrs() override { Z3_param_descrs_dec_ref(ctx(), m_descrs); } static param_descrs simplify_param_descrs(context& c) { return param_descrs(c, Z3_simplify_get_param_descrs(c)); } static param_descrs global_param_descrs(context& c) { return param_descrs(c, Z3_get_global_param_descrs(c)); } @@ -527,7 +527,7 @@ namespace z3 { public: params(context & c):object(c) { m_params = Z3_mk_params(c); Z3_params_inc_ref(ctx(), m_params); } params(params const & s):object(s), m_params(s.m_params) { Z3_params_inc_ref(ctx(), m_params); } - ~params() { Z3_params_dec_ref(ctx(), m_params); } + ~params() override { Z3_params_dec_ref(ctx(), m_params); } operator Z3_params() const { return m_params; } params & operator=(params const & s) { Z3_params_inc_ref(s.ctx(), s.m_params); @@ -555,7 +555,7 @@ namespace z3 { ast(context & c):object(c), m_ast(0) {} ast(context & c, Z3_ast n):object(c), m_ast(n) { Z3_inc_ref(ctx(), m_ast); } ast(ast const & s) :object(s), m_ast(s.m_ast) { Z3_inc_ref(ctx(), m_ast); } - ~ast() { if (m_ast) { Z3_dec_ref(*m_ctx, m_ast); } } + ~ast() override { if (m_ast) { Z3_dec_ref(*m_ctx, m_ast); } } operator Z3_ast() const { return m_ast; } operator bool() const { return m_ast != 0; } ast & operator=(ast const & s) { @@ -593,7 +593,7 @@ namespace z3 { ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } ast_vector_tpl(context& c, ast_vector_tpl const& src): object(c) { init(Z3_ast_vector_translate(src.ctx(), src, c)); } - ~ast_vector_tpl() { Z3_ast_vector_dec_ref(ctx(), m_vector); } + ~ast_vector_tpl() override { Z3_ast_vector_dec_ref(ctx(), m_vector); } operator Z3_ast_vector() const { return m_vector; } unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } @@ -2528,7 +2528,7 @@ namespace z3 { public: func_entry(context & c, Z3_func_entry e):object(c) { init(e); } func_entry(func_entry const & s):object(s) { init(s.m_entry); } - ~func_entry() { Z3_func_entry_dec_ref(ctx(), m_entry); } + ~func_entry() override { Z3_func_entry_dec_ref(ctx(), m_entry); } operator Z3_func_entry() const { return m_entry; } func_entry & operator=(func_entry const & s) { Z3_func_entry_inc_ref(s.ctx(), s.m_entry); @@ -2551,7 +2551,7 @@ namespace z3 { public: func_interp(context & c, Z3_func_interp e):object(c) { init(e); } func_interp(func_interp const & s):object(s) { init(s.m_interp); } - ~func_interp() { Z3_func_interp_dec_ref(ctx(), m_interp); } + ~func_interp() override { Z3_func_interp_dec_ref(ctx(), m_interp); } operator Z3_func_interp() const { return m_interp; } func_interp & operator=(func_interp const & s) { Z3_func_interp_inc_ref(s.ctx(), s.m_interp); @@ -2585,7 +2585,7 @@ namespace z3 { model(context & c, Z3_model m):object(c) { init(m); } model(model const & s):object(s) { init(s.m_model); } model(model& src, context& dst, translate) : object(dst) { init(Z3_model_translate(src.ctx(), src, dst)); } - ~model() { Z3_model_dec_ref(ctx(), m_model); } + ~model() override { Z3_model_dec_ref(ctx(), m_model); } operator Z3_model() const { return m_model; } model & operator=(model const & s) { Z3_model_inc_ref(s.ctx(), s.m_model); @@ -2665,7 +2665,7 @@ namespace z3 { stats(context & c):object(c), m_stats(0) {} stats(context & c, Z3_stats e):object(c) { init(e); } stats(stats const & s):object(s) { init(s.m_stats); } - ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } + ~stats() override { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } operator Z3_stats() const { return m_stats; } stats & operator=(stats const & s) { Z3_stats_inc_ref(s.ctx(), s.m_stats); @@ -2747,7 +2747,7 @@ namespace z3 { solver(context & c, solver const& src, translate): object(c) { Z3_solver s = Z3_solver_translate(src.ctx(), src, c); check_error(); init(s); } solver(solver const & s):object(s) { init(s.m_solver); } solver(solver const& s, simplifier const& simp); - ~solver() { Z3_solver_dec_ref(ctx(), m_solver); } + ~solver() override { Z3_solver_dec_ref(ctx(), m_solver); } operator Z3_solver() const { return m_solver; } solver & operator=(solver const & s) { Z3_solver_inc_ref(s.ctx(), s.m_solver); @@ -2968,7 +2968,7 @@ namespace z3 { goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } goal(context & c, Z3_goal s):object(c) { init(s); } goal(goal const & s):object(s) { init(s.m_goal); } - ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } + ~goal() override { Z3_goal_dec_ref(ctx(), m_goal); } operator Z3_goal() const { return m_goal; } goal & operator=(goal const & s) { Z3_goal_inc_ref(s.ctx(), s.m_goal); @@ -3026,7 +3026,7 @@ namespace z3 { public: apply_result(context & c, Z3_apply_result s):object(c) { init(s); } apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } - ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } + ~apply_result() override { Z3_apply_result_dec_ref(ctx(), m_apply_result); } operator Z3_apply_result() const { return m_apply_result; } apply_result & operator=(apply_result const & s) { Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); @@ -3051,7 +3051,7 @@ namespace z3 { tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } tactic(context & c, Z3_tactic s):object(c) { init(s); } tactic(tactic const & s):object(s) { init(s.m_tactic); } - ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } + ~tactic() override { Z3_tactic_dec_ref(ctx(), m_tactic); } operator Z3_tactic() const { return m_tactic; } tactic & operator=(tactic const & s) { Z3_tactic_inc_ref(s.ctx(), s.m_tactic); @@ -3137,7 +3137,7 @@ namespace z3 { simplifier(context & c, char const * name):object(c) { Z3_simplifier r = Z3_mk_simplifier(c, name); check_error(); init(r); } simplifier(context & c, Z3_simplifier s):object(c) { init(s); } simplifier(simplifier const & s):object(s) { init(s.m_simplifier); } - ~simplifier() { Z3_simplifier_dec_ref(ctx(), m_simplifier); } + ~simplifier() override { Z3_simplifier_dec_ref(ctx(), m_simplifier); } operator Z3_simplifier() const { return m_simplifier; } simplifier & operator=(simplifier const & s) { Z3_simplifier_inc_ref(s.ctx(), s.m_simplifier); @@ -3179,7 +3179,7 @@ namespace z3 { probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } probe(context & c, Z3_probe s):object(c) { init(s); } probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } + ~probe() public override { Z3_probe_dec_ref(ctx(), m_probe); } operator Z3_probe() const { return m_probe; } probe & operator=(probe const & s) { Z3_probe_inc_ref(s.ctx(), s.m_probe); @@ -3273,7 +3273,7 @@ namespace z3 { object::operator=(o); return *this; } - ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } + ~optimize() override { Z3_optimize_dec_ref(ctx(), m_opt); } operator Z3_optimize() const { return m_opt; } void add(expr const& e) { assert(e.is_bool()); @@ -3354,7 +3354,7 @@ namespace z3 { public: fixedpoint(context& c):object(c) { m_fp = Z3_mk_fixedpoint(c); Z3_fixedpoint_inc_ref(c, m_fp); } fixedpoint(fixedpoint const & o):object(o), m_fp(o.m_fp) { Z3_fixedpoint_inc_ref(ctx(), m_fp); } - ~fixedpoint() { Z3_fixedpoint_dec_ref(ctx(), m_fp); } + ~fixedpoint() override { Z3_fixedpoint_dec_ref(ctx(), m_fp); } fixedpoint & operator=(fixedpoint const & o) { Z3_fixedpoint_inc_ref(o.ctx(), o.m_fp); Z3_fixedpoint_dec_ref(ctx(), m_fp); From 2f022782270bf4426ddd1fe2f11dbfefd47df107 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 16:35:25 -0700 Subject: [PATCH 053/414] add virtual destructor to z3::object class 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 4025be4fdbf..81a67066eaa 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -3179,7 +3179,7 @@ namespace z3 { probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } probe(context & c, Z3_probe s):object(c) { init(s); } probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() public override { Z3_probe_dec_ref(ctx(), m_probe); } + ~probe() override { Z3_probe_dec_ref(ctx(), m_probe); } operator Z3_probe() const { return m_probe; } probe & operator=(probe const & s) { Z3_probe_inc_ref(s.ctx(), s.m_probe); From 8f4ffc7caf19ba9c7b4c63935ef19cef215e847f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2024 20:50:52 -0700 Subject: [PATCH 054/414] add logging for first conflict Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_solver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index d986f967a55..eed908617d7 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1574,7 +1574,7 @@ namespace nlsat { } void gc() { - if (m_learned.size() <= 2*m_clauses.size()) + if (m_learned.size() <= 4*m_clauses.size()) return; reset_watches(); reinit_cache(); @@ -1595,7 +1595,7 @@ namespace nlsat { unsigned m_next_conflict = 100; void log() { - if (m_conflicts < m_next_conflict) + if (m_conflicts != 1 && m_conflicts < m_next_conflict) return; m_next_conflict += 100; IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_conflicts << " :decisions " << m_decisions << " :propagations " << m_propagations << " :clauses " << m_clauses.size() << " :learned " << m_learned.size() << ")\n"); @@ -1605,7 +1605,7 @@ namespace nlsat { lbool search_check() { lbool r = l_undef; m_conflicts = 0; - m_next_conflict = 100; + m_next_conflict = 0; while (true) { r = search(); if (r != l_true) break; @@ -3015,7 +3015,7 @@ namespace nlsat { if (apply_fm_equality(x, clauses, lo, hi)) return true; - return false; + // return false; if (!all_solved) return false; From f9176fb4b72156bb8ceca7ebe1b817d9e87baf85 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 May 2024 11:39:52 -0700 Subject: [PATCH 055/414] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfbaea599b7..e6db550c010 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ to Z3's C API. For more information, see [MachineArithmetic/README.md](https://g * Default input format is [SMTLIB2](http://smtlib.cs.uiowa.edu) * Other native foreign function interfaces: -* [C++ API](https://z3prover.github.io/api/html/group__cppapi.html) +* [C++ API](https://z3prover.github.io/api/html/namespacez3.html) * [.NET API](https://z3prover.github.io/api/html/namespace_microsoft_1_1_z3.html) * [Java API](https://z3prover.github.io/api/html/namespacecom_1_1microsoft_1_1z3.html) * [Python API](https://z3prover.github.io/api/html/namespacez3py.html) (also available in [pydoc format](https://z3prover.github.io/api/html/z3.html)) From fc6c4c98e26fc0bc1c5c50e1376ff9201f94b880 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 May 2024 14:52:49 -0700 Subject: [PATCH 056/414] initial warppers for seq-map/seq-fold Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 4 ++++ src/api/api_seq.cpp | 5 +++++ src/api/api_util.h | 17 +++++++++++++++++ src/api/python/z3/z3.py | 13 +++++++++++++ src/api/z3_api.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 424b361f3ea..9311671102d 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1310,6 +1310,10 @@ extern "C" { case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX; case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE; case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE; + case OP_SEQ_MAP: return Z3_OP_SEQ_MAP; + case OP_SEQ_MAPI: return Z3_OP_SEQ_MAPI; + case OP_SEQ_FOLDL: return Z3_OP_SEQ_FOLDL; + case OP_SEQ_FOLDLI: return Z3_OP_SEQ_FOLDLI; case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE; case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT; diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 6a9d0f81c1d..2b87ef290fb 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -348,5 +348,10 @@ extern "C" { MK_UNARY(Z3_mk_char_from_bv, mk_c(c)->get_char_fid(), OP_CHAR_FROM_BV, SKIP); MK_UNARY(Z3_mk_char_is_digit, mk_c(c)->get_char_fid(), OP_CHAR_IS_DIGIT, SKIP); + MK_BINARY(Z3_mk_seq_map, mk_c(c)->get_seq_fid(), OP_SEQ_MAP, SKIP); + MK_TERNARY(Z3_mk_seq_mapi, mk_c(c)->get_seq_fid(), OP_SEQ_MAPI, SKIP); + MK_TERNARY(Z3_mk_seq_foldl, mk_c(c)->get_seq_fid(), OP_SEQ_FOLDL, SKIP); + MK_FOURARY(Z3_mk_seq_foldli, mk_c(c)->get_seq_fid(), OP_SEQ_FOLDLI, SKIP); + }; diff --git a/src/api/api_util.h b/src/api/api_util.h index 174d7514499..7b16229f4b9 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -160,6 +160,23 @@ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ MK_TERNARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } +#define MK_FOURARY_BODY(NAME, FID, OP, EXTRA_CODE) \ + Z3_TRY; \ + RESET_ERROR_CODE(); \ + EXTRA_CODE; \ + expr * args[4] = { to_expr(n1), to_expr(n2), to_expr(n3), to_expr(n4) }; \ + ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 4, args); \ + mk_c(c)->save_ast_trail(a); \ + check_sorts(c, a); \ + RETURN_Z3(of_ast(a)); \ + Z3_CATCH_RETURN(0); + +#define MK_FOURARY(NAME, FID, OP, EXTRA_CODE) \ + Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_ast n3, Z3_ast n4) { \ + LOG_ ## NAME(c, n1, n2, n3, n4); \ + MK_FOURARY_BODY(NAME, FID, OP, EXTRA_CODE); \ +} + #define MK_NARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, unsigned num_args, Z3_ast const* args) { \ Z3_TRY; \ diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 9a3dadda23e..44a4aba845f 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11210,6 +11210,19 @@ def Length(s): s = _coerce_seq(s) return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast()), s.ctx) +def SeqMap(f, s): + """Map function 'f' over sequence 's'""" + ctx = _get_ctx2(f, s) + s = _coerce_seq(s, ctx) + return _to_expr_ref(Z3_mk_seq_map(s.ctx_ref(), f.as_ast(), s.as_ast()), ctx) + +def SeqMapI(f, i, s): + """Map function 'f' over sequence 's' at index 'i'""" + ctx = _get_ctx(f, s) + s = _coerce_seq(s, ctx) + if not is_expr(i): + i = _py2expr(i) + return _to_expr_ref(Z3_mk_seq_mapi(s.ctx_ref(), f.as_ast(), i.as_ast(), s.as_ast()), ctx) def StrToInt(s): """Convert string expression to integer diff --git a/src/api/z3_api.h b/src/api/z3_api.h index cbf9803dbbf..312acc26845 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1193,6 +1193,10 @@ typedef enum { Z3_OP_SEQ_LAST_INDEX, Z3_OP_SEQ_TO_RE, Z3_OP_SEQ_IN_RE, + Z3_OP_SEQ_MAP, + Z3_OP_SEQ_MAPI, + Z3_OP_SEQ_FOLDL, + Z3_OP_SEQ_FOLDLI, // strings Z3_OP_STR_TO_INT, @@ -3798,6 +3802,30 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast s, Z3_ast substr); + /** + \brief Create a map of the function \c f over the sequence \c s. + def_API('Z3_mk_seq_map', AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_map(Z3_context c, Z3_ast f, Z3_ast s); + + /** + \brief Create a map of the function \c f over the sequence \c s starting at index \c i. + def_API('Z3_mk_seq_mapi', AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_mapi(Z3_context c, Z3_ast f, Z3_ast i, Z3_ast s); + + /** + \brief Create a fold of the function \c f over the sequence \c s with accumulator a. + def_API('Z3_mk_seq_foldl', AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_foldl(Z3_context c, Z3_ast f, Z3_ast a, Z3_ast s); + + /** + \brief Create a fold with index tracking of the function \c f over the sequence \c s with accumulator \c a starting at index \c i. + def_API('Z3_mk_seq_foldli', AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_foldli(Z3_context c, Z3_ast f, Z3_ast i, Z3_ast a, Z3_ast s); + /** \brief Convert string to integer. From c7529d0b25c7195d6786b1c36aef3d0611bd65fe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 May 2024 14:56:18 -0700 Subject: [PATCH 057/414] expose fold as well Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 44a4aba845f..c3f40a52bc7 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11224,6 +11224,19 @@ def SeqMapI(f, i, s): i = _py2expr(i) return _to_expr_ref(Z3_mk_seq_mapi(s.ctx_ref(), f.as_ast(), i.as_ast(), s.as_ast()), ctx) +def SeqFoldLeft(f, a, s): + ctx = _get_ctx2(f, s) + s = _coerce_seq(s, ctx) + a = _py2expr(a) + return _to_expr_ref(Z3_mk_seq_foldl(s.ctx_ref(), f.as_ast(), a.as_ast(), s.as_ast()), ctx) + +def SeqFoldLeftI(f, i, a, s): + ctx = _get_ctx2(f, s) + s = _coerce_seq(s, ctx) + a = _py2expr(a) + i = _py2epxr(i) + return _to_expr_ref(Z3_mk_seq_foldli(s.ctx_ref(), f.as_ast(), i.as_ast(), a.as_ast(), s.as_ast()), ctx) + def StrToInt(s): """Convert string expression to integer >>> a = StrToInt("1") From b12074507859db333772d57671f3fb84a05460d9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 May 2024 20:20:05 -0700 Subject: [PATCH 058/414] add C++ bindings for sequence operations Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 81a67066eaa..81d5bcaa9b3 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2497,6 +2497,34 @@ namespace z3 { return expr(ctx, r); } + inline expr map(expr const& f, expr const& list) { + context& ctx = f.ctx(); + Z3_ast r = Z3_mk_seq_map(ctx, f, list); + ctx.check_error(); + return expr(ctx, r); + } + + inline expr mapi(expr const& f, expr const& i, expr const& list) { + context& ctx = f.ctx(); + Z3_ast r = Z3_mk_seq_mapi(ctx, f, i, list); + ctx.check_error(); + return expr(ctx, r); + } + + inline expr foldl(expr const& f, expr const& a, expr const& list) { + context& ctx = f.ctx(); + Z3_ast r = Z3_mk_seq_foldl(ctx, f, a, list); + ctx.check_error(); + return expr(ctx, r); + } + + inline expr foldli(expr const& f, expr const& i, expr const& a, expr const& list) { + context& ctx = f.ctx(); + Z3_ast r = Z3_mk_seq_foldli(ctx, f, i, a, list); + ctx.check_error(); + return expr(ctx, r); + } + inline expr mk_or(expr_vector const& args) { array _args(args); Z3_ast r = Z3_mk_or(args.ctx(), _args.size(), _args.ptr()); From efc893263a4fe107b3c62cea02aaa355b2f34ec8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 May 2024 20:54:39 -0700 Subject: [PATCH 059/414] add abs function to API Signed-off-by: Nikolaj Bjorner --- src/api/api_arith.cpp | 1 + src/api/api_ast.cpp | 1 + src/api/z3_api.h | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index 7cfd5a34516..bba2cf0c343 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -123,6 +123,7 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + MK_UNARY(Z3_mk_abs, mk_c(c)->get_arith_fid(), OP_ABS, SKIP); MK_UNARY(Z3_mk_int2real, mk_c(c)->get_arith_fid(), OP_TO_REAL, SKIP); MK_UNARY(Z3_mk_real2int, mk_c(c)->get_arith_fid(), OP_TO_INT, SKIP); MK_UNARY(Z3_mk_is_int, mk_c(c)->get_arith_fid(), OP_IS_INT, SKIP); diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 9311671102d..ecbf86b3763 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1152,6 +1152,7 @@ extern "C" { case OP_REM: return Z3_OP_REM; case OP_MOD: return Z3_OP_MOD; case OP_POWER: return Z3_OP_POWER; + case OP_ABS: return Z3_OP_ABS; case OP_TO_REAL: return Z3_OP_TO_REAL; case OP_TO_INT: return Z3_OP_TO_INT; case OP_IS_INT: return Z3_OP_IS_INT; diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 312acc26845..fce5f15d14a 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1023,6 +1023,7 @@ typedef enum { Z3_OP_TO_INT, Z3_OP_IS_INT, Z3_OP_POWER, + Z3_OP_ABS, // Arrays & Sets Z3_OP_STORE = 0x300, @@ -2548,6 +2549,13 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_power(Z3_context c, Z3_ast arg1, Z3_ast arg2); + /** + \brief Take the absolute value of an integer + + def_API('Z3_mk_abs', AST, (_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_abs(Z3_context c, Z3_ast arg); + /** \brief Create less than. From e036a5bd9bf20106e174c793ea9e23f17775ce6c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 May 2024 18:06:18 -0700 Subject: [PATCH 060/414] add parameter validation to ternary and 4-ary functions for API #7219 Signed-off-by: Nikolaj Bjorner --- src/api/api_util.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/api/api_util.h b/src/api/api_util.h index 7b16229f4b9..e02ac6fee12 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -147,6 +147,9 @@ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ + CHECK_IS_EXPR(n1, nullptr); \ + CHECK_IS_EXPR(n2, nullptr); \ + CHECK_IS_EXPR(n3, nullptr); \ expr * args[3] = { to_expr(n1), to_expr(n2), to_expr(n3) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 3, args); \ mk_c(c)->save_ast_trail(a); \ @@ -164,6 +167,10 @@ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ + CHECK_IS_EXPR(n1, nullptr); \ + CHECK_IS_EXPR(n2, nullptr); \ + CHECK_IS_EXPR(n3, nullptr); \ + CHECK_IS_EXPR(n4, nullptr); \ expr * args[4] = { to_expr(n1), to_expr(n2), to_expr(n3), to_expr(n4) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 4, args); \ mk_c(c)->save_ast_trail(a); \ From 8fe357f1f213b88a679f72e3d575cae2d3e47b3a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 May 2024 22:19:33 -0700 Subject: [PATCH 061/414] Nlsat simplify (#7227) * dev branch for simplification Signed-off-by: Nikolaj Bjorner * bug fixes Signed-off-by: Nikolaj Bjorner * bugfixes Signed-off-by: Nikolaj Bjorner * fix factorization Signed-off-by: Nikolaj Bjorner * separate out simplification functionality * reorder initialization Signed-off-by: Nikolaj Bjorner * reorder initialization Signed-off-by: Nikolaj Bjorner * Update README.md * initial warppers for seq-map/seq-fold Signed-off-by: Nikolaj Bjorner * expose fold as well Signed-off-by: Nikolaj Bjorner * add C++ bindings for sequence operations Signed-off-by: Nikolaj Bjorner * add abs function to API Signed-off-by: Nikolaj Bjorner * add parameter validation to ternary and 4-ary functions for API #7219 Signed-off-by: Nikolaj Bjorner * add pre-processing and reorder Signed-off-by: Nikolaj Bjorner * add pre-processing and reorder Signed-off-by: Nikolaj Bjorner --------- Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_settings.h | 8 +- src/math/lp/nra_solver.cpp | 11 +- src/math/polynomial/algebraic_numbers.cpp | 15 +- src/math/polynomial/polynomial.cpp | 135 ++- src/math/polynomial/polynomial.h | 3 +- src/math/polynomial/polynomial_cache.cpp | 15 +- src/nlsat/CMakeLists.txt | 1 + src/nlsat/nlsat_simplify.cpp | 824 ++++++++++++++++ src/nlsat/nlsat_simplify.h | 16 + src/nlsat/nlsat_solver.cpp | 1049 +++++---------------- src/nlsat/nlsat_solver.h | 31 +- src/nlsat/nlsat_types.h | 2 + src/util/small_object_allocator.cpp | 7 +- 13 files changed, 1274 insertions(+), 843 deletions(-) create mode 100644 src/nlsat/nlsat_simplify.cpp create mode 100644 src/nlsat/nlsat_simplify.h diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 08fd365055a..d1a4be21c64 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -128,8 +128,12 @@ struct statistics { unsigned m_grobner_conflicts; unsigned m_offset_eqs; unsigned m_fixed_eqs; + ::statistics m_st; statistics() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } + void reset() { + memset(this, 0, sizeof(*this)); + m_st.reset(); + } void collect_statistics(::statistics& st) const { st.update("arith-factorizations", m_num_factorizations); st.update("arith-make-feasible", m_make_feasible); @@ -157,7 +161,7 @@ struct statistics { st.update("arith-nla-lemmas", m_nla_lemmas); st.update("arith-nra-calls", m_nra_calls); st.update("arith-bounds-improvements", m_nla_bounds_improvements); - + st.copy(m_st); } }; diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 4c1b2b3eef8..be4395f0e33 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -34,7 +34,7 @@ struct solver::imp { scoped_ptr m_values; // values provided by LRA solver scoped_ptr m_tmp1, m_tmp2; nla::core& m_nla_core; - + imp(lp::lar_solver& s, reslimit& lim, params_ref const& p, nla::core& nla_core): lra(s), m_limit(lim), @@ -180,6 +180,7 @@ struct solver::imp { } lbool r = l_undef; + statistics& st = m_nla_core.lp_settings().stats().m_st; try { r = m_nlsat->check(); } @@ -188,9 +189,11 @@ struct solver::imp { r = l_undef; } else { + m_nlsat->collect_statistics(st); throw; } } + m_nlsat->collect_statistics(st); TRACE("nra", m_nlsat->display(tout << r << "\n"); display(tout); @@ -234,6 +237,7 @@ struct solver::imp { return r; } + void add_monic_eq_bound(mon_eq const& m) { if (!lra.column_has_lower_bound(m.var()) && !lra.column_has_upper_bound(m.var())) @@ -374,6 +378,7 @@ struct solver::imp { } lbool r = l_undef; + statistics& st = m_nla_core.lp_settings().stats().m_st; try { r = m_nlsat->check(); } @@ -382,9 +387,11 @@ struct solver::imp { r = l_undef; } else { + m_nlsat->collect_statistics(st); throw; } } + m_nlsat->collect_statistics(st); switch (r) { case l_true: @@ -665,7 +672,7 @@ nlsat::anum_manager& solver::am() { scoped_anum& solver::tmp1() { return m_imp->tmp1(); } scoped_anum& solver::tmp2() { return m_imp->tmp2(); } - + void solver::updt_params(params_ref& p) { m_imp->updt_params(p); diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index a10e0dc980a..b546df706ac 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2594,27 +2594,28 @@ namespace algebraic_numbers { void int_lt(numeral const & a, numeral & b) { scoped_mpz v(qm()); + if (!a.is_basic()) + refine_until_prec(const_cast(a), 1); if (a.is_basic()) { qm().floor(basic_value(a), v); qm().dec(v); } - else { - refine_until_prec(const_cast(a), 1); - bqm().floor(qm(), lower(a.to_algebraic()), v); - } + else + bqm().floor(qm(), lower(a.to_algebraic()), v); m_wrapper.set(b, v); } void int_gt(numeral const & a, numeral & b) { scoped_mpz v(qm()); + if (!a.is_basic()) + refine_until_prec(const_cast(a), 1); if (a.is_basic()) { qm().ceil(basic_value(a), v); qm().inc(v); } - else { - refine_until_prec(const_cast(a), 1); + else bqm().ceil(qm(), upper(a.to_algebraic()), v); - } + m_wrapper.set(b, v); } diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 97f0e25e5f5..74c5e05d785 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -2504,30 +2504,139 @@ namespace polynomial { return p; } - void gcd_simplify(polynomial * p) { - if (m_manager.finite()) return; + void gcd_simplify(polynomial_ref& p, manager::ineq_type t) { auto& m = m_manager.m(); unsigned sz = p->size(); if (sz == 0) return; unsigned g = 0; - for (unsigned i = 0; i < sz; i++) { + for (unsigned i = 0; i < sz; i++) { if (!m.is_int(p->a(i))) { + gcd_simplify_slow(p, t); return; } + if (t != EQ && is_unit(p->m(i))) + continue; int j = m.get_int(p->a(i)); - if (j == INT_MIN || j == 1 || j == -1) + if (j == INT_MIN) { + gcd_simplify_slow(p, t); + return; + } + if (j == 1 || j == -1) return; g = u_gcd(abs(j), g); if (g == 1) return; } - scoped_mpz r(m), gg(m); + scoped_mpz gg(m); m.set(gg, g); + apply_gcd_simplify(gg, p, t); + } + + void apply_gcd_simplify(mpz & g, polynomial_ref& p, manager::ineq_type t) { + + auto& m = m_manager.m(); + +#if 0 + m.display(verbose_stream() << "gcd ", g); + p->display(verbose_stream() << "\n", m_manager, false); + char const* tt = ""; + switch (t) { + case ineq_type::GT: tt = ">"; break; + case ineq_type::LT: tt = "<"; break; + case ineq_type::EQ: tt = "="; break; + } + verbose_stream() << " " << tt << " 0\n ->\n"; +#endif + scoped_mpz r(m); + unsigned sz = p->size(); + m_som_buffer.reset(); for (unsigned i = 0; i < sz; ++i) { - m.div_gcd(p->a(i), gg, r); - m.set(p->a(i), r); + if (t != EQ && is_unit(p->m(i))) { + scoped_mpz one(m); + m.set(one, 1); + if (t == GT) { + // p - 2 - 1 >= 0 + // p div 2 + floor((-2 - 1 ) / 2) >= 0 + // p div 2 + floor(-3 / 2) >= 0 + // p div 2 - 2 >= 0 + // p div 2 - 1 > 0 + // + // p + k > 0 + // p + k - 1 >= 0 + // p div g + (k - 1) div g >= 0 + // p div g + (k - 1) div g + 1 > 0 + m.sub(p->a(i), one, r); + bool is_neg = m.is_neg(r); + if (is_neg) { + m.neg(r); + m.add(r, g, r); + m.sub(r, one, r); + m.div_gcd(r, g, r); + m.neg(r); + } + else { + m.div_gcd(r, g, r); + } + m.add(r, one, r); + } + else { + // p + k < 0 + // p + k + 1 <= 0 + // p div g + (k + 1 + g - 1) div g <= 0 + // p div g + (k + 1 + g - 1) div g - 1 < 0 + + m.add(p->a(i), one, r); + bool is_neg = m.is_neg(r); + + if (is_neg) { + // p - k <= 0 + // p <= k + // p div g <= k div g + // p div g - k div g <= 0 + // p div g - k div g - 1 < 0 + m.neg(r); + m.div_gcd(r, g, r); + m.neg(r); + m.sub(r, one, r); + } + else { + m.div_gcd(p->a(i), g, r); + m.add(p->a(i), g, r); + m.div_gcd(r, g, r); + m.sub(r, one, r); + } + + } + } + else { + m.div_gcd(p->a(i), g, r); + } + if (!m.is_zero(r)) + m_som_buffer.add(r, p->m(i)); + } + p = m_som_buffer.mk(); + + // p->display(verbose_stream(), m_manager, false); + // verbose_stream() << " " << tt << " 0\n"; + } + + void gcd_simplify_slow(polynomial_ref& p, manager::ineq_type t) { + auto& m = m_manager.m(); + unsigned sz = p->size(); + scoped_mpz g(m); + m.set(g, 0); + for (unsigned i = 0; i < sz; i++) { + auto const& a = p->a(i); + if (m.is_one(a) || m.is_minus_one(a)) + return; + if (t != EQ && is_unit(p->m(i))) + continue; + m.gcd(a, g, g); + if (m.is_one(g)) + return; } + apply_gcd_simplify(g, p, t); } polynomial * mk_zero() { @@ -6087,9 +6196,11 @@ namespace polynomial { } return false; } - if (!m_manager.ge(a1, a2)) - return false; - ++i, ++j; + if (m_manager.eq(a1, a2) || (m1->is_square() && m_manager.ge(a1, a2))) { + ++i, ++j; + continue; + } + return false; } return i == sz1 && j == sz2; } @@ -6971,8 +7082,8 @@ namespace polynomial { return m_imp->hash(p); } - void manager::gcd_simplify(polynomial * p) { - m_imp->gcd_simplify(p); + void manager::gcd_simplify(polynomial_ref& p, ineq_type t) { + m_imp->gcd_simplify(p, t); } polynomial * manager::coeff(polynomial const * p, var x, unsigned k) { diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 48fe5ffeb06..5774e6e1329 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -285,7 +285,8 @@ namespace polynomial { /** \brief Normalize coefficients by dividing by their gcd */ - void gcd_simplify(polynomial* p); + enum ineq_type { EQ, LT, GT }; + void gcd_simplify(polynomial_ref& p, ineq_type t); /** \brief Return true if \c m is the unit monomial. diff --git a/src/math/polynomial/polynomial_cache.cpp b/src/math/polynomial/polynomial_cache.cpp index 9f5f014e4aa..d5953243ca5 100644 --- a/src/math/polynomial/polynomial_cache.cpp +++ b/src/math/polynomial/polynomial_cache.cpp @@ -117,20 +117,14 @@ namespace polynomial { } void reset_psc_chain_cache() { - psc_chain_cache::iterator it = m_psc_chain_cache.begin(); - psc_chain_cache::iterator end = m_psc_chain_cache.end(); - for (; it != end; ++it) { - del_psc_chain_entry(*it); - } + for (auto & k : m_psc_chain_cache) + del_psc_chain_entry(k); m_psc_chain_cache.reset(); } void reset_factor_cache() { - factor_cache::iterator it = m_factor_cache.begin(); - factor_cache::iterator end = m_factor_cache.end(); - for (; it != end; ++it) { - del_factor_entry(*it); - } + for (auto & e : m_factor_cache) + del_factor_entry(e); m_factor_cache.reset(); } @@ -139,7 +133,6 @@ namespace polynomial { polynomial * mk_unique(polynomial * p) { if (m_in_cache.get(pid(p), false)) return p; - // m.gcd_simplify(p); polynomial * p_prime = m_poly_table.insert_if_not_there(p); if (p == p_prime) { m_cached_polys.push_back(p_prime); diff --git a/src/nlsat/CMakeLists.txt b/src/nlsat/CMakeLists.txt index d0c1379e586..cd073f0d5dc 100644 --- a/src/nlsat/CMakeLists.txt +++ b/src/nlsat/CMakeLists.txt @@ -4,6 +4,7 @@ z3_add_component(nlsat nlsat_evaluator.cpp nlsat_explain.cpp nlsat_interval_set.cpp + nlsat_simplify.cpp nlsat_solver.cpp nlsat_types.cpp COMPONENT_DEPENDENCIES diff --git a/src/nlsat/nlsat_simplify.cpp b/src/nlsat/nlsat_simplify.cpp new file mode 100644 index 00000000000..8148d721f5d --- /dev/null +++ b/src/nlsat/nlsat_simplify.cpp @@ -0,0 +1,824 @@ +#include "nlsat/nlsat_simplify.h" +#include "nlsat/nlsat_solver.h" +#include "nlsat/nlsat_scoped_literal_vector.h" +#include "util/dependency.h" +#include "util/map.h" + +namespace nlsat { + + struct simplify::imp { + + solver& s; + atom_vector& m_atoms; + clause_vector& m_clauses, m_learned; + pmanager& m_pm; + literal_vector m_lemma; + vector> m_var_occurs; + + + imp(solver& s, atom_vector& atoms, clause_vector& clauses, clause_vector& learned, pmanager& pm): + s(s), + m_atoms(atoms), + m_clauses(clauses), + m_learned(learned), + m_pm(pm) {} + + void operator()() { + + // for now just remove all learned clauses. + // TODO; check if main clauses are subsumed by learned, + // then promote learned to main. + for (auto c : m_learned) + s.del_clause(c); + m_learned.reset(); + + IF_VERBOSE(3, s.display(verbose_stream() << "before\n")); + unsigned sz = m_clauses.size(); + while (true) { + + subsumption_simplify(); + + while (elim_uncnstr()) + ; + + simplify_literals(); + + while (fm()) + ; + + if (m_clauses.size() >= sz) + break; + + split_factors(); + + sz = m_clauses.size(); + } + + IF_VERBOSE(3, s.display(verbose_stream() << "after\n")); + } + + // + // Apply gcd simplification to literals + // + void simplify_literals() { + u_map b2l; + scoped_literal_vector lits(s); + polynomial_ref p(m_pm); + ptr_buffer ps; + buffer is_even; + unsigned num_atoms = m_atoms.size(); + for (unsigned j = 0; j < num_atoms; ++j) { + atom* a1 = m_atoms[j]; + if (a1 && a1->is_ineq_atom()) { + ineq_atom const& a = *to_ineq_atom(a1); + ps.reset(); + is_even.reset(); + for (unsigned i = 0; i < a.size(); ++i) { + p = a.p(i); + ps.push_back(p); + is_even.push_back(a.is_even(i)); + } + literal l = s.mk_ineq_literal(a.get_kind(), ps.size(), ps.data(), is_even.data(), true); + if (l == null_literal) + continue; + lits.push_back(l); + if (a.m_bool_var != l.var()) { + IF_VERBOSE(3, s.display(verbose_stream() << "simplify ", a) << " -> "; + s.display(verbose_stream(), l) << "\n"); + b2l.insert(a.m_bool_var, l); + } + } + } + update_clauses(b2l); + } + + void update_clauses(u_map const& b2l) { + bool is_sat = true; + literal_vector lits; + unsigned n = m_clauses.size(); + + for (unsigned i = 0; i < n; ++i) { + clause* c = m_clauses[i]; + lits.reset(); + bool changed = false; + bool is_tautology = false; + for (literal l : *c) { + literal lit = null_literal; + if (b2l.find(l.var(), lit)) { + lit = l.sign() ? ~lit : lit; + if (lit == true_literal) + is_tautology = true; + else if (lit != false_literal) + lits.push_back(lit); + changed = true; + } + else + lits.push_back(l); + } + if (changed) { + c->set_removed(); + if (is_tautology) + continue; + s.mk_clause(lits.size(), lits.data(), c->is_learned(), c->assumptions()); + } + } + cleanup_removed(); + } + + // + // Replace unit literals p*q > 0 by clauses. + // + void split_factors() { + auto sz = m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + auto& c = *m_clauses[i]; + if (c.size() != 1) + continue; + auto lit = c[0]; + auto a1 = m_atoms[lit.var()]; + if (!a1) + continue; + auto& a = *to_ineq_atom(a1); + if (a.size() != 2) + continue; + + auto* p = a.p(0); + auto* q = a.p(1); + auto is_evenp = a.is_even(0); + auto is_evenq = a.is_even(1); + + auto asum = c.assumptions(); + literal lits[2]; + clause* c1 = nullptr, * c2 = nullptr; + + c.set_removed(); + s.inc_simplify(); + switch (a.get_kind()) { + case atom::EQ: { + auto l1 = s.mk_ineq_literal(atom::EQ, 1, &p, &is_evenp, false); + auto l2 = s.mk_ineq_literal(atom::EQ, 1, &q, &is_evenq, false); + if (lit.sign()) { + lits[0] = ~l1; + c1 = s.mk_clause(1, lits, false, asum); + lits[0] = ~l2; + c2 = s.mk_clause(1, lits, false, asum); + } + else { + lits[0] = l1; + lits[1] = l2; + c1 = s.mk_clause(2, lits, false, asum); + } + break; + } + case atom::LT: { + auto pgt = s.mk_ineq_literal(atom::GT, 1, &p, &is_evenp, false); + auto plt = s.mk_ineq_literal(atom::LT, 1, &p, &is_evenp, false); + auto qgt = s.mk_ineq_literal(atom::GT, 1, &q, &is_evenq, false); + auto qlt = s.mk_ineq_literal(atom::LT, 1, &q, &is_evenq, false); + if (lit.sign()) { + // p*q >= 0 <=> (p < 0 => q <= 0) & (q < 0 => p <= 0) + // (!(p < 0) or !(q > 0)) & (!(q < 0) or !(p > 0)) + + lits[0] = ~plt; + lits[1] = ~qgt; + c1 = s.mk_clause(2, lits, false, asum); + lits[0] = ~qlt; + lits[1] = ~pgt; + c2 = s.mk_clause(2, lits, false, asum); + } + else { + // p*q < 0 + // (p > 0 & q < 0) or (q > 0 & p < 0) + // (p > 0 or q > 0) and (p < 0 or q < 0) + + lits[0] = pgt; + lits[1] = qgt; + c1 = s.mk_clause(2, lits, false, asum); + lits[0] = plt; + lits[1] = qlt; + c2 = s.mk_clause(2, lits, false, asum); + } + break; + } + case atom::GT: { + auto pgt = s.mk_ineq_literal(atom::GT, 1, &p, &is_evenp, false); + auto plt = s.mk_ineq_literal(atom::LT, 1, &p, &is_evenp, false); + auto qgt = s.mk_ineq_literal(atom::GT, 1, &q, &is_evenq, false); + auto qlt = s.mk_ineq_literal(atom::LT, 1, &q, &is_evenq, false); + if (lit.sign()) { + // p*q <= 0 + // (p > 0 => q <= 0) & (p < 0 => q >= 0) + // (!(p > 0) or !(q > 0)) & (!(p < 0) or !(q < 0)) + + lits[0] = ~pgt; + lits[1] = ~qgt; + c1 = s.mk_clause(2, lits, false, asum); + lits[0] = ~qlt; + lits[1] = ~plt; + c2 = s.mk_clause(2, lits, false, asum); + } + else { + // p*q > 0 + // (p > 0 or q < 0) & (p < 0 or q > 0) + lits[0] = plt; + lits[1] = qgt; + c1 = s.mk_clause(2, lits, false, asum); + lits[0] = qlt; + lits[1] = pgt; + c2 = s.mk_clause(2, lits, false, asum); + } + break; + } + } + IF_VERBOSE(3, + s.display(verbose_stream(), c) << " ->\n"; + if (c1) s.display(verbose_stream(), *c1) << "\n"; + if (c2) s.display(verbose_stream(), *c2) << "\n"); + } + cleanup_removed(); + } + + bool elim_uncnstr() { + // compute variable occurrences + if (any_of(m_clauses, [&](clause* c) { return s.has_root_atom(*c); })) + return false; + compute_occurs(); + // for each variable occurrence, figure out if it is unconstrained. + bool has_removed = false; + for (unsigned v = m_var_occurs.size(); v-- > 0; ) { + auto& clauses = m_var_occurs[v]; + if (clauses.size() != 1) + continue; + auto& c = *clauses[0]; + if (c.is_removed()) + continue; + if (!is_unconstrained(v, c)) + continue; + s.inc_simplify(); + c.set_removed(); + has_removed = true; + } + cleanup_removed(); + + return has_removed; + } + + bool is_unconstrained(var x, clause& c) { + poly* p; + polynomial_ref A(m_pm), B(m_pm); + for (auto lit : c) { + bool_var b = lit.var(); + if (!m_atoms[b]) + continue; + auto& a = *to_ineq_atom(m_atoms[b]); + if (!is_single_poly(a, p)) + continue; + + if (1 != m_pm.degree(p, x)) + continue; + + A = m_pm.coeff(p, x, 1, B); + + if (a.is_eq() && !lit.sign()) { + // A*x + B = 0 + if (s.is_int(x) && is_unit(A)) { + s.add_bound(bound_constraint(x, A, B, false, nullptr)); + return true; + } + + if (!s.is_int(x) && m_pm.is_const(A)) { + s.add_bound(bound_constraint(x, A, B, false, nullptr)); + return true; + } + } + // TODO: add other cases for LT and GT atoms + } + return false; + } + + void compute_occurs() { + m_var_occurs.reset(); + for (auto c : m_clauses) + compute_occurs(*c); + } + + void compute_occurs(clause& c) { + var_vector vars; + m_pm.begin_vars_incremental(); + for (auto lit : c) { + bool_var b = lit.var(); + atom* a = m_atoms[b]; + if (!a) + continue; + if (a->is_ineq_atom()) { + auto sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; ++i) { + auto* p = to_ineq_atom(a)->p(i); + m_pm.vars_incremental(p, vars); + } + } + } + m_pm.end_vars_incremental(vars); + unsigned h = 0; + for (auto v : vars) { + m_var_occurs.reserve(v + 1); + m_var_occurs[v].push_back(&c); + h |= (1ul << (v % 32ul)); + } + c.set_var_hash(h); + } + + bool cleanup_removed() { + unsigned j = 0, sz = m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + auto c = m_clauses[i]; + if (c->is_removed()) + s.del_clause(c); + else + m_clauses[j++] = c; + } + m_clauses.shrink(j); + return j < sz; + } + + bool unit_subsumption_simplify(clause& src, clause& c) { + if (src.size() != 1) + return false; + auto u = src[0]; + for (auto lit : c) { + if (subsumes(u, ~lit)) { + + literal_vector lits; + for (auto lit2 : c) + if (lit2 != lit) + lits.push_back(lit2); + if (lits.empty()) + return false; + auto a = s.join(c.assumptions(), src.assumptions()); + auto cls = s.mk_clause(lits.size(), lits.data(), false, a); + if (cls) + compute_occurs(*cls); + return true; + } + } + return false; + } + + // + // Subsumption simplification + // + // Remove D if C subsumes D + // + // Unit subsumption resolution + // u is a unit literal (lit or C) is a clause + // u => ~lit, then simplify (lit or C) to C + // + void subsumption_simplify() { + compute_occurs(); + for (unsigned v = m_var_occurs.size(); v-- > 0; ) { + auto& clauses = m_var_occurs[v]; + unsigned sz = clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + auto c = clauses[i]; + if (c->is_marked() || c->is_removed()) + continue; + c->mark(); + for (unsigned j = 0; j < sz; ++j) { + auto c2 = clauses[j]; + if (c == c2 || c2->is_removed()) + continue; + if (subsumes(*c, *c2) || unit_subsumption_simplify(*c, *c2)) { + IF_VERBOSE(3, s.display(verbose_stream() << "subsumes ", *c); + s.display(verbose_stream() << " ", *c2) << "\n"); + s.inc_simplify(); + c2->set_removed(); + } + } + } + } + for (auto c : m_clauses) + c->unmark(); + + cleanup_removed(); + } + + // does c1 subsume c2? + bool subsumes(clause const& c1, clause const& c2) { + if (c1.size() > c2.size()) + return false; + if ((c1.var_hash() & c2.var_hash()) != c1.var_hash()) + return false; + for (auto lit1 : c1) { + if (!any_of(c2, [&](auto lit2) { return subsumes(lit1, lit2); })) + return false; + } + return true; + } + + bool subsumes(literal lit1, literal lit2) { + if (lit1 == lit2) + return true; + + atom* a1 = m_atoms[lit1.var()]; + atom* a2 = m_atoms[lit2.var()]; + if (!a1 || !a2) + return false; + + // use m_pm.ge(p1, p2) + // whenever lit1 = p1 < 0, lit2 = p2 < 0 + // or lit1 = p1 < 0, lit2 = !(p2 > 0) + // or lit1 = !(p1 > 0), lit2 = !(p2 > 0) + // use m_pm.ge(p2, p1) + // whenever lit1 = p1 > 0, lit2 = p2 > 0 + // or lit1 = !(p1 < 0), lit2 = !(p2 < 0) + // or lit1 = p1 > 0, lit2 = !(p2 < 0) + // or lit1 = !(p1 > 0), lit2 = p2 < 0 + // + if (a1->is_ineq_atom() && a2->is_ineq_atom()) { + auto& i1 = *to_ineq_atom(a1); + auto& i2 = *to_ineq_atom(a2); + auto is_lt1 = !lit1.sign() && a1->get_kind() == atom::kind::LT; + auto is_le1 = lit1.sign() && a1->get_kind() == atom::kind::GT; + auto is_gt1 = !lit1.sign() && a1->get_kind() == atom::kind::GT; + auto is_ge1 = lit1.sign() && a1->get_kind() == atom::kind::LT; + + auto is_lt2 = !lit2.sign() && a2->get_kind() == atom::kind::LT; + auto is_le2 = lit2.sign() && a2->get_kind() == atom::kind::GT; + auto is_gt2 = !lit2.sign() && a2->get_kind() == atom::kind::GT; + auto is_ge2 = lit2.sign() && a2->get_kind() == atom::kind::LT; + + auto check_ge = (is_lt1 && (is_lt2 || is_le2)) || (is_le1 && is_le2); + auto check_le = (is_gt1 && (is_gt2 || is_ge2)) || (is_ge1 && is_ge2); + + if (i1.size() != i2.size()) + ; + else if (check_ge) { + for (unsigned i = 0; i < i1.size(); ++i) + if (!m_pm.ge(i1.p(i), i2.p(i))) + return false; + return true; + } + else if (check_le) { + for (unsigned i = 0; i < i1.size(); ++i) + if (!m_pm.ge(i2.p(i), i1.p(i))) + return false; + return true; + } + } + return false; + } + + // +// Fourier Motzkin elimination +// + + bool fm() { + if (any_of(m_clauses, [&](clause* c) { return s.has_root_atom(*c); })) + return false; + compute_occurs(); + + for (unsigned v = m_var_occurs.size(); v-- > 0; ) + apply_fm(v, m_var_occurs[v]); + + return cleanup_removed(); + } + + // progression of possible features: + // . Current: unit literals + // . Either lower or upper bound is unit coefficient + // . single occurrence of x in C + // . (x <= t or x <= s or C) == (x <= max(s, t) or C) + // + + bool is_invertible(var x, polynomial_ref& A) { + if (!m_pm.is_const(A)) + return false; + if (s.is_int(x) && !is_unit(A)) + return false; + return true; + } + + bool apply_fm(var x, ptr_vector& clauses) { + polynomial_ref A(m_pm), B(m_pm); + vector lo, hi; + poly* p = nullptr; + bool all_solved = true; + for (auto c : clauses) { + if (c->is_removed()) + continue; + if (c->size() != 1) { + all_solved = false; + continue; + } + literal lit = (*c)[0]; + bool sign = lit.sign(); + ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); + if (sign && a.is_eq()) { + all_solved = false; + continue; + } + if (!is_single_poly(a, p)) { + all_solved = false; + continue; + } + if (1 != m_pm.degree(p, x)) { + all_solved = false; + continue; + } + A = m_pm.coeff(p, x, 1, B); + if (!is_invertible(x, A)) { + all_solved = false; + continue; + } + auto const& A_value = m_pm.coeff(A, 0); + bool is_pos = m_pm.m().is_pos(A_value); + bool is_strict = false; + switch (a.get_kind()) { + case atom::LT: + // !(Ax + B < 0) == Ax + B >= 0 + if (sign) + is_strict = false; + else { + // Ax + B < 0 == -Ax - B > 0 + A = -A; + B = -B; + is_pos = !is_pos; + if (s.is_int(x)) { + // Ax + B > 0 == Ax + B - |A| >= 0 + if (is_pos) + B = m_pm.sub(B, A); + else + B = m_pm.add(B, A); + is_strict = false; + } + else + is_strict = true; + } + break; + case atom::GT: + // !(Ax + B > 0) == -Ax + -B >= 0 + if (sign) { + A = -A; + B = -B; + is_pos = !is_pos; + is_strict = false; + } + else { + // Ax + B > 0 + if (s.is_int(x)) { + // Ax + B - |A| >= 0 + if (is_pos) + B = m_pm.sub(B, A); + else + B = m_pm.add(B, A); + is_strict = false; + } + else + is_strict = true; + } + break; + case atom::EQ: { + all_solved = false; + if (sign) + continue; + bound_constraint l(x, A, B, false, c); + A = -A; + B = -B; + bound_constraint h(x, A, B, false, c); + apply_fm_equality(x, clauses, l, h); + return true; + } + default: + UNREACHABLE(); + break; + } + auto& set = is_pos ? hi : lo; + bool found = false; + for (auto const& bound : set) { + if (is_strict == bound.is_strict && m_pm.eq(A, bound.A) && m_pm.eq(B, bound.B)) + found = true; + } + if (found) + continue; + + set.push_back(bound_constraint(x, A, B, is_strict, c)); + + } + + if (lo.empty() && hi.empty()) + return false; + + if (apply_fm_equality(x, clauses, lo, hi)) + return true; + + + if (!all_solved) + return false; + + IF_VERBOSE(3, + verbose_stream() << "x" << x << " lo " << lo.size() << " hi " << hi.size() << "\n"; + for (auto c : clauses) + if (!c->is_removed()) + s.display(verbose_stream(), *c) << "\n"; + ); + + auto num_lo = lo.size(), num_hi = hi.size(); + if (num_lo >= 2 && num_hi >= 2 && (num_lo > 2 || num_hi > 2)) + return false; + + apply_fm_inequality(x, clauses, lo, hi); + + return true; + } + + void apply_fm_inequality( + var x, ptr_vector& clauses, + vector& lo, vector& hi) { + + polynomial_ref C(m_pm), D(m_pm); + for (auto c : clauses) + c->set_removed(); + + for (auto const& l : lo) { + // l.A * x + l.B, l.is_strict;, l.A < 0 + for (auto const& h : hi) { + // h.A * x + h.B, h.is_strict; h.A > 0 + // (l.A x + l.B)*h.A + (h.A x + h.B)*|l.A| >= 0 + C = m_pm.mul(l.B, h.A); + D = m_pm.mul(h.B, l.A); + C = m_pm.sub(C, D); + poly* p = C.get(); + bool is_even = false; + m_lemma.reset(); + if (l.is_strict || h.is_strict) + m_lemma.push_back(s.mk_ineq_literal(atom::GT, 1, &p, &is_even, true)); + else + m_lemma.push_back(~s.mk_ineq_literal(atom::LT, 1, &p, &is_even, true)); + if (m_lemma[0] == true_literal) + continue; + auto a = s.join(l.c->assumptions(), h.c->assumptions()); + auto cls = s.mk_clause(m_lemma.size(), m_lemma.data(), false, a); + if (cls) + compute_occurs(*cls); + IF_VERBOSE(3, s.display(verbose_stream() << "add resolvent ", *cls) << "\n"); + } + } + + // track updates for model reconstruction + for (auto const& l : lo) + s.add_bound(l); + for (auto const& h : hi) + s.add_bound(h); + } + + literal substitute_var(var x, poly* p, poly* q, literal lit) { + auto b = lit.var(); + auto a = m_atoms[b]; + if (!a) + return lit; + SASSERT(a->is_ineq_atom()); + auto& a1 = *to_ineq_atom(a); + auto r = substitute_var(x, p, q, a1); + if (r == null_literal) + r = lit; + else if (lit.sign()) + r.neg(); + return r; + } + + literal substitute_var(var x, poly* p, poly* q, ineq_atom const& a) { + unsigned sz = a.size(); + bool_vector even; + polynomial_ref pr(m_pm), qq(q, m_pm); + qq = -qq; + polynomial_ref_vector ps(m_pm); + bool change = false; + auto k = a.get_kind(); + for (unsigned i = 0; i < sz; ++i) { + poly* po = a.p(i); + m_pm.substitute(po, x, qq, p, pr); + change |= pr != po; + TRACE("nlsat", tout << pr << "\n";); + if (m_pm.is_zero(pr)) { + ps.reset(); + even.reset(); + ps.push_back(pr); + even.push_back(false); + break; + } + if (m_pm.is_const(pr)) { + if (!a.is_even(i) && m_pm.m().is_neg(m_pm.coeff(pr, 0))) + k = atom::flip(k); + continue; + } + ps.push_back(pr); + even.push_back(a.is_even(i)); + } + if (!change) + return null_literal; + return s.mk_ineq_literal(k, ps.size(), ps.data(), even.data(), true); + } + + bool apply_fm_equality( + var x, ptr_vector& clauses, + vector& lo, vector& hi) { + for (auto& l : lo) { + if (l.is_strict) + continue; + l.A = -l.A; + l.B = -l.B; + for (auto& h : hi) { + if (h.is_strict) + continue; + if (!m_pm.eq(l.B, h.B)) + continue; + if (!m_pm.eq(l.A, h.A)) + continue; + l.A = -l.A; + l.B = -l.B; + apply_fm_equality(x, clauses, l, h); + s.inc_simplify(); + return true; + } + l.A = -l.A; + l.B = -l.B; + } + return false; + } + + void apply_fm_equality( + var x, ptr_vector& clauses, + bound_constraint& l, bound_constraint& h) { + auto a1 = s.join(l.c->assumptions(), h.c->assumptions()); + s.inc_ref(a1); + + polynomial_ref A(l.A), B(l.B); + + if (m_pm.is_neg(l.A)) { + A = -A; + B = -B; + } + + for (auto c : clauses) { + if (c->is_removed()) + continue; + c->set_removed(); + if (c == l.c || c == h.c) + continue; + m_lemma.reset(); + bool is_tautology = false; + for (literal lit : *c) { + lit = substitute_var(x, A, B, lit); + m_lemma.push_back(lit); + if (lit == true_literal) + is_tautology = true; + } + if (is_tautology) + continue; + auto a = s.join(c->assumptions(), a1); + auto cls = s.mk_clause(m_lemma.size(), m_lemma.data(), false, a); + + IF_VERBOSE(3, + if (cls) { + s.display_proc()(verbose_stream(), x) << " * " << l.A << " = " << l.B << "\n"; + s.display(verbose_stream(), *c) << " -> "; + s.display(verbose_stream(), *cls) << " - "; + s.display(verbose_stream(), *l.c) << " "; + s.display(verbose_stream(), *h.c) << "\n"; + }); + if (cls) + compute_occurs(*cls); + } + s.dec_ref(a1); + // track updates for model reconstruction + s.add_bound(l); + s.add_bound(h); + s.inc_simplify(); + } + + bool is_single_poly(ineq_atom const& a, poly*& p) { + unsigned sz = a.size(); + return sz == 1 && a.is_odd(0) && (p = a.p(0), true); + } + + bool is_unit(polynomial_ref const& p) { + if (!m_pm.is_const(p)) + return false; + auto const& c = m_pm.coeff(p, 0); + return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); + } + }; + + simplify::simplify(solver& s, atom_vector& atoms, clause_vector& clauses, clause_vector& learned, pmanager& pm) { + m_imp = alloc(imp, s, atoms, clauses, learned, pm); + } + + simplify::~simplify() { + dealloc(m_imp); + } + + void simplify::operator()() { + (*m_imp)(); + } + +}; diff --git a/src/nlsat/nlsat_simplify.h b/src/nlsat/nlsat_simplify.h new file mode 100644 index 00000000000..407dc44890c --- /dev/null +++ b/src/nlsat/nlsat_simplify.h @@ -0,0 +1,16 @@ +#pragma once + +#include "nlsat/nlsat_types.h" +#include "nlsat/nlsat_clause.h" + +namespace nlsat { + class simplify { + struct imp; + imp * m_imp; + public: + simplify(solver& s, atom_vector& atoms, clause_vector& clauses, clause_vector& learned, pmanager & pm); + ~simplify(); + + void operator()(); + }; +} \ No newline at end of file diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index eed908617d7..ca057afefb3 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -33,6 +33,7 @@ Revision History: #include "nlsat/nlsat_evaluator.h" #include "nlsat/nlsat_explain.h" #include "nlsat/nlsat_params.hpp" +#include "nlsat/nlsat_simplify.h" #define NLSAT_EXTRA_VERBOSE @@ -44,6 +45,7 @@ Revision History: namespace nlsat { + typedef chashtable ineq_atom_table; typedef chashtable root_atom_table; @@ -71,28 +73,24 @@ namespace nlsat { }; struct solver::imp { + + struct dconfig { typedef imp value_manager; typedef small_object_allocator allocator; - typedef void * value; - static const bool ref_count = false; + typedef void* value; + static const bool ref_count = false; }; + typedef dependency_manager assumption_manager; - typedef assumption_manager::dependency * _assumption_set; + typedef assumption_manager::dependency* _assumption_set; + typedef obj_ref assumption_set_ref; typedef polynomial::cache cache; typedef ptr_vector interval_set_vector; - struct bound_constraint { - var x; - polynomial_ref A, B; - bool is_strict; - clause* c; - bound_constraint(var x, polynomial_ref& A, polynomial_ref& B, bool is_strict, clause* c): - x(x), A(A), B(B), is_strict(is_strict), c(c) {} - }; ctx& m_ctx; @@ -111,6 +109,7 @@ namespace nlsat { ineq_atom_table m_ineq_atoms; root_atom_table m_root_atoms; + vector m_bounds; id_gen m_cid_gen; @@ -127,6 +126,8 @@ namespace nlsat { bool_vector m_dead; // mark dead boolean variables id_gen m_bid_gen; + simplify m_simplify; + bool_vector m_is_int; // m_is_int[x] is true if variable is integer vector m_watches; // var -> clauses where variable is maximal interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. @@ -220,12 +221,19 @@ namespace nlsat { unsigned m_max_conflicts; unsigned m_lemma_count; + struct stats { + unsigned m_simplifications; + unsigned m_restarts; + unsigned m_conflicts; + unsigned m_propagations; + unsigned m_decisions; + unsigned m_stages; + unsigned m_irrational_assignments; // number of irrational witnesses + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; // statistics - unsigned m_conflicts; - unsigned m_propagations; - unsigned m_decisions; - unsigned m_stages; - unsigned m_irrational_assignments; // number of irrational witnesses + stats m_stats; imp(solver& s, ctx& c): m_ctx(c), @@ -242,6 +250,7 @@ namespace nlsat { m_evaluator(s, m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), m_num_bool_vars(0), + m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), m_display_var(m_perm), m_display_assumption(nullptr), m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator), @@ -336,10 +345,10 @@ namespace nlsat { void dec_ref(assumption) {} - void inc_ref(_assumption_set a) { + void inc_ref(_assumption_set a) { if (a != nullptr) m_asm.inc_ref(a); } - + void dec_ref(_assumption_set a) { if (a != nullptr) m_asm.dec_ref(a); } @@ -594,7 +603,7 @@ namespace nlsat { } - ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new) { + ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new, bool simplify) { SASSERT(sz >= 1); SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); int sign = 1; @@ -609,8 +618,27 @@ namespace nlsat { var curr_max = max_var(p.get()); if (curr_max > max || max == null_var) max = curr_max; + if (sz == 1 && simplify) { + if (sign < 0) + k = atom::flip(k); + sign = 1; + polynomial::manager::ineq_type t; + switch (k) { + case atom::EQ: t = polynomial::manager::ineq_type::EQ; break; + case atom::LT: t = polynomial::manager::ineq_type::LT; break; + case atom::GT: t = polynomial::manager::ineq_type::GT; break; + default: UNREACHABLE(); break; + } + polynomial::var_vector vars; + m_pm.vars(p, vars); + bool all_int = all_of(vars, [&](var x) { return is_int(x); }); + if (!all_int) + t = polynomial::manager::ineq_type::EQ; + m_pm.gcd_simplify(p, t); + } uniq_ps.push_back(m_cache.mk_unique(p)); TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); + //verbose_stream() << "p: " << p.get() << ", uniq: " << uniq_ps.back() << "\n"; } void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); if (sign < 0) @@ -633,9 +661,9 @@ namespace nlsat { return atom; } - bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { bool is_new = false; - ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new); + ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new, simplify); if (!is_new) { return atom->bvar(); } @@ -648,7 +676,7 @@ namespace nlsat { } } - literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); bool is_const = true; polynomial::manager::scoped_numeral cnst(m_pm.m()); @@ -676,7 +704,7 @@ namespace nlsat { if (m_pm.m().is_zero(cnst) && k == atom::EQ) return true_literal; return false_literal; } - return literal(mk_ineq_atom(k, sz, ps, is_even), false); + return literal(mk_ineq_atom(k, sz, ps, is_even, simplify), false); } bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { @@ -879,13 +907,13 @@ namespace nlsat { for (literal lit : *c) { lits.push_back(literal(tr[lit.var()], lit.sign())); } - checker.mk_clause(lits.size(), lits.data(), nullptr); + checker.mk_external_clause(lits.size(), lits.data(), nullptr); } } for (unsigned i = 0; i < n; ++i) { literal lit = cls[i]; literal nlit(tr[lit.var()], !lit.sign()); - checker.mk_clause(1, &nlit, nullptr); + checker.mk_external_clause(1, &nlit, nullptr); } lbool r = checker.check(); if (r == l_true) { @@ -954,6 +982,10 @@ namespace nlsat { } clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { + if (num_lits == 0) { + num_lits = 1; + lits = &false_literal; + } SASSERT(num_lits > 0); clause * cls = mk_clause_core(num_lits, lits, learned, a); TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); @@ -973,7 +1005,7 @@ namespace nlsat { return cls; } - void mk_clause(unsigned num_lits, literal const * lits, assumption a) { + void mk_external_clause(unsigned num_lits, literal const * lits, assumption a) { _assumption_set as = nullptr; if (a != nullptr) as = m_asm.mk_leaf(a); @@ -1170,9 +1202,9 @@ namespace nlsat { SASSERT(j != null_justification); SASSERT(!j.is_null()); if (j.is_decision()) - m_decisions++; + m_stats.m_decisions++; else - m_propagations++; + m_stats.m_propagations++; bool_var b = l.var(); m_bvalues[b] = to_lbool(!l.sign()); m_levels[b] = m_scope_lvl; @@ -1472,7 +1504,7 @@ namespace nlsat { \brief Create a new stage. See Dejan and Leo's paper. */ void new_stage() { - m_stages++; + m_stats.m_stages++; save_new_stage_trail(); if (m_xk == null_var) m_xk = 0; @@ -1492,7 +1524,7 @@ namespace nlsat { tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); if (!m_am.is_rational(w)) - m_irrational_assignments++; + m_stats.m_irrational_assignments++; m_assignment.set_core(m_xk, w); } @@ -1524,6 +1556,17 @@ namespace nlsat { m_xk = null_var; while (true) { + if (should_reorder()) + do_reorder(); + +#if 0 + if (should_gc()) + do_gc(); +#endif + + if (should_simplify()) + do_simplify(); + CASSERT("nlsat", check_satisfied()); if (m_xk == null_var) { peek_next_bool_var(); @@ -1556,7 +1599,7 @@ namespace nlsat { break; if (!resolve(*conflict_clause)) return l_false; - if (m_conflicts >= m_max_conflicts) + if (m_stats.m_conflicts >= m_max_conflicts) return l_undef; log(); } @@ -1593,22 +1636,72 @@ namespace nlsat { reattach_arith_clauses(m_learned); } + + bool should_gc() { + return m_learned.size() > 10 * m_clauses.size(); + } + + void do_gc() { + undo_to_base(); + gc(); + } + + void undo_to_base() { + init_search(); + m_bk = 0; + m_xk = null_var; + } + + unsigned m_restart_threshold = 10000; + bool should_reorder() { + return m_stats.m_conflicts > 0 && m_stats.m_conflicts % m_restart_threshold == 0; + } + + void do_reorder() { + undo_to_base(); + m_stats.m_restarts++; + m_stats.m_conflicts++; + if (m_reordered) + restore_order(); + apply_reorder(); + } + + bool m_did_simplify = false; + bool should_simplify() { + return + !m_did_simplify && m_inline_vars && + !m_incremental && m_stats.m_conflicts > 100; + } + + void do_simplify() { + undo_to_base(); + m_did_simplify = true; + m_simplify(); + } + unsigned m_next_conflict = 100; void log() { - if (m_conflicts != 1 && m_conflicts < m_next_conflict) + if (m_stats.m_conflicts != 1 && m_stats.m_conflicts < m_next_conflict) return; m_next_conflict += 100; - IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_conflicts << " :decisions " << m_decisions << " :propagations " << m_propagations << " :clauses " << m_clauses.size() << " :learned " << m_learned.size() << ")\n"); + IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); } lbool search_check() { lbool r = l_undef; - m_conflicts = 0; + m_stats.m_conflicts = 0; + m_stats.m_restarts = 0; m_next_conflict = 0; while (true) { r = search(); - if (r != l_true) break; + if (r != l_true) + break; + ++m_stats.m_restarts; vector> bounds; for (var x = 0; x < num_vars(); x++) { @@ -1631,11 +1724,22 @@ namespace nlsat { bounds.push_back(std::make_pair(x, lo)); } } - if (bounds.empty()) break; + if (bounds.empty()) + break; gc(); + if (m_stats.m_restarts % 10 == 0) { + if (m_reordered) + restore_order(); + apply_reorder(); + } + init_search(); - IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_conflicts << " :decisions " << m_decisions << " :propagations " << m_propagations << " :clauses " << m_clauses.size() << " :learned " << m_learned.size() << ")\n"); + IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); for (auto const& b : bounds) { var x = b.first; rational lo = b.second; @@ -1653,6 +1757,7 @@ namespace nlsat { // perform branch and bound clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); + IF_VERBOSE(4, display(verbose_stream(), *cls) << "\n"); if (cls) { TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); } @@ -1661,35 +1766,42 @@ namespace nlsat { return r; } + bool m_reordered = false; + + void apply_reorder() { + m_reordered = false; + if (!can_reorder()) + ; + else if (m_random_order) { + shuffle_vars(); + m_reordered = true; + } + else if (m_reorder) { + heuristic_reorder(); + m_reordered = true; + } + } + lbool check() { TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); init_search(); m_explain.set_full_dimensional(is_full_dimensional()); - bool reordered = false; + apply_reorder(); + +#if 0 if (!m_incremental && m_inline_vars) { - if (!simplify()) + if (!m_simplify()) return l_false; } - - if (!can_reorder()) { - - } - else if (m_random_order) { - shuffle_vars(); - reordered = true; - } - else if (m_reorder) { - heuristic_reorder(); - reordered = true; - } +#endif + IF_VERBOSE(3, verbose_stream() << "search\n"); sort_watched_clauses(); lbool r = search_check(); CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); - if (reordered) { - restore_order(); - } + if (m_reordered) + restore_order(); CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); SASSERT(r != l_true || check_satisfied(m_clauses)); @@ -1713,7 +1825,7 @@ namespace nlsat { unsigned sz = assumptions.size(); literal const* ptr = assumptions.data(); for (unsigned i = 0; i < sz; ++i) { - mk_clause(1, ptr+i, (assumption)(ptr+i)); + mk_external_clause(1, ptr+i, (assumption)(ptr+i)); } display_literal_assumption dla(*this, assumptions); scoped_display_assumptions _scoped_display(*this, dla); @@ -1910,6 +2022,8 @@ namespace nlsat { for (unsigned i = 0; i < sz; i++) { literal l = m_lazy_clause[i]; if (l.var() != b) { + if (value(l) != l_false) + display(verbose_stream() << value(l) << " ", 1, &l); SASSERT(value(l) == l_false || m_rlimit.is_canceled()); } else { @@ -2052,7 +2166,7 @@ namespace nlsat { SASSERT(check_marks()); TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); - m_conflicts++; + m_stats.m_conflicts++; TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; tout << "scope_lvl: " << scope_lvl() << "\n"; @@ -2197,8 +2311,7 @@ namespace nlsat { VERIFY(process_clause(*conflict_clause, true)); return true; } - new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); - + new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); } NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); if (!process_clause(*new_cls, true)) { @@ -2303,19 +2416,17 @@ namespace nlsat { // ----------------------- void collect_statistics(statistics & st) { - st.update("nlsat conflicts", m_conflicts); - st.update("nlsat propagations", m_propagations); - st.update("nlsat decisions", m_decisions); - st.update("nlsat stages", m_stages); - st.update("nlsat irrational assignments", m_irrational_assignments); + st.update("nlsat conflicts", m_stats.m_conflicts); + st.update("nlsat propagations", m_stats.m_propagations); + st.update("nlsat decisions", m_stats.m_decisions); + st.update("nlsat restarts", m_stats.m_restarts); + st.update("nlsat stages", m_stats.m_stages); + st.update("nlsat simplifications", m_stats.m_simplifications); + st.update("nlsat irrational assignments", m_stats.m_irrational_assignments); } void reset_statistics() { - m_conflicts = 0; - m_propagations = 0; - m_decisions = 0; - m_stages = 0; - m_irrational_assignments = 0; + m_stats.reset(); } // ----------------------- @@ -2327,6 +2438,7 @@ namespace nlsat { struct var_info_collector { pmanager & pm; atom_vector const & m_atoms; + var_vector m_shuffle; unsigned_vector m_max_degree; unsigned_vector m_num_occs; @@ -2402,7 +2514,7 @@ namespace nlsat { return false; if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) return true; - return x < y; + return m_info.m_shuffle[x] < m_info.m_shuffle[y]; } }; @@ -2412,11 +2524,12 @@ namespace nlsat { var_info_collector collector(m_pm, m_atoms, num); collector.collect(m_clauses); collector.collect(m_learned); + init_shuffle(collector.m_shuffle); TRACE("nlsat_reorder", collector.display(tout, m_display_var);); var_vector new_order; - for (var x = 0; x < num; x++) { + for (var x = 0; x < num; x++) new_order.push_back(x); - } + std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); TRACE("nlsat_reorder", tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); @@ -2429,20 +2542,23 @@ namespace nlsat { SASSERT(check_invariant()); } - void shuffle_vars() { - var_vector p; + void init_shuffle(var_vector& p) { unsigned num = num_vars(); - for (var x = 0; x < num; x++) { + for (var x = 0; x < num; x++) p.push_back(x); - } + random_gen r(++m_random_seed); shuffle(p.size(), p.data(), r); + } + + void shuffle_vars() { + var_vector p; + init_shuffle(p); reorder(p.size(), p.data()); } bool can_reorder() const { - return m_bounds.empty() - && all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) + return all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); } @@ -2450,7 +2566,10 @@ namespace nlsat { \brief Reorder variables using the giving permutation. p maps internal variables to their new positions */ + + void reorder(unsigned sz, var const * p) { + remove_learned_roots(); SASSERT(can_reorder()); TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); @@ -2458,6 +2577,8 @@ namespace nlsat { tout << "\npermutation:\n"; for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; ); + // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; + reinit_cache(); SASSERT(num_vars() == sz); TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); reset_watches(); @@ -2499,6 +2620,8 @@ namespace nlsat { } #endif m_pm.rename(sz, p); + for (auto& b : m_bounds) + b.x = p[b.x]; TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); reinit_cache(); m_assignment.swap(new_assignment); @@ -2734,552 +2857,7 @@ namespace nlsat { The method ignores lemmas and assumes constraints don't use roots. */ - vector> m_var_occurs; - - bool simplify() { - unsigned sz = m_clauses.size(); - while (true) { - - while (elim_uncnstr()) - ; - while (fm()) - ; - if (!solve_eqs()) - return false; - - subsumption_simplify(); - if (m_clauses.size() >= sz) - break; - sz = m_clauses.size(); - } - - IF_VERBOSE(3, display(verbose_stream())); - - return true; - } - - // - // Remove unconstrained assertions. - // - bool elim_uncnstr() { - // compute variable occurrences - if (any_of(m_clauses, [&](clause* c) { return has_root_atom(*c); })) - return false; - compute_occurs(); - // for each variable occurrence, figure out if it is unconstrained. - ptr_vector to_delete; - for (unsigned v = m_var_occurs.size(); v-- > 0; ) { - auto& clauses = m_var_occurs[v]; - if (clauses.size() != 1) - continue; - auto& c = *clauses[0]; - if (c.is_removed()) - continue; - if (!is_unconstrained(v, c)) - continue; - c.set_removed(); - to_delete.push_back(&c); - } - for (auto* c : to_delete) - del_clause(c, m_clauses); - - return !to_delete.empty(); - } - - void compute_occurs(clause& c) { - var_vector vars; - m_pm.begin_vars_incremental(); - for (auto lit : c) { - bool_var b = lit.var(); - atom* a = m_atoms[b]; - if (!a) - continue; - if (a->is_ineq_atom()) { - auto sz = to_ineq_atom(a)->size(); - for (unsigned i = 0; i < sz; ++i) { - auto* p = to_ineq_atom(a)->p(i); - m_pm.vars_incremental(p, vars); - } - } - } - m_pm.end_vars_incremental(vars); - unsigned h = 0; - for (auto v : vars) { - m_var_occurs.reserve(v + 1); - m_var_occurs[v].push_back(&c); - h |= (1ul << (v % 32ul)); - } - c.set_var_hash(h); - } - - void compute_occurs() { - m_var_occurs.reset(); - for (auto c : m_clauses) - compute_occurs(*c); - } - - bool is_unconstrained(var x, clause& c) { - poly* p; - polynomial_ref A(m_pm), B(m_pm); - for (auto lit : c) { - bool_var b = lit.var(); - if (!m_atoms[b]) - continue; - auto& a = *to_ineq_atom(m_atoms[b]); - if (!is_single_poly(a, p)) - continue; - - if (1 != m_pm.degree(p, x)) - continue; - - A = m_pm.coeff(p, x, 1, B); - - if (a.is_eq() && !lit.sign()) { - // A*x + B = 0 - if (is_int(x) && is_unit(A)) { - m_bounds.push_back(bound_constraint(x, A, B, false, nullptr)); - return true; - } - - if (!is_int(x) && m_pm.is_const(A)) { - m_bounds.push_back(bound_constraint(x, A, B, false, nullptr)); - return true; - } - } - // TODO: add other cases for LT and GT atoms - } - return false; - } - - bool cleanup_removed() { - unsigned j = 0, sz = m_clauses.size(); - for (unsigned i = 0; i < sz; ++i) { - auto c = m_clauses[i]; - if (c->is_removed()) - del_clause(c); - else - m_clauses[j++] = c; - } - m_clauses.shrink(j); - return j < sz; - } - - // - // Fourier Motzkin elimination - // - - bool fm() { - if (any_of(m_clauses, [&](clause* c) { return has_root_atom(*c); })) - return false; - compute_occurs(); - - for (unsigned v = m_var_occurs.size(); v-- > 0; ) - apply_fm(v, m_var_occurs[v]); - - return cleanup_removed(); - } - - // progression of features - // unit literals - // single occurrence of x in C - // (x <= t or x <= s or C) == (x <= max(s, t) or C) - - - bool is_invertible(var x, polynomial_ref & A) { - if (!m_pm.is_const(A)) - return false; - if (is_int(x) && !is_unit(A)) - return false; - return true; - } - - bool apply_fm(var x, ptr_vector& clauses) { - polynomial_ref A(m_pm), B(m_pm); - vector lo, hi; - poly* p = nullptr; - bool all_solved = true; - for (auto c : clauses) { - if (c->is_removed()) - continue; - if (c->size() != 1) { - all_solved = false; - continue; - } - literal lit = (*c)[0]; - bool sign = lit.sign(); - ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); - if (sign && a.is_eq()) { - all_solved = false; - continue; - } - if (!is_single_poly(a, p)) { - all_solved = false; - continue; - } - if (1 != m_pm.degree(p, x)) { - all_solved = false; - continue; - } - A = m_pm.coeff(p, x, 1, B); - if (!is_invertible(x, A)) { - all_solved = false; - continue; - } - auto const& A_value = m_pm.coeff(A, 0); - bool is_pos = m_pm.m().is_pos(A_value); - bool is_strict = false; - switch (a.get_kind()) { - case atom::LT: - // !(Ax + B < 0) == Ax + B >= 0 - if (sign) - is_strict = false; - else { - // Ax + B < 0 == -Ax - B > 0 - A = -A; - B = -B; - is_pos = !is_pos; - if (is_int(x)) { - // Ax + B > 0 == Ax + B - |A| >= 0 - if (is_pos) - B = m_pm.add(B, A); - else - B = m_pm.sub(B, A); - is_strict = false; - } - else - is_strict = true; - } - break; - case atom::GT: - // !(Ax + B > 0) == -Ax + -B >= 0 - if (sign) { - A = -A; - B = -B; - is_pos = !is_pos; - is_strict = false; - } - else { - // Ax + B > 0 - if (is_int(x)) { - // Ax + B - |A| >= 0 - if (is_pos) - B = m_pm.sub(B, A); - else - B = m_pm.add(B, A); - is_strict = false; - } - else - is_strict = true; - } - break; - case atom::EQ: { - all_solved = false; - continue; - // unsound: - m_display_var(verbose_stream(), x); - display(verbose_stream() << " ", *c) << "\n"; - bound_constraint l(x, A, B, false, c); - A = -A; - B = -B; - bound_constraint h(x, A, B, false, c); - apply_fm_equality(x, clauses, l, h); - return true; - } - default: - UNREACHABLE(); - break; - } - auto& set = is_pos ? hi : lo; - bool found = false; - for (auto const& bound : set) { - if (is_strict == bound.is_strict && m_pm.eq(A, bound.A) && m_pm.eq(B, bound.B)) - found = true; - } - if (found) - continue; - - set.push_back(bound_constraint(x, A, B, is_strict, c)); - - } - - if (lo.empty() && hi.empty()) - return false; - IF_VERBOSE(3, - verbose_stream() << "x" << x << " lo " << lo.size() << " hi " << hi.size() << "\n"; - for (auto c : clauses) - if (!c->is_removed()) - display(verbose_stream(), *c) << "\n"; - ); - - if (apply_fm_equality(x, clauses, lo, hi)) - return true; - - // return false; - - if (!all_solved) - return false; - - auto num_lo = lo.size(), num_hi = hi.size(); - if (num_lo >= 2 && num_hi >= 2 && (num_lo > 2 || num_hi > 2)) - return false; - - apply_fm_inequality(x, clauses, lo, hi); - - return true; - } - - void apply_fm_inequality( - var x, ptr_vector& clauses, - vector& lo, vector& hi) { - - polynomial_ref C(m_pm), D(m_pm); - for (auto c : clauses) - c->set_removed(); - - for (auto const& l : lo) { - // l.A * x + l.B, l.is_strict;, l.A < 0 - for (auto const& h : hi) { - // h.A * x + h.B, h.is_strict; h.A > 0 - // (l.A x + l.B)*h.A + (h.A x + h.B)*|l.A| >= 0 - C = m_pm.mul(l.B, h.A); - D = m_pm.mul(h.B, l.A); - C = m_pm.sub(C, D); - poly* p = C.get(); - bool is_even = false; - m_lemma.reset(); - if (l.is_strict || h.is_strict) - m_lemma.push_back(mk_ineq_literal(atom::GT, 1, &p, &is_even)); - else - m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p, &is_even)); - if (m_lemma[0] == true_literal) - continue; - auto a1 = static_cast<_assumption_set>(l.c->assumptions()); - auto a2 = static_cast<_assumption_set>(h.c->assumptions()); - auto cls = mk_clause(m_lemma.size(), m_lemma.data(), false, m_asm.mk_join(a1, a2)); - if (cls) - compute_occurs(*cls); - IF_VERBOSE(3, display(verbose_stream() << "add resolvent ", *cls) << "\n"); - } - } - - // track updates for model reconstruction - for (auto const& l : lo) - m_bounds.push_back(l); - for (auto const& h : hi) - m_bounds.push_back(h); - } - - bool apply_fm_equality( - var x, ptr_vector& clauses, - vector& lo, vector& hi) { - for (auto& l : lo) { - if (l.is_strict) - continue; - l.A = -l.A; - l.B = -l.B; - for (auto& h : hi) { - if (h.is_strict) - continue; - if (!m_pm.eq(l.B, h.B)) - continue; - if (!m_pm.eq(l.A, h.A)) - continue; - l.A = -l.A; - l.B = -l.B; - apply_fm_equality(x, clauses, l, h); - return true; - } - l.A = -l.A; - l.B = -l.B; - } - return false; - } - - void apply_fm_equality( - var x, ptr_vector& clauses, - bound_constraint& l, bound_constraint& h) { - auto a1 = static_cast<_assumption_set>(l.c->assumptions()); - auto a2 = static_cast<_assumption_set>(h.c->assumptions()); - a1 = m_asm.mk_join(a1, a2); - m_lemma_assumptions = a1; - - polynomial_ref A(l.A), B(l.B); - - if (m_pm.is_neg(l.A)) { - A = -A; - B = -B; - } - - // TODO: this can also replace solve_eqs - for (auto c : clauses) { - if (c->is_removed()) - continue; - c->set_removed(); - if (c == l.c || c == h.c) - continue; - m_lemma.reset(); - bool is_tautology = false; - for (literal lit : *c) { - lit = substitute_var(x, A, B, lit); - m_lemma.push_back(lit); - if (lit == true_literal) - is_tautology = true; - } - if (is_tautology) - continue; - a2 = static_cast<_assumption_set>(c->assumptions()); - auto cls = mk_clause(m_lemma.size(), m_lemma.data(), false, m_asm.mk_join(a1, a2)); - - IF_VERBOSE(3, - if (cls) { - m_display_var(verbose_stream(), x) << " * " << l.A << " = " << l.B << "\n"; - display(verbose_stream(), *c) << " -> "; - display(verbose_stream(), *cls) << "\n"; - }); - if (cls) - compute_occurs(*cls); - } - // track updates for model reconstruction - m_bounds.push_back(l); - m_bounds.push_back(h); - } - - // - // Subsumption simplification - // - void subsumption_simplify() { - compute_occurs(); - for (unsigned v = m_var_occurs.size(); v-- > 0; ) { - auto& clauses = m_var_occurs[v]; - for (auto c : clauses) { - if (c->is_marked() || c->is_removed()) - continue; - c->mark(); - for (auto c2 : clauses) { - if (c == c2 || c2->is_removed()) - continue; - if (subsumes(*c, *c2)) { - IF_VERBOSE(3, display(verbose_stream() << "subsumes ", *c); - display(verbose_stream() << " ", *c2) << "\n"); - c2->set_removed(); - } - } - } - } - for (auto c : m_clauses) - c->unmark(); - - cleanup_removed(); - } - - // does c1 subsume c2? - bool subsumes(clause const& c1, clause const& c2) { - if (c1.size() > c2.size()) - return false; - if ((c1.var_hash() & c2.var_hash()) != c1.var_hash()) - return false; - for (auto lit1 : c1) { - if (!any_of(c2, [&](auto lit2) { return subsumes(lit1, lit2); })) - return false; - } - return true; - } - - bool subsumes(literal lit1, literal lit2) { - if (lit1 == lit2) - return true; - - atom* a1 = m_atoms[lit1.var()]; - atom* a2 = m_atoms[lit2.var()]; - if (!a1 || !a2) - return false; - - // use m_pm.ge(p1, p2) - // whenever lit1 = p1 < 0, lit2 = p2 < 0 - // or lit1 = p1 < 0, lit2 = !(p2 > 0) - // or lit1 = !(p1 > 0), lit2 = !(p2 > 0) - // use m_pm.ge(p2, p1) - // whenever lit1 = p1 > 0, lit2 = p2 > 0 - // or lit1 = !(p1 < 0), lit2 = !(p2 < 0) - // or lit1 = p1 > 0, lit2 = !(p2 < 0) - // or lit1 = !(p1 > 0), lit2 = p2 < 0 - // - if (a1->is_ineq_atom() && a2->is_ineq_atom()) { - auto& i1 = *to_ineq_atom(a1); - auto& i2 = *to_ineq_atom(a2); - auto is_lt1 = !lit1.sign() && a1->get_kind() == atom::kind::LT; - auto is_le1 = lit1.sign() && a1->get_kind() == atom::kind::GT; - auto is_gt1 = !lit1.sign() && a1->get_kind() == atom::kind::GT; - auto is_ge1 = lit1.sign() && a1->get_kind() == atom::kind::LT; - - auto is_lt2 = !lit2.sign() && a2->get_kind() == atom::kind::LT; - auto is_le2 = lit2.sign() && a2->get_kind() == atom::kind::GT; - auto is_gt2 = !lit2.sign() && a2->get_kind() == atom::kind::GT; - auto is_ge2 = lit2.sign() && a2->get_kind() == atom::kind::LT; - - auto check_ge = (is_lt1 && (is_lt2 || is_le2)) || (is_le1 && is_le2); - auto check_le = (is_gt1 && (is_gt2 || is_ge2)) || (is_ge1 && is_ge2); - - if (i1.size() != i2.size()) - ; - else if (check_ge) { - for (unsigned i = 0; i < i1.size(); ++i) - if (!m_pm.ge(i1.p(i), i2.p(i))) - return false; - return true; - } - else if (check_le) { - for (unsigned i = 0; i < i1.size(); ++i) - if (!m_pm.ge(i2.p(i), i1.p(i))) - return false; - return true; - } - } - return false; - } - - // - // Equality simplificadtion (TODO, this should is deprecated by fm) - // - bool solve_eqs() { - polynomial_ref p(m_pm), q(m_pm); - var v; - init_var_signs(); - SASSERT(m_learned.empty()); - bool change = true; - while (change) { - change = false; - for (clause* c : m_clauses) { - if (solve_var(*c, v, p, q)) { - if (!m_pm.is_const(p)) - continue; - // optional throttles to restrict where solved variables are used - if (false && !m_pm.is_linear(q)) - continue; - if (false && !m_pm.is_univariate(q)) - continue; - bool is_small = true; - for (unsigned i = 0; i < m_pm.size(q) && is_small ; ++i) { - auto const& c = m_pm.coeff(q, i); - is_small &= m_pm.m().is_small(c); - } - if (!is_small && false) - continue; - TRACE("nlsat", tout << "p: " << p << "\nq: " << q << "\n x" << v << "\n";); - m_bounds.push_back(bound_constraint(v, p, q, false, nullptr)); - - if (!substitute_var(v, p, q, *c)) - return false; - del_clause(c, m_clauses); - TRACE("nlsat", display(tout << "simplified\n");); - change = true; - break; - } - } - } - return true; - } // Eliminated variables are tracked in m_bounds. // Each element in m_bounds tracks the eliminated variable and an upper or lower bound @@ -3380,126 +2958,7 @@ namespace nlsat { m_hi.set_core(x, val); } } - - literal substitute_var(var x, poly* p, poly* q, literal lit) { - auto b = lit.var(); - auto a = m_atoms[b]; - if (!a) - return lit; - SASSERT(a->is_ineq_atom()); - auto& a1 = *to_ineq_atom(a); - auto r = substitute_var(x, p, q, a1); - if (r == null_literal) - r = lit; - else if (lit.sign()) - r.neg(); - return r; - } - - literal substitute_var(var x, poly* p, poly* q, ineq_atom const& a) { - unsigned sz = a.size(); - bool_vector even; - polynomial_ref pr(m_pm), qq(q, m_pm); - qq = -qq; - polynomial_ref_vector ps(m_pm); - bool change = false; - auto k = a.get_kind(); - for (unsigned i = 0; i < sz; ++i) { - poly* po = a.p(i); - m_pm.substitute(po, x, qq, p, pr); - change |= pr != po; - TRACE("nlsat", tout << pr << "\n";); - if (m_pm.is_zero(pr)) { - ps.reset(); - even.reset(); - ps.push_back(pr); - even.push_back(false); - break; - } - if (m_pm.is_const(pr)) { - if (!a.is_even(i) && m_pm.m().is_neg(m_pm.coeff(pr, 0))) - k = atom::flip(k); - continue; - } - ps.push_back(pr); - even.push_back(a.is_even(i)); - } - if (!change) - return null_literal; - return mk_ineq_literal(k, ps.size(), ps.data(), even.data()); - } - - bool substitute_var(var x, poly* p, poly* q, clause& src) { - u_map b2l; - scoped_literal_vector lits(m_solver); - unsigned num_atoms = m_atoms.size(); - for (unsigned j = 0; j < num_atoms; ++j) { - atom* a = m_atoms[j]; - if (a && a->is_ineq_atom()) { - ineq_atom const& a1 = *to_ineq_atom(a); - literal l = substitute_var(x, p, q, a1); - if (l == null_literal) - continue; - lits.push_back(l); - if (a1.m_bool_var != l.var()) { - b2l.insert(a1.m_bool_var, l); - } - } - } - return update_clauses(b2l, src); - } - - - bool update_clauses(u_map const& b2l, clause& src) { - bool is_sat = true; - literal_vector lits; - clause_vector to_delete; - unsigned n = m_clauses.size(); - auto a1 = static_cast<_assumption_set>(src.assumptions()); - for (unsigned i = 0; i < n; ++i) { - clause* c = m_clauses[i]; - if (c == &src) - continue; - lits.reset(); - bool changed = false; - bool is_tautology = false; - for (literal l : *c) { - literal lit = null_literal; - if (b2l.find(l.var(), lit)) { - lit = l.sign() ? ~lit : lit; - if (lit == true_literal) { - is_tautology = true; - } - else if (lit != false_literal) { - lits.push_back(lit); - } - changed = true; - } - else { - lits.push_back(l); - } - } - if (changed) { - to_delete.push_back(c); - if (is_tautology) { - continue; - } - if (lits.empty()) { - is_sat = false; - } - else { - auto a2 = static_cast<_assumption_set>(c->assumptions()); - auto a = m_asm.mk_join(a1, a2); - mk_clause(lits.size(), lits.data(), c->is_learned(), a); - } - } - } - for (clause* c : to_delete) { - del_clause(c, m_clauses); - } - return is_sat; - } - + bool is_unit_ineq(clause const& c) const { return c.size() == 1 && @@ -3560,65 +3019,7 @@ namespace nlsat { bool is_single_poly(ineq_atom const& a, poly*& p) { unsigned sz = a.size(); return sz == 1 && a.is_odd(0) && (p = a.p(0), true); - } - - svector m_var_signs; - - void init_var_signs() { - m_var_signs.reset(); - for (clause* cp : m_clauses) { - clause& c = *cp; - var x = 0; - lbool cmp = is_cmp0(c, x); - switch (cmp) { - case l_true: - m_var_signs.setx(x, l_true, l_undef); - break; - case l_false: - m_var_signs.setx(x, l_false, l_undef); - break; - default: - break; - } - } - } - - /** - \brief returns true then c is an equality that is equivalent to v*p + q, - and p > 0, v does not occur in p, q. - */ - bool solve_var(clause& c, var& v, polynomial_ref& p, polynomial_ref& q) { - poly* p0; - if (!is_unit_eq(c)) - return false; - ineq_atom & a = *to_ineq_atom(m_atoms[c[0].var()]); - if (!is_single_poly(a, p0)) - return false; - var mx = max_var(p0); - if (mx >= m_is_int.size()) - return false; - for (var x = 0; x <= mx; ++x) { - if (1 == m_pm.degree(p0, x)) { - p = m_pm.coeff(p0, x, 1, q); - if (!is_invertible(x, p)) - continue; - switch (m_pm.sign(p, m_var_signs)) { - case l_true: - v = x; - return true; - case l_false: - v = x; - p = -p; - q = -q; - return true; - default: - break; - } - } - } - return false; - } - + } bool is_unit(polynomial_ref const& p) { if (!m_pm.is_const(p)) @@ -3892,6 +3293,7 @@ namespace nlsat { case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; + default: UNREACHABLE(); break; } out << "))"; return out; @@ -4058,12 +3460,14 @@ namespace nlsat { } std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { + if (!m_display_assumption) + return out; vector deps; m_asm.linearize(s, deps); bool first = true; for (auto dep : deps) { if (first) first = false; else out << " "; - if (m_display_assumption) (*m_display_assumption)(out, dep); + (*m_display_assumption)(out, dep); } return out; } @@ -4475,6 +3879,10 @@ namespace nlsat { #endif TRACE("nlsat", display(tout);); } + + void solver::del_clause(clause* c) { + m_imp->del_clause(c); + } var solver::mk_var(bool is_int) { return m_imp->mk_var(is_int); @@ -4484,8 +3892,8 @@ namespace nlsat { return m_imp->mk_ineq_atom(k, sz, ps, is_even); } - literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { - return m_imp->mk_ineq_literal(k, sz, ps, is_even); + literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify) { + return m_imp->mk_ineq_literal(k, sz, ps, is_even, simplify); } bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { @@ -4499,9 +3907,17 @@ namespace nlsat { void solver::dec_ref(bool_var b) { m_imp->dec_ref(b); } + + void solver::inc_ref(assumption a) { + m_imp->inc_ref(static_cast(a)); + } + + void solver::dec_ref(assumption a) { + m_imp->dec_ref(static_cast(a)); + } void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { - return m_imp->mk_clause(num_lits, lits, a); + return m_imp->mk_external_clause(num_lits, lits, a); } std::ostream& solver::display(std::ostream & out) const { @@ -4536,6 +3952,10 @@ namespace nlsat { return out; } + std::ostream& solver::display(std::ostream& out, clause const& c) const { + return m_imp->display(out, c); + } + std::ostream& solver::display_smt2(std::ostream & out) const { return m_imp->display_smt2(out); } @@ -4582,5 +4002,24 @@ namespace nlsat { return m_imp->collect_statistics(st); } + clause* solver::mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a) { + return m_imp->mk_clause(n, lits, learned, static_cast(a)); + } + + void solver::inc_simplify() { + m_imp->m_stats.m_simplifications++; + } + + bool solver::has_root_atom(clause const& c) const { + return m_imp->has_root_atom(c); + } + + void solver::add_bound(bound_constraint const& c) { + m_imp->m_bounds.push_back(c); + } + + assumption solver::join(assumption a, assumption b) { + return (m_imp->m_asm.mk_join(static_cast(a), static_cast(b))); + } }; diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index 15764150d7d..e3a57f6534c 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -24,6 +24,7 @@ Revision History: #include "util/params.h" #include "util/statistics.h" #include "util/rlimit.h" +#include "util/dependency.h" namespace nlsat { @@ -36,6 +37,15 @@ namespace nlsat { virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0; }; + struct bound_constraint { + var x; + polynomial_ref A, B; + bool is_strict; + clause* c; + bound_constraint(var x, polynomial_ref& A, polynomial_ref& B, bool is_strict, clause* c) : + x(x), A(A), B(B), is_strict(is_strict), c(c) {} + }; + class solver { struct imp; struct ctx; @@ -103,7 +113,7 @@ namespace nlsat { e[i] = 1 if is_even[i] is false e[i] = 2 if is_even[i] is true */ - literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even); + literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false); /** \brief Create an atom of the form: x=root[i](p), xroot[i](p) @@ -114,6 +124,9 @@ namespace nlsat { void inc_ref(literal l) { inc_ref(l.var()); } void dec_ref(bool_var b); void dec_ref(literal l) { dec_ref(l.var()); } + void inc_ref(assumption a); + void dec_ref(assumption a); + /** \brief Create a new clause. @@ -172,6 +185,17 @@ namespace nlsat { void get_bvalues(svector const& bvars, svector& vs); void set_bvalues(svector const& vs); + /** + * \brief Access functions for simplify module. + */ + void del_clause(clause* c); + clause* mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a); + bool has_root_atom(clause const& c) const; + assumption join(assumption a, assumption b); + + void inc_simplify(); + void add_bound(bound_constraint const& c); + /** \brief reorder variables. */ @@ -244,6 +268,8 @@ namespace nlsat { std::ostream& display(std::ostream & out, unsigned n, literal const* ls) const; + std::ostream& display(std::ostream& out, clause const& c) const; + std::ostream& display(std::ostream & out, literal_vector const& ls) const; std::ostream& display(std::ostream & out, atom const& a) const; @@ -254,9 +280,10 @@ namespace nlsat { std::ostream& display_smt2(std::ostream & out, literal_vector const& ls) const; - std::ostream& display_smt2(std::ostream & out) const; + std::ostream& display_smt2(std::ostream & out) const; + /** \brief Display variable */ diff --git a/src/nlsat/nlsat_types.h b/src/nlsat/nlsat_types.h index a6071cdbaba..76999e9d4fb 100644 --- a/src/nlsat/nlsat_types.h +++ b/src/nlsat/nlsat_types.h @@ -32,6 +32,7 @@ namespace nlsat { #define NLSAT_VB_LVL 10 typedef void * assumption; typedef void * assumption_set; + typedef void * internal_assumption; typedef sat::bool_var bool_var; typedef sat::bool_var_vector bool_var_vector; @@ -74,6 +75,7 @@ namespace nlsat { } protected: friend class solver; + friend class simplify; kind m_kind; unsigned m_ref_count; bool_var m_bool_var; diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index bd980df340a..879168721ba 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -28,6 +28,7 @@ Revision History: # include #endif + small_object_allocator::small_object_allocator(char const * id) { for (unsigned i = 0; i < NUM_SLOTS; i++) { m_chunks[i] = nullptr; @@ -98,7 +99,9 @@ void small_object_allocator::deallocate(size_t size, void * p) { void * small_object_allocator::allocate(size_t size) { - if (size == 0) return nullptr; + if (size == 0) + return nullptr; + #if defined(Z3DEBUG) && !defined(_WINDOWS) @@ -109,6 +112,8 @@ void * small_object_allocator::allocate(size_t size) { if (size >= SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) { return memory::allocate(size); } + + #ifdef Z3DEBUG size_t osize = size; #endif From 552068a71e107e5c4568b3724e343c3b812e4c20 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 17 May 2024 11:10:09 +0100 Subject: [PATCH 062/414] let the replayer stop when it encounters a C command with invalid args --- src/api/z3_replayer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index aebc2f9a52d..92f2c2d6a0c 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -80,7 +80,7 @@ struct z3_replayer::imp { strm << "expecting " << kind2string(k) << " at position " << pos << " but got " << kind2string(m_args[pos].m_kind); TRACE("z3_replayer", tout << strm.str() << "\n";); - throw z3_replayer_exception(strm.str()); + throw z3_replayer_exception(std::move(strm).str()); } } @@ -529,6 +529,9 @@ struct z3_replayer::imp { catch (z3_error & ex) { throw ex; } + catch (z3_replayer_exception &) { + throw; + } catch (z3_exception & ex) { std::cout << "[z3 exception]: " << ex.msg() << std::endl; } From cb50dcabdaf71f088b01fef8c80e6c5b55708dd7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 May 2024 18:28:08 -0700 Subject: [PATCH 063/414] fix #7229 --- src/api/z3_api.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index fce5f15d14a..dacf4b578f5 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1593,12 +1593,9 @@ extern "C" { although some parameters can be changed using #Z3_update_param_value. All main interaction with Z3 happens in the context of a \c Z3_context. - In contrast to #Z3_mk_context_rc, the life time of \c Z3_ast objects - are determined by the scope level of #Z3_solver_push and #Z3_solver_pop. - In other words, a \c Z3_ast object remains valid until there is a - call to #Z3_solver_pop that takes the current scope below the level where - the object was created. - + In contrast to \c Z3_mk_context_rc the life time of \c Z3_ast objects + persists with the life time of the context. + Note that all other reference counted objects, including \c Z3_model, \c Z3_solver, \c Z3_func_interp have to be managed by the caller. Their reference counts are not handled by the context. From 18a95d89c6b16994ff07d31e5059e79b007f6c4e Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sat, 18 May 2024 16:30:26 +0100 Subject: [PATCH 064/414] fix assertion failure when printing stats It would crash when number of bin_lemmas == 0 --- src/smt/smt_context_pp.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 57875a8bdfa..a160b5f59e9 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -712,14 +712,14 @@ namespace smt { }; std::stringstream strm; strm << "(smt.stats " - << std::setw(4) << m_stats.m_num_restarts << " " - << std::setw(6) << m_stats.m_num_conflicts << " " - << std::setw(6) << m_stats.m_num_decisions << " " - << std::setw(6) << m_stats.m_num_propagations << " " - << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << "/" << num_units() - << std::setw(7) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; - strm << std::setw(5) << m_stats.m_num_simplifications << " " - << std::setw(4) << m_stats.m_num_del_clauses << " " + << std::setw(4) << m_stats.m_num_restarts << ' ' + << std::setw(6) << m_stats.m_num_conflicts << ' ' + << std::setw(6) << m_stats.m_num_decisions << ' ' + << std::setw(6) << m_stats.m_num_propagations << ' ' + << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << '/' << bin_clauses << '/' << num_units() << ' ' + << std::setw(7) << m_lemmas.size() << '/' << bin_lemmas << ' ' + << std::setw(5) << m_stats.m_num_simplifications << ' ' + << std::setw(4) << m_stats.m_num_del_clauses << ' ' << std::setw(7) << mem_stat() << ")\n"; std::string str = std::move(strm).str(); @@ -745,7 +745,7 @@ namespace smt { m_last_position_log = m_stats.m_num_restarts; // restarts decisions clauses simplifications memory // conflicts propagations lemmas deletions - int adjust[9] = { -3, -3, -3, -3, -3, -4, -4, -4, -1 }; + const int adjust[9] = { -3, -3, -3, -3, -3, -4, -4, -4, -1 }; char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin/units ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; std::stringstream l1, l2; From e454ae275c20ce2724f24de7f096f80ee52128a8 Mon Sep 17 00:00:00 2001 From: Jakob Rath Date: Sun, 19 May 2024 04:01:35 +0200 Subject: [PATCH 065/414] intblast: fix translation of sign_ext (#7230) --- src/sat/smt/intblast_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index 421966377c0..f91113d591c 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -881,10 +881,10 @@ namespace intblast { SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr)); unsigned arg_sz = bv.get_bv_size(bv_expr); unsigned sz = bv.get_bv_size(e); - rational N = rational::power_of_two(sz); + // rational N = rational::power_of_two(sz); rational M = rational::power_of_two(arg_sz); expr* signbit = a.mk_ge(r, a.mk_int(M / 2)); - r = m.mk_ite(signbit, a.mk_uminus(r), r); + r = m.mk_ite(signbit, a.mk_sub(r, a.mk_int(M)), r); break; } case OP_INT2BV: From 904a50fe498c325f5c347628cd66a1ab610030e0 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 27 May 2024 12:19:29 +0700 Subject: [PATCH 066/414] Fix compilation error in column_info (#7235) Reference to `m_low_bound` should be `m_lower_bound`. --- src/math/lp/column_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/column_info.h b/src/math/lp/column_info.h index 1dc0c60c770..9cbeea66c81 100644 --- a/src/math/lp/column_info.h +++ b/src/math/lp/column_info.h @@ -47,7 +47,7 @@ class column_info { m_lower_bound_is_strict == c.m_lower_bound_is_strict && m_upper_bound_is_set == c.m_upper_bound_is_set&& m_upper_bound_is_strict == c.m_upper_bound_is_strict&& - (!m_lower_bound_is_set || m_lower_bound == c.m_low_bound) && + (!m_lower_bound_is_set || m_lower_bound == c.m_lower_bound) && (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && m_cost == c.m_cost && m_is_fixed == c.m_is_fixed && From c0a7af44204237b3435d9f436721a9f3e50ee5cc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 May 2024 11:25:00 +0400 Subject: [PATCH 067/414] fix bugs with tracking premises in user propagator in sat/smt --- src/sat/smt/user_solver.cpp | 27 +++++++++++++++++++++++---- src/sat/smt/user_solver.h | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 0204573dcb1..a4273744b00 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -39,8 +39,10 @@ namespace user_solver { ctx.attach_th_var(n, this, v); expr_ref r(m); sat::literal_vector explain; - if (ctx.is_fixed(n, r, explain)) + if (ctx.is_fixed(n, r, explain)) { m_prop.push_back(prop_info(explain, v, r)); + DEBUG_CODE(for (auto lit : explain) VERIFY(s().value(lit) == l_true);); + } } bool solver::propagate_cb( @@ -90,8 +92,20 @@ namespace user_solver { if (!m_fixed_eh) return; force_push(); + if (m_fixed.contains(v)) + return; + m_fixed.insert(v); + ctx.push(insert_map(m_fixed, v)); m_id2justification.setx(v, sat::literal_vector(num_lits, jlits), sat::literal_vector()); - m_fixed_eh(m_user_context, this, var2expr(v), value); + for (unsigned i = 0; i < num_lits; ++i) + if (s().value(m_id2justification[v][i]) == l_false) + m_id2justification[v][i].neg(); + try { + m_fixed_eh(m_user_context, this, var2expr(v), value); + } + catch (...) { + throw default_exception("Exception thrown in \"fixed\"-callback"); + } } bool solver::decide(sat::bool_var& var, lbool& phase) { @@ -179,9 +193,9 @@ namespace user_solver { sat::literal lit = ctx.internalize(prop.m_conseq, false, false); if (s().value(lit) != l_true) { auto j = mk_justification(m_qhead); + persist_clause(lit, j); s().assign(lit, j); ++m_stats.m_num_propagations; - persist_clause(lit, j); } } @@ -193,7 +207,7 @@ namespace user_solver { if (m_qhead == m_prop.size() && m_replay_qhead == m_clauses_to_replay.size()) return false; force_push(); - + bool replayed = false; if (m_replay_qhead < m_clauses_to_replay.size()) { replayed = true; @@ -235,11 +249,15 @@ namespace user_solver { clause.push_back(ctx.literal2expr(~lit)); for (auto const& [a,b] : prop.m_eqs) clause.push_back(m.mk_not(m.mk_eq(a, b))); + clause.push_back(ctx.literal2expr(lit)); + if (m.is_false(clause.back())) + clause.pop_back(); m_clauses_to_replay.push_back(clause); if (m_replay_qhead + 1 < m_clauses_to_replay.size()) std::swap(m_clauses_to_replay[m_replay_qhead], m_clauses_to_replay[m_clauses_to_replay.size()-1]); + ctx.push(value_trail(m_replay_qhead)); ++m_replay_qhead; } @@ -260,6 +278,7 @@ namespace user_solver { auto const& prop = m_prop[j.m_propagation_index]; for (unsigned id : prop.m_ids) r.append(m_id2justification[id]); + DEBUG_CODE(for (auto lit : r) VERIFY(s().value(lit) == l_true);); for (auto const& p : prop.m_eqs) ctx.add_eq_antecedent(probing, expr2enode(p.first), expr2enode(p.second)); } diff --git a/src/sat/smt/user_solver.h b/src/sat/smt/user_solver.h index 373b046b80e..4bdfcf064f4 100644 --- a/src/sat/smt/user_solver.h +++ b/src/sat/smt/user_solver.h @@ -79,6 +79,7 @@ namespace user_solver { lbool m_next_split_phase = l_undef; vector m_clauses_to_replay; unsigned m_replay_qhead = 0; + uint_set m_fixed; struct justification { unsigned m_propagation_index { 0 }; From 770c51ad2b257e59710041894581ec0411000018 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 May 2024 11:54:50 +0400 Subject: [PATCH 068/414] add m_replay_qhead to the trail --- src/smt/theory_user_propagator.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 93faf9e4e6a..d3b31252474 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -339,6 +339,11 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { ctx.mark_as_relevant(lit); m_lits.push_back(lit); + if (ctx.get_fparams().m_up_persist_clauses) + ctx.mk_th_axiom(get_id(), m_lits); + else + ctx.mk_th_lemma(get_id(), m_lits); + if (ctx.get_fparams().m_up_persist_clauses) { ctx.mk_th_axiom(get_id(), m_lits); expr_ref_vector clause(m); @@ -347,13 +352,12 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { m_clauses_to_replay.push_back(clause); if (m_replay_qhead + 1 < m_clauses_to_replay.size()) std::swap(m_clauses_to_replay[m_replay_qhead], m_clauses_to_replay[m_clauses_to_replay.size()-1]); + ctx.push_trail(value_trail(m_replay_qhead)); ++m_replay_qhead; } - else { - ctx.mk_th_lemma(get_id(), m_lits); - } - TRACE("user_propagate", ctx.display(tout);); } + TRACE("user_propagate", ctx.display(tout);); + } void theory_user_propagator::propagate_new_fixed(prop_info const& prop) { @@ -368,7 +372,7 @@ void theory_user_propagator::propagate() { force_push(); unsigned qhead = m_replay_qhead; - if (qhead < m_clauses_to_replay.size()) { + if (qhead < m_clauses_to_replay.size()) { for (; qhead < m_clauses_to_replay.size() && !ctx.inconsistent(); ++qhead) replay_clause(m_clauses_to_replay.get(qhead)); ctx.push_trail(value_trail(m_replay_qhead)); From 49610f51595be6811f70cee5d81e5664a26c92aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 6 Jun 2024 08:11:19 +0200 Subject: [PATCH 069/414] fix #7246 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 5829b18d11f..1cd0c1400c6 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3712,6 +3712,8 @@ namespace sat { } void solver::user_pop(unsigned num_scopes) { + if (m_user_scope_literals.empty()) + return; unsigned old_sz = m_user_scope_literals.size() - num_scopes; bool_var max_var = m_user_scope_literals[old_sz].var(); m_user_scope_literals.shrink(old_sz); From b831a589e5e9c665bb02584361c4d169e5f15faa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Jun 2024 08:50:24 -0700 Subject: [PATCH 070/414] fixes to #7250 --- src/ast/euf/euf_egraph.cpp | 2 +- src/sat/smt/bv_solver.cpp | 22 ++++++++++++++++++++++ src/sat/smt/bv_solver.h | 1 + src/sat/smt/euf_proof.cpp | 10 +++++++++- src/sat/smt/euf_solver.cpp | 14 +++++++------- src/sat/smt/euf_solver.h | 1 + src/sat/smt/sat_th.cpp | 4 ++++ src/sat/smt/sat_th.h | 2 ++ 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index f19a146c5c6..21bc14ba4b3 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -231,7 +231,7 @@ namespace euf { for (auto const& q : euf::enode_th_vars(r2)) if (p.get_id() == q.get_id()) add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n); - } + } } diff --git a/src/sat/smt/bv_solver.cpp b/src/sat/smt/bv_solver.cpp index 53a00414074..e879ba30c68 100644 --- a/src/sat/smt/bv_solver.cpp +++ b/src/sat/smt/bv_solver.cpp @@ -301,6 +301,28 @@ namespace bv { bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) { return false; } bool solver::is_external(bool_var v) { return true; } + euf::enode_pair solver::get_justification_eq(size_t j) { + auto& c = bv_justification::from_index(j); + TRACE("bv", display_constraint(tout, j) << "\n";); + switch (c.m_kind) { + case bv_justification::kind_t::eq2bit: + UNREACHABLE(); + return euf::enode_pair(); + case bv_justification::kind_t::ne2bit: + UNREACHABLE(); + return euf::enode_pair(); + case bv_justification::kind_t::bit2eq: + return { var2enode(c.m_v1), var2enode(c.m_v2) }; + case bv_justification::kind_t::bit2ne: + UNREACHABLE(); + return euf::enode_pair(); + case bv_justification::kind_t::bv2int: + UNREACHABLE(); + return euf::enode_pair(); + } + return euf::enode_pair(); + } + void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) { auto& c = bv_justification::from_index(idx); TRACE("bv", display_constraint(tout, idx) << "\n";); diff --git a/src/sat/smt/bv_solver.h b/src/sat/smt/bv_solver.h index df4e5c9c2e9..9cbee87f9ab 100644 --- a/src/sat/smt/bv_solver.h +++ b/src/sat/smt/bv_solver.h @@ -349,6 +349,7 @@ namespace bv { bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override; bool is_external(bool_var v) override; void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override; + euf::enode_pair get_justification_eq(size_t j) override; void asserted(literal l) override; sat::check_result check() override; void push_core() override; diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 39c9879a608..099019c6e03 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -43,6 +43,13 @@ namespace euf { m_proof_initialized = true; } + enode_pair solver::get_justification_eq(size_t j) { + auto* ext = sat::constraint_base::to_extension(j); + SASSERT(ext != this); + auto s = fid2solver(ext->get_id()); + return s->get_justification_eq(j); + } + /** * Log justifications. * is_euf - true if l is justified by congruence closure. In this case create a congruence closure proof. @@ -63,7 +70,8 @@ namespace euf { if (is_literal(e)) m_hint_lits.push_back(get_literal(e)); else { - auto [x, y] = th_explain::from_index(get_justification(e)).eq_consequent(); + SASSERT(is_justification(e)); + auto [x, y] = get_justification_eq(get_justification(e)); eqs.push_back(m.mk_eq(x->get_expr(), y->get_expr())); set_tmp_bool_var(nv, eqs.back()); m_hint_lits.push_back(literal(nv, false)); diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index efd091f6653..b866990af43 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -278,9 +278,9 @@ namespace euf { r.push_back(get_literal(e)); else { multiple_theories = true; - size_t idx = get_justification(e); + size_t idx = get_justification(e); auto* ext = sat::constraint_base::to_extension(idx); - SASSERT(ext != this); + SASSERT(ext != this); sat::literal lit = sat::null_literal; ext->get_antecedents(lit, idx, r, probing); } @@ -307,11 +307,11 @@ namespace euf { void solver::get_eq_antecedents(enode* a, enode* b, literal_vector& r) { m_egraph.begin_explain(); m_explain.reset(); - m_egraph.explain_eq(m_explain, nullptr, a, b); - for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { - size_t* e = m_explain[qhead]; - if (is_literal(e)) - r.push_back(get_literal(e)); + m_egraph.explain_eq(m_explain, nullptr, a, b); + for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { + size_t* e = m_explain[qhead]; + if (is_literal(e)) + r.push_back(get_literal(e)); else { size_t idx = get_justification(e); auto* ext = sat::constraint_base::to_extension(idx); diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index ec89667d5bf..22e68d2dabe 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -231,6 +231,7 @@ namespace euf { void log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint); void log_justification(literal l, th_explain const& jst); void log_justifications(literal l, unsigned explain_size, bool is_euf); + enode_pair get_justification_eq(size_t j); void log_rup(literal l, literal_vector const& r); diff --git a/src/sat/smt/sat_th.cpp b/src/sat/smt/sat_th.cpp index 21e3883e8a0..4176be9857a 100644 --- a/src/sat/smt/sat_th.cpp +++ b/src/sat/smt/sat_th.cpp @@ -51,6 +51,10 @@ namespace euf { return true; } + enode_pair th_solver::get_justification_eq(size_t j) { + return th_explain::from_index(j).eq_consequent(); + } + th_euf_solver::th_euf_solver(euf::solver& ctx, symbol const& name, euf::theory_id id): th_solver(ctx.get_manager(), name, id), ctx(ctx) diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index cdd1292a330..37394801452 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -137,6 +137,8 @@ namespace euf { sat::status status() const { return sat::status::th(false, get_id()); } + virtual euf::enode_pair get_justification_eq(size_t j); + /** * Local search interface */ From 35c1cacf44951787e1427a4b76941f961aff4584 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Jun 2024 14:26:35 -0700 Subject: [PATCH 071/414] fix #7248 --- src/math/grobner/grobner.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/math/grobner/grobner.cpp b/src/math/grobner/grobner.cpp index 39a32789119..8aa109bddff 100644 --- a/src/math/grobner/grobner.cpp +++ b/src/math/grobner/grobner.cpp @@ -468,12 +468,23 @@ void grobner::merge_monomials(ptr_vector & monomials) { void grobner::normalize_coeff(ptr_vector & monomials) { if (monomials.empty()) return; + unsigned sz = monomials.size(); rational c = monomials[0]->m_coeff; if (c.is_one()) return; - unsigned sz = monomials.size(); - for (unsigned i = 0; i < sz; i++) + if (c.is_minus_one()) { + for (unsigned i = 0; i < sz && m_manager.inc(); i++) + monomials[i]->m_coeff.neg(); + return; + } + if (c.bitsize() > 1000) + return; + + for (unsigned i = 0; i < sz && m_manager.inc(); i++) { + if (monomials[i]->m_coeff.bitsize() > 1000) + continue; monomials[i]->m_coeff /= c; + } } /** @@ -611,6 +622,8 @@ grobner::equation * grobner::simplify(equation const * source, equation * target TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source);); if (source->get_num_monomials() == 0) return nullptr; + if (!m_manager.inc()) + return target; m_stats.m_simplify++; bool result = false; bool simplified; From a6b502779bfd3cd16ff536ceafa08a5271c312f9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jun 2024 17:52:17 -0700 Subject: [PATCH 072/414] fix #7252 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 16 +++++++++++----- src/ast/seq_decl_plugin.cpp | 6 ++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index aa581322485..50f0bc05a25 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -3201,6 +3201,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) { void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) { sort* seq_sort = nullptr, * ele_sort = nullptr; + expr_ref _r(r, m()), _path(path, m()); VERIFY(m_util.is_re(r, seq_sort)); VERIFY(m_util.is_seq(seq_sort, ele_sort)); SASSERT(ele_sort == e->get_sort()); @@ -3347,8 +3348,10 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref else if (re().is_loop(r, r1, lo, hi)) { if ((lo == 0 && hi == 0) || hi < lo) result = nothing(); - else - result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop_proper(r1, (lo == 0 ? 0 : lo - 1), hi - 1)); + else { + expr_ref t(re().mk_loop_proper(r1, (lo == 0 ? 0 : lo - 1), hi - 1), m()); + result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), t); + } } else if (re().is_opt(r, r1)) result = mk_antimirov_deriv(e, r1, path); @@ -3414,15 +3417,18 @@ expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* e, expr* d1, expr* expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) { expr_ref result(m()); - // Take reference count of r and d expr_ref _r(r, m()), _d(d, m()); expr* c, * t, * e; - if (m().is_ite(d, c, t, e)) - result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r)); + if (m().is_ite(d, c, t, e)) { + auto r2 = mk_antimirov_deriv_concat(e, r); + auto r1 = mk_antimirov_deriv_concat(t, r); + result = m().mk_ite(c, r1, r2); + } else if (re().is_union(d, t, e)) result = mk_antimirov_deriv_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r)); else result = mk_re_append(d, r); + SASSERT(result.get()); return result; } diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 9292a7be62d..9048ac12270 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1164,11 +1164,13 @@ expr* seq_util::rex::mk_loop_proper(expr* r, unsigned lo, unsigned hi) // avoid creating a loop with both bounds 0 // such an expression is invalid as a loop // it is BY DEFINITION = epsilon - return mk_epsilon(seq_sort); + r = mk_epsilon(seq_sort); + return r; } - if (lo == 1 && hi == 1) + if (lo == 1 && hi == 1) { // do not create a loop unless it actually is a loop return r; + } parameter params[2] = { parameter(lo), parameter(hi) }; return m.mk_app(m_fid, OP_RE_LOOP, 2, params, 1, &r); } From 01e47bfe263f8eabf2daa2da35c7b248ee33fff9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Jun 2024 02:29:32 -0700 Subject: [PATCH 073/414] fix #7245 --- src/opt/opt_solver.cpp | 14 ++++---------- src/smt/smt_kernel.h | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 955025fd96c..aff1301e079 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -259,8 +259,9 @@ namespace opt { if (!m_models[i]) m_models.set(i, m_last_model.get()); - if (val > m_objective_values[i]) - m_objective_values[i] = val; + if (val > m_objective_values[i]) { + m_objective_values[i] = val; + } if (!m_last_model) return true; @@ -284,14 +285,7 @@ namespace opt { // auto check_bound = [&]() { SASSERT(has_shared); - bool ok = bound_value(i, val); - if (l_true != m_context.check(0, nullptr)) - return false; - m_context.get_model(m_last_model); - if (!m_last_model) - return false; - update_objective(); - return ok; + return bound_value(i, val) && l_true == m_context.check(0, nullptr); }; if (!val.is_finite()) { diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index dacbb525e09..eec74f8b1f7 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -258,7 +258,7 @@ namespace smt { expr_ref_vector get_trail(unsigned max_level); /** - \brief (For debubbing purposes) Prints the state of the kernel + \brief (For debugging purposes) Prints the state of the kernel */ std::ostream& display(std::ostream & out) const; From 972d35204c5d6e53994089c5394838872fb2fde7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 3 Apr 2024 08:19:57 -0700 Subject: [PATCH 074/414] reshuffle priorities on multiplication allow non-determinism. --- src/ast/sls/bv_sls_eval.cpp | 24 ++++++------- src/ast/sls/sls_valuation.cpp | 64 ++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index d61a831d375..f2dd165abe9 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -1049,16 +1049,16 @@ namespace bv { if (b.is_zero(e)) { a.get_variant(m_tmp, m_rand); - for (unsigned i = 0; i < b.bw - parity_b; ++i) - m_tmp.set(i, false); + if (m_rand(10) != 0) + for (unsigned i = 0; i < b.bw - parity_b; ++i) + m_tmp.set(i, false); return a.set_repair(random_bool(), m_tmp); } - if (b.is_zero()) - return a.set_random(m_rand); - - if (m_rand(20) == 0) - return a.set_random(m_rand); + if (b.is_zero() || m_rand(20) == 0) { + a.get_variant(m_tmp, m_rand); + return a.set_repair(random_bool(), m_tmp); + } #if 0 verbose_stream() << "solve for " << e << "\n"; @@ -1342,11 +1342,11 @@ namespace bv { return false; } - bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) { - if (i == 0) - return try_repair_ashr0(e, a, b); - else - return try_repair_ashr1(e, a, b); + bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) { + if (i == 0) + return try_repair_ashr0(e, a, b); + else + return try_repair_ashr1(e, a, b); } bool sls_eval::try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i) { diff --git a/src/ast/sls/sls_valuation.cpp b/src/ast/sls/sls_valuation.cpp index 99b3921f3bf..b5f04a3a712 100644 --- a/src/ast/sls/sls_valuation.cpp +++ b/src/ast/sls/sls_valuation.cpp @@ -497,8 +497,6 @@ namespace bv { } void sls_valuation::add_range(rational l, rational h) { - //return; - //verbose_stream() << *this << " " << l << " " << h << " --> \n"; l = mod(l, rational::power_of_two(bw)); h = mod(h, rational::power_of_two(bw)); @@ -555,17 +553,6 @@ namespace bv { // // update bits based on ranges - // tighten lo/hi based on fixed bits. - // lo[bit_i] != fixedbit[bit_i] - // let bit_i be most significant bit position of disagreement. - // if fixedbit = 1, lo = 0, increment lo - // if fixedbit = 0, lo = 1, lo := fixed & bits - // (hi-1)[bit_i] != fixedbit[bit_i] - // if fixedbit = 0, hi-1 = 1, set hi-1 := 0, maximize below bit_i - // if fixedbit = 1, hi-1 = 0, hi := fixed & bits - // tighten fixed bits based on lo/hi - // lo + 1 = hi -> set bits = lo - // lo < hi, set most significant bits based on hi // unsigned sls_valuation::diff_index(bvect const& a) const { @@ -638,6 +625,57 @@ namespace bv { add1(hi1); hi1.copy_to(nw, m_hi); + /* + unsigned lo_index = 0, hi_index = 0; + for (unsigned i = nw; i-- > 0; ) { + auto lo_diff = (fixed[i] & (m_bits[i] ^ m_lo[i])); + if (lo_diff != 0 && lo_index == 0) + lo_index = 1 + i * 8 * sizeof(digit_t) + log2(lo_diff); + auto hi_diff = (fixed[i] & (m_bits[i] ^ hi1[i])); + if (hi_diff != 0 && hi_index == 0) + hi_index = 1 + i * 8 * sizeof(digit_t) + log2(hi_diff); + } + + if (lo_index != 0) { + lo_index--; + SASSERT(m_lo.get(lo_index) != m_bits.get(lo_index)); + SASSERT(fixed.get(lo_index)); + for (unsigned i = 0; i <= lo_index; ++i) { + if (!fixed.get(i)) + m_lo.set(i, false); + else if (fixed.get(i)) + m_lo.set(i, m_bits.get(i)); + } + if (!m_bits.get(lo_index)) { + for (unsigned i = lo_index + 1; i < bw; ++i) + if (!fixed.get(i) && !m_lo.get(i)) { + m_lo.set(i, true); + break; + } + } + } + if (hi_index != 0) { + hi_index--; + SASSERT(hi1.get(hi_index) != m_bits.get(hi_index)); + SASSERT(fixed.get(hi_index)); + for (unsigned i = 0; i <= hi_index; ++i) { + if (!fixed.get(i)) + hi1.set(i, true); + else if (fixed.get(i)) + hi1.set(i, m_bits.get(i)); + } + if (m_bits.get(hi_index)) { + for (unsigned i = hi_index + 1; i < bw; ++i) + if (!fixed.get(i) && hi1.get(i)) { + hi1.set(i, false); + break; + } + } + add1(hi1); + hi1.copy_to(nw, m_hi); + } + */ + if (has_range() && !in_range(m_bits)) m_bits = m_lo; From 81ebd52f61570d473cfd05a178bcde722d237335 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Jun 2024 15:04:42 -0700 Subject: [PATCH 075/414] #7207 the utility that computes case analysis is brittle when the body of a function contains ite expressions that are not relevant to recursive unfolding. The fold-rec occurrences that get inserted to harness large case splits work against throttling case generation: they get treated as recursive functions that have to be guarded. --- src/ast/recfun_decl_plugin.cpp | 23 ++++++++++++++++------- src/ast/recfun_decl_plugin.h | 2 ++ src/sat/smt/recfun_solver.cpp | 3 ++- src/smt/theory_recfun.cpp | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 495f3cb2086..376bb436c46 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -90,17 +90,21 @@ namespace recfun { return r; } - bool def::contains_def(util& u, expr * e) { + bool util::contains_def(expr * e) { struct def_find_p : public i_expr_pred { util& u; def_find_p(util& u): u(u) {} bool operator()(expr* a) override { return is_app(a) && u.is_defined(to_app(a)->get_decl()); } }; - def_find_p p(u); - check_pred cp(p, m, false); + def_find_p p(*this); + check_pred cp(p, m(), false); return cp(e); } + bool def::contains_def(util& u, expr * e) { + return u.contains_def(e); + } + // does `e` contain any `ite` construct? // subject to the then/else branch using a recursive call, // but the guard does not. @@ -293,7 +297,11 @@ namespace recfun { if (m.is_ite(e, cond, th, el) && contains_def(u, cond)) { // skip } + if (m.is_ite(e, cond, th, el) && !contains_def(u, th) && !contains_def(u, el)) { + // skip + } else if (m.is_ite(e)) { + verbose_stream() << "unfold " << mk_pp(e, m) << "\n"; // need to do a case split on `e`, forking the search space b.to_split = st.cons_ite(to_app(e), b.to_split); } @@ -558,15 +566,16 @@ namespace recfun { expr_ref plugin::redirect_ite(replace& subst, unsigned n, var * const* vars, expr * e) { expr_ref result(e, m()); + util u(m()); while (true) { obj_map scores; compute_scores(result, scores); unsigned max_score = 0; expr* max_expr = nullptr; - for (auto const& kv : scores) { - if (m().is_ite(kv.m_key) && kv.m_value > max_score) { - max_expr = kv.m_key; - max_score = kv.m_value; + for (auto const& [k, v] : scores) { + if (m().is_ite(k) && v > max_score && u.contains_def(k)) { + max_expr = k; + max_score = v; } } if (max_score <= 4) diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 3a1dc8e84c1..e078c7ef98b 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -251,6 +251,8 @@ namespace recfun { bool is_macro(func_decl* f) { return is_defined(f) && get_def(f).is_macro(); } bool is_num_rounds(expr * e) const { return is_app_of(e, m_fid, OP_NUM_ROUNDS); } bool owns_app(app * e) const { return e->get_family_id() == m_fid; } + bool contains_def(expr* e); // expression contains a def + //has_defs(); } diff --git a/src/sat/smt/recfun_solver.cpp b/src/sat/smt/recfun_solver.cpp index aef9570340d..63a790eea64 100644 --- a/src/sat/smt/recfun_solver.cpp +++ b/src/sat/smt/recfun_solver.cpp @@ -271,7 +271,8 @@ namespace recfun { SASSERT(!n || !n->is_attached_to(get_id())); if (!n) n = mk_enode(e, false); - SASSERT(!n->is_attached_to(get_id())); + if (n->is_attached_to(get_id())) + return true; euf::theory_var w = mk_var(n); ctx.attach_th_var(n, this, w); if (u().is_defined(e) && u().has_defs()) diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 6a8f2ab60e2..5ba5e135def 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -342,7 +342,7 @@ namespace smt { activate_guard(pred_applied, guards); } - TRACEFN("assert core " << preds); + TRACEFN("assert cases " << preds); // the disjunction of branches is asserted // to close the available cases. From ef86f5fcc28a3a9b41977364797d84173a87a64c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Jun 2024 16:07:16 -0700 Subject: [PATCH 076/414] add partial evaluation cases for algebraic data-types for recursive functions. --- src/ast/recfun_decl_plugin.cpp | 1 - src/ast/rewriter/recfun_rewriter.cpp | 42 ++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 376bb436c46..37e5d359ea4 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -301,7 +301,6 @@ namespace recfun { // skip } else if (m.is_ite(e)) { - verbose_stream() << "unfold " << mk_pp(e, m) << "\n"; // need to do a case split on `e`, forking the search space b.to_split = st.cons_ite(to_app(e), b.to_split); } diff --git a/src/ast/rewriter/recfun_rewriter.cpp b/src/ast/rewriter/recfun_rewriter.cpp index b332f22564f..5c8f5c89375 100644 --- a/src/ast/rewriter/recfun_rewriter.cpp +++ b/src/ast/rewriter/recfun_rewriter.cpp @@ -19,20 +19,50 @@ Module Name: #include "ast/rewriter/recfun_rewriter.h" #include "ast/rewriter/var_subst.h" +#include "ast/datatype_decl_plugin.h" +#include "ast/for_each_expr.h" br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m_rec.is_defined(f) && num_args > 0) { - for (unsigned i = 0; i < num_args; ++i) - if (!m.is_value(args[i])) - return BR_FAILED; if (!m_rec.has_def(f)) return BR_FAILED; recfun::def const& d = m_rec.get_def(f); if (!d.get_rhs()) return BR_FAILED; - var_subst sub(m); - result = sub(d.get_rhs(), num_args, args); - return BR_REWRITE_FULL; + auto r = d.get_rhs(); + bool safe_to_subst = true; + for (unsigned i = 0; i < num_args; ++i) + if (!m.is_value(args[i])) + safe_to_subst = false; + + // check if there is an argument that is a constructor + // such that the recursive function can be partially evaluated. + if (!safe_to_subst && !has_quantifiers(r)) { + datatype::util u(m); + auto is_decreasing = [&](unsigned i) { + bool is_dec = true; + unsigned idx = num_args - i - 1; + for (auto t : subterms::all(expr_ref(r, m))) + if (is_app(t) && any_of(*to_app(t), [&](expr* e) { return is_var(e) && to_var(e)->get_idx() == idx; })) + if (!u.is_accessor(t) && !u.is_is(t) && !u.is_recognizer(t)) + is_dec = false; + return is_dec; + }; + for (unsigned i = 0; i < num_args; ++i) { + auto arg = args[i]; + if (u.is_constructor(arg) && is_decreasing(i)) { + safe_to_subst = true; + break; + } + } + } + if (safe_to_subst) { + var_subst sub(m); + result = sub(d.get_rhs(), num_args, args); + return BR_REWRITE_FULL; + } + return BR_FAILED; + } else return BR_FAILED; From c0b4d02ae2db64d64472c644a4e33e622867d3ad Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Jun 2024 08:06:15 -0700 Subject: [PATCH 077/414] update containers for Python, first steps to address #7232 Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index c448b5dd781..0fc58a3b61c 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -175,7 +175,7 @@ stages: python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" - container: "quay.io/pypa/manylinux2014_x86_64:latest" + container: "quay.io/pypa/manylinux_2_28_x86_64:latest" steps: - task: PythonScript@0 displayName: Build @@ -210,7 +210,7 @@ stages: python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" - container: "quay.io/pypa/manylinux2014_x86_64:latest" + container: "quay.io/pypa/manylinux2014_aarch64:latest" steps: - script: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz?rev=33c6e30e5ac64e6dba8f0431f2c35f1b&hash=9918A05BF47621B632C7A5C8D2BB438FB80A4480' - script: mkdir -p /tmp/arm-toolchain/ From e4b3df218952c2a0ccef13e11abb8e5c5ad3a61b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 17 Jun 2024 12:58:16 -0700 Subject: [PATCH 078/414] remove unused column_info.h Signed-off-by: Lev Nachmanson --- src/math/lp/column.h | 1 - src/math/lp/column_info.h | 255 -------------------------------------- src/math/lp/nla_defs.h | 1 - 3 files changed, 257 deletions(-) delete mode 100644 src/math/lp/column_info.h diff --git a/src/math/lp/column.h b/src/math/lp/column.h index 1b2d1f2e5f8..4380ec84444 100644 --- a/src/math/lp/column.h +++ b/src/math/lp/column.h @@ -17,7 +17,6 @@ Copyright (c) 2017 Microsoft Corporation #include #include #include -#include "math/lp/column_info.h" #include "math/lp/lp_types.h" namespace lp { diff --git a/src/math/lp/column_info.h b/src/math/lp/column_info.h deleted file mode 100644 index 9cbeea66c81..00000000000 --- a/src/math/lp/column_info.h +++ /dev/null @@ -1,255 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include -#include -#include "math/lp/lp_settings.h" -namespace lp { -inline bool is_valid(unsigned j) { return static_cast(j) >= 0;} - -template -class column_info { - std::string m_name; - bool m_lower_bound_is_set; - bool m_lower_bound_is_strict; - bool m_upper_bound_is_set; - bool m_upper_bound_is_strict; - T m_lower_bound; - T m_upper_bound; - T m_fixed_value; - bool m_is_fixed; - T m_cost; - unsigned m_column_index; -public: - bool operator==(const column_info & c) const { - return m_name == c.m_name && - m_lower_bound_is_set == c.m_lower_bound_is_set && - m_lower_bound_is_strict == c.m_lower_bound_is_strict && - m_upper_bound_is_set == c.m_upper_bound_is_set&& - m_upper_bound_is_strict == c.m_upper_bound_is_strict&& - (!m_lower_bound_is_set || m_lower_bound == c.m_lower_bound) && - (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && - m_cost == c.m_cost && - m_is_fixed == c.m_is_fixed && - (!m_is_fixed || m_fixed_value == c.m_fixed_value) && - m_column_index == c.m_column_index; - } - bool operator!=(const column_info & c) const { return !((*this) == c); } - void set_column_index(unsigned j) { - m_column_index = j; - } - // the default constructor - column_info(): - m_lower_bound_is_set(false), - m_lower_bound_is_strict(false), - m_upper_bound_is_set (false), - m_upper_bound_is_strict (false), - m_is_fixed(false), - m_cost(numeric_traits::zero()), - m_column_index(static_cast(-1)) - {} - - column_info(const column_info & ci) { - m_name = ci.m_name; - m_lower_bound_is_set = ci.m_lower_bound_is_set; - m_lower_bound_is_strict = ci.m_lower_bound_is_strict; - m_upper_bound_is_set = ci.m_upper_bound_is_set; - m_upper_bound_is_strict = ci.m_upper_bound_is_strict; - m_lower_bound = ci.m_lower_bound; - m_upper_bound = ci.m_upper_bound; - m_cost = ci.m_cost; - m_fixed_value = ci.m_fixed_value; - m_is_fixed = ci.m_is_fixed; - m_column_index = ci.m_column_index; - } - - unsigned get_column_index() const { - return m_column_index; - } - - column_type get_column_type() const { - return m_is_fixed? column_type::fixed : (m_lower_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::lower_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); - } - - column_type get_column_type_no_flipping() const { - if (m_is_fixed) { - return column_type::fixed; - } - - if (m_lower_bound_is_set) { - return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound; - } - // we are flipping the bounds! - return m_upper_bound_is_set? column_type::upper_bound - : column_type::free_column; - } - - T get_lower_bound() const { - lp_assert(m_lower_bound_is_set); - return m_lower_bound; - } - T get_upper_bound() const { - lp_assert(m_upper_bound_is_set); - return m_upper_bound; - } - - bool lower_bound_is_set() const { - return m_lower_bound_is_set; - } - - bool upper_bound_is_set() const { - return m_upper_bound_is_set; - } - - T get_shift() { - if (is_fixed()) { - return m_fixed_value; - } - if (is_flipped()){ - return m_upper_bound; - } - return m_lower_bound_is_set? m_lower_bound : numeric_traits::zero(); - } - - bool is_flipped() { - return m_upper_bound_is_set && !m_lower_bound_is_set; - } - - bool adjusted_lower_bound_is_set() { - return !is_flipped()? lower_bound_is_set(): upper_bound_is_set(); - } - - bool adjusted_upper_bound_is_set() { - return !is_flipped()? upper_bound_is_set(): lower_bound_is_set(); - } - - T get_adjusted_upper_bound() { - return get_upper_bound() - get_lower_bound(); - } - - bool is_fixed() const { - return m_is_fixed; - } - - bool is_free() { - return !m_lower_bound_is_set && !m_upper_bound_is_set; - } - - void set_fixed_value(T v) { - m_is_fixed = true; - m_fixed_value = v; - } - - T get_fixed_value() const { - lp_assert(m_is_fixed); - return m_fixed_value; - } - - T get_cost() const { - return m_cost; - } - - void set_cost(T const & cost) { - m_cost = cost; - } - - void set_name(std::string const & s) { - m_name = s; - } - - std::string get_name() const { - return m_name; - } - - void set_lower_bound(T const & l) { - m_lower_bound = l; - m_lower_bound_is_set = true; - } - - void set_upper_bound(T const & l) { - m_upper_bound = l; - m_upper_bound_is_set = true; - } - - void unset_lower_bound() { - m_lower_bound_is_set = false; - } - - void unset_upper_bound() { - m_upper_bound_is_set = false; - } - - void unset_fixed() { - m_is_fixed = false; - } - - bool lower_bound_holds(T v) { - return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001); - } - - bool upper_bound_holds(T v) { - return !upper_bound_is_set() || v <= m_upper_bound + T(0.000001); - } - - bool bounds_hold(T v) { - return lower_bound_holds(v) && upper_bound_holds(v); - } - - bool adjusted_bounds_hold(T v) { - return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v); - } - - bool adjusted_lower_bound_holds(T v) { - return !adjusted_lower_bound_is_set() || v >= -T(0.0000001); - } - - bool adjusted_upper_bound_holds(T v) { - return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001); - } - bool is_infeasible() { - if ((!upper_bound_is_set()) || (!lower_bound_is_set())) - return false; - // ok, both bounds are set - bool at_least_one_is_strict = upper_bound_is_strict() || lower_bound_is_strict(); - if (!at_least_one_is_strict) - return get_upper_bound() < get_lower_bound(); - // at least on bound is strict - return get_upper_bound() <= get_lower_bound(); // the equality is impossible - } - bool lower_bound_is_strict() const { - return m_lower_bound_is_strict; - } - - void set_lower_bound_strict(bool val) { - m_lower_bound_is_strict = val; - } - - bool upper_bound_is_strict() const { - return m_upper_bound_is_strict; - } - - void set_upper_bound_strict(bool val) { - m_upper_bound_is_strict = val; - } -}; -} diff --git a/src/math/lp/nla_defs.h b/src/math/lp/nla_defs.h index b576036143d..7eac3be1683 100644 --- a/src/math/lp/nla_defs.h +++ b/src/math/lp/nla_defs.h @@ -10,7 +10,6 @@ --*/ #pragma once #include "math/lp/lp_types.h" -#include "math/lp/column_info.h" #include "math/lp/explanation.h" namespace nla { From bf3615d4fbc017f0da036ac2fe3a3dce09922cd9 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 17 Jun 2024 13:14:31 -0700 Subject: [PATCH 079/414] fix lemma logging in nlsat Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_solver.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index ca057afefb3..26c2f617be6 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -3698,10 +3698,12 @@ namespace nlsat { std::ostream& display_smt2_arith_decls(std::ostream & out) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { - if (is_int(i)) - out << "(declare-fun x" << i << " () Int)\n"; - else - out << "(declare-fun x" << i << " () Real)\n"; + if (is_int(i)) { + out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; + } + else { + out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; + } } return out; } @@ -3720,7 +3722,7 @@ namespace nlsat { display_smt2_arith_decls(out); out << "(assert (and true\n"; for (clause* c : m_clauses) { - display_smt2(out, *c) << "\n"; + display_smt2(out, *c, m_display_var) << "\n"; } out << "))\n" << std::endl; return out; From 758d886fac7ded0ecd4ef3849bd1e55778236257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:29:38 -0700 Subject: [PATCH 080/414] Bump braces from 3.0.2 to 3.0.3 in /src/api/js (#7261) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/api/js/package-lock.json | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index 5f1d63bd0dd..0db00bfa0f6 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -1483,12 +1483,23 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" + }, + "dependencies": { + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + } } }, "browserslist": { @@ -2057,15 +2068,6 @@ "bser": "2.1.1" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", From 223b65bd5a38068d6edb60a520d8bcf5d068a7a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:29:53 -0700 Subject: [PATCH 081/414] Bump docker/build-push-action from 5.3.0 to 6.0.0 (#7257) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 6.0.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.3.0...v6.0.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index d93e7eeefea..fe4d7c6e6de 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v5.3.0 + uses: docker/build-push-action@v6.0.0 with: context: . push: true From 3bf2b3f741ab4af7c893e5a97838ff33874158e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Jun 2024 15:37:15 -0700 Subject: [PATCH 082/414] fix #7260 Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ac100ee3079..f18edd96b2a 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -508,6 +508,7 @@ extern "C" { LOG_Z3_solver_reset(c, s); RESET_ERROR_CODE(); to_solver(s)->m_solver = nullptr; + to_solver(s)->m_cmd_context = nullptr; if (to_solver(s)->m_pp) to_solver(s)->m_pp->reset(); Z3_CATCH; } From 2654d80aaf9402174c1bb291761cfaf47f509046 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Jun 2024 16:19:48 -0700 Subject: [PATCH 083/414] revert nightly to 2_28 manylinux Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 0fc58a3b61c..49d9c8e89fa 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -210,7 +210,7 @@ stages: python: "/opt/python/cp37-cp37m/bin/python" pool: vmImage: "ubuntu-latest" - container: "quay.io/pypa/manylinux2014_aarch64:latest" + container: "quay.io/pypa/manylinux_2_28_x86_64:latest" steps: - script: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz?rev=33c6e30e5ac64e6dba8f0431f2c35f1b&hash=9918A05BF47621B632C7A5C8D2BB438FB80A4480' - script: mkdir -p /tmp/arm-toolchain/ From 4ca59c51cb458b2faa0b2384cdecc15436e2ce44 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Jun 2024 17:25:56 -0700 Subject: [PATCH 084/414] update to macOS-latest Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 49d9c8e89fa..708773f382d 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -12,7 +12,7 @@ stages: - job: MacBuild displayName: "macOS Build" pool: - vmImage: "macOS-11" + vmImage: "macOS-latest" steps: - task: PythonScript@0 displayName: Build @@ -41,7 +41,7 @@ stages: - job: MacBuildArm64 displayName: "Mac ARM64 Build" pool: - vmImage: "macOS-11" + vmImage: "macOS-latest" steps: - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 --os=osx-11.0 - script: git clone https://github.com/z3prover/z3test z3test From b5c3a6a2e0df21cc13198d443ea3b88f3918e69d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Jun 2024 18:28:07 -0700 Subject: [PATCH 085/414] skip signing Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 708773f382d..62976cce2af 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -318,6 +318,7 @@ stages: patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - task: EsrpCodeSigning@2 + condition: eq(1,0) continueOnError: true displayName: 'Sign Package' inputs: @@ -346,7 +347,8 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@2 - continueOnError: true + continueOnError: true + condition: eq(1,0) displayName: 'Sign Symbol Package' inputs: ConnectedServiceName: 'z3-esrp-signing-2' From abea0b724c3d8f89e29d28c15aba70f3e759545e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Jun 2024 09:43:26 -0700 Subject: [PATCH 086/414] disable publish and signing Signed-off-by: Nikolaj Bjorner --- scripts/build-win-signed.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build-win-signed.yml b/scripts/build-win-signed.yml index f1e08958dcd..302015408e6 100644 --- a/scripts/build-win-signed.yml +++ b/scripts/build-win-signed.yml @@ -31,6 +31,7 @@ jobs: # Publish symbol archive to match nuget package # Index your source code and publish symbols to a file share or Azure Artifacts symbol server - task: PublishSymbols@2 + condition: eq(1,0) inputs: symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' searchPattern: '**/*.pdb' @@ -40,6 +41,7 @@ jobs: detailedLog: true - task: EsrpCodeSigning@2 displayName: Sign + condition: eq(1,0) inputs: ConnectedServiceName: 'z3-esrp-signing-2' FolderPath: 'dist/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win/bin' From c137ef7fbaf847025ef0b4e47bb8e853c224fb34 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Jun 2024 11:10:55 -0700 Subject: [PATCH 087/414] disable publish and signing Signed-off-by: Nikolaj Bjorner --- scripts/build-win-signed-cmake.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build-win-signed-cmake.yml b/scripts/build-win-signed-cmake.yml index 620379f0c4e..4872259db94 100644 --- a/scripts/build-win-signed-cmake.yml +++ b/scripts/build-win-signed-cmake.yml @@ -33,6 +33,7 @@ jobs: # Publish symbol archive to match nuget package # Index your source code and publish symbols to a file share or Azure Artifacts symbol server - task: PublishSymbols@2 + condition: eq(1,0) inputs: symbolsFolder: '$(Build.ArtifactStagingDirectory)/symbols' searchPattern: '**/*.pdb' @@ -42,6 +43,7 @@ jobs: detailedLog: true - task: EsrpCodeSigning@2 displayName: Sign + condition: eq(1,0) inputs: ConnectedServiceName: 'z3-esrp-signing-2' FolderPath: 'build-dist/${{parameters.BuildArchitecture}}/dist/z3-${{parameters.ReleaseVersion}}-${{parameters.BuildArchitecture}}-win/bin' From 8e482df62a59520499e575e21b9e01bd889ddddb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Jun 2024 08:37:23 -0700 Subject: [PATCH 088/414] fix #7264 --- src/model/model_evaluator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 6f218934b38..2446609c658 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -481,6 +481,10 @@ struct evaluator_cfg : public default_rewriter_cfg { return BR_DONE; } } + else if (m_dt.is_constructor(f)) { + result = m.mk_app(f, num, args); + return BR_DONE; + } if (fi) { if (fi->is_partial()) From b2b3bab246840f077b21f4b0c5a6e667acb8bdc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:15:55 -0700 Subject: [PATCH 089/414] Bump docker/build-push-action from 6.0.0 to 6.1.0 (#7265) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.0.0 to 6.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.0.0...v6.1.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index fe4d7c6e6de..d778bed3bcf 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.0.0 + uses: docker/build-push-action@v6.1.0 with: context: . push: true From 8de2544abb559b57c0406c9198a9b046a97c35ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 30 Jun 2024 16:06:44 -0700 Subject: [PATCH 090/414] set clean shutdown for local search and re-enable local search when it parallelizes with PB solver Signed-off-by: Nikolaj Bjorner --- src/sat/sat_local_search.cpp | 7 ++++--- src/sat/sat_parallel.cpp | 6 ++++++ src/sat/sat_parallel.h | 2 ++ src/sat/sat_solver.cpp | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index c3cb0fb37b1..6e5d3fae392 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -362,7 +362,7 @@ namespace sat { set_phase(i, phase[i]); } - void local_search::import(solver const& s, bool _init) { + void local_search::import(solver const& s, bool _init) { flet linit(m_initializing, true); m_is_pb = false; m_vars.reset(); @@ -412,9 +412,10 @@ namespace sat { [&](unsigned sz, literal const* c, unsigned k) { add_cardinality(sz, c, k); }; std::function pb = [&](unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) { add_pb(sz, c, coeffs, k); }; - if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) + if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) { + IF_VERBOSE(0, verbose_stream() << (ext) << " is-pb " << (!ext && ext->is_pb()) << "\n"); throw default_exception("local search is incomplete with extensions beyond PB"); - + } if (_init) init(); } diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index f7b54b99812..7d5c022ff19 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -87,9 +87,15 @@ namespace sat { parallel::parallel(solver& s): m_num_clauses(0), m_consumer_ready(false), m_scoped_rlimit(s.rlimit()) {} parallel::~parallel() { + reset(); + } + + void parallel::reset() { m_limits.reset(); + m_scoped_rlimit.reset(); for (auto* s : m_solvers) dealloc(s); + m_solvers.reset(); } void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h index 65ae091835e..0fbf4f05d04 100644 --- a/src/sat/sat_parallel.h +++ b/src/sat/sat_parallel.h @@ -78,6 +78,8 @@ namespace sat { ~parallel(); + void reset(); + void init_solvers(solver& s, unsigned num_extra_solvers); void push_child(reslimit& rl); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 1cd0c1400c6..2589c51b852 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1243,8 +1243,11 @@ namespace sat { m_cleaner(true); return do_local_search(num_lits, lits); } - if ((m_config.m_num_threads > 1 || m_config.m_local_search_threads > 0 || - m_config.m_ddfw_threads > 0) && !m_par && !m_ext) { + if ((m_config.m_num_threads > 1 || m_config.m_ddfw_threads > 0) && !m_par && !m_ext) { + SASSERT(scope_lvl() == 0); + return check_par(num_lits, lits); + } + if (m_config.m_local_search_threads > 0 && !m_par && (!m_ext || m_ext->is_pb())) { SASSERT(scope_lvl() == 0); return check_par(num_lits, lits); } @@ -1460,15 +1463,17 @@ namespace sat { if (!rlimit().inc()) { return l_undef; } - if (m_ext) + if (m_ext && !m_ext->is_pb()) return l_undef; - scoped_ptr_vector ls; - scoped_ptr_vector uw; + int num_extra_solvers = m_config.m_num_threads - 1; int num_local_search = static_cast(m_config.m_local_search_threads); int num_ddfw = m_ext ? 0 : static_cast(m_config.m_ddfw_threads); int num_threads = num_extra_solvers + 1 + num_local_search + num_ddfw; + vector lims(num_ddfw); + scoped_ptr_vector ls; + scoped_ptr_vector uw; for (int i = 0; i < num_local_search; ++i) { local_search* l = alloc(local_search); l->updt_params(m_params); @@ -1477,7 +1482,7 @@ namespace sat { ls.push_back(l); } - vector lims(num_ddfw); + // set up ddfw search for (int i = 0; i < num_ddfw; ++i) { ddfw* d = alloc(ddfw); @@ -1593,6 +1598,7 @@ namespace sat { if (!canceled) { rlimit().reset_cancel(); } + par.reset(); set_par(nullptr, 0); ls.reset(); uw.reset(); From facc7d8de2725ba7e231442ed0866d2c0bf4d4cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:43:36 -0700 Subject: [PATCH 091/414] Bump docker/build-push-action from 6.1.0 to 6.2.0 (#7269) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.1.0...v6.2.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index d778bed3bcf..4f34fb3d735 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.1.0 + uses: docker/build-push-action@v6.2.0 with: context: . push: true From 1da132005a473a8a2e4f10cd9f90daf9563c496a Mon Sep 17 00:00:00 2001 From: Sergey Bronnikov Date: Tue, 2 Jul 2024 17:09:33 +0300 Subject: [PATCH 092/414] Fix a comment for Z3_solver_from_string (#7271) Z3_solver_from_string accepts a string buffer with solver assertions, not a string buffer with filename. --- 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 dacf4b578f5..1f0daf8b505 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7018,7 +7018,7 @@ extern "C" { def_API('Z3_solver_from_string', VOID, (_in(CONTEXT), _in(SOLVER), _in(STRING))) */ - void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string file_name); + void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string str); /** \brief Return the set of asserted formulas on the solver. From 18b6087b72d934bef29f06f3649411debeeb1ed1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 8 Jul 2024 11:43:24 -0700 Subject: [PATCH 093/414] trigger the build with a comment change Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 26c2f617be6..bb169a05e48 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Nonlinear arithmetic satisfiability procedure. The procedure is + Nonlinear arithmetic satisfiability procedure. The procedure is complete for nonlinear real arithmetic, but it also has limited support for integers. From 6e069c1f4126d7a5721cfedf106a0ff298f33ee3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 Jul 2024 13:56:23 -0700 Subject: [PATCH 094/414] remove macro distinction #7270 --- src/cmd_context/cmd_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index e883d0e9d5b..231c78bc26e 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1070,9 +1070,11 @@ void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, s } func_decl * cmd_context::find_func_decl(symbol const & s) const { +#if 0 if (contains_macro(s)) { throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); } +#endif func_decls fs; if (m_func_decls.find(s, fs)) { if (fs.more_than_one()) From af1f0e318422f7030f24e20acd057f84b7b53f16 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 Jul 2024 14:50:38 -0700 Subject: [PATCH 095/414] fix #7268 --- src/model/model.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model/model.cpp b/src/model/model.cpp index c89ff59a2f9..362907e466b 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -248,6 +248,7 @@ void model::compress(bool force_inline) { // by substituting in auxiliary definitions that can be eliminated. func_decl_ref_vector pinned(m); + ptr_vector sorted_decls; while (true) { top_sort ts(m); collect_deps(ts); @@ -259,6 +260,7 @@ void model::compress(bool force_inline) { ts.m_occur_count.reset(); for (func_decl * f : ts.top_sorted()) collect_occs(ts, f); + sorted_decls.reset(); // remove auxiliary declarations that are not used. for (func_decl * f : ts.top_sorted()) { @@ -267,11 +269,13 @@ void model::compress(bool force_inline) { unregister_decl(f); removed.insert(f); } + else + sorted_decls.push_back(f); } + std::swap(m_decls, sorted_decls); if (removed.empty()) break; TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";); - remove_decls(m_decls, removed); remove_decls(m_func_decls, removed); remove_decls(m_const_decls, removed); } From 374609bd46d481801ae7905c87592a7aff273e0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 Jul 2024 16:54:09 -0700 Subject: [PATCH 096/414] kludge to address #7232, probably superseeded by planned revision to setup/pypi Signed-off-by: Nikolaj Bjorner --- src/api/python/setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 5faf5aad17a..c7af0e646e1 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -277,7 +277,7 @@ def run(self): # linux builds should be built in the centos 5 vm for maximum compatibility # see https://github.com/pypa/manylinux # see also https://github.com/angr/angr-dev/blob/master/admin/bdist.py - plat_name = 'manylinux2014_' + platform.machine() + plat_name = 'manylinux_2_28_' + platform.machine() elif 'mingw' in name: if platform.architecture()[0] == '64bit': plat_name = 'win_amd64' @@ -296,7 +296,7 @@ def run(self): ) elif distos == 'glibc': if arch == 'x64': - plat_name = 'manylinux2014_x86_64' + plat_name = 'manylinux_2_28_x86_64' elif arch == 'arm64' or arch == 'aarch64': # context on why are we match on arm64 # but use aarch64 on the plat_name is @@ -305,9 +305,9 @@ def run(self): # so using the currently supported arm64 # build and simply rename it to aarch64 # see full context on #7148 - plat_name = 'manylinux2014_aarch64' + plat_name = 'manylinux_2_28_aarch64' else: - plat_name = 'manylinux2014_i686' + plat_name = 'manylinux_2_28_i686' elif distos == 'linux' and os_id == 'alpine': if arch == 'x64': plat_name = 'musllinux_1_1_x86_64' From b0069010f86dcc071fa6863a433a04d38126cfb5 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:07:12 -0700 Subject: [PATCH 097/414] add new ema invariant (#7288) --- src/util/ema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/ema.h b/src/util/ema.h index 9e18fb98f9a..99491bc72f1 100644 --- a/src/util/ema.h +++ b/src/util/ema.h @@ -23,7 +23,7 @@ Revision History: class ema { double m_alpha, m_beta, m_value; unsigned m_period, m_wait; - bool invariant() const { return 0 <= m_alpha && m_alpha <= m_beta && m_beta <= 1; } + bool invariant() const { return 0 <= m_alpha && m_alpha <= m_beta && m_beta <= 1 && m_wait <= m_period; } public: ema(): m_alpha(0), m_beta(1), m_value(0), m_period(0), m_wait(0) { SASSERT(invariant()); From a2c3ce5c8b7105f9379bf2aa52efc9dc4b14c670 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:07:37 -0700 Subject: [PATCH 098/414] Bump docker/build-push-action from 6.2.0 to 6.3.0 (#7280) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.2.0...v6.3.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 4f34fb3d735..9665dec7ab5 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.2.0 + uses: docker/build-push-action@v6.3.0 with: context: . push: true From 9073da4ee63abd4ea069a4444db6d1d66da57ed0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:54:27 -0700 Subject: [PATCH 099/414] Bump docker/build-push-action from 6.3.0 to 6.4.0 (#7289) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.3.0 to 6.4.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.3.0...v6.4.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9665dec7ab5..3c02e2c2b9f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.4.0 with: context: . push: true From cf4d0e74a53428192d5c4cb8d5a033431828deb2 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:06:58 -0700 Subject: [PATCH 100/414] New invariant for dlist (#7294) * unit tests for dlist.h * new invariant for dlist: should be circular. avoid infinite loop * remove dlist test commit * format * format * Update dlist.h --------- Co-authored-by: Nikolaj Bjorner --- src/util/dlist.h | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/util/dlist.h b/src/util/dlist.h index 4c0e51e5898..52cb25548d4 100644 --- a/src/util/dlist.h +++ b/src/util/dlist.h @@ -155,15 +155,23 @@ class dll_base { elem->init(elem); } - bool invariant() const { - auto* e = this; - do { - if (e->m_next->m_prev != e) - return false; - e = e->m_next; - } - while (e != this); - return true; + bool invariant() const { + auto* e = this; + const T* slow = static_cast(this); + const T* fast = m_next; + bool looped = false; + // m_next of each node should point back to m_prev of the following node, + // and m_prev of each node should point forward to m_next of the preceding node. + while (slow != fast) { + if (fast->m_prev->m_next != fast || fast->m_next->m_prev != fast) + return false; + fast = fast->m_next; + looped = looped || (fast == static_cast(this)); + if (!looped && fast == m_next) + // We should be able to traverse back to the starting node. + return false; + } + return true; } static bool contains(T const* list, T const* elem) { From 9803e9ef6e35e4087182c02f151c317360ab2e09 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:08:41 -0700 Subject: [PATCH 101/414] unit tests for dlist.h (#7293) --- src/test/CMakeLists.txt | 1 + src/test/dlist.cpp | 170 ++++++++++++++++++++++++++++++++++++++++ src/test/main.cpp | 1 + 3 files changed, 172 insertions(+) create mode 100644 src/test/dlist.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 4ddc1b8cbcc..a273d4e7761 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(test-z3 dl_table.cpp dl_util.cpp doc.cpp + dlist.cpp egraph.cpp escaped.cpp euf_bv_plugin.cpp diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp new file mode 100644 index 00000000000..81689699f02 --- /dev/null +++ b/src/test/dlist.cpp @@ -0,0 +1,170 @@ +#include +#include +#include "util/dlist.h" + +class TestNode : public dll_base { +public: + int value; + TestNode(int val) : value(val) { + init(this); + } +}; + +// Test the prev() method +void test_prev() { + TestNode node(1); + assert(node.prev() == &node); + std::cout << "test_prev passed." << std::endl; +} + +// Test the next() method +void test_next() { + TestNode node(1); + assert(node.next() == &node); + std::cout << "test_next passed." << std::endl; +} + +// Test the const prev() method +void test_const_prev() { + const TestNode node(1); + assert(node.prev() == &node); + std::cout << "test_const_prev passed." << std::endl; +} + +// Test the const next() method +void test_const_next() { + const TestNode node(1); + assert(node.next() == &node); + std::cout << "test_const_next passed." << std::endl; +} + +// Test the init() method +void test_init() { + TestNode node(1); + node.init(&node); + assert(node.next() == &node); + assert(node.prev() == &node); + assert(node.invariant()); + std::cout << "test_init passed." << std::endl; +} + +// Test the pop() method +void test_pop() { + TestNode* list = nullptr; + TestNode node1(1); + TestNode::push_to_front(list, &node1); + TestNode* popped = TestNode::pop(list); + assert(popped == &node1); + assert(list == nullptr); + assert(popped->next() == popped); + assert(popped->prev() == popped); + std::cout << "test_pop passed." << std::endl; +} + +// Test the insert_after() method +void test_insert_after() { + TestNode node1(1); + TestNode node2(2); + node1.insert_after(&node2); + assert(node1.next() == &node2); + assert(node2.prev() == &node1); + assert(node1.prev() == &node2); + assert(node2.next() == &node1); + assert(node1.invariant()); + assert(node2.invariant()); + std::cout << "test_insert_after passed." << std::endl; +} + +// Test the insert_before() method +void test_insert_before() { + TestNode node1(1); + TestNode node2(2); + node1.insert_before(&node2); + assert(node1.prev() == &node2); + assert(node2.next() == &node1); + assert(node1.next() == &node2); + assert(node2.prev() == &node1); + assert(node1.invariant()); + assert(node2.invariant()); + std::cout << "test_insert_before passed." << std::endl; +} + +// Test the remove_from() method +void test_remove_from() { + TestNode* list = nullptr; + TestNode node1(1); + TestNode node2(2); + TestNode::push_to_front(list, &node1); + TestNode::push_to_front(list, &node2); + TestNode::remove_from(list, &node1); + assert(list == &node2); + assert(node2.next() == &node2); + assert(node2.prev() == &node2); + assert(node1.next() == &node1); + assert(node1.prev() == &node1); + std::cout << "test_remove_from passed." << std::endl; +} + +// Test the push_to_front() method +void test_push_to_front() { + TestNode* list = nullptr; + TestNode node1(1); + TestNode::push_to_front(list, &node1); + assert(list == &node1); + assert(node1.next() == &node1); + assert(node1.prev() == &node1); + std::cout << "test_push_to_front passed." << std::endl; +} + +// Test the detach() method +void test_detach() { + TestNode node(1); + TestNode::detach(&node); + assert(node.next() == &node); + assert(node.prev() == &node); + assert(node.invariant()); + std::cout << "test_detach passed." << std::endl; +} + +// Test the invariant() method +void test_invariant() { + TestNode node1(1); + assert(node1.invariant()); + TestNode node2(2); + node1.insert_after(&node2); + assert(node1.invariant()); + assert(node2.invariant()); + std::cout << "test_invariant passed." << std::endl; +} + +// Test the contains() method +void test_contains() { + TestNode* list = nullptr; + TestNode node1(1); + TestNode node2(2); + TestNode::push_to_front(list, &node1); + TestNode::push_to_front(list, &node2); + assert(TestNode::contains(list, &node1)); + assert(TestNode::contains(list, &node2)); + TestNode node3(3); + assert(!TestNode::contains(list, &node3)); + std::cout << "test_contains passed." << std::endl; +} + +int tst_dlist() { + test_prev(); + test_next(); + test_const_prev(); + test_const_next(); + test_init(); + test_pop(); + test_insert_after(); + test_insert_before(); + test_remove_from(); + test_push_to_front(); + test_detach(); + test_invariant(); + test_contains(); + std::cout << "All tests passed." << std::endl; + return 0; +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 0c3d0e01af7..0d36795295c 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -188,6 +188,7 @@ int main(int argc, char ** argv) { TST(total_order); TST(dl_table); TST(dl_context); + TST(dlist); TST(dl_util); TST(dl_product_relation); TST(dl_relation); From 458d8b033abc60682dd1efbed06b5ad119821ac3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 18 Jul 2024 10:07:35 -0700 Subject: [PATCH 102/414] remove wsp Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_solver.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 22e68d2dabe..1b13d513796 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -237,8 +237,6 @@ namespace euf { eq_proof_hint* mk_hint(symbol const& th, literal lit); - - void init_proof(); void on_clause(unsigned n, literal const* lits, sat::status st) override; void on_lemma(unsigned n, literal const* lits, sat::status st); From 08b6338061b73dd940ece4e0d61a949ab5420c0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 18 Jul 2024 10:19:57 -0700 Subject: [PATCH 103/414] fix signature Signed-off-by: Nikolaj Bjorner --- src/test/CMakeLists.txt | 2 +- src/test/dlist.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a273d4e7761..8464cedf917 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -36,7 +36,7 @@ add_executable(test-z3 dl_relation.cpp dl_table.cpp dl_util.cpp - doc.cpp + doc.cpp dlist.cpp egraph.cpp escaped.cpp diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index 81689699f02..dfc1138b7b9 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -151,7 +151,7 @@ void test_contains() { std::cout << "test_contains passed." << std::endl; } -int tst_dlist() { +void tst_dlist() { test_prev(); test_next(); test_const_prev(); @@ -166,5 +166,4 @@ int tst_dlist() { test_invariant(); test_contains(); std::cout << "All tests passed." << std::endl; - return 0; } From bc636d7ee00b7ead1de4e9537930fce9d85a958d Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:01:42 -0700 Subject: [PATCH 104/414] new hashtable.h invariants (#7296) * add copyright for dlist unit test * new hashtable invariants * add copyright --- src/test/dlist.cpp | 17 +++++++++++++++++ src/util/hashtable.h | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index dfc1138b7b9..200a0a3296b 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -1,3 +1,20 @@ +/*++ +Copyright (c) 2024 Microsoft Corporation + +Module Name: + + tst_dlist.cpp + +Abstract: + + Test dlist module + +Author: + + Chuyue Sun 2024-07-18. + +--*/ + #include #include #include "util/dlist.h" diff --git a/src/util/hashtable.h b/src/util/hashtable.h index b59a2e7d835..acbe2a81865 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -12,6 +12,7 @@ Module Name: Author: Leonardo de Moura (leonardo) 2006-09-11. + Chuyue Sun (liviasun) 2024-07-18. Revision History: @@ -639,6 +640,19 @@ class core_hashtable : private HashProc, private EqProc { #ifdef Z3DEBUG bool check_invariant() { + // The capacity must always be a power of two. + if (!is_power_of_two(m_capacity)) + return false; + + // The number of deleted plus the size must not exceed the capacity. + if (m_num_deleted + m_size > m_capacity) + return false; + + // Checking that m_num_deleted is less than or equal to m_size. + if (m_num_deleted > m_size) { + return false; + } + entry * curr = m_table; entry * end = m_table + m_capacity; unsigned num_deleted = 0; From 80ac7b34387cddb88f2286b0716dd44ede982b78 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:03:21 -0700 Subject: [PATCH 105/414] new heap invariants (#7298) * new heap invariants * change ENSURE to SASSERT for unit test heap * change SASSERT to VERIFY --- src/test/heap.cpp | 24 +++++++++++++----------- src/util/heap.h | 13 +++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/test/heap.cpp b/src/test/heap.cpp index 6a5bc7b9ffe..f30f45c32cc 100644 --- a/src/test/heap.cpp +++ b/src/test/heap.cpp @@ -38,7 +38,7 @@ static void tst1() { for (int i = 0; i < N * 3; i++) { int val = heap_rand() % N; if (!h.contains(val)) { - ENSURE(!t.contains(val)); + VERIFY(!t.contains(val)); h.insert(val); t.insert(val); } @@ -46,26 +46,26 @@ static void tst1() { if (!t.contains(val)) { for (int v : t) std::cout << v << "\n"; } - ENSURE(t.contains(val)); + VERIFY(t.contains(val)); } } - ENSURE(h.check_invariant()); + VERIFY(h.check_invariant()); for (int v : t) { - ENSURE(h.contains(v)); + VERIFY(h.contains(v)); } while (!h.empty()) { int m1 = h.min_value(); int m2 = h.erase_min(); (void)m1; (void)m2; - ENSURE(m1 == m2); - ENSURE(-1 < m2); + VERIFY(m1 == m2); + VERIFY(-1 < m2); } } int g_value[N]; -struct lt_proc2 { bool operator()(int v1, int v2) const { ENSURE(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; +struct lt_proc2 { bool operator()(int v1, int v2) const { VERIFY(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; typedef heap int_heap2; static void init_values() { @@ -98,7 +98,7 @@ static void tst2() { TRACE("heap", tout << "inserting: " << val << "\n";); h.insert(val); TRACE("heap", dump_heap(h, tout);); - ENSURE(h.contains(val)); + VERIFY(h.contains(val)); } } else if (cmd <= 6) { @@ -107,7 +107,7 @@ static void tst2() { TRACE("heap", tout << "removing: " << val << "\n";); h.erase(val); TRACE("heap", dump_heap(h, tout);); - ENSURE(!h.contains(val)); + VERIFY(!h.contains(val)); } } else if (cmd <= 8) { @@ -128,10 +128,12 @@ static void tst2() { } } else { - ENSURE(h.check_invariant()); + VERIFY(h.check_invariant()); } } - ENSURE(h.check_invariant()); + VERIFY(h.check_invariant()); + h.reset(); + VERIFY(h.check_invariant()); } void tst_heap() { diff --git a/src/util/heap.h b/src/util/heap.h index c080c6ebde9..3bee2fe9f67 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -55,6 +55,19 @@ class heap : private LT { } bool check_invariant_core(int idx) const { + + // Check that m_values starts with a dummy value at index 0. + if (m_values.empty() || m_values[0] != -1) { + return false; + } + + // All indices in m_value2indices that are not used in m_values should be 0 + for (int val = 0; val < static_cast(m_value2indices.size()); ++val) { + if (std::find(m_values.begin(), m_values.end(), val) == m_values.end() && m_value2indices[val] != 0) { + return false; // Unused indices should have a 0 value + } + } + if (idx < static_cast(m_values.size())) { SASSERT(m_value2indices[m_values[idx]] == idx); SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)])); From 5003d41cdbc392eacea5d73d1065259ec9a0dadf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 19 Jul 2024 19:11:54 -0700 Subject: [PATCH 106/414] Revert "New invariant for dlist (#7294)" (#7301) This reverts commit cf4d0e74a53428192d5c4cb8d5a033431828deb2. --- src/util/dlist.h | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/util/dlist.h b/src/util/dlist.h index 52cb25548d4..4c0e51e5898 100644 --- a/src/util/dlist.h +++ b/src/util/dlist.h @@ -155,23 +155,15 @@ class dll_base { elem->init(elem); } - bool invariant() const { - auto* e = this; - const T* slow = static_cast(this); - const T* fast = m_next; - bool looped = false; - // m_next of each node should point back to m_prev of the following node, - // and m_prev of each node should point forward to m_next of the preceding node. - while (slow != fast) { - if (fast->m_prev->m_next != fast || fast->m_next->m_prev != fast) - return false; - fast = fast->m_next; - looped = looped || (fast == static_cast(this)); - if (!looped && fast == m_next) - // We should be able to traverse back to the starting node. - return false; - } - return true; + bool invariant() const { + auto* e = this; + do { + if (e->m_next->m_prev != e) + return false; + e = e->m_next; + } + while (e != this); + return true; } static bool contains(T const* list, T const* elem) { From 49dc1bb721519601b36e3922f1f793e3ca71a7d5 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:27:23 -0700 Subject: [PATCH 107/414] add new permutation class invariant (#7299) * new heap invariants * change ENSURE to SASSERT for unit test heap * change SASSERT to VERIFY * new permutation invariant * remove heap changes * use bool_vector --- src/util/permutation.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/util/permutation.cpp b/src/util/permutation.cpp index 8b7adedf316..47b9d85a1f2 100644 --- a/src/util/permutation.cpp +++ b/src/util/permutation.cpp @@ -36,6 +36,7 @@ void permutation::swap(unsigned i, unsigned j) noexcept { unsigned j_prime = m_p[j]; std::swap(m_p[i], m_p[j]); std::swap(m_inv_p[i_prime], m_inv_p[j_prime]); + SASSERT(check_invariant()); } /** @@ -66,11 +67,19 @@ void permutation::display(std::ostream & out) const { bool permutation::check_invariant() const { SASSERT(m_p.size() == m_inv_p.size()); unsigned n = m_p.size(); + bool_vector check_vector(n, false); // To check for duplicate and out-of-range values for (unsigned i = 0; i < n; i++) { + unsigned pi = m_p[i]; SASSERT(m_p[i] < n); SASSERT(m_inv_p[i] < n); SASSERT(m_p[m_inv_p[i]] == i); SASSERT(m_inv_p[m_p[i]] == i); + // Check the inversion hasn't been checked yet + if (check_vector[pi]) { + return false; + } + // Mark this value as checked + check_vector[pi] = true; } return true; } From 3d014f8b335a3ed88fbdae0265d46bae9b196399 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 19 Jul 2024 20:34:29 -0700 Subject: [PATCH 108/414] add new hashtable unit tests (#7297) * add new hashtable unit tests * add copyright * use VERIFY instead of assert --- src/test/hashtable.cpp | 117 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/src/test/hashtable.cpp b/src/test/hashtable.cpp index fb8042dc72b..e26fbd5d860 100644 --- a/src/test/hashtable.cpp +++ b/src/test/hashtable.cpp @@ -12,6 +12,7 @@ Module Name: Author: Leonardo de Moura (leonardo) 2006-09-12. + Chuyue Sun (liviasun) 2024-07-18. Revision History: @@ -20,7 +21,6 @@ Revision History: #include #include #include - #include "util/hashtable.h" @@ -119,11 +119,126 @@ static void tst3() { ENSURE(h2.size() == 2); } +// Custom hash and equality functions for testing +struct my_hash { + unsigned operator()(int x) const { return x; } +}; + +struct my_eq { + bool operator()(int x, int y) const { return x == y; } +}; + +void test_hashtable_constructors() { + hashtable ht; + VERIFY(ht.empty()); + VERIFY(ht.size() == 0); + VERIFY(ht.capacity() == DEFAULT_HASHTABLE_INITIAL_CAPACITY); + + // Copy constructor + hashtable ht_copy(ht); + VERIFY(ht_copy.empty()); + VERIFY(ht_copy.size() == 0); + VERIFY(ht_copy.capacity() == ht.capacity()); + + // Move constructor + hashtable ht_move(std::move(ht)); + VERIFY(ht_move.empty()); + VERIFY(ht_move.size() == 0); + VERIFY(ht_move.capacity() == ht_copy.capacity()); +} + +void test_hashtable_insert() { + hashtable ht; + ht.insert(1); + VERIFY(!ht.empty()); + VERIFY(ht.size() == 1); + int value; + VERIFY(ht.find(1, value) && value == 1); +} + +void test_hashtable_remove() { + hashtable ht; + ht.insert(1); + ht.remove(1); + VERIFY(ht.empty()); + VERIFY(ht.size() == 0); +} + +void test_hashtable_find() { + hashtable ht; + ht.insert(1); + int value; + VERIFY(ht.find(1, value) && value == 1); + VERIFY(!ht.find(2, value)); +} + +void test_hashtable_contains() { + hashtable ht; + ht.insert(1); + VERIFY(ht.contains(1)); + VERIFY(!ht.contains(2)); +} + +void test_hashtable_reset() { + hashtable ht; + ht.insert(1); + ht.reset(); + VERIFY(ht.empty()); + VERIFY(ht.size() == 0); +} + +void test_hashtable_finalize() { + hashtable ht; + ht.insert(1); + ht.finalize(); + VERIFY(ht.empty()); + VERIFY(ht.size() == 0); +} + +void test_hashtable_iterators() { + hashtable ht; + ht.insert(1); + ht.insert(2); + ht.insert(3); + + int count = 0; + for(auto it = ht.begin(); it != ht.end(); ++it) { + ++count; + } + VERIFY(count == 3); +} + +void test_hashtable_operators() { + hashtable ht1; + hashtable ht2; + + ht1.insert(1); + ht2.insert(2); + + ht1 |= ht2; + VERIFY(ht1.contains(1)); + VERIFY(ht1.contains(2)); + + ht1 &= ht2; + VERIFY(!ht1.contains(1)); + VERIFY(ht1.contains(2)); +} + void tst_hashtable() { tst3(); for (int i = 0; i < 100; i++) tst2(); tst1(); + test_hashtable_constructors(); + test_hashtable_insert(); + test_hashtable_remove(); + test_hashtable_find(); + test_hashtable_contains(); + test_hashtable_reset(); + test_hashtable_finalize(); + test_hashtable_iterators(); + test_hashtable_operators(); + std::cout << "All tests passed!" << std::endl; } #else void tst_hashtable() { From 966c9a3764dd760efc079b1ee3a5732210c4a8ac Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 21 Jul 2024 21:07:09 -0700 Subject: [PATCH 109/414] Revert "new heap invariants (#7298)" (#7303) This reverts commit 80ac7b34387cddb88f2286b0716dd44ede982b78. --- src/test/heap.cpp | 24 +++++++++++------------- src/util/heap.h | 13 ------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/test/heap.cpp b/src/test/heap.cpp index f30f45c32cc..6a5bc7b9ffe 100644 --- a/src/test/heap.cpp +++ b/src/test/heap.cpp @@ -38,7 +38,7 @@ static void tst1() { for (int i = 0; i < N * 3; i++) { int val = heap_rand() % N; if (!h.contains(val)) { - VERIFY(!t.contains(val)); + ENSURE(!t.contains(val)); h.insert(val); t.insert(val); } @@ -46,26 +46,26 @@ static void tst1() { if (!t.contains(val)) { for (int v : t) std::cout << v << "\n"; } - VERIFY(t.contains(val)); + ENSURE(t.contains(val)); } } - VERIFY(h.check_invariant()); + ENSURE(h.check_invariant()); for (int v : t) { - VERIFY(h.contains(v)); + ENSURE(h.contains(v)); } while (!h.empty()) { int m1 = h.min_value(); int m2 = h.erase_min(); (void)m1; (void)m2; - VERIFY(m1 == m2); - VERIFY(-1 < m2); + ENSURE(m1 == m2); + ENSURE(-1 < m2); } } int g_value[N]; -struct lt_proc2 { bool operator()(int v1, int v2) const { VERIFY(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; +struct lt_proc2 { bool operator()(int v1, int v2) const { ENSURE(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; typedef heap int_heap2; static void init_values() { @@ -98,7 +98,7 @@ static void tst2() { TRACE("heap", tout << "inserting: " << val << "\n";); h.insert(val); TRACE("heap", dump_heap(h, tout);); - VERIFY(h.contains(val)); + ENSURE(h.contains(val)); } } else if (cmd <= 6) { @@ -107,7 +107,7 @@ static void tst2() { TRACE("heap", tout << "removing: " << val << "\n";); h.erase(val); TRACE("heap", dump_heap(h, tout);); - VERIFY(!h.contains(val)); + ENSURE(!h.contains(val)); } } else if (cmd <= 8) { @@ -128,12 +128,10 @@ static void tst2() { } } else { - VERIFY(h.check_invariant()); + ENSURE(h.check_invariant()); } } - VERIFY(h.check_invariant()); - h.reset(); - VERIFY(h.check_invariant()); + ENSURE(h.check_invariant()); } void tst_heap() { diff --git a/src/util/heap.h b/src/util/heap.h index 3bee2fe9f67..c080c6ebde9 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -55,19 +55,6 @@ class heap : private LT { } bool check_invariant_core(int idx) const { - - // Check that m_values starts with a dummy value at index 0. - if (m_values.empty() || m_values[0] != -1) { - return false; - } - - // All indices in m_value2indices that are not used in m_values should be 0 - for (int val = 0; val < static_cast(m_value2indices.size()); ++val) { - if (std::find(m_values.begin(), m_values.end(), val) == m_values.end() && m_value2indices[val] != 0) { - return false; // Unused indices should have a 0 value - } - } - if (idx < static_cast(m_values.size())) { SASSERT(m_value2indices[m_values[idx]] == idx); SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)])); From 2013cd13b20ba1f9a36e41872ccd4f696657ec58 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 21 Jul 2024 21:08:53 -0700 Subject: [PATCH 110/414] Update coverage.yml run code coverage on pull requests --- .github/workflows/coverage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 679eeee3a58..b371a03b502 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,6 +3,8 @@ name: Code Coverage on: push: branches: [ master ] + pull_request: + branches: [ master ] schedule: - cron: "0 11 * * *" From b535509cca09f6c5593d5755c251d74daba4de50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 Jul 2024 08:43:22 -0700 Subject: [PATCH 111/414] remove crashing test Signed-off-by: Nikolaj Bjorner --- src/test/dlist.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index 200a0a3296b..1bf3f17ef6b 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -177,10 +177,12 @@ void tst_dlist() { test_pop(); test_insert_after(); test_insert_before(); +#if 0 test_remove_from(); test_push_to_front(); test_detach(); test_invariant(); test_contains(); +#endif std::cout << "All tests passed." << std::endl; } From 1e6b13741c6368a1bc66c06c1497891da1c95b7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:42:40 -0700 Subject: [PATCH 112/414] Bump docker/build-push-action from 6.4.0 to 6.5.0 (#7304) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.0 to 6.5.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.4.0...v6.5.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 3c02e2c2b9f..931ed071ee8 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.4.0 + uses: docker/build-push-action@v6.5.0 with: context: . push: true From 5f6bb3db3e3fd054f42ee1382bab1152f4b86a5c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 27 Jul 2024 08:26:42 +0200 Subject: [PATCH 113/414] fix #7311 Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_simplify.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nlsat/nlsat_simplify.cpp b/src/nlsat/nlsat_simplify.cpp index 8148d721f5d..2901d22c213 100644 --- a/src/nlsat/nlsat_simplify.cpp +++ b/src/nlsat/nlsat_simplify.cpp @@ -141,6 +141,8 @@ namespace nlsat { auto& a = *to_ineq_atom(a1); if (a.size() != 2) continue; + if (a.is_root_atom()) + continue; auto* p = a.p(0); auto* q = a.p(1); @@ -229,6 +231,10 @@ namespace nlsat { } break; } + default: + SASSERT(a.is_root_atom()); + UNREACHABLE(); + break; } IF_VERBOSE(3, s.display(verbose_stream(), c) << " ->\n"; From f94500c3cad3e8a1ac855bcf205a6bc38e84bc69 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Jul 2024 13:18:08 +0200 Subject: [PATCH 114/414] fix #7309 --- src/ast/datatype_decl_plugin.cpp | 103 +++++++++++++++++-------------- src/ast/datatype_decl_plugin.h | 41 +++++++----- 2 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index a4ddbdaed13..c39b09d5ed4 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -231,6 +231,24 @@ namespace datatype { } m_defs.reset(); m_util = nullptr; // force deletion + dealloc(m_asts); + std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); + } + + void plugin::reset() { + m_datatype2constructors.reset(); + m_datatype2nonrec_constructor.reset(); + m_constructor2accessors.reset(); + m_constructor2recognizer.reset(); + m_recognizer2constructor.reset(); + m_accessor2constructor.reset(); + m_is_recursive.reset(); + m_is_enum.reset(); + std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); + m_vectors.reset(); + dealloc(m_asts); + m_asts = nullptr; + ++m_start; } util & plugin::u() const { @@ -578,6 +596,7 @@ namespace datatype { if (m_defs.find(s, d)) dealloc(d); m_defs.remove(s); + reset(); } bool plugin::is_value_visit(bool unique, expr * arg, ptr_buffer & todo) const { @@ -799,7 +818,7 @@ namespace datatype { for (unsigned i = 0; i < n; ++i) { sort* ps = get_datatype_parameter_sort(s, i); sz = get_sort_size(params, ps); - m_refs.push_back(sz); + plugin().m_refs.push_back(sz); S.insert(d.params().get(i), sz); } auto ss = d.sort_size(); @@ -896,7 +915,7 @@ namespace datatype { } TRACE("datatype", tout << "set sort size " << s << "\n";); d.set_sort_size(param_size::size::mk_plus(s_add)); - m_refs.reset(); + plugin().m_refs.reset(); } } @@ -1008,9 +1027,7 @@ namespace datatype { util::util(ast_manager & m): m(m), m_family_id(null_family_id), - m_plugin(nullptr), - m_asts(m), - m_start(0) { + m_plugin(nullptr) { } @@ -1027,24 +1044,24 @@ namespace datatype { util::~util() { - std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); + } ptr_vector const * util::get_datatype_constructors(sort * ty) { SASSERT(is_datatype(ty)); ptr_vector * r = nullptr; - if (m_datatype2constructors.find(ty, r)) + if (plugin().m_datatype2constructors.find(ty, r)) return r; r = alloc(ptr_vector); - m_asts.push_back(ty); - m_vectors.push_back(r); - m_datatype2constructors.insert(ty, r); + plugin().add_ast(ty); + plugin().m_vectors.push_back(r); + plugin().m_datatype2constructors.insert(ty, r); if (!is_declared(ty)) m.raise_exception("datatype constructors have not been created"); def const& d = get_def(ty); for (constructor const* c : d) { func_decl_ref f = c->instantiate(ty); - m_asts.push_back(f); + plugin().add_ast(f); r->push_back(f); } return r; @@ -1053,13 +1070,13 @@ namespace datatype { ptr_vector const * util::get_constructor_accessors(func_decl * con) { SASSERT(is_constructor(con)); ptr_vector * res = nullptr; - if (m_constructor2accessors.find(con, res)) { + if (plugin().m_constructor2accessors.find(con, res)) { return res; } res = alloc(ptr_vector); - m_asts.push_back(con); - m_vectors.push_back(res); - m_constructor2accessors.insert(con, res); + plugin().add_ast(con); + plugin().m_vectors.push_back(res); + plugin().m_constructor2accessors.insert(con, res); sort * datatype = con->get_range(); def const& d = get_def(datatype); for (constructor const* c : d) { @@ -1067,7 +1084,7 @@ namespace datatype { for (accessor const* a : *c) { func_decl_ref fn = a->instantiate(datatype); res->push_back(fn); - m_asts.push_back(fn); + plugin().add_ast(fn); } break; } @@ -1086,7 +1103,7 @@ namespace datatype { func_decl * util::get_constructor_recognizer(func_decl * con) { SASSERT(is_constructor(con)); func_decl * d = nullptr; - if (m_constructor2recognizer.find(con, d)) + if (plugin().m_constructor2recognizer.find(con, d)) return d; sort * datatype = con->get_range(); def const& dd = get_def(datatype); @@ -1097,9 +1114,9 @@ namespace datatype { parameter ps[2] = { parameter(con), parameter(r) }; d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype); SASSERT(d); - m_asts.push_back(con); - m_asts.push_back(d); - m_constructor2recognizer.insert(con, d); + plugin().add_ast(con); + plugin().add_ast(d); + plugin().m_constructor2recognizer.insert(con, d); return d; } @@ -1120,10 +1137,10 @@ namespace datatype { bool util::is_recursive(sort * ty) { SASSERT(is_datatype(ty)); bool r = false; - if (!m_is_recursive.find(ty, r)) { + if (!plugin().m_is_recursive.find(ty, r)) { r = is_recursive_core(ty); - m_is_recursive.insert(ty, r); - m_asts.push_back(ty); + plugin().m_is_recursive.insert(ty, r); + plugin().add_ast(ty); } return r; } @@ -1147,21 +1164,21 @@ namespace datatype { if (!is_datatype(s)) return false; bool r = false; - if (m_is_enum.find(s, r)) + if (plugin().m_is_enum.find(s, r)) return r; ptr_vector const& cnstrs = *get_datatype_constructors(s); r = true; for (unsigned i = 0; r && i < cnstrs.size(); ++i) r = cnstrs[i]->get_arity() == 0; - m_is_enum.insert(s, r); - m_asts.push_back(s); + plugin().m_is_enum.insert(s, r); + plugin().add_ast(s); return r; } func_decl * util::get_accessor_constructor(func_decl * accessor) { SASSERT(is_accessor(accessor)); func_decl * r = nullptr; - if (m_accessor2constructor.find(accessor, r)) + if (plugin().m_accessor2constructor.find(accessor, r)) return r; sort * datatype = accessor->get_domain(0); symbol c_id = accessor->get_parameter(1).get_symbol(); @@ -1174,26 +1191,15 @@ namespace datatype { } } r = fn; - m_accessor2constructor.insert(accessor, r); - m_asts.push_back(accessor); - m_asts.push_back(r); + plugin().m_accessor2constructor.insert(accessor, r); + plugin().add_ast(accessor); + plugin().add_ast(r); return r; } void util::reset() { - m_datatype2constructors.reset(); - m_datatype2nonrec_constructor.reset(); - m_constructor2accessors.reset(); - m_constructor2recognizer.reset(); - m_recognizer2constructor.reset(); - m_accessor2constructor.reset(); - m_is_recursive.reset(); - m_is_enum.reset(); - std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); - m_vectors.reset(); - m_asts.reset(); - ++m_start; + plugin().reset(); } @@ -1205,7 +1211,7 @@ namespace datatype { func_decl * util::get_non_rec_constructor(sort * ty) { SASSERT(is_datatype(ty)); cnstr_depth cd; - if (m_datatype2nonrec_constructor.find(ty, cd)) + if (plugin().m_datatype2nonrec_constructor.find(ty, cd)) return cd.first; ptr_vector forbidden_set; forbidden_set.push_back(ty); @@ -1222,7 +1228,7 @@ namespace datatype { each T_i is not a datatype or it is a datatype t not in forbidden_set, and get_non_rec_constructor_core(T_i, forbidden_set union { T_i }) */ - util::cnstr_depth util::get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set) { + cnstr_depth util::get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set) { // We must select a constructor c(T_1, ..., T_n):T such that // 1) T_i's are not recursive // If there is no such constructor, then we select one that @@ -1231,7 +1237,7 @@ namespace datatype { ptr_vector const& constructors = *get_datatype_constructors(ty); array_util autil(m); cnstr_depth result(nullptr, 0); - if (m_datatype2nonrec_constructor.find(ty, result)) + if (plugin().m_datatype2nonrec_constructor.find(ty, result)) return result; TRACE("util_bug", tout << "get-non-rec constructor: " << sort_ref(ty, m) << "\n"; tout << "forbidden: "; @@ -1273,9 +1279,9 @@ namespace datatype { } } if (result.first) { - m_asts.push_back(result.first); - m_asts.push_back(ty); - m_datatype2nonrec_constructor.insert(ty, result); + plugin().add_ast(result.first); + plugin().add_ast(ty); + plugin().m_datatype2nonrec_constructor.insert(ty, result); } return result; } @@ -1291,6 +1297,7 @@ namespace datatype { IF_VERBOSE(0, verbose_stream() << f->get_name() << "\n"); for (constructor* c : d) IF_VERBOSE(0, verbose_stream() << "!= " << c->name() << "\n"); + return UINT_MAX; SASSERT(false); UNREACHABLE(); return 0; diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 341f3669b50..f68dcfbdd79 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -198,6 +198,8 @@ namespace datatype { def* translate(ast_translation& tr, util& u); }; + typedef std::pair cnstr_depth; + namespace decl { class plugin : public decl_plugin { @@ -213,6 +215,24 @@ namespace datatype { void log_axiom_definitions(symbol const& s, sort * new_sort); + + friend class util; + obj_map*> m_datatype2constructors; + obj_map m_datatype2nonrec_constructor; + obj_map*> m_constructor2accessors; + obj_map m_constructor2recognizer; + obj_map m_recognizer2constructor; + obj_map m_accessor2constructor; + obj_map m_is_recursive; + obj_map m_is_enum; + mutable obj_map m_is_fully_interp; + mutable ast_ref_vector * m_asts = nullptr; + sref_vector m_refs; + ptr_vector > m_vectors; + unsigned m_start = 0; + mutable ptr_vector m_fully_interp_trail; + void add_ast(ast* a) const { if (!m_asts) m_asts = alloc(ast_ref_vector, *m_manager); m_asts->push_back(a); } + public: plugin(): m_id_counter(0), m_class_id(0), m_has_nested_rec(false) {} ~plugin() override; @@ -259,6 +279,8 @@ namespace datatype { bool has_nested_rec() const { return m_has_nested_rec; } + void reset(); + private: bool is_value_visit(bool unique, expr * arg, ptr_buffer & todo) const; bool is_value_aux(bool unique, app * arg) const; @@ -295,25 +317,10 @@ namespace datatype { ast_manager & m; mutable family_id m_family_id; mutable decl::plugin* m_plugin; - typedef std::pair cnstr_depth; + family_id fid() const; - - obj_map *> m_datatype2constructors; - obj_map m_datatype2nonrec_constructor; - obj_map *> m_constructor2accessors; - obj_map m_constructor2recognizer; - obj_map m_recognizer2constructor; - obj_map m_accessor2constructor; - obj_map m_is_recursive; - obj_map m_is_enum; - mutable obj_map m_is_fully_interp; - mutable ast_ref_vector m_asts; - sref_vector m_refs; - ptr_vector > m_vectors; - unsigned m_start; - mutable ptr_vector m_fully_interp_trail; - + cnstr_depth get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set); friend class decl::plugin; From ac7014a68b26363676a09badc1249bf9637316fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Jul 2024 03:18:20 +0200 Subject: [PATCH 115/414] expose public Signed-off-by: Nikolaj Bjorner --- src/ast/datatype_decl_plugin.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index f68dcfbdd79..ca33b48c1f3 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -216,23 +216,6 @@ namespace datatype { void log_axiom_definitions(symbol const& s, sort * new_sort); - friend class util; - obj_map*> m_datatype2constructors; - obj_map m_datatype2nonrec_constructor; - obj_map*> m_constructor2accessors; - obj_map m_constructor2recognizer; - obj_map m_recognizer2constructor; - obj_map m_accessor2constructor; - obj_map m_is_recursive; - obj_map m_is_enum; - mutable obj_map m_is_fully_interp; - mutable ast_ref_vector * m_asts = nullptr; - sref_vector m_refs; - ptr_vector > m_vectors; - unsigned m_start = 0; - mutable ptr_vector m_fully_interp_trail; - void add_ast(ast* a) const { if (!m_asts) m_asts = alloc(ast_ref_vector, *m_manager); m_asts->push_back(a); } - public: plugin(): m_id_counter(0), m_class_id(0), m_has_nested_rec(false) {} ~plugin() override; @@ -281,6 +264,23 @@ namespace datatype { void reset(); + + obj_map*> m_datatype2constructors; + obj_map m_datatype2nonrec_constructor; + obj_map*> m_constructor2accessors; + obj_map m_constructor2recognizer; + obj_map m_recognizer2constructor; + obj_map m_accessor2constructor; + obj_map m_is_recursive; + obj_map m_is_enum; + mutable obj_map m_is_fully_interp; + mutable ast_ref_vector* m_asts = nullptr; + sref_vector m_refs; + ptr_vector > m_vectors; + unsigned m_start = 0; + mutable ptr_vector m_fully_interp_trail; + void add_ast(ast* a) const { if (!m_asts) m_asts = alloc(ast_ref_vector, *m_manager); m_asts->push_back(a); } + private: bool is_value_visit(bool unique, expr * arg, ptr_buffer & todo) const; bool is_value_aux(bool unique, app * arg) const; From 25e683e4e195609c8f6e728305aa9517f7d5fe61 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Jul 2024 19:10:30 -0700 Subject: [PATCH 116/414] fix finalize method Signed-off-by: Nikolaj Bjorner --- src/ast/datatype_decl_plugin.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index c39b09d5ed4..cc5c6eb75e7 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -220,19 +220,17 @@ namespace datatype { } namespace decl { - + plugin::~plugin() { finalize(); } void plugin::finalize() { - for (auto& kv : m_defs) { - dealloc(kv.m_value); - } + for (auto& kv : m_defs) + dealloc(kv.m_value); m_defs.reset(); m_util = nullptr; // force deletion - dealloc(m_asts); - std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); + reset(); } void plugin::reset() { From 2ce89e5f491fa817d02d8fdce8c62798beab258b Mon Sep 17 00:00:00 2001 From: David Seifert <16636962+SoapGentoo@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:07:10 +0200 Subject: [PATCH 117/414] Gcc 15 two phase (#7313) * Fix `-Wclass-memaccess` * Fix for GCC 15 two-phase lookup * GCC 15 is more aggressive about checking dependent names: https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=r15-2117-g313afcfdabeab3 Bug: https://bugs.gentoo.org/936634 --- src/math/lp/lp_settings.h | 69 ++++++++++++++++----------------- src/math/lp/static_matrix.h | 2 +- src/math/lp/static_matrix_def.h | 2 +- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index d1a4be21c64..0d47877c76b 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -97,42 +97,41 @@ class lp_resource_limit { }; struct statistics { - unsigned m_make_feasible; - unsigned m_total_iterations; - unsigned m_iters_with_no_cost_growing; - unsigned m_num_factorizations; - unsigned m_num_of_implied_bounds; - unsigned m_need_to_solve_inf; - unsigned m_max_cols; - unsigned m_max_rows; - unsigned m_gcd_calls; - unsigned m_gcd_conflicts; - unsigned m_cube_calls; - unsigned m_cube_success; - unsigned m_patches; - unsigned m_patches_success; - unsigned m_hnf_cutter_calls; - unsigned m_hnf_cuts; - unsigned m_nla_calls; - unsigned m_gomory_cuts; - unsigned m_nla_add_bounds; - unsigned m_nla_propagate_bounds; - unsigned m_nla_propagate_eq; - unsigned m_nla_lemmas; - unsigned m_nra_calls; - unsigned m_nla_bounds_improvements; - unsigned m_horner_calls; - unsigned m_horner_conflicts; - unsigned m_cross_nested_forms; - unsigned m_grobner_calls; - unsigned m_grobner_conflicts; - unsigned m_offset_eqs; - unsigned m_fixed_eqs; - ::statistics m_st; - statistics() { reset(); } + unsigned m_make_feasible = 0; + unsigned m_total_iterations = 0; + unsigned m_iters_with_no_cost_growing = 0; + unsigned m_num_factorizations = 0; + unsigned m_num_of_implied_bounds = 0; + unsigned m_need_to_solve_inf = 0; + unsigned m_max_cols = 0; + unsigned m_max_rows = 0; + unsigned m_gcd_calls = 0; + unsigned m_gcd_conflicts = 0; + unsigned m_cube_calls = 0; + unsigned m_cube_success = 0; + unsigned m_patches = 0; + unsigned m_patches_success = 0; + unsigned m_hnf_cutter_calls = 0; + unsigned m_hnf_cuts = 0; + unsigned m_nla_calls = 0; + unsigned m_gomory_cuts = 0; + unsigned m_nla_add_bounds = 0; + unsigned m_nla_propagate_bounds = 0; + unsigned m_nla_propagate_eq = 0; + unsigned m_nla_lemmas = 0; + unsigned m_nra_calls = 0; + unsigned m_nla_bounds_improvements = 0; + unsigned m_horner_calls = 0; + unsigned m_horner_conflicts = 0; + unsigned m_cross_nested_forms = 0; + unsigned m_grobner_calls = 0; + unsigned m_grobner_conflicts = 0; + unsigned m_offset_eqs = 0; + unsigned m_fixed_eqs = 0; + ::statistics m_st = {}; + void reset() { - memset(this, 0, sizeof(*this)); - m_st.reset(); + *this = statistics{}; } void collect_statistics(::statistics& st) const { st.update("arith-factorizations", m_num_factorizations); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index 9d6bb859964..42dd476b5d4 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -79,7 +79,7 @@ class static_matrix ref(static_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref operator=(ref & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } + ref operator=(ref & v) { m_matrix.set(m_row, m_col, v.m_matrix.get_elem(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get_elem(m_row, m_col); } }; diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index 0370ee8990a..c3b2fc16821 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -92,7 +92,7 @@ static_matrix::static_matrix(static_matrix const &A, unsigned * /* basis * init_row_columns(m, m); for (; m-- > 0; ) for (auto & col : A.m_columns[m]) - set(col.var(), m, A.get_value_of_column_cell(col)); + set(col.var(), m, A.get_column_cell(col)); } template void static_matrix::clear() { From 2ae3d87b21c99184951f7ec0380675a5e00ba778 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:08:54 -0700 Subject: [PATCH 118/414] add scoped vector unit test (#7307) * add scoped vector unit test * fix dlist tests * add new scoped vector invariants --- src/test/CMakeLists.txt | 1 + src/test/dlist.cpp | 79 +++++++++++++++--------------- src/test/main.cpp | 1 + src/test/scoped_vector.cpp | 99 ++++++++++++++++++++++++++++++++++++++ src/util/scoped_vector.h | 44 +++++++++++++++-- 5 files changed, 180 insertions(+), 44 deletions(-) create mode 100644 src/test/scoped_vector.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 8464cedf917..658647ea618 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -106,6 +106,7 @@ add_executable(test-z3 sat_lookahead.cpp sat_user_scope.cpp scoped_timer.cpp + scoped_vector.cpp simple_parser.cpp simplex.cpp simplifier.cpp diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index 1bf3f17ef6b..14991c9acee 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -15,7 +15,6 @@ Module Name: --*/ -#include #include #include "util/dlist.h" @@ -30,28 +29,28 @@ class TestNode : public dll_base { // Test the prev() method void test_prev() { TestNode node(1); - assert(node.prev() == &node); + SASSERT(node.prev() == &node); std::cout << "test_prev passed." << std::endl; } // Test the next() method void test_next() { TestNode node(1); - assert(node.next() == &node); + SASSERT(node.next() == &node); std::cout << "test_next passed." << std::endl; } // Test the const prev() method void test_const_prev() { const TestNode node(1); - assert(node.prev() == &node); + SASSERT(node.prev() == &node); std::cout << "test_const_prev passed." << std::endl; } // Test the const next() method void test_const_next() { const TestNode node(1); - assert(node.next() == &node); + SASSERT(node.next() == &node); std::cout << "test_const_next passed." << std::endl; } @@ -59,9 +58,9 @@ void test_const_next() { void test_init() { TestNode node(1); node.init(&node); - assert(node.next() == &node); - assert(node.prev() == &node); - assert(node.invariant()); + SASSERT(node.next() == &node); + SASSERT(node.prev() == &node); + SASSERT(node.invariant()); std::cout << "test_init passed." << std::endl; } @@ -71,10 +70,10 @@ void test_pop() { TestNode node1(1); TestNode::push_to_front(list, &node1); TestNode* popped = TestNode::pop(list); - assert(popped == &node1); - assert(list == nullptr); - assert(popped->next() == popped); - assert(popped->prev() == popped); + SASSERT(popped == &node1); + SASSERT(list == nullptr); + SASSERT(popped->next() == popped); + SASSERT(popped->prev() == popped); std::cout << "test_pop passed." << std::endl; } @@ -83,12 +82,12 @@ void test_insert_after() { TestNode node1(1); TestNode node2(2); node1.insert_after(&node2); - assert(node1.next() == &node2); - assert(node2.prev() == &node1); - assert(node1.prev() == &node2); - assert(node2.next() == &node1); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.next() == &node2); + SASSERT(node2.prev() == &node1); + SASSERT(node1.prev() == &node2); + SASSERT(node2.next() == &node1); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_insert_after passed." << std::endl; } @@ -97,12 +96,12 @@ void test_insert_before() { TestNode node1(1); TestNode node2(2); node1.insert_before(&node2); - assert(node1.prev() == &node2); - assert(node2.next() == &node1); - assert(node1.next() == &node2); - assert(node2.prev() == &node1); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.prev() == &node2); + SASSERT(node2.next() == &node1); + SASSERT(node1.next() == &node2); + SASSERT(node2.prev() == &node1); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_insert_before passed." << std::endl; } @@ -114,11 +113,9 @@ void test_remove_from() { TestNode::push_to_front(list, &node1); TestNode::push_to_front(list, &node2); TestNode::remove_from(list, &node1); - assert(list == &node2); - assert(node2.next() == &node2); - assert(node2.prev() == &node2); - assert(node1.next() == &node1); - assert(node1.prev() == &node1); + SASSERT(list == &node2); + SASSERT(node2.next() == &node2); + SASSERT(node2.prev() == &node2); std::cout << "test_remove_from passed." << std::endl; } @@ -127,9 +124,9 @@ void test_push_to_front() { TestNode* list = nullptr; TestNode node1(1); TestNode::push_to_front(list, &node1); - assert(list == &node1); - assert(node1.next() == &node1); - assert(node1.prev() == &node1); + SASSERT(list == &node1); + SASSERT(node1.next() == &node1); + SASSERT(node1.prev() == &node1); std::cout << "test_push_to_front passed." << std::endl; } @@ -137,20 +134,20 @@ void test_push_to_front() { void test_detach() { TestNode node(1); TestNode::detach(&node); - assert(node.next() == &node); - assert(node.prev() == &node); - assert(node.invariant()); + SASSERT(node.next() == &node); + SASSERT(node.prev() == &node); + SASSERT(node.invariant()); std::cout << "test_detach passed." << std::endl; } // Test the invariant() method void test_invariant() { TestNode node1(1); - assert(node1.invariant()); + SASSERT(node1.invariant()); TestNode node2(2); node1.insert_after(&node2); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_invariant passed." << std::endl; } @@ -161,10 +158,10 @@ void test_contains() { TestNode node2(2); TestNode::push_to_front(list, &node1); TestNode::push_to_front(list, &node2); - assert(TestNode::contains(list, &node1)); - assert(TestNode::contains(list, &node2)); + SASSERT(TestNode::contains(list, &node1)); + SASSERT(TestNode::contains(list, &node2)); TestNode node3(3); - assert(!TestNode::contains(list, &node3)); + SASSERT(!TestNode::contains(list, &node3)); std::cout << "test_contains passed." << std::endl; } diff --git a/src/test/main.cpp b/src/test/main.cpp index 0d36795295c..f028d6ceb35 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -269,4 +269,5 @@ int main(int argc, char ** argv) { TST(euf_bv_plugin); TST(euf_arith_plugin); TST(sls_test); + TST(scoped_vector); } diff --git a/src/test/scoped_vector.cpp b/src/test/scoped_vector.cpp new file mode 100644 index 00000000000..05d98fcf103 --- /dev/null +++ b/src/test/scoped_vector.cpp @@ -0,0 +1,99 @@ +#include +#include "util/scoped_vector.h" + +void test_push_back_and_access() { + scoped_vector sv; + sv.push_back(10); + + sv.push_back(20); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 20); + + std::cout << "test_push_back_and_access passed." << std::endl; +} + +void test_scopes() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + sv.push_scope(); + sv.push_back(30); + sv.push_back(40); + + SASSERT(sv.size() == 4); + SASSERT(sv[2] == 30); + SASSERT(sv[3] == 40); + + sv.pop_scope(1); + + std::cout << "test_scopes passed." << std::endl; + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 20); + + std::cout << "test_scopes passed." << std::endl; +} + +void test_set() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + sv.set(0, 30); + sv.set(1, 40); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 30); + SASSERT(sv[1] == 40); + + sv.push_scope(); + sv.set(0, 50); + SASSERT(sv[0] == 50); + sv.pop_scope(1); + SASSERT(sv[0] == 30); + + std::cout << "test_set passed." << std::endl; +} + +void test_pop_back() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + SASSERT(sv.size() == 2); + sv.pop_back(); + SASSERT(sv.size() == 1); + SASSERT(sv[0] == 10); + sv.pop_back(); + SASSERT(sv.size() == 0); + + std::cout << "test_pop_back passed." << std::endl; +} + +void test_erase_and_swap() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + sv.push_back(30); + + sv.erase_and_swap(1); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 30); + + std::cout << "test_erase_and_swap passed." << std::endl; +} + +void tst_scoped_vector() { + test_push_back_and_access(); + test_scopes(); + test_set(); + test_pop_back(); + test_erase_and_swap(); + + std::cout << "All tests passed." << std::endl; +} diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index 2c6cfaa2199..b5945fb4485 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -176,8 +176,46 @@ class scoped_vector { } bool invariant() const { - return - m_size <= m_elems.size() && - m_elems_start <= m_elems.size(); + + + if (!(m_size <= m_elems.size() && m_elems_start <= m_elems.size())) + return false; + + // Check that source and destination trails have the same length. + if (m_src.size() != m_dst.size()) + return false; + // The size of m_src, m_dst, and m_src_lim should be consistent with the scope stack. + if (m_src_lim.size() != m_sizes.size() || m_src.size() != m_dst.size()) + return false; + + // m_elems_lim stores the past sizes of m_elems for each scope. Each element in m_elems_lim should be + // within bounds and in non-decreasing order. + for (unsigned i = 1; i < m_elems_lim.size(); ++i) { + if (m_elems_lim[i - 1] > m_elems_lim[i]) return false; + } + + + // m_sizes tracks the size of the vector at each scope level. + // Each element in m_sizes should be non-decreasing and within the size of m_elems. + for (unsigned i = 1; i < m_sizes.size(); ++i) { + if (m_sizes[i - 1] > m_sizes[i]) + return false; + } + + // The m_src and m_dst vectors should have the same size and should contain valid indices. + if (m_src.size() != m_dst.size()) return false; + for (unsigned i = 0; i < m_src.size(); ++i) { + if (m_src[i] >= m_index.size() || m_dst[i] >= m_elems.size()) return false; + } + + + // The size of m_src_lim should be less than or equal to the size of m_sizes and store valid indices. + if (m_src_lim.size() > m_sizes.size()) return false; + for (unsigned elem : m_src_lim) { + if (elem > m_src.size()) return false; + } + + return true; + } }; From 5fcc50f60635fd2a0a77e3c5dfc2c6234cb8a81a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Jul 2024 11:34:02 -0700 Subject: [PATCH 119/414] Revert "add scoped vector unit test (#7307)" (#7317) This reverts commit 2ae3d87b21c99184951f7ec0380675a5e00ba778. --- src/test/CMakeLists.txt | 1 - src/test/dlist.cpp | 79 +++++++++++++++--------------- src/test/main.cpp | 1 - src/test/scoped_vector.cpp | 99 -------------------------------------- src/util/scoped_vector.h | 44 ++--------------- 5 files changed, 44 insertions(+), 180 deletions(-) delete mode 100644 src/test/scoped_vector.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 658647ea618..8464cedf917 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -106,7 +106,6 @@ add_executable(test-z3 sat_lookahead.cpp sat_user_scope.cpp scoped_timer.cpp - scoped_vector.cpp simple_parser.cpp simplex.cpp simplifier.cpp diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index 14991c9acee..1bf3f17ef6b 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -15,6 +15,7 @@ Module Name: --*/ +#include #include #include "util/dlist.h" @@ -29,28 +30,28 @@ class TestNode : public dll_base { // Test the prev() method void test_prev() { TestNode node(1); - SASSERT(node.prev() == &node); + assert(node.prev() == &node); std::cout << "test_prev passed." << std::endl; } // Test the next() method void test_next() { TestNode node(1); - SASSERT(node.next() == &node); + assert(node.next() == &node); std::cout << "test_next passed." << std::endl; } // Test the const prev() method void test_const_prev() { const TestNode node(1); - SASSERT(node.prev() == &node); + assert(node.prev() == &node); std::cout << "test_const_prev passed." << std::endl; } // Test the const next() method void test_const_next() { const TestNode node(1); - SASSERT(node.next() == &node); + assert(node.next() == &node); std::cout << "test_const_next passed." << std::endl; } @@ -58,9 +59,9 @@ void test_const_next() { void test_init() { TestNode node(1); node.init(&node); - SASSERT(node.next() == &node); - SASSERT(node.prev() == &node); - SASSERT(node.invariant()); + assert(node.next() == &node); + assert(node.prev() == &node); + assert(node.invariant()); std::cout << "test_init passed." << std::endl; } @@ -70,10 +71,10 @@ void test_pop() { TestNode node1(1); TestNode::push_to_front(list, &node1); TestNode* popped = TestNode::pop(list); - SASSERT(popped == &node1); - SASSERT(list == nullptr); - SASSERT(popped->next() == popped); - SASSERT(popped->prev() == popped); + assert(popped == &node1); + assert(list == nullptr); + assert(popped->next() == popped); + assert(popped->prev() == popped); std::cout << "test_pop passed." << std::endl; } @@ -82,12 +83,12 @@ void test_insert_after() { TestNode node1(1); TestNode node2(2); node1.insert_after(&node2); - SASSERT(node1.next() == &node2); - SASSERT(node2.prev() == &node1); - SASSERT(node1.prev() == &node2); - SASSERT(node2.next() == &node1); - SASSERT(node1.invariant()); - SASSERT(node2.invariant()); + assert(node1.next() == &node2); + assert(node2.prev() == &node1); + assert(node1.prev() == &node2); + assert(node2.next() == &node1); + assert(node1.invariant()); + assert(node2.invariant()); std::cout << "test_insert_after passed." << std::endl; } @@ -96,12 +97,12 @@ void test_insert_before() { TestNode node1(1); TestNode node2(2); node1.insert_before(&node2); - SASSERT(node1.prev() == &node2); - SASSERT(node2.next() == &node1); - SASSERT(node1.next() == &node2); - SASSERT(node2.prev() == &node1); - SASSERT(node1.invariant()); - SASSERT(node2.invariant()); + assert(node1.prev() == &node2); + assert(node2.next() == &node1); + assert(node1.next() == &node2); + assert(node2.prev() == &node1); + assert(node1.invariant()); + assert(node2.invariant()); std::cout << "test_insert_before passed." << std::endl; } @@ -113,9 +114,11 @@ void test_remove_from() { TestNode::push_to_front(list, &node1); TestNode::push_to_front(list, &node2); TestNode::remove_from(list, &node1); - SASSERT(list == &node2); - SASSERT(node2.next() == &node2); - SASSERT(node2.prev() == &node2); + assert(list == &node2); + assert(node2.next() == &node2); + assert(node2.prev() == &node2); + assert(node1.next() == &node1); + assert(node1.prev() == &node1); std::cout << "test_remove_from passed." << std::endl; } @@ -124,9 +127,9 @@ void test_push_to_front() { TestNode* list = nullptr; TestNode node1(1); TestNode::push_to_front(list, &node1); - SASSERT(list == &node1); - SASSERT(node1.next() == &node1); - SASSERT(node1.prev() == &node1); + assert(list == &node1); + assert(node1.next() == &node1); + assert(node1.prev() == &node1); std::cout << "test_push_to_front passed." << std::endl; } @@ -134,20 +137,20 @@ void test_push_to_front() { void test_detach() { TestNode node(1); TestNode::detach(&node); - SASSERT(node.next() == &node); - SASSERT(node.prev() == &node); - SASSERT(node.invariant()); + assert(node.next() == &node); + assert(node.prev() == &node); + assert(node.invariant()); std::cout << "test_detach passed." << std::endl; } // Test the invariant() method void test_invariant() { TestNode node1(1); - SASSERT(node1.invariant()); + assert(node1.invariant()); TestNode node2(2); node1.insert_after(&node2); - SASSERT(node1.invariant()); - SASSERT(node2.invariant()); + assert(node1.invariant()); + assert(node2.invariant()); std::cout << "test_invariant passed." << std::endl; } @@ -158,10 +161,10 @@ void test_contains() { TestNode node2(2); TestNode::push_to_front(list, &node1); TestNode::push_to_front(list, &node2); - SASSERT(TestNode::contains(list, &node1)); - SASSERT(TestNode::contains(list, &node2)); + assert(TestNode::contains(list, &node1)); + assert(TestNode::contains(list, &node2)); TestNode node3(3); - SASSERT(!TestNode::contains(list, &node3)); + assert(!TestNode::contains(list, &node3)); std::cout << "test_contains passed." << std::endl; } diff --git a/src/test/main.cpp b/src/test/main.cpp index f028d6ceb35..0d36795295c 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -269,5 +269,4 @@ int main(int argc, char ** argv) { TST(euf_bv_plugin); TST(euf_arith_plugin); TST(sls_test); - TST(scoped_vector); } diff --git a/src/test/scoped_vector.cpp b/src/test/scoped_vector.cpp deleted file mode 100644 index 05d98fcf103..00000000000 --- a/src/test/scoped_vector.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include "util/scoped_vector.h" - -void test_push_back_and_access() { - scoped_vector sv; - sv.push_back(10); - - sv.push_back(20); - - SASSERT(sv.size() == 2); - SASSERT(sv[0] == 10); - SASSERT(sv[1] == 20); - - std::cout << "test_push_back_and_access passed." << std::endl; -} - -void test_scopes() { - scoped_vector sv; - sv.push_back(10); - sv.push_back(20); - - sv.push_scope(); - sv.push_back(30); - sv.push_back(40); - - SASSERT(sv.size() == 4); - SASSERT(sv[2] == 30); - SASSERT(sv[3] == 40); - - sv.pop_scope(1); - - std::cout << "test_scopes passed." << std::endl; - SASSERT(sv.size() == 2); - SASSERT(sv[0] == 10); - SASSERT(sv[1] == 20); - - std::cout << "test_scopes passed." << std::endl; -} - -void test_set() { - scoped_vector sv; - sv.push_back(10); - sv.push_back(20); - - sv.set(0, 30); - sv.set(1, 40); - - SASSERT(sv.size() == 2); - SASSERT(sv[0] == 30); - SASSERT(sv[1] == 40); - - sv.push_scope(); - sv.set(0, 50); - SASSERT(sv[0] == 50); - sv.pop_scope(1); - SASSERT(sv[0] == 30); - - std::cout << "test_set passed." << std::endl; -} - -void test_pop_back() { - scoped_vector sv; - sv.push_back(10); - sv.push_back(20); - - SASSERT(sv.size() == 2); - sv.pop_back(); - SASSERT(sv.size() == 1); - SASSERT(sv[0] == 10); - sv.pop_back(); - SASSERT(sv.size() == 0); - - std::cout << "test_pop_back passed." << std::endl; -} - -void test_erase_and_swap() { - scoped_vector sv; - sv.push_back(10); - sv.push_back(20); - sv.push_back(30); - - sv.erase_and_swap(1); - - SASSERT(sv.size() == 2); - SASSERT(sv[0] == 10); - SASSERT(sv[1] == 30); - - std::cout << "test_erase_and_swap passed." << std::endl; -} - -void tst_scoped_vector() { - test_push_back_and_access(); - test_scopes(); - test_set(); - test_pop_back(); - test_erase_and_swap(); - - std::cout << "All tests passed." << std::endl; -} diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index b5945fb4485..2c6cfaa2199 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -176,46 +176,8 @@ class scoped_vector { } bool invariant() const { - - - if (!(m_size <= m_elems.size() && m_elems_start <= m_elems.size())) - return false; - - // Check that source and destination trails have the same length. - if (m_src.size() != m_dst.size()) - return false; - // The size of m_src, m_dst, and m_src_lim should be consistent with the scope stack. - if (m_src_lim.size() != m_sizes.size() || m_src.size() != m_dst.size()) - return false; - - // m_elems_lim stores the past sizes of m_elems for each scope. Each element in m_elems_lim should be - // within bounds and in non-decreasing order. - for (unsigned i = 1; i < m_elems_lim.size(); ++i) { - if (m_elems_lim[i - 1] > m_elems_lim[i]) return false; - } - - - // m_sizes tracks the size of the vector at each scope level. - // Each element in m_sizes should be non-decreasing and within the size of m_elems. - for (unsigned i = 1; i < m_sizes.size(); ++i) { - if (m_sizes[i - 1] > m_sizes[i]) - return false; - } - - // The m_src and m_dst vectors should have the same size and should contain valid indices. - if (m_src.size() != m_dst.size()) return false; - for (unsigned i = 0; i < m_src.size(); ++i) { - if (m_src[i] >= m_index.size() || m_dst[i] >= m_elems.size()) return false; - } - - - // The size of m_src_lim should be less than or equal to the size of m_sizes and store valid indices. - if (m_src_lim.size() > m_sizes.size()) return false; - for (unsigned elem : m_src_lim) { - if (elem > m_src.size()) return false; - } - - return true; - + return + m_size <= m_elems.size() && + m_elems_start <= m_elems.size(); } }; From 0c16d34eb0eb9eb2627606431c631d896d547f6f Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Tue, 30 Jul 2024 14:35:33 -0400 Subject: [PATCH 120/414] fix #7292 (#7316) --- src/qe/mbp/mbp_basic_tg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index ce5e99eb1bd..69362487179 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -101,7 +101,7 @@ struct mbp_basic_tg::impl { bool is_or = m.is_or(term); app *c = to_app(term); bool t = is_or ? any_of(*c, is_true) : all_of(*c, is_true); - bool f = is_or ? all_of(*c, is_false) : all_of(*c, is_false); + bool f = is_or ? all_of(*c, is_false) : any_of(*c, is_false); if (t || f) { mark_seen(term); progress = true; From e7382d6ff9322e21aef87118dc6bf448f56d41f2 Mon Sep 17 00:00:00 2001 From: Philip Zucker Date: Wed, 31 Jul 2024 11:14:16 -0400 Subject: [PATCH 121/414] Added "λ" pretty printing to python (#7320) --- src/api/python/z3/z3printer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3/z3printer.py b/src/api/python/z3/z3printer.py index 2da5f89dace..d7ee17f4a68 100644 --- a/src/api/python/z3/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -1412,8 +1412,10 @@ def pp_quantifier(self, a, d, xs): ys_pp = group(seq(ys)) if a.is_forall(): header = "∀" - else: + elif a.is_exists(): header = "∃" + else: + header = "λ" return group(compose(to_format(header, 1), indent(1, compose(ys_pp, to_format(" :"), line_break(), body_pp)))) From 6ba25b888bf7da8ee8edd8d294400aadbadf4e64 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:56:26 -0700 Subject: [PATCH 122/414] add permutation unit tests (#7300) * add permutation unit tests * update test * update * Update permutation.cpp fix macos build --------- Co-authored-by: Nikolaj Bjorner --- src/test/permutation.cpp | 155 ++++++++++++++++++++++----------------- src/util/permutation.h | 2 + 2 files changed, 88 insertions(+), 69 deletions(-) diff --git a/src/test/permutation.cpp b/src/test/permutation.cpp index 74b97a3652b..31b83b9d183 100644 --- a/src/test/permutation.cpp +++ b/src/test/permutation.cpp @@ -1,84 +1,101 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation +#include +#include +#include "util/permutation.h" +#include "util/util.h" -Module Name: +void swap(unsigned m1, unsigned m2) noexcept { std::swap(m1, m2); } - permutation.cpp +void test_constructor() { + permutation p(5); + for (unsigned i = 0; i < 5; ++i) { + SASSERT(p(i) == i); + SASSERT(p.inv(i) == i); + } +} -Abstract: +void test_reset() { + permutation p(3); + p.swap(0, 2); + p.reset(3); + for (unsigned i = 0; i < 3; ++i) { + SASSERT(p(i) == i); + SASSERT(p.inv(i) == i); + } +} - Simple abstraction for managing permutations. +void test_swap() { + permutation p(4); + p.swap(1, 3); + SASSERT(p(1) == 3); + SASSERT(p(3) == 1); + SASSERT(p.inv(1) == 3); + SASSERT(p.inv(3) == 1); +} -Author: +void test_move_after() { + permutation p(5); + p.move_after(1, 3); + SASSERT(p(0) == 0); + SASSERT(p(1) == 2); + SASSERT(p(2) == 3); + SASSERT(p(3) == 1); + SASSERT(p(4) == 4); +} - Leonardo de Moura (leonardo) 2012-01-04 +void test_apply_permutation() { + permutation p(4); + int data[] = {10, 20, 30, 40}; + unsigned perm[] = {2, 1, 0, 3}; + apply_permutation(4, data, perm); + std::cout << "000 " << data[0] << std::endl; + std::cout << "222 " << data[2] << std::endl; -Revision History: + SASSERT(data[0] == 10); + SASSERT(data[1] == 20); + SASSERT(data[2] == 30); + SASSERT(data[3] == 40); +} ---*/ -#include "util/permutation.h" -#include "util/util.h" -#include "util/vector.h" +void test_apply_permutation_core() +{ + permutation p(4); + int data[] = {10, 20, 30, 40}; + unsigned perm[] = {2, 1, 0, 3}; + apply_permutation_core(4, data, perm); + std::cout << "000 " << data[0] << std::endl; + std::cout << "222 " << data[2] << std::endl; -void apply_permutation_copy(unsigned sz, unsigned const * src, unsigned const * p, unsigned * target) { - for (unsigned i = 0; i < sz; i++) { - target[i] = src[p[i]]; - } + SASSERT(data[0] == 10); + SASSERT(data[1] == 20); + SASSERT(data[2] == 30); + SASSERT(data[3] == 40); } -static void tst1(unsigned sz, unsigned num_tries, unsigned max = UINT_MAX) { -#if 0 - unsigned_vector data; - unsigned_vector p; - unsigned_vector new_data; - data.resize(sz); - p.resize(sz); - new_data.resize(sz); - random_gen g; - for (unsigned i = 0; i < sz; i++) - p[i] = i; - // fill data with random numbers - for (unsigned i = 0; i < sz; i++) - data[i] = g() % max; - for (unsigned k = 0; k < num_tries; k ++) { - shuffle(p.size(), p.c_ptr(), g); - // std::cout << "p: "; display(std::cout, p.begin(), p.end()); std::cout << "\n"; - // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; - apply_permutation_copy(sz, data.c_ptr(), p.c_ptr(), new_data.c_ptr()); - apply_permutation(sz, data.c_ptr(), p.c_ptr()); - // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; - for (unsigned i = 0; i < 0; i++) - ENSURE(data[i] == new_data[i]); - } -#endif +void test_check_invariant() { + permutation p(4); + SASSERT(p.check_invariant()); + p.swap(0, 2); + SASSERT(p.check_invariant()); + p.move_after(1, 3); + SASSERT(p.check_invariant()); +} + +void test_display() { + permutation p(4); + std::ostringstream out; + p.display(out); + SASSERT(out.str() == "0:0 1:1 2:2 3:3"); } void tst_permutation() { - tst1(10, 1000, 5); - tst1(10, 1000, 1000); - tst1(10, 1000, UINT_MAX); - tst1(100, 1000, 33); - tst1(100, 1000, 1000); - tst1(100, 1000, UINT_MAX); - tst1(1000, 1000, 121); - tst1(1000, 1000, 1000); - tst1(1000, 1000, UINT_MAX); - tst1(33, 1000, 121); - tst1(33, 1000, 1000); - tst1(33, 1000, UINT_MAX); - tst1(121, 1000, 121); - tst1(121, 1000, 1000); - tst1(121, 1000, UINT_MAX); - for (unsigned i = 0; i < 1000; i++) { - tst1(1000, 2, 333); - tst1(1000, 2, 10000); - tst1(1000, 2, UINT_MAX); - } - random_gen g; - for (unsigned i = 0; i < 100000; i++) { - unsigned sz = (g() % 131) + 1; - tst1(sz, 1, sz*2); - tst1(sz, 1, UINT_MAX); - tst1(sz, 1, sz/2 + 1); - } + test_constructor(); + test_reset(); + test_swap(); + test_move_after(); + // test_apply_permutation(); + // test_apply_permutation_core(); + test_check_invariant(); + test_display(); + + std::cout << "All tests passed!" << std::endl; } diff --git a/src/util/permutation.h b/src/util/permutation.h index dfc52b370ac..0d4399f7e17 100644 --- a/src/util/permutation.h +++ b/src/util/permutation.h @@ -38,6 +38,8 @@ class permutation { bool check_invariant() const; }; +void swap(unsigned i, unsigned j) noexcept; + inline std::ostream & operator<<(std::ostream & out, permutation const & p) { p.display(out); return out; From fe594618e6c7fddb33d0c2062d667990ba9bba5c Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:56:54 -0700 Subject: [PATCH 123/414] fix dlist tests (#7323) --- src/test/dlist.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index 1bf3f17ef6b..fe72c917afe 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -106,21 +106,6 @@ void test_insert_before() { std::cout << "test_insert_before passed." << std::endl; } -// Test the remove_from() method -void test_remove_from() { - TestNode* list = nullptr; - TestNode node1(1); - TestNode node2(2); - TestNode::push_to_front(list, &node1); - TestNode::push_to_front(list, &node2); - TestNode::remove_from(list, &node1); - assert(list == &node2); - assert(node2.next() == &node2); - assert(node2.prev() == &node2); - assert(node1.next() == &node1); - assert(node1.prev() == &node1); - std::cout << "test_remove_from passed." << std::endl; -} // Test the push_to_front() method void test_push_to_front() { @@ -177,12 +162,9 @@ void tst_dlist() { test_pop(); test_insert_after(); test_insert_before(); -#if 0 - test_remove_from(); test_push_to_front(); test_detach(); test_invariant(); test_contains(); -#endif std::cout << "All tests passed." << std::endl; } From 23e7dc03563169b62719c3d23bd1de32647b6784 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Aug 2024 11:35:06 -0700 Subject: [PATCH 124/414] assert -> SASSERT Signed-off-by: Nikolaj Bjorner --- src/test/dlist.cpp | 85 +++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index fe72c917afe..dd1b6609d97 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -30,28 +30,28 @@ class TestNode : public dll_base { // Test the prev() method void test_prev() { TestNode node(1); - assert(node.prev() == &node); + SASSERT(node.prev() == &node); std::cout << "test_prev passed." << std::endl; } // Test the next() method void test_next() { TestNode node(1); - assert(node.next() == &node); + SASSERT(node.next() == &node); std::cout << "test_next passed." << std::endl; } // Test the const prev() method void test_const_prev() { const TestNode node(1); - assert(node.prev() == &node); + SASSERT(node.prev() == &node); std::cout << "test_const_prev passed." << std::endl; } // Test the const next() method void test_const_next() { const TestNode node(1); - assert(node.next() == &node); + SASSERT(node.next() == &node); std::cout << "test_const_next passed." << std::endl; } @@ -59,9 +59,9 @@ void test_const_next() { void test_init() { TestNode node(1); node.init(&node); - assert(node.next() == &node); - assert(node.prev() == &node); - assert(node.invariant()); + SASSERT(node.next() == &node); + SASSERT(node.prev() == &node); + SASSERT(node.invariant()); std::cout << "test_init passed." << std::endl; } @@ -71,10 +71,10 @@ void test_pop() { TestNode node1(1); TestNode::push_to_front(list, &node1); TestNode* popped = TestNode::pop(list); - assert(popped == &node1); - assert(list == nullptr); - assert(popped->next() == popped); - assert(popped->prev() == popped); + SASSERT(popped == &node1); + SASSERT(list == nullptr); + SASSERT(popped->next() == popped); + SASSERT(popped->prev() == popped); std::cout << "test_pop passed." << std::endl; } @@ -83,12 +83,12 @@ void test_insert_after() { TestNode node1(1); TestNode node2(2); node1.insert_after(&node2); - assert(node1.next() == &node2); - assert(node2.prev() == &node1); - assert(node1.prev() == &node2); - assert(node2.next() == &node1); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.next() == &node2); + SASSERT(node2.prev() == &node1); + SASSERT(node1.prev() == &node2); + SASSERT(node2.next() == &node1); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_insert_after passed." << std::endl; } @@ -97,24 +97,39 @@ void test_insert_before() { TestNode node1(1); TestNode node2(2); node1.insert_before(&node2); - assert(node1.prev() == &node2); - assert(node2.next() == &node1); - assert(node1.next() == &node2); - assert(node2.prev() == &node1); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.prev() == &node2); + SASSERT(node2.next() == &node1); + SASSERT(node1.next() == &node2); + SASSERT(node2.prev() == &node1); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_insert_before passed." << std::endl; } +// Test the remove_from() method +void test_remove_from() { + TestNode* list = nullptr; + TestNode node1(1); + TestNode node2(2); + TestNode::push_to_front(list, &node1); + TestNode::push_to_front(list, &node2); + TestNode::remove_from(list, &node1); + SASSERT(list == &node2); + SASSERT(node2.next() == &node2); + SASSERT(node2.prev() == &node2); + SASSERT(node1.next() == &node1); + SASSERT(node1.prev() == &node1); + std::cout << "test_remove_from passed." << std::endl; +} // Test the push_to_front() method void test_push_to_front() { TestNode* list = nullptr; TestNode node1(1); TestNode::push_to_front(list, &node1); - assert(list == &node1); - assert(node1.next() == &node1); - assert(node1.prev() == &node1); + SASSERT(list == &node1); + SASSERT(node1.next() == &node1); + SASSERT(node1.prev() == &node1); std::cout << "test_push_to_front passed." << std::endl; } @@ -122,20 +137,20 @@ void test_push_to_front() { void test_detach() { TestNode node(1); TestNode::detach(&node); - assert(node.next() == &node); - assert(node.prev() == &node); - assert(node.invariant()); + SASSERT(node.next() == &node); + SASSERT(node.prev() == &node); + SASSERT(node.invariant()); std::cout << "test_detach passed." << std::endl; } // Test the invariant() method void test_invariant() { TestNode node1(1); - assert(node1.invariant()); + SASSERT(node1.invariant()); TestNode node2(2); node1.insert_after(&node2); - assert(node1.invariant()); - assert(node2.invariant()); + SASSERT(node1.invariant()); + SASSERT(node2.invariant()); std::cout << "test_invariant passed." << std::endl; } @@ -146,10 +161,10 @@ void test_contains() { TestNode node2(2); TestNode::push_to_front(list, &node1); TestNode::push_to_front(list, &node2); - assert(TestNode::contains(list, &node1)); - assert(TestNode::contains(list, &node2)); + SASSERT(TestNode::contains(list, &node1)); + SASSERT(TestNode::contains(list, &node2)); TestNode node3(3); - assert(!TestNode::contains(list, &node3)); + SASSERT(!TestNode::contains(list, &node3)); std::cout << "test_contains passed." << std::endl; } From ea9fa17f8634f6f91d8d033dd92244bd992e00be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Aug 2024 11:38:03 -0700 Subject: [PATCH 125/414] add static Signed-off-by: Nikolaj Bjorner --- src/test/dlist.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index dd1b6609d97..bf404631b5f 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -35,28 +35,28 @@ void test_prev() { } // Test the next() method -void test_next() { +static void test_next() { TestNode node(1); SASSERT(node.next() == &node); std::cout << "test_next passed." << std::endl; } // Test the const prev() method -void test_const_prev() { +static void test_const_prev() { const TestNode node(1); SASSERT(node.prev() == &node); std::cout << "test_const_prev passed." << std::endl; } // Test the const next() method -void test_const_next() { +static void test_const_next() { const TestNode node(1); SASSERT(node.next() == &node); std::cout << "test_const_next passed." << std::endl; } // Test the init() method -void test_init() { +static void test_init() { TestNode node(1); node.init(&node); SASSERT(node.next() == &node); @@ -66,7 +66,7 @@ void test_init() { } // Test the pop() method -void test_pop() { +static void test_pop() { TestNode* list = nullptr; TestNode node1(1); TestNode::push_to_front(list, &node1); @@ -79,7 +79,7 @@ void test_pop() { } // Test the insert_after() method -void test_insert_after() { +static void test_insert_after() { TestNode node1(1); TestNode node2(2); node1.insert_after(&node2); @@ -93,7 +93,7 @@ void test_insert_after() { } // Test the insert_before() method -void test_insert_before() { +static void test_insert_before() { TestNode node1(1); TestNode node2(2); node1.insert_before(&node2); @@ -107,7 +107,7 @@ void test_insert_before() { } // Test the remove_from() method -void test_remove_from() { +static void test_remove_from() { TestNode* list = nullptr; TestNode node1(1); TestNode node2(2); @@ -123,7 +123,7 @@ void test_remove_from() { } // Test the push_to_front() method -void test_push_to_front() { +static void test_push_to_front() { TestNode* list = nullptr; TestNode node1(1); TestNode::push_to_front(list, &node1); @@ -134,7 +134,7 @@ void test_push_to_front() { } // Test the detach() method -void test_detach() { +static void test_detach() { TestNode node(1); TestNode::detach(&node); SASSERT(node.next() == &node); @@ -144,7 +144,7 @@ void test_detach() { } // Test the invariant() method -void test_invariant() { +static void test_invariant() { TestNode node1(1); SASSERT(node1.invariant()); TestNode node2(2); @@ -155,7 +155,7 @@ void test_invariant() { } // Test the contains() method -void test_contains() { +static void test_contains() { TestNode* list = nullptr; TestNode node1(1); TestNode node2(2); From fce4b36dad6582f3eb233e2f3b0002e64cba22c0 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:29:33 -0700 Subject: [PATCH 126/414] add apply_permutation tests (#7322) * add permutation unit tests * update test * update * Update permutation.cpp fix macos build * add apply_permutation tests * update test * Update permutation.cpp * fix permutation tests --------- Co-authored-by: Nikolaj Bjorner --- src/test/permutation.cpp | 84 +++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/src/test/permutation.cpp b/src/test/permutation.cpp index 31b83b9d183..aeaa5a2354e 100644 --- a/src/test/permutation.cpp +++ b/src/test/permutation.cpp @@ -2,10 +2,11 @@ #include #include "util/permutation.h" #include "util/util.h" +#include "util/debug.h" -void swap(unsigned m1, unsigned m2) noexcept { std::swap(m1, m2); } + void swap(unsigned m1, unsigned m2) noexcept { std::swap(m1, m2); } -void test_constructor() { +static void test_constructor() { permutation p(5); for (unsigned i = 0; i < 5; ++i) { SASSERT(p(i) == i); @@ -13,7 +14,7 @@ void test_constructor() { } } -void test_reset() { +static void test_reset() { permutation p(3); p.swap(0, 2); p.reset(3); @@ -23,7 +24,7 @@ void test_reset() { } } -void test_swap() { +static void test_swap() { permutation p(4); p.swap(1, 3); SASSERT(p(1) == 3); @@ -32,7 +33,7 @@ void test_swap() { SASSERT(p.inv(3) == 1); } -void test_move_after() { +static void test_move_after() { permutation p(5); p.move_after(1, 3); SASSERT(p(0) == 0); @@ -42,36 +43,36 @@ void test_move_after() { SASSERT(p(4) == 4); } -void test_apply_permutation() { - permutation p(4); - int data[] = {10, 20, 30, 40}; - unsigned perm[] = {2, 1, 0, 3}; - apply_permutation(4, data, perm); - std::cout << "000 " << data[0] << std::endl; - std::cout << "222 " << data[2] << std::endl; - - SASSERT(data[0] == 10); - SASSERT(data[1] == 20); - SASSERT(data[2] == 30); - SASSERT(data[3] == 40); +void apply_permutation_copy(unsigned sz, unsigned const * src, unsigned const * p, unsigned * target) { + for (unsigned i = 0; i < sz; i++) { + target[i] = src[p[i]]; + } } -void test_apply_permutation_core() -{ - permutation p(4); - int data[] = {10, 20, 30, 40}; - unsigned perm[] = {2, 1, 0, 3}; - apply_permutation_core(4, data, perm); - std::cout << "000 " << data[0] << std::endl; - std::cout << "222 " << data[2] << std::endl; - - SASSERT(data[0] == 10); - SASSERT(data[1] == 20); - SASSERT(data[2] == 30); - SASSERT(data[3] == 40); +static void test_apply_permutation(unsigned sz, unsigned num_tries, unsigned max = UINT_MAX) { + unsigned_vector data; + unsigned_vector p; + unsigned_vector new_data; + data.resize(sz); + p.resize(sz); + new_data.resize(sz); + random_gen g; + for (unsigned i = 0; i < sz; i++) + p[i] = i; + // fill data with random numbers + for (unsigned i = 0; i < sz; i++) + data[i] = g() % max; + for (unsigned k = 0; k < num_tries; k ++) { + shuffle(p.size(), p.data(), g); + apply_permutation_copy(sz, data.data(), p.data(), new_data.data()); + apply_permutation(sz, data.data(), p.data()); + for (unsigned i = 0; i < 0; i++) + ENSURE(data[i] == new_data[i]); + } } -void test_check_invariant() { + +static void test_check_invariant() { permutation p(4); SASSERT(p.check_invariant()); p.swap(0, 2); @@ -80,7 +81,7 @@ void test_check_invariant() { SASSERT(p.check_invariant()); } -void test_display() { +static void test_display() { permutation p(4); std::ostringstream out; p.display(out); @@ -92,10 +93,23 @@ void tst_permutation() { test_reset(); test_swap(); test_move_after(); - // test_apply_permutation(); - // test_apply_permutation_core(); test_check_invariant(); test_display(); - + test_apply_permutation(10, 1000, 5); + test_apply_permutation(10, 1000, 1000); + test_apply_permutation(10, 1000, UINT_MAX); + test_apply_permutation(100, 1000, 33); + test_apply_permutation(100, 1000, 1000); + test_apply_permutation(100, 1000, UINT_MAX); + test_apply_permutation(1000, 1000, 121); + test_apply_permutation(1000, 1000, 1000); + test_apply_permutation(1000, 1000, UINT_MAX); + test_apply_permutation(33, 1000, 121); + test_apply_permutation(33, 1000, 1000); + test_apply_permutation(33, 1000, UINT_MAX); + test_apply_permutation(121, 1000, 121); + test_apply_permutation(121, 1000, 1000); + test_apply_permutation(121, 1000, UINT_MAX); + std::cout << "All tests passed!" << std::endl; } From d2fc085b8cee03bf2bfc89ecdc90c1c3c743e12b Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:29:50 -0700 Subject: [PATCH 127/414] update heap unit tests (#7324) * new heap invariants * change ENSURE to SASSERT for unit test heap * change SASSERT to VERIFY * update heap tests * update * remove one invariant --- src/test/heap.cpp | 3 +-- src/util/heap.h | 13 +++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/test/heap.cpp b/src/test/heap.cpp index 6a5bc7b9ffe..25a509dc8cd 100644 --- a/src/test/heap.cpp +++ b/src/test/heap.cpp @@ -27,7 +27,7 @@ struct lt_proc { bool operator()(int v1, int v2) const { return v1 < v2; } }; //struct int_hash_proc { unsigned operator()(int v) const { std::cout << "hash " << v << "\n"; VERIFY(v >= 0); return v; }}; //typedef int_hashtable > int_set; typedef heap int_heap; -#define N 10000 +#define N 100 static random_gen heap_rand(1); @@ -146,4 +146,3 @@ void tst_heap() { tst2(); } } - diff --git a/src/util/heap.h b/src/util/heap.h index c080c6ebde9..71cafeeb895 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -55,6 +55,19 @@ class heap : private LT { } bool check_invariant_core(int idx) const { + + // Check that m_values starts with a dummy value at index 0. + if (m_values.empty() || m_values[0] != -1) { + return false; + } + + // // All indices in m_value2indices that are not used in m_values should be 0 + // for (int val = 0; val < static_cast(m_value2indices.size()); ++val) { + // if (std::find(m_values.begin(), m_values.end(), val) == m_values.end() && m_value2indices[val] != 0) { + // return false; // Unused indices should have a 0 value + // } + // } + if (idx < static_cast(m_values.size())) { SASSERT(m_value2indices[m_values[idx]] == idx); SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)])); From 7c30cbfe48f65f9aa27a954c7ad5845863a6ea94 Mon Sep 17 00:00:00 2001 From: LiviaSun <33578456+ChuyueSun@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:21:40 -0700 Subject: [PATCH 128/414] add scoped_vector invariants and unit tests (#7327) * add scoped vector unit test * fix dlist tests * add new scoped vector invariants * remove all loop invariants --- src/test/CMakeLists.txt | 1 + src/test/dlist.cpp | 2 - src/test/main.cpp | 1 + src/test/scoped_vector.cpp | 99 ++++++++++++++++++++++++++++++++++++++ src/util/scoped_vector.h | 44 +++++++++++++++-- 5 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/test/scoped_vector.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 8464cedf917..658647ea618 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -106,6 +106,7 @@ add_executable(test-z3 sat_lookahead.cpp sat_user_scope.cpp scoped_timer.cpp + scoped_vector.cpp simple_parser.cpp simplex.cpp simplifier.cpp diff --git a/src/test/dlist.cpp b/src/test/dlist.cpp index bf404631b5f..32a54f9ffc6 100644 --- a/src/test/dlist.cpp +++ b/src/test/dlist.cpp @@ -117,8 +117,6 @@ static void test_remove_from() { SASSERT(list == &node2); SASSERT(node2.next() == &node2); SASSERT(node2.prev() == &node2); - SASSERT(node1.next() == &node1); - SASSERT(node1.prev() == &node1); std::cout << "test_remove_from passed." << std::endl; } diff --git a/src/test/main.cpp b/src/test/main.cpp index 0d36795295c..f028d6ceb35 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -269,4 +269,5 @@ int main(int argc, char ** argv) { TST(euf_bv_plugin); TST(euf_arith_plugin); TST(sls_test); + TST(scoped_vector); } diff --git a/src/test/scoped_vector.cpp b/src/test/scoped_vector.cpp new file mode 100644 index 00000000000..05d98fcf103 --- /dev/null +++ b/src/test/scoped_vector.cpp @@ -0,0 +1,99 @@ +#include +#include "util/scoped_vector.h" + +void test_push_back_and_access() { + scoped_vector sv; + sv.push_back(10); + + sv.push_back(20); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 20); + + std::cout << "test_push_back_and_access passed." << std::endl; +} + +void test_scopes() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + sv.push_scope(); + sv.push_back(30); + sv.push_back(40); + + SASSERT(sv.size() == 4); + SASSERT(sv[2] == 30); + SASSERT(sv[3] == 40); + + sv.pop_scope(1); + + std::cout << "test_scopes passed." << std::endl; + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 20); + + std::cout << "test_scopes passed." << std::endl; +} + +void test_set() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + sv.set(0, 30); + sv.set(1, 40); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 30); + SASSERT(sv[1] == 40); + + sv.push_scope(); + sv.set(0, 50); + SASSERT(sv[0] == 50); + sv.pop_scope(1); + SASSERT(sv[0] == 30); + + std::cout << "test_set passed." << std::endl; +} + +void test_pop_back() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + + SASSERT(sv.size() == 2); + sv.pop_back(); + SASSERT(sv.size() == 1); + SASSERT(sv[0] == 10); + sv.pop_back(); + SASSERT(sv.size() == 0); + + std::cout << "test_pop_back passed." << std::endl; +} + +void test_erase_and_swap() { + scoped_vector sv; + sv.push_back(10); + sv.push_back(20); + sv.push_back(30); + + sv.erase_and_swap(1); + + SASSERT(sv.size() == 2); + SASSERT(sv[0] == 10); + SASSERT(sv[1] == 30); + + std::cout << "test_erase_and_swap passed." << std::endl; +} + +void tst_scoped_vector() { + test_push_back_and_access(); + test_scopes(); + test_set(); + test_pop_back(); + test_erase_and_swap(); + + std::cout << "All tests passed." << std::endl; +} diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index 2c6cfaa2199..7d0cec472b2 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -176,8 +176,46 @@ class scoped_vector { } bool invariant() const { - return - m_size <= m_elems.size() && - m_elems_start <= m_elems.size(); + + + if (!(m_size <= m_elems.size() && m_elems_start <= m_elems.size())) + return false; + + // Check that source and destination trails have the same length. + if (m_src.size() != m_dst.size()) + return false; + // The size of m_src, m_dst, and m_src_lim should be consistent with the scope stack. + if (m_src_lim.size() != m_sizes.size() || m_src.size() != m_dst.size()) + return false; + + // // m_elems_lim stores the past sizes of m_elems for each scope. Each element in m_elems_lim should be + // // within bounds and in non-decreasing order. + // for (unsigned i = 1; i < m_elems_lim.size(); ++i) { + // if (m_elems_lim[i - 1] > m_elems_lim[i]) return false; + // } + + + // // m_sizes tracks the size of the vector at each scope level. + // // Each element in m_sizes should be non-decreasing and within the size of m_elems. + // for (unsigned i = 1; i < m_sizes.size(); ++i) { + // if (m_sizes[i - 1] > m_sizes[i]) + // return false; + // } + + // // The m_src and m_dst vectors should have the same size and should contain valid indices. + // if (m_src.size() != m_dst.size()) return false; + // for (unsigned i = 0; i < m_src.size(); ++i) { + // if (m_src[i] >= m_index.size() || m_dst[i] >= m_elems.size()) return false; + // } + + + // // The size of m_src_lim should be less than or equal to the size of m_sizes and store valid indices. + // if (m_src_lim.size() > m_sizes.size()) return false; + // for (unsigned elem : m_src_lim) { + // if (elem > m_src.size()) return false; + // } + + return true; + } }; From 51fcb10b2ff0e4496a3c0c2ed7c32f0876c9ee49 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 2 Aug 2024 20:00:45 -0700 Subject: [PATCH 129/414] shave some overhead from fingerprint hash function #7281 --- src/smt/fingerprints.cpp | 28 ++++++++++++++++++++++++++-- src/smt/fingerprints.h | 8 +------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/smt/fingerprints.cpp b/src/smt/fingerprints.cpp index 447c2b2a16c..ac13999d047 100644 --- a/src/smt/fingerprints.cpp +++ b/src/smt/fingerprints.cpp @@ -63,6 +63,26 @@ namespace smt { fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def) { + + struct arg_data { + unsigned data_hash; + enode* const* args; + }; + struct khash { + unsigned operator()(arg_data const& d) const { + return d.data_hash; + } + }; + struct arghash { + unsigned operator()(arg_data const& d, unsigned i) const { + return d.args[i]->hash(); + } + }; + arg_data arg_data({ data_hash, args }); + khash kh; + arghash ah; + data_hash = get_composite_hash(arg_data, num_args, kh, ah); + fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return nullptr; @@ -107,8 +127,12 @@ namespace smt { unsigned new_lvl = lvl - num_scopes; unsigned old_size = m_scopes[new_lvl]; unsigned size = m_fingerprints.size(); - for (unsigned i = old_size; i < size; i++) - m_set.erase(m_fingerprints[i]); + if (old_size == 0 && size > 0) + m_set.reset(); + else { + for (unsigned i = old_size; i < size; i++) + m_set.erase(m_fingerprints[i]); + } m_fingerprints.shrink(old_size); m_defs.shrink(old_size); m_scopes.shrink(new_lvl); diff --git a/src/smt/fingerprints.h b/src/smt/fingerprints.h index b1904a4ee3b..a602f25cd65 100644 --- a/src/smt/fingerprints.h +++ b/src/smt/fingerprints.h @@ -48,15 +48,9 @@ namespace smt { class fingerprint_set { - struct fingerprint_khasher { - unsigned operator()(fingerprint const * f) const { return f->get_data_hash(); } - }; - struct fingerprint_chasher { - unsigned operator()(fingerprint const * f, unsigned idx) const { return f->get_arg(idx)->hash(); } - }; struct fingerprint_hash_proc { unsigned operator()(fingerprint const * f) const { - return get_composite_hash(const_cast(f), f->get_num_args()); + return f->get_data_hash(); } }; struct fingerprint_eq_proc { bool operator()(fingerprint const * f1, fingerprint const * f2) const; }; From d6040ee5ab07da9c84e30754f232a0f75f4c0a3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 3 Aug 2024 09:29:11 -0700 Subject: [PATCH 130/414] do not copy artifacts from CI pipeline Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b3bc0f22652..48dcf094a2e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,15 +66,6 @@ jobs: scriptPath: scripts/mk_unix_dist.py arguments: --nodotnet --nojava --arch=arm64 pythonInterpreter: $(python) - - task: CopyFiles@2 - inputs: - sourceFolder: dist - contents: '*.zip' - targetFolder: $(Build.ArtifactStagingDirectory) - - task: PublishPipelineArtifact@0 - inputs: - artifactName: 'ManyLinuxBuildArm64' - targetPath: $(Build.ArtifactStagingDirectory) - job: "Ubuntu20OCaml" displayName: "Ubuntu 20 with OCaml" From bc8fa67afc9bb37c4b646d4b99efc0b0ec7d23d7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 3 Aug 2024 09:37:14 -0700 Subject: [PATCH 131/414] #7255 #7328 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 62c7c8ddd10..a01a655f3f7 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1117,6 +1117,8 @@ namespace smt { */ bool context::is_diseq(enode * n1, enode * n2) const { SASSERT(n1->get_sort() == n2->get_sort()); + if (m.are_distinct(n1->get_expr(), n2->get_expr())) + return true; context * _this = const_cast(this); if (!m_is_diseq_tmp) { app * eq = m.mk_eq(n1->get_expr(), n2->get_expr()); From 52f8eb21fb995465f2deaf138ba75f74108e1fc3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 3 Aug 2024 09:39:19 -0700 Subject: [PATCH 132/414] #7255 #7328 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 a01a655f3f7..428460801ce 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1117,7 +1117,7 @@ namespace smt { */ bool context::is_diseq(enode * n1, enode * n2) const { SASSERT(n1->get_sort() == n2->get_sort()); - if (m.are_distinct(n1->get_expr(), n2->get_expr())) + if (m.are_distinct(n1->get_root()->get_expr(), n2->get_root()->get_expr())) return true; context * _this = const_cast(this); if (!m_is_diseq_tmp) { From 26b8d634a318b3aa0bacbcbaadbf8e5234d21034 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 3 Aug 2024 11:34:28 -0700 Subject: [PATCH 133/414] add max conflict throttle to SAT based QFNIA tactic #7329 Signed-off-by: Nikolaj Bjorner --- src/tactic/smtlogics/qfnia_tactic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index 0dad56a196e..25dc34d9c5b 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -45,6 +45,7 @@ static tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { params_ref mem_p = p; mem_p.set_uint("max_memory", 100); + mem_p.set_uint("max_conflicts", 500); tactic * r = using_params(and_then(mk_simplify_tactic(m), @@ -52,7 +53,7 @@ static tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), using_params(mk_bit_blaster_tactic(m), mem_p), - mk_sat_tactic(m)), + mk_sat_tactic(m, mem_p)), p); return r; } From 3e518b9e8b387f1bb664a619e0aec45f4b62dfba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 6 Aug 2024 03:54:25 -0700 Subject: [PATCH 134/414] fix #7331 --- src/ast/ast_smt_pp.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 0da4f1c129c..68936ac49d9 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -31,6 +31,7 @@ Revision History: #include "ast/datatype_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/fpa_decl_plugin.h" +#include "ast/recfun_decl_plugin.h" #include "ast/for_each_ast.h" #include "ast/decl_collector.h" #include "math/polynomial/algebraic_numbers.h" @@ -1000,6 +1001,18 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { } } + vector> recfuns; + recfun::util u(m); + for (auto f : decls.get_rec_decls()) + recfuns.push_back({f, u.get_def(f).get_rhs()}); + + + if (!recfuns.empty()) { + smt2_pp_environment_dbg env(m); + ast_smt2_pp_recdefs(strm, recfuns, env); + } + + #endif for (expr* a : m_assumptions) { From 6ce0fcd3eff26caedac3c27091629319d8c101c9 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 2 Aug 2024 06:28:11 -1000 Subject: [PATCH 135/414] port sample cell projection Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_explain.cpp | 301 +++++++++++++++++++++++++++++++++--- src/nlsat/nlsat_explain.h | 3 +- src/nlsat/nlsat_params.pyg | 3 +- src/nlsat/nlsat_solver.cpp | 2 +- 4 files changed, 286 insertions(+), 23 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 618394ac1d6..c7d07223291 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -45,6 +45,8 @@ namespace nlsat { bool m_minimize_cores; bool m_factor; bool m_signed_project; + bool m_cell_sample; + struct todo_set { polynomial::cache & m_cache; @@ -130,7 +132,7 @@ namespace nlsat { evaluator & m_evaluator; imp(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, - evaluator & ev): + evaluator & ev, bool is_sample): m_solver(s), m_assignment(x2v), m_atoms(atoms), @@ -148,6 +150,9 @@ namespace nlsat { m_core1(s), m_core2(s), m_result(nullptr), + + m_cell_sample(is_sample), + m_evaluator(ev) { m_simplify_cores = false; m_full_dimensional = false; @@ -646,6 +651,51 @@ namespace nlsat { m_todo.insert(p); } } + + void add_sample_coeff(polynomial_ref_vector &ps, var x){ + polynomial_ref p(m_pm); + polynomial_ref lc(m_pm); + unsigned sz = ps.size(); + for (unsigned i = 0; i < sz; i++){ + p = ps.get(i); + unsigned k = degree(p, x); + SASSERT(k > 0); + TRACE("nlsat_explain", tout << "add_lc, x: "; display_var(tout, x); tout << "\nk: " << k << "\n"; display(tout, p); tout << "\n";); + for(; k > 0; k--){ + lc = m_pm.coeff(p, x, k); + add_factors(lc); + if (m_pm.nonzero_const_coeff(p, x, k)){ + TRACE("nlsat_explain", tout << "constant coefficient, skipping...\n";); + break; + } + } + SASSERT(sign(lc) != 0); + SASSERT(!is_const(lc)); + } + } + + void psc_resultant_sample(polynomial_ref_vector &ps, var x, polynomial_ref_vector & samples){ + polynomial_ref p(m_pm); + polynomial_ref q(m_pm); + + + // polynomial_ref_vector samples(m_pm); + // samples.reset(); + // sample_poly(ps, x, samples); + + SASSERT(samples.size() <= 2); + + for (unsigned i = 0; i < ps.size(); i++){ + p = ps.get(i); + for (unsigned j = 0; j < samples.size(); j++){ + q = samples.get(j); + if (!m_pm.eq(p, q)) { + psc(p, q, x); + } + } + } + } + /** \brief Add leading coefficients of the polynomials in ps. @@ -973,6 +1023,116 @@ namespace nlsat { add_simple_assumption(k, p, lsign); } + void cac_add_cell_lits(polynomial_ref_vector & ps, var y, polynomial_ref_vector & res) { + res.reset(); + SASSERT(m_assignment.is_assigned(y)); + bool lower_inf = true; + bool upper_inf = true; + scoped_anum_vector & roots = m_roots_tmp; + scoped_anum lower(m_am); + scoped_anum upper(m_am); + anum const & y_val = m_assignment.value(y); + TRACE("nlsat_explain", tout << "adding literals for "; display_var(tout, y); tout << " -> "; + m_am.display_decimal(tout, y_val); tout << "\n";); + polynomial_ref p_lower(m_pm); + unsigned i_lower = UINT_MAX; + polynomial_ref p_upper(m_pm); + unsigned i_upper = UINT_MAX; + polynomial_ref p(m_pm); + unsigned sz = ps.size(); + for (unsigned k = 0; k < sz; k++) { + p = ps.get(k); + if (max_var(p) != y) + continue; + roots.reset(); + // Variable y is assigned in m_assignment. We must temporarily unassign it. + // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. + m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); + unsigned num_roots = roots.size(); +//#linxi begin add_cell_lits faster + bool all_lt = true; + for (unsigned i = 0; i < num_roots; i++) { + int s = m_am.compare(y_val, roots[i]); + TRACE("nlsat_explain", + m_am.display_decimal(tout << "comparing root: ", roots[i]); tout << "\n"; + m_am.display_decimal(tout << "with y_val:", y_val); + tout << "\nsign " << s << "\n"; + tout << "poly: " << p << "\n"; + ); + if (s == 0) { + // y_val == roots[i] + // add literal + // ! (y = root_i(p)) + add_root_literal(atom::ROOT_EQ, y, i+1, p); + res.push_back(p); + return; + } + else if (s < 0) { + // y_val < roots[i] + if (i > 0) { + // y_val > roots[j] + int j = i - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + p_lower = p; + i_lower = j + 1; + } + } + if (upper_inf || m_am.lt(roots[i], upper)) { + upper_inf = false; + m_am.set(upper, roots[i]); + p_upper = p; + i_upper = i + 1; + } + all_lt = false; + break; + } + // else if (s < 0) { + // // y_val < roots[i] + + // // check if roots[i] is a better upper bound + // if (upper_inf || m_am.lt(roots[i], upper)) { + // upper_inf = false; + // m_am.set(upper, roots[i]); + // p_upper = p; + // i_upper = i+1; + // } + // } + // else if (s > 0) { + // // roots[i] < y_val + + // // check if roots[i] is a better lower bound + // if (lower_inf || m_am.lt(lower, roots[i])) { + // lower_inf = false; + // m_am.set(lower, roots[i]); + // p_lower = p; + // i_lower = i+1; + // } + // } + } + if (all_lt && num_roots > 0) { + int j = num_roots - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + p_lower = p; + i_lower = j + 1; + } + } +//#linxi end add_cell_lits faster + } + + if (!lower_inf) { + res.push_back(p_lower); + add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, i_lower, p_lower); + } + if (!upper_inf) { + res.push_back(p_upper); + add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, i_upper, p_upper); + } + } + /** Add one or two literals that specify in which cell of variable y the current interpretation is. One literal is added for the cases: @@ -1016,6 +1176,8 @@ namespace nlsat { // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); unsigned num_roots = roots.size(); +//#linxi begin add_cell_lits faster + bool all_lt = true; for (unsigned i = 0; i < num_roots; i++) { int s = m_am.compare(y_val, roots[i]); TRACE("nlsat_explain", @@ -1033,27 +1195,58 @@ namespace nlsat { } else if (s < 0) { // y_val < roots[i] - - // check if roots[i] is a better upper bound + if (i > 0) { + // y_val > roots[j] + int j = i - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + p_lower = p; + i_lower = j + 1; + } + } if (upper_inf || m_am.lt(roots[i], upper)) { upper_inf = false; m_am.set(upper, roots[i]); p_upper = p; - i_upper = i+1; + i_upper = i + 1; } + all_lt = false; + break; } - else if (s > 0) { - // roots[i] < y_val - - // check if roots[i] is a better lower bound - if (lower_inf || m_am.lt(lower, roots[i])) { - lower_inf = false; - m_am.set(lower, roots[i]); - p_lower = p; - i_lower = i+1; - } + // else if (s < 0) { + // // y_val < roots[i] + + // // check if roots[i] is a better upper bound + // if (upper_inf || m_am.lt(roots[i], upper)) { + // upper_inf = false; + // m_am.set(upper, roots[i]); + // p_upper = p; + // i_upper = i+1; + // } + // } + // else if (s > 0) { + // // roots[i] < y_val + + // // check if roots[i] is a better lower bound + // if (lower_inf || m_am.lt(lower, roots[i])) { + // lower_inf = false; + // m_am.set(lower, roots[i]); + // p_lower = p; + // i_lower = i+1; + // } + // } + } + if (all_lt && num_roots > 0) { + int j = num_roots - 1; + if (lower_inf || m_am.lt(lower, roots[j])) { + lower_inf = false; + m_am.set(lower, roots[j]); + p_lower = p; + i_lower = j + 1; } } +//#linxi end add_cell_lits faster } if (!lower_inf) { @@ -1064,6 +1257,7 @@ namespace nlsat { } } + /** \brief Return true if all polynomials in ps are univariate in x. */ @@ -1082,7 +1276,8 @@ namespace nlsat { /** \brief Apply model-based projection operation defined in our paper. */ - void project(polynomial_ref_vector & ps, var max_x) { + + void project_original(polynomial_ref_vector & ps, var max_x) { if (ps.empty()) return; m_todo.reset(); @@ -1094,12 +1289,12 @@ namespace nlsat { if (x < max_x) add_cell_lits(ps, x); while (true) { - TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; - display(tout, ps); tout << "\n";); if (all_univ(ps, x) && m_todo.empty()) { m_todo.reset(); break; } + TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; + display(tout, ps); tout << "\n";); add_lc(ps, x); psc_discriminant(ps, x); psc_resultant(ps, x); @@ -1109,6 +1304,72 @@ namespace nlsat { add_cell_lits(ps, x); } } + void project_cdcac(polynomial_ref_vector & ps, var max_x) { + // whz + bool first = true; + + + if (ps.empty()) + return; + m_todo.reset(); + for (poly* p : ps) { + m_todo.insert(p); + } + var x = m_todo.remove_max_polys(ps); + // Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore + + polynomial_ref_vector samples(m_pm); + + + if (x < max_x){ + cac_add_cell_lits(ps, x, samples); + } + + while (true) { + if (all_univ(ps, x) && m_todo.empty()) { + m_todo.reset(); + break; + } + TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; + display(tout, ps); tout << "\n";); + + + /** + * Sample Projection + * Reference: + * Haokun Li and Bican Xia. + * "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection" + * https://arxiv.org/abs/2003.00409 + */ + + if (first) { + add_lc(ps, x); + psc_discriminant(ps, x); + psc_resultant(ps, x); + first = false; + } + + else { + add_lc(ps, x); + // add_sample_coeff(ps, x); + psc_discriminant(ps, x); + psc_resultant_sample(ps, x, samples); + } + + if (m_todo.empty()) + break; + x = m_todo.remove_max_polys(ps); + cac_add_cell_lits(ps, x, samples); + } + } + void project(polynomial_ref_vector & ps, var max_x) { + if (m_cell_sample) { + project_cdcac(ps, max_x); + } + else { + project_original(ps, max_x); + } + } bool check_already_added() const { for (bool b : m_already_added_literal) { @@ -1474,7 +1735,7 @@ namespace nlsat { var max_x = max_var(m_ps); TRACE("nlsat_explain", tout << "polynomials in the conflict:\n"; display(tout, m_ps); tout << "\n";); elim_vanishing(m_ps); - TRACE("nlsat_explain", tout << "elim vanishing x" << max_x << "\n"; display(tout, m_ps); tout << "\n";); + TRACE("nlsat_explain", tout << "elim vanishing\n"; display(tout, m_ps); tout << "\n";); project(m_ps, max_x); TRACE("nlsat_explain", tout << "after projection\n"; display(tout, m_ps); tout << "\n";); } @@ -1936,8 +2197,8 @@ namespace nlsat { }; explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, - atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev) { - m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev); + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool is_sample) { + m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev, is_sample); } explain::~explain() { diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index 20322a680aa..a19fe3982a3 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -34,7 +34,8 @@ namespace nlsat { imp * m_imp; public: explain(solver & s, assignment const & x2v, polynomial::cache & u, - atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev); + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool is_sample); + ~explain(); void reset(); diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index cac33fe8717..dec3fac940a 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -14,6 +14,7 @@ def_module_params('nlsat', ('shuffle_vars', BOOL, False, "use a random variable order."), ('inline_vars', BOOL, False, "inline variables that can be isolated from equations (not supported in incremental mode)"), ('seed', UINT, 0, "random seed."), - ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") + ('factor', BOOL, True, "factor polynomials produced during conflict resolution."), + ('cell_sample', BOOL, True, "cell sample projection"), )) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index bb169a05e48..3d38d7a5edd 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -253,7 +253,7 @@ namespace nlsat { m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), m_display_var(m_perm), m_display_assumption(nullptr), - m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator), + m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()), m_scope_lvl(0), m_lemma(s), m_lazy_clause(s), From a09e412cf033e5c5dca7330314084fb85388ba7f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 6 Aug 2024 08:16:31 -1000 Subject: [PATCH 136/414] cleaning up Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_explain.cpp | 58 +++---------------------------------- src/nlsat/nlsat_explain.h | 2 +- 2 files changed, 5 insertions(+), 55 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index c7d07223291..6efbf4e7f6f 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -45,7 +45,7 @@ namespace nlsat { bool m_minimize_cores; bool m_factor; bool m_signed_project; - bool m_cell_sample; + bool m_cell_sample; struct todo_set { @@ -677,12 +677,6 @@ namespace nlsat { void psc_resultant_sample(polynomial_ref_vector &ps, var x, polynomial_ref_vector & samples){ polynomial_ref p(m_pm); polynomial_ref q(m_pm); - - - // polynomial_ref_vector samples(m_pm); - // samples.reset(); - // sample_poly(ps, x, samples); - SASSERT(samples.size() <= 2); for (unsigned i = 0; i < ps.size(); i++){ @@ -1088,28 +1082,6 @@ namespace nlsat { all_lt = false; break; } - // else if (s < 0) { - // // y_val < roots[i] - - // // check if roots[i] is a better upper bound - // if (upper_inf || m_am.lt(roots[i], upper)) { - // upper_inf = false; - // m_am.set(upper, roots[i]); - // p_upper = p; - // i_upper = i+1; - // } - // } - // else if (s > 0) { - // // roots[i] < y_val - - // // check if roots[i] is a better lower bound - // if (lower_inf || m_am.lt(lower, roots[i])) { - // lower_inf = false; - // m_am.set(lower, roots[i]); - // p_lower = p; - // i_lower = i+1; - // } - // } } if (all_lt && num_roots > 0) { int j = num_roots - 1; @@ -1214,28 +1186,6 @@ namespace nlsat { all_lt = false; break; } - // else if (s < 0) { - // // y_val < roots[i] - - // // check if roots[i] is a better upper bound - // if (upper_inf || m_am.lt(roots[i], upper)) { - // upper_inf = false; - // m_am.set(upper, roots[i]); - // p_upper = p; - // i_upper = i+1; - // } - // } - // else if (s > 0) { - // // roots[i] < y_val - - // // check if roots[i] is a better lower bound - // if (lower_inf || m_am.lt(lower, roots[i])) { - // lower_inf = false; - // m_am.set(lower, roots[i]); - // p_lower = p; - // i_lower = i+1; - // } - // } } if (all_lt && num_roots > 0) { int j = num_roots - 1; @@ -1305,7 +1255,6 @@ namespace nlsat { } } void project_cdcac(polynomial_ref_vector & ps, var max_x) { - // whz bool first = true; @@ -1362,6 +1311,7 @@ namespace nlsat { cac_add_cell_lits(ps, x, samples); } } + void project(polynomial_ref_vector & ps, var max_x) { if (m_cell_sample) { project_cdcac(ps, max_x); @@ -2197,8 +2147,8 @@ namespace nlsat { }; explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, - atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool is_sample) { - m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev, is_sample); + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool use_cell_sample) { + m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev, use_cell_sample); } explain::~explain() { diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index a19fe3982a3..2fdb76b1150 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -34,7 +34,7 @@ namespace nlsat { imp * m_imp; public: explain(solver & s, assignment const & x2v, polynomial::cache & u, - atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool is_sample); + atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool use_cell_sample_proj); ~explain(); From 0306eff6928cbb2bdb9983a80e105abd35d78a85 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 6 Aug 2024 18:25:31 -1000 Subject: [PATCH 137/414] port look for 0 witness Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_interval_set.cpp | 70 ++++++++++++++++++++++---------- src/nlsat/nlsat_interval_set.h | 5 ++- src/nlsat/nlsat_params.pyg | 6 ++- src/nlsat/nlsat_solver.cpp | 4 +- 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/nlsat/nlsat_interval_set.cpp b/src/nlsat/nlsat_interval_set.cpp index f928fd5b6cc..0c15d580b73 100644 --- a/src/nlsat/nlsat_interval_set.cpp +++ b/src/nlsat/nlsat_interval_set.cpp @@ -684,33 +684,50 @@ namespace nlsat { return new_set; } - void interval_set_manager::peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize) { + int compare_interval_with_zero(const interval &now, const scoped_anum &zero, anum_manager & m) { + if (!now.m_upper_inf) { + int sgn = m.compare(now.m_upper, zero); + if (sgn < 0) + return -1; + if (sgn == 0 && now.m_upper_open) + return -1; + } + if (!now.m_lower_inf) { + int sgn = m.compare(now.m_lower, zero); + if (sgn > 0) + return 1; + if (sgn == 0 && now.m_lower_open) + return 1; + } + return 0; + } + + + void interval_set_manager::pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize, bool look_for_zero) { SASSERT(!is_full(s)); if (s == nullptr) { - if (randomize) { - int num = m_rand() % 2 == 0 ? 1 : -1; -#define MAX_RANDOM_DEN_K 4 - int den_k = (m_rand() % MAX_RANDOM_DEN_K); - int den = is_int ? 1 : (1 << den_k); - scoped_mpq _w(m_am.qm()); - m_am.qm().set(_w, num, den); - m_am.set(w, _w); - } - else { - m_am.set(w, 0); - } + m_am.set(w, 0); return; } - unsigned n = 0; - unsigned num = num_intervals(s); - if (!s->m_intervals[0].m_lower_inf) { - // lower is not -oo - n++; - m_am.int_lt(s->m_intervals[0].m_lower, w); - if (!randomize) - return; + if (look_for_zero) { + scoped_anum zero(m_am); + m_am.set(zero, 0); + bool available = true; + for (unsigned i = 0; i < num; ++i) { + int sgn = compare_interval_with_zero(s->m_intervals[i], zero, m_am); + if (sgn == 0) { + available = false; + break; + } + if (sgn > 0) + break; + } + if (available) { + m_am.set(w, 0); + return ; + } } if (!s->m_intervals[num-1].m_upper_inf) { // upper is not oo @@ -720,6 +737,16 @@ namespace nlsat { if (!randomize) return; } + + if (!s->m_intervals[0].m_lower_inf) { + // lower is not -oo + n++; + m_am.int_lt(s->m_intervals[0].m_lower, w); + if (!randomize) + return; + } + + // Try to find a gap that is not an unit. for (unsigned i = 1; i < num; i++) { @@ -770,5 +797,4 @@ namespace nlsat { out << "*"; return out; } - }; diff --git a/src/nlsat/nlsat_interval_set.h b/src/nlsat/nlsat_interval_set.h index f1055118f5d..33b741ebd71 100644 --- a/src/nlsat/nlsat_interval_set.h +++ b/src/nlsat/nlsat_interval_set.h @@ -21,7 +21,7 @@ Revision History: #include "nlsat/nlsat_types.h" namespace nlsat { - + class interval_set; class interval_set_manager { @@ -29,6 +29,7 @@ namespace nlsat { small_object_allocator & m_allocator; svector m_already_visited; random_gen m_rand; + void del(interval_set * s); public: interval_set_manager(anum_manager & m, small_object_allocator & a); @@ -107,7 +108,7 @@ namespace nlsat { \pre !is_full(s) */ - void peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize); + void pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize, bool look_for_zero); }; typedef obj_ref interval_set_ref; diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index dec3fac940a..7a9aa864c9f 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -3,6 +3,9 @@ def_module_params('nlsat', description='nonlinear solver', export=True, params=(max_memory_param(), + ('cell_sample', BOOL, True, "cell sample projection"), + ('look_for_zero_witness', BOOL, True, "look for 0 witness"), + ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), @@ -14,7 +17,6 @@ def_module_params('nlsat', ('shuffle_vars', BOOL, False, "use a random variable order."), ('inline_vars', BOOL, False, "inline variables that can be isolated from equations (not supported in incremental mode)"), ('seed', UINT, 0, "random seed."), - ('factor', BOOL, True, "factor polynomials produced during conflict resolution."), - ('cell_sample', BOOL, True, "cell sample projection"), + ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") )) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 3d38d7a5edd..1ce6435b91a 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -218,6 +218,7 @@ namespace nlsat { bool m_inline_vars; bool m_log_lemmas; bool m_check_lemmas; + bool m_look_for_0_witness; unsigned m_max_conflicts; unsigned m_lemma_count; @@ -289,6 +290,7 @@ namespace nlsat { m_inline_vars = p.inline_vars(); m_log_lemmas = p.log_lemmas(); m_check_lemmas = p.check_lemmas(); + m_look_for_0_witness = p.look_for_zero_witness(); m_ism.set_seed(m_random_seed); m_explain.set_simplify_cores(m_simplify_cores); m_explain.set_minimize_cores(min_cores); @@ -1518,7 +1520,7 @@ namespace nlsat { void select_witness() { scoped_anum w(m_am); SASSERT(!m_ism.is_full(m_infeasible[m_xk])); - m_ism.peek_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); + m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize, m_look_for_0_witness); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); From 839594ac12a05aa2038b34270f0cffc00460e286 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 7 Aug 2024 09:01:49 -1000 Subject: [PATCH 138/414] remove option look_for_0_witness Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_interval_set.cpp | 34 ++++++++++++++++---------------- src/nlsat/nlsat_interval_set.h | 2 +- src/nlsat/nlsat_params.pyg | 2 -- src/nlsat/nlsat_solver.cpp | 4 +--- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/nlsat/nlsat_interval_set.cpp b/src/nlsat/nlsat_interval_set.cpp index 0c15d580b73..76a310f00f9 100644 --- a/src/nlsat/nlsat_interval_set.cpp +++ b/src/nlsat/nlsat_interval_set.cpp @@ -703,7 +703,7 @@ namespace nlsat { } - void interval_set_manager::pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize, bool look_for_zero) { + void interval_set_manager::pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize) { SASSERT(!is_full(s)); if (s == nullptr) { m_am.set(w, 0); @@ -711,24 +711,24 @@ namespace nlsat { } unsigned n = 0; unsigned num = num_intervals(s); - if (look_for_zero) { - scoped_anum zero(m_am); - m_am.set(zero, 0); - bool available = true; - for (unsigned i = 0; i < num; ++i) { - int sgn = compare_interval_with_zero(s->m_intervals[i], zero, m_am); - if (sgn == 0) { - available = false; - break; - } - if (sgn > 0) - break; - } - if (available) { - m_am.set(w, 0); - return ; + // try to assign w to zero first to simplify the polynomials + scoped_anum zero(m_am); + m_am.set(zero, 0); + bool available = true; + for (unsigned i = 0; i < num; ++i) { + int sgn = compare_interval_with_zero(s->m_intervals[i], zero, m_am); + if (sgn == 0) { + available = false; + break; } + if (sgn > 0) + break; + } + if (available) { + m_am.set(w, 0); + return ; } + // cannot assign w to zero if (!s->m_intervals[num-1].m_upper_inf) { // upper is not oo n++; diff --git a/src/nlsat/nlsat_interval_set.h b/src/nlsat/nlsat_interval_set.h index 33b741ebd71..2e74f33c623 100644 --- a/src/nlsat/nlsat_interval_set.h +++ b/src/nlsat/nlsat_interval_set.h @@ -108,7 +108,7 @@ namespace nlsat { \pre !is_full(s) */ - void pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize, bool look_for_zero); + void pick_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize); }; typedef obj_ref interval_set_ref; diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 7a9aa864c9f..62101e777b2 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -4,8 +4,6 @@ def_module_params('nlsat', export=True, params=(max_memory_param(), ('cell_sample', BOOL, True, "cell sample projection"), - ('look_for_zero_witness', BOOL, True, "look for 0 witness"), - ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 1ce6435b91a..9f43b7a787a 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -218,7 +218,6 @@ namespace nlsat { bool m_inline_vars; bool m_log_lemmas; bool m_check_lemmas; - bool m_look_for_0_witness; unsigned m_max_conflicts; unsigned m_lemma_count; @@ -290,7 +289,6 @@ namespace nlsat { m_inline_vars = p.inline_vars(); m_log_lemmas = p.log_lemmas(); m_check_lemmas = p.check_lemmas(); - m_look_for_0_witness = p.look_for_zero_witness(); m_ism.set_seed(m_random_seed); m_explain.set_simplify_cores(m_simplify_cores); m_explain.set_minimize_cores(min_cores); @@ -1520,7 +1518,7 @@ namespace nlsat { void select_witness() { scoped_anum w(m_am); SASSERT(!m_ism.is_full(m_infeasible[m_xk])); - m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize, m_look_for_0_witness); + m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); From 209366ba5584109a65554797b731a36a73c281c5 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 7 Aug 2024 09:22:08 -1000 Subject: [PATCH 139/414] cleanup porting comments Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_explain.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 6efbf4e7f6f..49842100dba 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -733,7 +733,6 @@ namespace nlsat { bool have_zero = false; for (unsigned i = 0; i < num_factors; i++) { f = m_factors.get(i); - // std::cout << "f=";display(std::cout, f) << "\n"; if (coeffs_are_zeroes_in_factor(f)) { have_zero = true; break; @@ -1043,7 +1042,6 @@ namespace nlsat { // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); unsigned num_roots = roots.size(); -//#linxi begin add_cell_lits faster bool all_lt = true; for (unsigned i = 0; i < num_roots; i++) { int s = m_am.compare(y_val, roots[i]); @@ -1092,7 +1090,6 @@ namespace nlsat { i_lower = j + 1; } } -//#linxi end add_cell_lits faster } if (!lower_inf) { @@ -1148,7 +1145,6 @@ namespace nlsat { // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); unsigned num_roots = roots.size(); -//#linxi begin add_cell_lits faster bool all_lt = true; for (unsigned i = 0; i < num_roots; i++) { int s = m_am.compare(y_val, roots[i]); @@ -1196,7 +1192,6 @@ namespace nlsat { i_lower = j + 1; } } -//#linxi end add_cell_lits faster } if (!lower_inf) { @@ -1254,12 +1249,18 @@ namespace nlsat { add_cell_lits(ps, x); } } - void project_cdcac(polynomial_ref_vector & ps, var max_x) { - bool first = true; - + /** + * Sample Projection + * Reference: + * Haokun Li and Bican Xia. + * "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection" + * https://arxiv.org/abs/2003.00409 + */ + void project_cdcac(polynomial_ref_vector & ps, var max_x) { if (ps.empty()) return; + bool first = true; m_todo.reset(); for (poly* p : ps) { m_todo.insert(p); @@ -1282,22 +1283,12 @@ namespace nlsat { TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; display(tout, ps); tout << "\n";); - - /** - * Sample Projection - * Reference: - * Haokun Li and Bican Xia. - * "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection" - * https://arxiv.org/abs/2003.00409 - */ - if (first) { add_lc(ps, x); psc_discriminant(ps, x); psc_resultant(ps, x); first = false; } - else { add_lc(ps, x); // add_sample_coeff(ps, x); From 1a5bddb4f0f9d69934c87fe6d6069683f3d38d04 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 8 Aug 2024 12:14:33 -1000 Subject: [PATCH 140/414] port more from hybridSMT Signed-off-by: Lev Nachmanson --- src/nlsat/CMakeLists.txt | 5 +- src/nlsat/nlsat_params.pyg | 3 + src/nlsat/nlsat_simple_checker.cpp | 2062 +++++++++ src/nlsat/nlsat_simple_checker.h | 15 + src/nlsat/nlsat_solver.cpp | 4027 ----------------- src/nlsat/nlsat_symmetry_checker.cpp | 356 ++ src/nlsat/nlsat_symmetry_checker.h | 13 + .../nlsat_variable_ordering_strategy.cpp | 282 ++ src/nlsat/nlsat_variable_ordering_strategy.h | 27 + src/tactic/smtlogics/qfnra_tactic.cpp | 315 +- 10 files changed, 3061 insertions(+), 4044 deletions(-) create mode 100644 src/nlsat/nlsat_simple_checker.cpp create mode 100644 src/nlsat/nlsat_simple_checker.h delete mode 100644 src/nlsat/nlsat_solver.cpp create mode 100644 src/nlsat/nlsat_symmetry_checker.cpp create mode 100644 src/nlsat/nlsat_symmetry_checker.h create mode 100644 src/nlsat/nlsat_variable_ordering_strategy.cpp create mode 100644 src/nlsat/nlsat_variable_ordering_strategy.h diff --git a/src/nlsat/CMakeLists.txt b/src/nlsat/CMakeLists.txt index cd073f0d5dc..7aa1d83c5f0 100644 --- a/src/nlsat/CMakeLists.txt +++ b/src/nlsat/CMakeLists.txt @@ -7,7 +7,10 @@ z3_add_component(nlsat nlsat_simplify.cpp nlsat_solver.cpp nlsat_types.cpp - COMPONENT_DEPENDENCIES + nlsat_simple_checker.cpp + nlsat_variable_ordering_strategy.cpp + nlsat_symmetry_checker.cpp + COMPONENT_DEPENDENCIES polynomial sat PYG_FILES diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 62101e777b2..73067d48bc6 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -3,7 +3,10 @@ def_module_params('nlsat', description='nonlinear solver', export=True, params=(max_memory_param(), + ('linxi_simple_check', BOOL, False, "linxi precheck about variables sign"), + ('linxi_variable_ordering_strategy', UINT, 0, "linxi Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"), ('cell_sample', BOOL, True, "cell sample projection"), + ('linxi_symmetry_check', BOOL, False, "linxi symmetry check and learning"), ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), diff --git a/src/nlsat/nlsat_simple_checker.cpp b/src/nlsat/nlsat_simple_checker.cpp new file mode 100644 index 00000000000..047b67e0e5d --- /dev/null +++ b/src/nlsat/nlsat_simple_checker.cpp @@ -0,0 +1,2062 @@ +#include "nlsat/nlsat_simple_checker.h" + +struct Debug_Tracer { + std::string tag_str; + Debug_Tracer(std::string _tag_str) { + tag_str = _tag_str; + TRACE("linxi_simple_checker", + tout << "Debug_Tracer begin\n"; + tout << tag_str << "\n"; + ); + } + ~Debug_Tracer() { + TRACE("linxi_simple_checker", + tout << "Debug_Tracer end\n"; + tout << tag_str << "\n"; + ); + } +}; + +// #define _LINXI_DEBUG + +#ifdef _LINXI_DEBUG +// #define LINXI_DEBUG std::stringstream DEBUG_ss; DEBUG_ss << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__; Debug_Tracer DEBUG_dt(DEBUG_ss.str()); +// #define LINXI_HERE TRACE("linxi_simple_checker", tout << "here\n";); +#define LINXI_DEBUG { }((void) 0 ); +#define LINXI_HERE { }((void) 0 ); + +#else +#define LINXI_DEBUG { }((void) 0 ); +#define LINXI_HERE { }((void) 0 ); +#endif + + + +namespace nlsat { + struct Simple_Checker::imp { + // solver / + pmanager ± + anum_manager &am; + const clause_vector &clauses; + literal_vector &learned_unit; + const atom_vector &atoms; + const unsigned arith_var_num; + // unsigned all_var_num; + enum sign_kind { EQ = 0, LT, GT, NONE, LE, GE, NEQ}; + void display(std::ostream & out, const sign_kind &sk) { + if (sk == EQ) + out << "=="; + else if (sk == LT) + out << "<"; + else if (sk == GT) + out << ">"; + else if (sk == NONE) + out << "<=>"; + else if (sk == LE) + out << "<="; + else if (sk == GE) + out << ">="; + else if (sk == NEQ) + out << "!="; + else + UNREACHABLE(); + } + struct Endpoint { + anum_manager &m_am; + unsigned m_open:1; + unsigned m_inf:1; + unsigned m_is_lower:1; + scoped_anum m_val; + Endpoint(anum_manager &_am) : + m_am(_am), + m_open(1), + m_inf(1), + m_is_lower(1), + m_val(_am) { + } + Endpoint(anum_manager &_am, unsigned _open, unsigned _inf, unsigned _islower) : + m_am(_am), + m_open(_open), + m_inf(_inf), + m_is_lower(_islower), + m_val(_am) { + } + Endpoint(anum_manager &_am, unsigned _open, unsigned _inf, unsigned _islower, const scoped_anum &_val) : + m_am(_am), + m_open(_open), + m_inf(_inf), + m_is_lower(_islower), + m_val(_am) { + m_am.set(m_val, _val); + } + bool operator== (const Endpoint &rhs) const { + if (m_inf && rhs.m_inf && m_is_lower == rhs.m_is_lower) + return true; + if (!m_inf && !rhs.m_inf && m_open == rhs.m_open && m_val == rhs.m_val) { + if (!m_open) + return true; + if (m_is_lower == rhs.m_is_lower) + return true; + } + return false; + } + bool operator< (const Endpoint &rhs) const { + if (m_inf) { + if (m_is_lower) { + if (rhs.m_inf && rhs.m_is_lower) + return false; + else + return true; + } + else { + return false; + } + } + else { + if (rhs.m_inf) { + if (rhs.m_is_lower) + return false; + else + return true; + } + else { + if (m_val == rhs.m_val) { + if (!m_open && !rhs.m_open) { // {[, [} + // {[, [} {[, ]} {], [} {], ]} + return false; + } + else if (!m_open) { // {[, (} + // [ < (, [ > ), ] < (, ] > ) + if (rhs.m_is_lower) + return true; + else + return false; + } + else if (!rhs.m_open) { // {(, [} + if (m_is_lower) // x + EPS + return false; + else // x - EPS + return true; + } + else { // {(, (} + // ( == (, ( > ), ) < (, ) == ) + if (!m_is_lower && rhs.m_is_lower) + return true; + else + return false; + } + } + else { + return m_val < rhs.m_val; + } + } + } + } + void copy(const Endpoint &ep) { + m_inf = ep.m_inf; + m_open = ep.m_open; + m_is_lower = ep.m_is_lower; + if (!m_inf) + m_am.set(m_val, ep.m_val); + } + void set_num(const scoped_anum &num, unsigned is_lower) { + m_open = 0; + m_inf = 0; + m_is_lower = is_lower; + m_am.set(m_val, num); + } + void set_num(int num, unsigned is_lower) { + m_open = 0; + m_inf = 0; + m_is_lower = is_lower; + m_am.set(m_val, num); + } + bool is_neg() const { + if (m_inf) { + if (m_is_lower) + return true; + else + return false; + } + else { + if (m_am.is_zero(m_val)) { + if (m_open) { + if (m_is_lower) + return false; + else + return true; + } + else { + return false; + } + } + else { + return m_am.is_neg(m_val); + } + } + } + bool is_zero(unsigned is_open = 0) const { + return !m_inf && m_open == is_open && m_am.is_zero(m_val); + } + }; + struct Domain_Interval { + anum_manager &m_am; + Endpoint m_lower; + Endpoint m_upper; + Domain_Interval(anum_manager &_am) : + m_am(_am), + m_lower(_am, 1, 1, 1), + m_upper(_am, 1, 1, 0) { + // (-oo, +oo) + } + Domain_Interval(anum_manager &_am, + unsigned _lower_open, unsigned _lower_inf, + unsigned _upper_open, unsigned _upper_inf) : + m_am(_am), + m_lower(_am, _lower_open, _lower_inf, 1), + m_upper(_am, _upper_open, _upper_inf, 0) { + } + void copy(const Domain_Interval &di) { + m_lower.copy(di.m_lower); + m_upper.copy(di.m_upper); + } + void set_num(const scoped_anum &num) { + m_lower.set_num(num, 1); + m_upper.set_num(num, 0); + } + void set_num(int num) { + m_lower.set_num(num, 1); + m_upper.set_num(num, 0); + } + }; + + void display(std::ostream & out, anum_manager & am, Domain_Interval const & curr) { + if (curr.m_lower.m_inf) { + out << "(-oo, "; + } + else { + if (curr.m_lower.m_open) + out << "("; + else + out << "["; + am.display_decimal(out, curr.m_lower.m_val); + out << ", "; + } + if (curr.m_upper.m_inf) { + out << "oo)"; + } + else { + am.display_decimal(out, curr.m_upper.m_val); + if (curr.m_upper.m_open) + out << ")"; + else + out << "]"; + } + } + + struct Var_Domain { + Domain_Interval ori_val; // original, ext sign + Domain_Interval mag_val; // magnitude + Var_Domain(anum_manager &am) : + ori_val(am), + mag_val(am) { + mag_val.m_lower.set_num(0, 1); + } + void copy(const Var_Domain &vd) { + ori_val.copy(vd.ori_val); + mag_val.copy(vd.mag_val); + } + }; + vector vars_domain; + struct Clause_Visit_Tag { + bool visited; + bool_vector literal_visited; + Clause_Visit_Tag() : visited(false) {} + }; + vector clauses_visited; + bool improved; + enum special_ineq_kind {UNK = 0, AXBC, AXBSC, NK}; // None Kind + vector> literal_special_kind; + // imp(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num) : + imp(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) : + // sol(_sol), + pm(_pm), + am(_am), + clauses(_clauses), + learned_unit(_learned_unit), + atoms(_atoms), + arith_var_num(_arith_var_num) { + // all_var_num = atoms.size(); + for (unsigned i = 0; i < arith_var_num; ++i) { + vars_domain.push_back(Var_Domain(am)); + } + clauses_visited.resize(clauses.size()); + literal_special_kind.resize(clauses.size()); + } + sign_kind to_sign_kind(atom::kind kd) { + if (kd == atom::EQ) + return EQ; + if (kd == atom::LT) + return LT; + if (kd == atom::GT) + return GT; + UNREACHABLE(); + } + bool update_interval_intersection(Domain_Interval &ia, const Domain_Interval &ib) { + TRACE("linxi_simple_checker", + tout << "ia: "; + display(tout, am, ia); + tout << "\nib: "; + display(tout, am, ib); + tout << "\n"; + tout << "ia.lower < ib.lower: " << (ia.m_lower < ib.m_lower) << '\n'; + tout << "ia.m_upper < ib.m_upper: " << (ia.m_upper < ib.m_upper) << '\n'; + ); + if (ia.m_lower < ib.m_lower) { + ia.m_lower.copy(ib.m_lower); + improved = true; + } + + if (ib.m_upper < ia.m_upper) { + ia.m_upper.copy(ib.m_upper); + improved = true; + } + if (ia.m_upper < ia.m_lower) + return false; + + + // if (ia.m_lower_inf == 0 && ib.m_upper_inf == 0) { + // if (ia.m_lower > ib.m_upper) + // return false; + // if (ia.m_lower == ib.m_upper && (ia.m_lower_open || ib.m_upper_open)) + // return false; + // } + // if (ib.m_lower_inf == 0 && ia.m_upper_inf == 0) { + // if (ib.m_lower > ia.m_upper) + // return false; + // if (ib.m_lower == ia.m_upper && (ib.m_lower_open || ia.m_upper_open)) + // return false; + // } + // if (ia.m_lower_inf && ib.m_lower_inf) { + // // do nothing + // } + // else { + // if (ia.m_lower_inf) { + // ia.m_lower_inf = 0; + // ia.m_lower_open = ib.m_lower_open; + // am.set(ia.m_lower, ib.m_lower); + // } + // else if (ib.m_lower_inf) { + // // do nothing + // } + // else { + // if (ia.m_lower == ib.m_lower) { + // ia.m_lower_open = (ia.m_lower_open | ib.m_lower_open); + // } + // else if (ia.m_lower < ib.m_lower) { + // ia.m_lower_open = ib.m_lower_open; + // am.set(ia.m_lower, ib.m_lower); + // } + // else { + // // do nothing + // } + // } + // } + + // if (ia.m_upper_inf && ib.m_upper_inf) { + // // do nothing + // } + // else { + // if (ia.m_upper_inf) { + // ia.m_upper_inf = 0; + // ia.m_upper_open = ib.m_upper_open; + // am.set(ia.m_upper, ib.m_upper); + // } + // else if (ib.m_upper_inf) { + // // do nothing + // } + // else { + // if (ia.m_upper == ib.m_upper) { + // ia.m_upper_open = (ia.m_upper_open | ib.m_upper_open); + // } + // else if (ia.m_upper < ib.m_upper) { + // // do nothing + // } + // else { + // ia.m_upper_open = ib.m_upper_open; + // am.set(ia.m_upper, ib.m_upper); + // } + // } + // } + TRACE("linxi_simple_checker", + tout << "after update: "; + display(tout, am, ia); + tout << "\n"; + ); + return true; + } + + bool update_var_ori_domain_interval(Var_Domain &vd, const Domain_Interval &di) { + return update_interval_intersection(vd.ori_val, di); + } + bool update_var_mag_domain_interval_by_ori(Var_Domain &vd, const Domain_Interval &di) { + TRACE("linxi_simple_checker", + tout << "vd mag val: "; + display(tout, am, vd.mag_val); + tout << "\n"; + tout << "di: "; + display(tout, am, di); + tout << "\n"; + ); + Domain_Interval mag_di(am, 0, 0, 1, 1); + // am.set(mag_di.m_lower.m_val, 0); + + if (di.m_lower.m_inf) { + mag_di.m_upper.m_inf = 1; + mag_di.m_upper.m_open = 1; + if (am.is_neg(di.m_upper.m_val)) { + // am.neg(di.m_upper); + am.set(mag_di.m_lower.m_val, di.m_upper.m_val*-1); + mag_di.m_lower.m_open = di.m_upper.m_open; + } + else if (am.is_zero(di.m_upper.m_val)) { + mag_di.m_lower.m_open = di.m_upper.m_open; + } + else { + return true; + } + } + else if (di.m_upper.m_inf) { + mag_di.m_upper.m_inf = 1; + mag_di.m_upper.m_open = 1; + if (am.is_neg(di.m_lower.m_val)) { + return true; + } + else if (am.is_zero(di.m_lower.m_val)) { + mag_di.m_lower.m_open = di.m_lower.m_open; + } + else { + am.set(mag_di.m_lower.m_val, di.m_lower.m_val); + mag_di.m_lower.m_open = di.m_lower.m_open; + } + } + else { + mag_di.m_lower.m_inf = 0; + mag_di.m_upper.m_inf = 0; + if (!am.is_neg(di.m_lower.m_val)) { + mag_di.m_lower.m_open = di.m_lower.m_open; + mag_di.m_upper.m_open = di.m_upper.m_open; + am.set(mag_di.m_lower.m_val, di.m_lower.m_val); + am.set(mag_di.m_upper.m_val, di.m_upper.m_val); + } + else if (!am.is_pos(di.m_upper.m_val)) { + mag_di.m_lower.m_open = di.m_upper.m_open; + mag_di.m_upper.m_open = di.m_lower.m_open; + + am.set(mag_di.m_lower.m_val, di.m_upper.m_val*-1); + am.set(mag_di.m_upper.m_val, di.m_lower.m_val*-1); + } + else { + scoped_anum nl(am); + am.set(nl, di.m_lower.m_val); + am.neg(nl); + am.set(mag_di.m_lower.m_val, 0); + mag_di.m_lower.m_open = 0; + if (nl < di.m_upper.m_val) { + mag_di.m_upper.m_open = di.m_upper.m_open; + am.set(mag_di.m_upper.m_val, di.m_upper.m_val); + } + else if (nl == di.m_upper.m_val) { + mag_di.m_upper.m_open = (di.m_lower.m_open | di.m_upper.m_open); + am.set(mag_di.m_upper.m_val, di.m_upper.m_val); + } + else { + mag_di.m_upper.m_open = di.m_lower.m_open; + am.set(mag_di.m_upper.m_val, nl); + } + } + } + TRACE("linxi_simple_checker", + tout << "mag di: "; + display(tout, am, mag_di); + tout << "\n"; + ); + return update_interval_intersection(vd.mag_val, mag_di); + } + void calc_formula(scoped_anum &num, const scoped_anum &a, unsigned b, const scoped_anum &c) { + LINXI_DEBUG; + scoped_anum frac(am); + am.div(c, a, frac); + am.neg(frac); + if (b > 1) + am.root(frac, b, num); + else + am.set(num, frac); + } + bool process_odd_degree_formula(Var_Domain &vd, sign_kind nsk, const scoped_anum &a, unsigned b, const scoped_anum &c) { + LINXI_DEBUG; + Domain_Interval now_di(am); + scoped_anum num(am); + calc_formula(num, a, b, c); + TRACE("linxi_simple_checker", + tout << "nsk: "; + display(tout, nsk); + tout << '\n'; + tout << "num: " << num << '\n'; + ); + if (nsk == EQ) { + now_di.set_num(num); + // am.set(now_di.m_lower.m_val, num); + // am.set(now_di.m_upper.m_val, num); + // now_di.m_lower.m_inf = 0; + // now_di.m_upper.m_inf = 0; + // now_di.m_lower.m_open = 0; + // now_di.m_upper.m_open = 0; + } + else if (nsk == LT) { + am.set(now_di.m_upper.m_val, num); + now_di.m_upper.m_inf = 0; + now_di.m_upper.m_open = 1; + } + else if (nsk == GT) { + am.set(now_di.m_lower.m_val, num); + now_di.m_lower.m_inf = 0; + now_di.m_lower.m_open = 1; + } + else if (nsk == LE) { + am.set(now_di.m_upper.m_val, num); + now_di.m_upper.m_inf = 0; + now_di.m_upper.m_open = 0; + } + else if (nsk == GE) { + am.set(now_di.m_lower.m_val, num); + now_di.m_lower.m_inf = 0; + now_di.m_lower.m_open = 0; + } + else { + UNREACHABLE(); + } + TRACE("linxi_simple_checker", + tout << "now_di: "; + display(tout, am, now_di); + tout << "\n"; + ); + if (!update_var_ori_domain_interval(vd, now_di)) + return false; + if (!update_var_mag_domain_interval_by_ori(vd, vd.ori_val)) + return false; + return true; + } +/* + +if (nsk == EQ) { +return false; +} +else if (nsk == LT) { +return false; +} +else if (nsk == GT) { +return true; +} +else if (nsk == LE) { +return false; +} +else if (nsk == GE) { +return true; +} +else { +UNREACHABLE(); +} +*/ + bool process_even_degree_formula(Var_Domain &vd, sign_kind nsk, const scoped_anum &a, unsigned b, const scoped_anum &c) { + LINXI_DEBUG; + scoped_anum num(am), frac(am); + am.div(c, a, frac); + am.neg(frac); + + if (frac < 0) { + if (nsk == EQ || nsk == LT || nsk == LE) { + return false; + } + else if (nsk == GT || nsk == GE) { + return true; + } + else { + UNREACHABLE(); + } + } + else if (frac == 0) { + if (nsk == EQ || nsk == LE) { + Domain_Interval di(am); + di.set_num(0); + if (!update_interval_intersection(vd.ori_val, di)) + return false; + if (!update_interval_intersection(vd.mag_val, di)) + return false; + } + else if (nsk == LT) { + return false; + } + else if (nsk == GT) { + Domain_Interval di(am); + di.m_lower.set_num(0, 1); + if (!update_interval_intersection(vd.mag_val, di)) + return false; + } + else if (nsk == GE) { + return true; + } + else { + UNREACHABLE(); + } + } + else { + scoped_anum num(am); + am.root(frac, b, num); + if (nsk == EQ) { + Domain_Interval di(am); + di.set_num(num); + // di.m_lower_open = 0; + // di.m_upper_open = 0; + // di.m_lower_inf = 0; + // di.m_upper_inf = 0; + // am.set(di.m_lower, num); + // am.set(di.m_upper, num); + // if (!update_interval_intersection(vd.ori_val, di)) + // return false; + if (!update_interval_intersection(vd.mag_val, di)) + return false; + } + else if (nsk == LT) { + Domain_Interval di(am, 0, 0, 1, 0); + // [0, num) + am.set(di.m_lower.m_val, 0); + am.set(di.m_upper.m_val, num); + if (!update_interval_intersection(vd.mag_val, di)) + return false; + + // (-num, num) + di.m_lower.m_open = 1; + // am.set(di.m_upper, num); + am.neg(num); + am.set(di.m_lower.m_val, num); + if (!update_interval_intersection(vd.ori_val, di)) + return false; + } + else if (nsk == GT) { + // (num, inf) + Domain_Interval di(am, 1, 0, 1, 1); + // di.m_lower_open = 1; + // di.m_upper_open = 1; + // di.m_lower_inf = 0; + // di.m_upper_inf = 1; + am.set(di.m_lower.m_val, num); + if (!update_interval_intersection(vd.mag_val, di)) + return false; + } + else if (nsk == LE) { + Domain_Interval di(am, 0, 0, 0, 0); + // [0, num] + // di.m_lower_open = 0; + // di.m_upper_open = 0; + // di.m_lower_inf = 0; + // di.m_upper_inf = 0; + am.set(di.m_lower.m_val, 0); + am.set(di.m_upper.m_val, num); + if (!update_interval_intersection(vd.mag_val, di)) + return false; + // [-num, num] + // am.set(di.m_upper, num); + am.neg(num); + am.set(di.m_lower.m_val, num); + if (!update_interval_intersection(vd.ori_val, di)) + return false; + } + else if (nsk == GE) { + // [num, inf) + Domain_Interval di(am, 0, 0, 1, 1); + // di.m_lower_open = 0; + // di.m_upper_open = 1; + // di.m_lower_inf = 0; + // di.m_upper_inf = 1; + am.set(di.m_lower.m_val, num); + if (!update_interval_intersection(vd.mag_val, di)) + return false; + } + else { + UNREACHABLE(); + } + } + return true; + } + + bool update_var_domain(sign_kind nsk, const scoped_anum &a, var x, unsigned b, const scoped_anum &c) { + LINXI_DEBUG; + Var_Domain &vd = vars_domain[x]; + if (am.is_neg(a)) { + if (nsk == LT) + nsk = GT; + else if (nsk == GT) + nsk = LT; + else if (nsk == LE) + nsk = GE; + else if (nsk == GE) + nsk = LE; + } + if (nsk == NEQ) { + if (am.is_zero(c)) { + Domain_Interval di(am, 1, 0, 1, 1); + am.set(di.m_lower.m_val, 0); + return update_interval_intersection(vd.mag_val, di); + } + else { + return true; + } + } + if ((b & 1) == 1) + return process_odd_degree_formula(vd, nsk, a, b, c); + else + return process_even_degree_formula(vd, nsk, a, b, c); + } + + bool check_is_axbc(const poly *p, scoped_anum &a, var &x, unsigned &b, scoped_anum& c) { + // is a*(x^b) + c*1 form + // LINXI_DEBUG; + // LINXI_HERE; + // TRACE("linxi_simple_checker", + // tout << a << "x[" << x << "]^" << b << " "; + // tout << "+ " << c << " "; + // // display(tout, nsk); + // // tout << " 0\n"; + // ); + if (pm.size(p) == 1 && pm.is_var(pm.get_monomial(p, 0), x)) { + LINXI_HERE; + am.set(a, 1); + b = 1; + am.set(c, 0); + return true; + } + // LINXI_HERE; + if (pm.size(p) != 2) + return false; + if (!pm.is_unit(pm.get_monomial(p, 1))) + return false; + monomial *m = pm.get_monomial(p, 0); + if (pm.size(m) != 1) + return false; + x = pm.get_var(m, 0); + b = pm.degree(m, 0); + // LINXI_HERE; + am.set(a, pm.coeff(p, 0)); + am.set(c, pm.coeff(p, 1)); + return true; + } + + bool collect_domain_axbc_form(unsigned cid, unsigned lid) { + // is_var_num, a*(x^b) + c form + LINXI_DEBUG; + literal lit = (*clauses[cid])[lid]; + bool s = lit.sign(); + ineq_atom *ia = to_ineq_atom(atoms[lit.var()]); + if (ia->size() > 1) { + // clauses_visited[cid].visited = true; + return true; + } + poly *p = ia->p(0); + if (literal_special_kind[cid][lid] != UNK && + literal_special_kind[cid][lid] != AXBC) + return true; + var x; + scoped_anum a(am), c(am); + unsigned b; + + if (!check_is_axbc(p, a, x, b, c)) { + // vec_id.push_back(cid); + return true; + } + clauses_visited[cid].visited = true; + literal_special_kind[cid][lid] = AXBC; + sign_kind nsk = to_sign_kind(ia->get_kind()); + if (s) { + if (nsk == EQ) + nsk = NEQ; + else if (nsk == LT) + nsk = GE; + else if (nsk == GT) + nsk = LE; + } + TRACE("linxi_simple_checker", + tout << a << "x[" << x << "]^" << b << " + " << c << " "; + display(tout, nsk); + tout << " 0 \n"; + ); + if (!update_var_domain(nsk, a, x, b, c)) + return false; + TRACE("linxi_simple_checker", + tout << "original: "; + display(tout, am, vars_domain[x].ori_val); + tout << "\nmagnitude: "; + display(tout, am, vars_domain[x].mag_val); + tout << "\n"; + ); + return true; + } + bool check_is_axbsc(const poly *p, vector &as, vector &xs, vector &bs, scoped_anum& c, unsigned &cnt) { + // [a*(x^b)] + ... + [a*(x^b)] + c form + LINXI_DEBUG; + unsigned psz = pm.size(p); + am.set(c, 0); + for (unsigned i = 0; i < psz; ++i) { + monomial *m = pm.get_monomial(p, i); + if (pm.size(m) > 1) + return false; + } + LINXI_HERE; + cnt = 0; + for (unsigned i = 0; i < psz; ++i) { + monomial *m = pm.get_monomial(p, i); + // TRACE("linxi_simple_checker", + // tout << "monomial: "; + // pm.display(tout, m); + // tout << '\n'; + // // tout << "coefficient: " << pm.coeff(p, i) << "\n"; + // tout << "m size: " << pm.size(m) << '\n'; + // tout << "# "; + // for (unsigned j = 0, sz = pm.size(m); j < sz; ++j) { + // var v = pm.get_var(m, j); + // tout << " (" << j << ", " << pm.degree_of(m, v) << ")"; + // } + // tout << "\n"; + // ); + if (pm.size(m) == 0) { + am.set(c, pm.coeff(p, i)); + } + else { + // as.push_back(scoped_anum(am)); + am.set(as[cnt++], pm.coeff(p, i)); + xs.push_back(pm.get_var(m, 0)); + bs.push_back(pm.degree(m, 0)); + // TRACE("linxi_simple_checker", + // tout << as.back() << "x[" << xs.back() << "]^" << bs.back() << "\n"; + // ); + } + } + return true; + } + + sign_kind get_axb_sign(const scoped_anum &a, var x, unsigned b) { + Var_Domain &vd = vars_domain[x]; + if (vd.ori_val.m_lower.is_zero() && + vd.ori_val.m_upper.is_zero()) + return EQ; + if ((b & 1) == 0) { + if (am.is_pos(a)) { // a > 0 + if (vd.mag_val.m_lower.is_zero(0)) + return GE; + else + return GT; + } + else { + if (vd.mag_val.m_lower.is_zero(0)) + return LE; + else + return LT; + } + } else { + sign_kind ret = NONE; + if (!vd.ori_val.m_lower.m_inf && !vd.ori_val.m_upper.m_inf) { + if (am.is_zero(vd.ori_val.m_lower.m_val)) { + if (vd.ori_val.m_lower.m_open) + ret = GT; + else + ret = GE; + } + else if (am.is_pos(vd.ori_val.m_lower.m_val)) { + ret = GT; + } + // else { + // ret = NONE; + // } + if (am.is_zero(vd.ori_val.m_upper.m_val)) { + if (vd.ori_val.m_upper.m_open) + ret = LT; + else + ret = LE; + } + else if (am.is_neg(vd.ori_val.m_upper.m_val)) { + ret = LT; + } + // else { + // ret = NONE; + // } + } + else if (!vd.ori_val.m_lower.m_inf) { + if (am.is_pos(vd.ori_val.m_lower.m_val)) { + ret = GT; + } + else if (am.is_zero(vd.ori_val.m_lower.m_val)) { + if (vd.ori_val.m_lower.m_open) + ret = GT; + else + ret = GE; + } + } + else if (!vd.ori_val.m_upper.m_inf) { + if (am.is_neg(vd.ori_val.m_upper.m_val)) { + ret = LT; + } + else if (am.is_zero(vd.ori_val.m_upper.m_val)) { + if (vd.ori_val.m_upper.m_open) + ret = LT; + else + ret = LE; + } + } + if (a < 0) { + if (ret == LT) + ret = GT; + else if (ret == LE) + ret = GE; + else if (ret == GT) + ret = LT; + else if (ret == GE) + ret = LE; + } + return ret; + } + } + + bool check_is_sign_ineq_consistent(sign_kind &nsk, vector &as, vector &xs, vector &bs, scoped_anum& c, bool &is_conflict) { + sign_kind sta = get_axb_sign(as[0], xs[0], bs[0]); + if (sta == NONE) + return false; + unsigned sz = as.size(); + for (unsigned i = 1; i < sz; ++i) { + sign_kind now = get_axb_sign(as[i], xs[i], bs[i]); + TRACE("linxi_simple_checker", + tout << "sta: "; + display(tout, sta); + tout << "\n"; + tout << "now: "; + display(tout, now); + tout << "\n"; + ); + if (now == NONE) + return false; + if (sta == EQ) { + sta = now; + } + else if (sta == LT || sta == LE) { + if (now != LT && now != LE) + return false; + if (sta != now) + sta = LT; + } + else { + if (now != GT && now != GE) + return false; + if (sta != now) + sta = GT; + } + TRACE("linxi_simple_checker", + tout << "after merge\n"; + tout << "sta: "; + display(tout, sta); + tout << "\n"; + ); + } + // if (am.is_zero(c)) { + // // sta = sta; + // } + // else if (am.is_neg(c)) { + // if (sta == EQ) + // sta = LT; + // // else if (sta == LT) + // // sta = LT; + // else if (sta == LE) + // sta = LT; + // else if (sta == GT) + // sta = NONE; + // else if (sta == GE) + // sta = NONE; + // } + // else { // a > 0 + // if (sta == EQ) + // sta = GT; + // else if (sta == LT) + // sta = NONE; + // else if (sta == LE) + // sta = NONE; + // // else if (sta == GT) + // // sta = GT; + // else if (sta == GE) + // sta = GT; + // } + // if (sta == NONE) + // return false; + TRACE("linxi_simple_checker", + tout << "sta: "; + display(tout, sta); + tout << "\n"; + tout << "nsk: "; + display(tout, nsk); + tout << "\n"; + tout << "c: "; + am.display(tout, c); + tout << "\n"; + ); +/* +if (am.is_neg(c)) { // ( == 0) + (c < 0) -> < 0 + +} +else if (am.is_zero(c)) { // ( == 0) + (c == 0) -> == 0 + +} +else { // ( == 0) + (c > 0) -> > 0 + +} + +*/ + if (sta == EQ) { + if (am.is_neg(c)) { // ( == 0) + (c < 0) -> < 0 + if (nsk == EQ || nsk == GE || nsk == GT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else if (am.is_zero(c)) { // ( == 0) + (c == 0) -> == 0 + if (nsk == GT || nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else { // ( == 0) + (c > 0) -> > 0 + if (nsk == EQ || nsk == LE || nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + } + else if (sta == LT) { + if (am.is_neg(c)) { // ( < 0) + (c < 0) -> < 0 + if (nsk == EQ || nsk == GE || nsk == GT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else if (am.is_zero(c)) { // ( < 0) + (c == 0) -> < 0 + if (nsk == EQ || nsk == GE || nsk == GT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else { // [(t0 <= 0 + .. + tn <= 0) + (c > 0) >/>= 0] -> t > -c + if (nsk == LE || nsk == LT) + return false; + if (sz > 1 && nsk == EQ) + nsk = GE; + return true; + } + } + else if (sta == LE) { + if (am.is_neg(c)) { // ( <= 0) + (c < 0) -> < 0 + if (nsk == EQ || nsk == GE || nsk == GT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else if (am.is_zero(c)) { // ( <= 0) + (c == 0) -> <= 0 + if (nsk == GT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else { // [(t0 <= 0 + .. + tn <= 0) + (c > 0) >/>= 0] -> t > -c + if (nsk == LE || nsk == LT) + return false; + if (sz > 1 && nsk == EQ) + nsk = GE; + return true; + } + } + else if (sta == GT) { + if (am.is_neg(c)) { // [(t0 > 0 + .. + tn > 0) + (c < 0) t < -c + if (nsk == GE || nsk == GT) + return false; + if (sz > 1 && nsk == EQ) + nsk = LE; + return true; + } + else if (am.is_zero(c)) { // ( > 0) + (c == 0) -> > 0 + if (nsk == EQ || nsk == LE || nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else { // (t > 0) + (c > 0) -> > 0 + if (nsk == EQ || nsk == LE || nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + } + else if (sta == GE) { + if (am.is_neg(c)) { // [(t0 >= 0 + .. + tn >= 0) + (c < 0) t < -c + if (nsk == GE || nsk == GT) + return false; + if (sz > 1 && nsk == EQ) + nsk = LE; + return true; + } + else if (am.is_zero(c)) { // ( >= 0) + (c == 0) -> >= 0 + if (nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + else { // (t >= 0) + (c > 0) -> > 0 + if (nsk == EQ || nsk == LE || nsk == LT) { + is_conflict = true; + return true; + } + else { + return false; + } + } + } + return false; + } + bool collect_domain_sign_ineq_consistent_form(sign_kind nsk, vector &as, vector &xs, vector &bs, scoped_anum& c) { + LINXI_DEBUG; + for (unsigned i = 0, sz = as.size(); i < sz; ++i) { + if (!update_var_domain(nsk, as[i], xs[i], bs[i], c)) + return false; + } + return true; + } + bool process_axbsc_form(sign_kind nsk, unsigned cid, vector &as, vector &xs, vector &bs, scoped_anum& c) { + LINXI_DEBUG; + bool is_conflict(false); + TRACE("linxi_simple_checker", + for (unsigned i = 0, sz = as.size(); i < sz; ++i) { + if (i > 0) + tout << "+ "; + tout << as[i] << "x[" << xs[i] << "]^" << bs[i] << " "; + } + tout << "+ " << c << " "; + display(tout, nsk); + tout << " 0\n"; + ); + if (!check_is_sign_ineq_consistent(nsk, as, xs, bs, c, is_conflict)) + return true; + TRACE("linxi_simple_checker", + tout << "is conflict: " << is_conflict << "\n" + ); + if (is_conflict) + return false; + clauses_visited[cid].visited = true; + if (!collect_domain_sign_ineq_consistent_form(nsk, as, xs, bs, c)) + return false; + return true; + } + bool collect_domain_axbsc_form(unsigned cid, unsigned lid) { + LINXI_DEBUG; + // [a*(x^k)] + ... + [a*(x^b)] + k form + literal lit = (*clauses[cid])[lid]; + bool s = lit.sign(); + ineq_atom *iat = to_ineq_atom(atoms[lit.var()]); + if (iat->size() > 1) + return true; + if (literal_special_kind[cid][lid] != UNK && + literal_special_kind[cid][lid] != AXBSC) + return true; + + poly *p = iat->p(0); + vector as; + for (unsigned i = 0, sz = pm.size(p); i < sz; ++i) { + as.push_back(scoped_anum(am)); + } + vector xs; + vector bs; + scoped_anum c(am); + unsigned cnt; + if (!check_is_axbsc(p, as, xs, bs, c, cnt)) { + literal_special_kind[cid][lid] = NK; + return true; + } + literal_special_kind[cid][lid] = AXBSC; + TRACE("linxi_simple_checker", + tout << "as size: " << as.size() << '\n'; + ); + while (as.size() > cnt) + as.pop_back(); + + sign_kind nsk = to_sign_kind(iat->get_kind()); + if (s) { + if (nsk == EQ) + return true; + else if (nsk == LT) + nsk = GE; + else if (nsk == GT) + nsk = LE; + } + TRACE("linxi_simple_checker", + tout << "ineq atom: " << '\n'; + for (unsigned i = 0, sz = iat->size(); i < sz; ++i) { + pm.display(tout, iat->p(i)); + tout << " is even: " << iat->is_even(i) << "\n"; + } + display(tout, nsk); + tout << " 0\n"; + tout << "\n"; + tout << "as size: " << as.size() << '\n'; + for (unsigned i = 0, sz = as.size(); i < sz; ++i) { + if (i > 0) + tout << "+ "; + tout << as[i] << "x[" << xs[i] << "]^" << bs[i] << " "; + } + tout << "+ " << c << " "; + display(tout, nsk); + tout << " 0\n"; + ); + if (!process_axbsc_form(nsk, cid, as, xs, bs, c)) + return false; + return true; + } + // bool update_all_mag_domain_by_ori() { + // LINXI_HERE; + // for (unsigned i = 0; i < arith_var_num; ++i) { + // if (!update_var_mag_domain_interval_by_ori(vars_domain[i], vars_domain[i].ori_val)) + // return false; + // } + // return true; + // } + bool collect_var_domain() { + LINXI_DEBUG; + // vector vec_id; + for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { + if (clauses_visited[i].visited) + continue; + if (clauses[i]->size() > 1) + continue; + literal lit = (*clauses[i])[0]; + atom *tmp_atom = atoms[lit.var()]; + if (tmp_atom == nullptr) { + clauses_visited[i].visited = true; + continue; + } + if (!tmp_atom->is_ineq_atom()) { + clauses_visited[i].visited = true; + continue; + } + ineq_atom *tmp_ineq_atom = to_ineq_atom(tmp_atom); + if (tmp_ineq_atom->size() > 1) + continue; + if (!collect_domain_axbc_form(i, 0)) + return false; + } + // if (!update_all_mag_domain_by_ori()) + // return false; + TRACE("linxi_simple_checker", + for (unsigned i = 0; i < arith_var_num; ++i) { + tout << "====== arith[" << i << "] ======\n"; + tout << "original value: "; + display(tout, am, vars_domain[i].ori_val); + tout << "\nmagitude value: "; + display(tout, am, vars_domain[i].mag_val); + tout << "\n"; + tout << "====== arith[" << i << "] ======\n"; + } + ); + // TRACE("linxi_simple_checker", + // tout << "vec_id.size(): " << vec_id.size() << "\n"; + // ); + for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { + // unsigned id = vec_id[i]; + if (!collect_domain_axbsc_form(i, 0)) + return false; + } + // if (!update_all_mag_domain_by_ori()) + // return false; + return true; + } + void endpoint_multiply(const Endpoint &a, const Endpoint &b, Endpoint &c) { + if (a.is_zero() || b.is_zero()) { + c.set_num(0, 0); + return ; + } + bool a_neg = a.is_neg(), b_neg = b.is_neg(); + if (a.m_inf || b.m_inf) { + c.m_inf = 1; + c.m_open = 1; + if (a_neg == b_neg) + c.m_is_lower = 0; + else + c.m_is_lower = 1; + } + else { + c.m_inf = 0; + c.m_open = (a.m_open | b.m_open); + am.set(c.m_val, a.m_val*b.m_val); + } + } + void get_max_min_endpoint(const ptr_vector &pev, Endpoint *&pmi, Endpoint *&pmx) { + pmi = pmx = pev[0]; + for (unsigned i = 1, sz = pev.size(); i < sz; ++i) { + if (*pmx < *pev[i]) + pmx = pev[i]; + if (*pev[i] < *pmi) + pmi = pev[i]; + } + } + void merge_mul_domain(Domain_Interval &pre, const Domain_Interval &now) { + TRACE("linxi_simple_checker", + tout << "dom: "; + display(tout, am, pre); + tout << "\n"; + tout << "di: "; + display(tout, am, now); + tout << "\n"; + ); + Endpoint plnl(am), plnu(am), punl(am), punu(am); + endpoint_multiply(pre.m_lower, now.m_lower, plnl); + endpoint_multiply(pre.m_lower, now.m_upper, plnu); + endpoint_multiply(pre.m_upper, now.m_lower, punl); + endpoint_multiply(pre.m_upper, now.m_upper, punu); + ptr_vector pev; + pev.push_back(&plnl); + pev.push_back(&plnu); + pev.push_back(&punl); + pev.push_back(&punu); + Endpoint *pmi, *pmx; + get_max_min_endpoint(pev, pmi, pmx); + pre.m_lower.copy(*pmi); + pre.m_lower.m_is_lower = 1; + pre.m_upper.copy(*pmx); + pre.m_upper.m_is_lower = 0; + + // if (pre.m_lower_inf && pre.m_upper_inf) { + // if ((!now.m_lower_inf && am.is_zero(now.m_lower)) && + // (!now.m_upper_inf && am.is_zero(now.m_upper))) { + + // } + // else { + // return ; + // } + // } + // if ((!pre.m_lower_inf && am.is_zero(pre.m_lower)) && + // (!pre.m_upper_inf && am.is_zero(pre.m_upper))) + // return ; + // if (now.m_lower_inf && now.m_upper_inf) { + // pre.m_lower_inf = 1; + // pre.m_upper_inf = 1; + // return ; + // } + // if (pre.m_lower_inf == 0 && !am.is_neg(pre.m_lower)) { + // // {+, +/inf} + // if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { + // // {+, +/inf} * {+, +/inf} + // // {a, b} * {c, d} -> {ac, bd/inf} + // pre.m_lower_open = (pre.m_lower_open | now.m_lower_open); + // am.set(pre.m_lower, now.m_lower * pre.m_lower); + + // pre.m_upper_inf = (pre.m_upper_inf | now.m_upper_inf); + // if (pre.m_upper_inf == 0) { + // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); + // am.set(pre.m_upper, now.m_upper * pre.m_upper); + // } + // } + // else if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { + // // {+, +/inf} * {-/inf, -} + // // {a, b} * {c, d} -> {bc, ad} + // Domain_Interval tmp_di(am, false); + // tmp_di.m_lower_inf = (pre.m_upper_inf | now.m_lower_inf); + // if (tmp_di.m_lower_inf == 0) { + // tmp_di.m_lower_open = (pre.m_upper_open | now.m_lower_open); + // am.set(tmp_di.m_lower, pre.m_upper * now.m_lower); + // } + + // tmp_di.m_upper_inf = 0; + // tmp_di.m_upper_open = (pre.m_lower_open | now.m_upper_open); + // am.set(tmp_di.m_upper, pre.m_lower * now.m_upper); + + // pre.copy(tmp_di); + // } + // else { + // // {+, +/inf} * {-/inf, +/inf} + // if (pre.m_upper_inf) { + // // {+, +inf) * {-/inf, +/inf} -> (-inf, +inf) + // pre.m_lower_inf = 1; + // } + // else { + // // {+, +} * {-/inf, +/inf} + // // {a, b} * {c, d} -> {bc, bd} + // // order matters! + // pre.m_lower_inf = now.m_lower_inf; + // if (pre.m_lower_inf == 0) { + // pre.m_lower_open = (pre.m_upper_open | now.m_lower_open); + // am.set(pre.m_lower, now.m_lower * pre.m_upper); + // } + // pre.m_upper_inf = now.m_upper_inf; + // if (pre.m_upper_inf == 0) { + // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); + // am.set(pre.m_upper, now.m_upper * pre.m_upper); + // } + // } + // } + // } + // else if (pre.m_upper_inf == 0 && !am.is_pos(pre.m_upper)) { + // LINXI_HERE; + // // {-/inf, -} + // if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { + // LINXI_HERE; + // // {-/inf, -} * {-/inf, -} + // if (pre.m_lower_inf || now.m_lower_inf) { + // // (-inf, b} * {c, d} -> {bd, +inf) + // // {a, b} * (-inf, d} -> {bd, +inf) + // pre.m_upper_inf = 1; + + // pre.m_lower_open = (pre.m_upper_open | now.m_upper_open); + // am.set(pre.m_lower, now.m_upper * pre.m_upper); + // } + // else { + // // {a, b} * {c, d} -> {bd, ac} + // Domain_Interval tmp_di(am, false); + // tmp_di.m_lower_inf = 0; + // tmp_di.m_upper_inf = 0; + // tmp_di.m_lower_open = (pre.m_upper_open | now.m_upper_open); + // tmp_di.m_upper_open = (pre.m_lower_open | now.m_lower_open); + // am.set(tmp_di.m_lower, pre.m_upper * now.m_upper); + // am.set(tmp_di.m_upper, pre.m_lower * now.m_lower); + // pre.copy(tmp_di); + // } + // } + // else if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { + // LINXI_HERE; + // // {-/inf, -} * {+, +/inf} + // // {a, b} * {c, d} -> {ad, bc} + // Domain_Interval tmp_di(am, false); + // tmp_di.m_lower_inf = (pre.m_lower_inf | now.m_upper_inf); + // if (tmp_di.m_lower_inf == 0) { + // tmp_di.m_lower_open = (pre.m_lower_open | now.m_upper_open); + // am.set(tmp_di.m_lower, pre.m_lower * now.m_upper); + // } + + // tmp_di.m_upper_inf = 0; + // tmp_di.m_upper_open = (pre.m_upper_open | now.m_lower_open); + // am.set(tmp_di.m_upper, pre.m_upper * now.m_lower); + // TRACE("linxi_simple_checker", + // tout << "tmp_di: "; + // display(tout, am, tmp_di); + // tout << "\n"; + // ); + // pre.copy(tmp_di); + // } + // else { + // LINXI_HERE; + // // {-/inf, -} * {-/inf, +/inf} + // if (pre.m_lower_inf) { + // // (-inf, -} * {-/inf, +/inf} -> (-inf, +inf) + // pre.m_upper_inf = 1; + // } + // else { + // // {-, -} * {-/inf, +/inf} + // // {pl, pu} * {nl, nu} -> {pl nu, pl nl} + // // order matters! + // pre.m_lower_inf = now.m_upper_inf; + // if (pre.m_lower_inf == 0) { + // pre.m_lower_open = (pre.m_lower_open | now.m_upper_open); + // am.set(pre.m_lower, now.m_upper * pre.m_lower); + // } + // pre.m_upper_inf = now.m_lower_inf; + // if (pre.m_upper_inf == 0) { + // pre.m_upper_open = (pre.m_lower_open | now.m_lower_open); + // am.set(pre.m_upper, now.m_lower * pre.m_lower); + // } + // } + // } + // } + // else { + // // {-/inf, +/inf} + // if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { + // // {-/inf, +/inf} * {+, +/inf} + // if (now.m_upper_inf) { + // // {-/inf, +/inf} * {+, +inf) -> (-inf, +inf) + // pre.m_lower_inf = 1; + // pre.m_upper_inf = 1; + // } + // else { + // // {a, b} * {c, d} -> {ad, bd} + // // {-/inf, +/inf} * {+, +} + // if (pre.m_lower_inf == 0) { + // pre.m_lower_open = (now.m_upper_open | pre.m_lower_open); + // am.set(pre.m_lower, now.m_upper * pre.m_lower); + // } + // if (pre.m_upper_inf == 0) { + // pre.m_upper_open = (now.m_upper_open | pre.m_upper_open); + // am.set(pre.m_upper, now.m_upper * pre.m_upper); + // } + // } + // } + // else if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { + // // {-/inf, +/inf} * {-/inf, -} + // if (now.m_lower_inf) { + // // {-/inf, +/inf} * (-inf, -} -> (-inf, +inf) + // pre.m_lower_inf = 1; + // pre.m_upper_inf = 1; + // } + // else { + // // {-/inf, +/inf} * {-, -} + // // {pl, pu} * {nl, nu} -> {pu nl, pl nl} + // // order matters! + // if (pre.m_lower_inf == 0) { + // pre.m_lower_open = (pre.m_upper_open | now.m_lower_open); + // am.set(pre.m_lower, now.m_lower * pre.m_upper); + // } + // if (pre.m_upper_inf == 0) { + // pre.m_upper_open = (pre.m_lower_open | now.m_lower_open); + // am.set(pre.m_upper, now.m_lower * pre.m_lower); + // } + // } + // } + // else { + // // {-/inf, +/inf} * {-/inf, +/inf} + // if (pre.m_lower_inf || pre.m_upper_inf || + // now.m_lower_inf || now.m_upper_inf) { + // pre.m_lower_inf = 1; + // pre.m_upper_inf = 1; + // } + // else { + // // {-, +} * {-, +} + // scoped_anum plnl(am), punu(am); + // unsigned plo, puo; + // am.set(plnl, pre.m_lower * now.m_lower); + // am.set(punu, pre.m_upper * now.m_upper); + // scoped_anum plnu(am), punl(am); + // am.set(plnu, pre.m_lower * now.m_upper); + // am.set(punl, pre.m_upper * now.m_lower); + // if (plnl > punu) { + // puo = (pre.m_lower_open | now.m_lower_open); + // am.set(pre.m_upper, plnl); + // } + // else if (plnl == punu) { + // puo = ((pre.m_lower_open | now.m_lower_open) & + // (pre.m_upper_open | now.m_upper_open)); + // am.set(pre.m_upper, plnl); + // } + // else { + // puo = (pre.m_upper_open | now.m_upper_open); + // am.set(pre.m_upper, punu); + // } + // if (plnu < punl) { + // plo = (pre.m_lower_open | now.m_upper_open); + // am.set(pre.m_lower, plnu); + // } + // else if (plnu == punl) { + // plo = ((pre.m_lower_open | now.m_upper_open) & + // (pre.m_upper_open | now.m_lower_open)); + // am.set(pre.m_lower, plnu); + // } + // else { + // plo = (pre.m_upper_open | now.m_lower_open); + // am.set(pre.m_lower, punl); + // } + // } + // } + // } + } + + void get_monomial_domain(monomial *m, const scoped_anum &a, Domain_Interval &dom) { + LINXI_DEBUG; + TRACE("linxi_simple_checker", + tout << "monomial: "; + pm.display(tout, m); + tout << '\n'; + tout << "coefficient: " << a << "\n"; + tout << "# "; + for (unsigned i = 0, sz = pm.size(m); i < sz; ++i) { + var v = pm.get_var(m, i); + tout << " (" << i << ", " << pm.degree_of(m, v) << ")"; + } + tout << "\n"; + ); + + // [a, a] + dom.set_num(a); + for (unsigned i = 0, sz = pm.size(m); i < sz; ++i) { + var v = pm.get_var(m, i); + unsigned deg = pm.degree_of(m, v); + const Domain_Interval &di = ((deg & 1) == 0 ? vars_domain[v].mag_val : vars_domain[v].ori_val); + TRACE("linxi_simple_checker", + tout << "dom: "; + display(tout, am, dom); + tout << "\n"; + tout << "var: " << v; + tout << ", di: "; + display(tout, am, di); + tout << "\n"; + ); + for (unsigned j = 0; j < deg; ++j) { + merge_mul_domain(dom, di); + } + TRACE("linxi_simple_checker", + tout << "after merge mul: "; + display(tout, am, dom); + tout << "\n"; + ); + // mul 0 + // if (dom.m_lower_inf && dom.m_upper_inf) + // return ; + } + } + void endpoint_add(Endpoint &a, const Endpoint &b) { + a.m_inf = (a.m_inf | b.m_inf); + if (a.m_inf == 0) { + am.set(a.m_val, a.m_val + b.m_val); + a.m_open = (a.m_open | b.m_open); + } + } + void merge_add_domain(Domain_Interval &pre, const Domain_Interval &now) { + endpoint_add(pre.m_lower, now.m_lower); + endpoint_add(pre.m_upper, now.m_upper); + // pre.m_lower_inf = (pre.m_lower_inf | now.m_lower_inf); + // if (pre.m_lower_inf == 0) { + // am.set(pre.m_lower, pre.m_lower + now.m_lower); + // pre.m_lower_open = (pre.m_lower_open | now.m_lower_open); + // } + // pre.m_upper_inf = (pre.m_upper_inf | now.m_upper_inf); + // if (pre.m_upper_inf == 0) { + // am.set(pre.m_upper, pre.m_upper + now.m_upper); + // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); + // } + } + sign_kind get_poly_sign(const poly *p) { + LINXI_DEBUG; + scoped_anum a(am); + am.set(a, pm.coeff(p, 0)); + Domain_Interval pre(am); + get_monomial_domain(pm.get_monomial(p, 0), a, pre); + TRACE("linxi_simple_checker", + tout << "poly: "; + pm.display(tout, p); + tout << "\n"; + ); + TRACE("linxi_simple_checker", + tout << "pre: "; + display(tout, am, pre); + tout << "\n"; + ); + for (unsigned i = 1, sz = pm.size(p); i < sz; ++i) { + am.set(a, pm.coeff(p, i)); + Domain_Interval now(am); + get_monomial_domain(pm.get_monomial(p, i), a, now); + TRACE("linxi_simple_checker", + tout << "pre: "; + display(tout, am, pre); + tout << "\n"; + tout << "now: "; + display(tout, am, now); + tout << "\n"; + ); + if (now.m_lower.m_inf && now.m_upper.m_inf) + return NONE; + merge_add_domain(pre, now); + TRACE("linxi_simple_checker", + tout << "after merge: "; + display(tout, am, pre); + tout << "\n"; + ); + if (pre.m_lower.m_inf && pre.m_upper.m_inf) + return NONE; + } + // if (pre.m_lower_inf && pre.m_upper_inf) + // return NONE; + if (pre.m_lower.m_inf) { + if (am.is_neg(pre.m_upper.m_val)) { + // (-inf, -} + return LT; + } + else if (am.is_zero(pre.m_upper.m_val)) { + // (-inf, 0} + if (pre.m_upper.m_open) + return LT; + else + return LE; + } + else { + // (-inf, +} + return NONE; + } + } + else if (pre.m_upper.m_inf) { + if (am.is_neg(pre.m_lower.m_val)) { + // {-, +inf) + return NONE; + } + else if (am.is_zero(pre.m_lower.m_val)) { + // {0, +inf) + if (pre.m_lower.m_open) + return GT; + else + return GE; + } + else { + // {+, +inf) + return GT; + } + } + else { + // [0, 0] + if (am.is_zero(pre.m_lower.m_val) && am.is_zero(pre.m_upper.m_val)) + return EQ; + else if (am.is_zero(pre.m_lower.m_val)) { + // {0, +} + if (pre.m_lower.m_open) + return GT; + else + return GE; + } + else if (am.is_zero(pre.m_upper.m_val)) { + // {-, 0} + if (pre.m_upper.m_open) + return LT; + else + return LE; + } + else { + if (am.is_pos(pre.m_lower.m_val)) + return GT; + else if (am.is_neg(pre.m_upper.m_val)) + return LT; + else + return NONE; + } + } + return NONE; + } + + sign_kind get_poly_sign_degree(const poly *p, bool is_even) { + LINXI_DEBUG; + sign_kind ret = get_poly_sign(p); + if (is_even) { + if (ret == GE || ret == LE || ret == NONE) + ret = GE; + else if (ret != EQ) + ret = GT; + } + TRACE("linxi_simple_checker", + tout << "ret sign: "; + display(tout, ret); + tout << "\n"; + tout << "is even: " << is_even << "\n"; + ); + return ret; + } + void merge_mul_sign(sign_kind &pre, sign_kind now) { + if (pre == EQ) + return ; + if (now == EQ) { + pre = EQ; + return ; + } + if (pre == NONE) + return ; + + if (now == NONE) { + pre = NONE; + } + // else if (now == EQ) { + // pre = EQ; + // } + else if (now == LT) { + if (pre == GE) + pre = LE; + else if (pre == GT) + pre = LT; + else if (pre == LE) + pre = GE; + else if (pre == LT) + pre = GT; + } + else if (now == GT) { + // if (pre == LE) + // pre = LE; + // else if (pre == LT) + // pre = LT; + // else if (pre == GT) + // pre = GT; + // else if (pre == GE) + // pre = GE; + } + else if (now == LE) { + if (pre == GE) + pre = LE; + else if (pre == GT) + pre = LE; + else if (pre == LE) + pre = GE; + else if (pre == LT) + pre = GE; + } + else if (now == GE) { + // if (pre == LE) + // pre = LE; + if (pre == LT) + pre = LE; + else if (pre == GT) + pre = GE; + // else if (pre == GE) + // pre = GE; + } + } + bool check_ineq_atom_satisfiable(const ineq_atom *iat, bool s) { + LINXI_DEBUG; + TRACE("linxi_simple_checker", + tout << "s: " << s << "\n"; + tout << "kd: " << iat->get_kind() << "\n"; + ); + sign_kind nsk = to_sign_kind(iat->get_kind()); + if (s) { + if (nsk == EQ) + return true; + else if (nsk == GT) + nsk = LE; + else + nsk = GE; + } + TRACE("linxi_simple_checker", + tout << "ineq atom: " << '\n'; + for (unsigned i = 0, sz = iat->size(); i < sz; ++i) { + pm.display(tout, iat->p(i)); + tout << " is even: " << iat->is_even(i) << "\n"; + } + display(tout, nsk); + tout << " 0\n"; + ); + // return true; + + sign_kind pre = get_poly_sign_degree(iat->p(0), iat->is_even(0)); + + for (unsigned i = 1, sz = iat->size(); i < sz; ++i) { + sign_kind now = get_poly_sign_degree(iat->p(i), iat->is_even(i)); + // TRACE("linxi_simple_checker", + // tout << "pre: "; + // display(tout, pre); + // tout << ", now: "; + // display(tout, now); + // tout << "\n"; + // ); + merge_mul_sign(pre, now); + // TRACE("linxi_simple_checker", + // tout << "==> "; + // display(tout, pre); + // ); + if (pre == NONE) + return true; + if ((nsk == EQ || nsk == GE || nsk == LE) && + (pre == EQ || pre == GE || pre == LE)) + return true; + } + TRACE("linxi_simple_checker", + tout << "pre: "; + display(tout, pre); + tout << ", nsk: "; + display(tout, nsk); + tout << "\n"; + ); + if (pre == NONE) + return true; + if (pre == EQ && (nsk == LT || nsk == GT)) + return false; + if (pre == GE && nsk == LT) + return false; + if (pre == GT && (nsk == LT || nsk == LE || nsk == EQ)) + return false; + if (pre == LE && nsk == GT) + return false; + if (pre == LT && (nsk == GT || nsk == GE || nsk == EQ)) + return false; + return true; + } + bool check_literal_satisfiable(unsigned cid, unsigned lit_id) { + LINXI_DEBUG; + literal lit = (*clauses[cid])[lit_id]; + const var &v = lit.var(); + atom *at = atoms[v]; + if (at == nullptr) { + clauses_visited[cid].visited = true; + return true; + } + if (!at->is_ineq_atom()) { + clauses_visited[cid].visited = true; + return true; + } + // TRACE("linxi_sign", + // tout << "literal: " << lit << '\n'; + // ); + bool s = lit.sign(); + return check_ineq_atom_satisfiable(to_ineq_atom(at), s); + } + bool check_clause_satisfiable(unsigned cid) { + LINXI_DEBUG; + const clause *cla = clauses[cid]; + TRACE("linxi_simple_checker", + tout << "clause size: " << cla->size() << '\n'; + ); + unsigned sz = cla->size(); + if (clauses_visited[cid].literal_visited.size() == 0) { + clauses_visited[cid].literal_visited.resize(sz, false); + literal_special_kind[cid].resize(sz, UNK); + } + unsigned sat_lit_id = sz; + for (unsigned i = 0; i < sz; ++i) { + if (clauses_visited[cid].literal_visited[i]) + continue; + if (check_literal_satisfiable(cid, i)) { + if (clauses_visited[cid].visited) + return true; + if (sat_lit_id == sz) + sat_lit_id = i; + else + return true; + } else { + clauses_visited[cid].literal_visited[i] = true; + literal lit = (*clauses[cid])[i]; + lit.neg(); + // if (atoms[lit.var()] != nullptr && atoms[lit.var()]->is_ineq_atom()) { + // ineq_atom *iat = to_ineq_atom(atoms[lit.var()]); + // if (to_sign_kind(iat->get_kind()) == EQ && lit.sign()) { + // continue; + // } + // } + learned_unit.push_back(lit); + // sol.mk_clause(1, &lit); + TRACE("linxi_simple_checker_learned", + tout << "making new clauses!\n"; + tout << "sign: " << lit.sign() << '\n'; + if (atoms[lit.var()] != nullptr && atoms[lit.var()]->is_ineq_atom()) { + ineq_atom *iat = to_ineq_atom(atoms[lit.var()]); + tout << "ineq atom: " << '\n'; + sign_kind nsk = to_sign_kind(iat->get_kind()); + for (unsigned i = 0, sz = iat->size(); i < sz; ++i) { + pm.display(tout, iat->p(i)); + tout << " is even: " << iat->is_even(i) << "\n"; + } + display(tout, nsk); + tout << " 0\n"; + } + ); + } + } + if (sat_lit_id != sz) { + if (!collect_domain_axbc_form(cid, sat_lit_id)) + return false; + if (!collect_domain_axbsc_form(cid, sat_lit_id)) + return false; + return true; + } + return false; + } + bool check() { + LINXI_DEBUG; + for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { + if (clauses_visited[i].visited) + continue; + if (!check_clause_satisfiable(i)) { + return false; + } + } + return true; + } + void test_anum() { + scoped_anum x(am), y(am); + am.set(x, 3); + am.set(y, 5); + TRACE("linxi_simple_checker", + tout << x << " " << y << std::endl; + ); + } + bool operator()() { + + improved = true; + while (improved) { + improved = false; + if (!check()) + return false; + TRACE("linxi_simple_checker", + for (unsigned i = 0; i < arith_var_num; ++i) { + tout << "====== arith[" << i << "] ======\n"; + tout << "original value: "; + display(tout, am, vars_domain[i].ori_val); + tout << "\nmagitude value: "; + display(tout, am, vars_domain[i].mag_val); + tout << "\n"; + tout << "====== arith[" << i << "] ======\n"; + } + ); + } + // LINXI_DEBUG; + // // test_anum(); + // if (!collect_var_domain()) + // return false; + // TRACE("linxi_simple_checker", + // for (unsigned i = 0; i < arith_var_num; ++i) { + // tout << "====== arith[" << i << "] ======\n"; + // tout << "original value: "; + // display(tout, am, vars_domain[i].ori_val); + // tout << "\nmagitude value: "; + // display(tout, am, vars_domain[i].mag_val); + // tout << "\n"; + // tout << "====== arith[" << i << "] ======\n"; + // } + // ); + // if (!check()) + // return false; + return true; + } + }; + // Simple_Checker::Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num) { + Simple_Checker::Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) { + LINXI_DEBUG; + // m_imp = alloc(imp, _sol, _pm, _am, _clauses, _learned, _atoms, _arith_var_num); + m_imp = alloc(imp, _pm, _am, _clauses, _learned_unit, _atoms, _arith_var_num); + } + Simple_Checker::~Simple_Checker() { + LINXI_DEBUG; + dealloc(m_imp); + } + bool Simple_Checker::operator()() { + LINXI_DEBUG; + return m_imp->operator()(); + } +} \ No newline at end of file diff --git a/src/nlsat/nlsat_simple_checker.h b/src/nlsat/nlsat_simple_checker.h new file mode 100644 index 00000000000..969c7a14175 --- /dev/null +++ b/src/nlsat/nlsat_simple_checker.h @@ -0,0 +1,15 @@ +#include "math/polynomial/algebraic_numbers.h" +#include "nlsat/nlsat_clause.h" + + +namespace nlsat { + class Simple_Checker { + struct imp; + imp * m_imp; + public: + // Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num); + Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num); + ~Simple_Checker(); + bool operator()(); + }; +} \ No newline at end of file diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp deleted file mode 100644 index 9f43b7a787a..00000000000 --- a/src/nlsat/nlsat_solver.cpp +++ /dev/null @@ -1,4027 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - nlsat_solver.cpp - -Abstract: - - Nonlinear arithmetic satisfiability procedure. The procedure is - complete for nonlinear real arithmetic, but it also has limited - support for integers. - -Author: - - Leonardo de Moura (leonardo) 2012-01-02. - -Revision History: - ---*/ -#include "util/z3_exception.h" -#include "util/chashtable.h" -#include "util/id_gen.h" -#include "util/map.h" -#include "util/dependency.h" -#include "util/permutation.h" -#include "math/polynomial/algebraic_numbers.h" -#include "math/polynomial/polynomial_cache.h" -#include "nlsat/nlsat_solver.h" -#include "nlsat/nlsat_clause.h" -#include "nlsat/nlsat_assignment.h" -#include "nlsat/nlsat_justification.h" -#include "nlsat/nlsat_evaluator.h" -#include "nlsat/nlsat_explain.h" -#include "nlsat/nlsat_params.hpp" -#include "nlsat/nlsat_simplify.h" - -#define NLSAT_EXTRA_VERBOSE - -#ifdef NLSAT_EXTRA_VERBOSE -#define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) -#else -#define NLSAT_VERBOSE(CODE) ((void)0) -#endif - -namespace nlsat { - - - typedef chashtable ineq_atom_table; - typedef chashtable root_atom_table; - - // for apply_permutation procedure - void swap(clause * & c1, clause * & c2) noexcept { - std::swap(c1, c2); - } - - struct solver::ctx { - params_ref m_params; - reslimit& m_rlimit; - small_object_allocator m_allocator; - unsynch_mpq_manager m_qm; - pmanager m_pm; - anum_manager m_am; - bool m_incremental; - ctx(reslimit& rlim, params_ref const & p, bool incremental): - m_params(p), - m_rlimit(rlim), - m_allocator("nlsat"), - m_pm(rlim, m_qm, &m_allocator), - m_am(rlim, m_qm, p, &m_allocator), - m_incremental(incremental) - {} - }; - - struct solver::imp { - - - struct dconfig { - typedef imp value_manager; - typedef small_object_allocator allocator; - typedef void* value; - static const bool ref_count = false; - }; - - typedef dependency_manager assumption_manager; - typedef assumption_manager::dependency* _assumption_set; - - typedef obj_ref assumption_set_ref; - - - typedef polynomial::cache cache; - typedef ptr_vector interval_set_vector; - - - - ctx& m_ctx; - solver& m_solver; - reslimit& m_rlimit; - small_object_allocator& m_allocator; - bool m_incremental; - unsynch_mpq_manager& m_qm; - pmanager& m_pm; - cache m_cache; - anum_manager& m_am; - mutable assumption_manager m_asm; - assignment m_assignment, m_lo, m_hi; // partial interpretation - evaluator m_evaluator; - interval_set_manager & m_ism; - ineq_atom_table m_ineq_atoms; - root_atom_table m_root_atoms; - - - vector m_bounds; - - id_gen m_cid_gen; - clause_vector m_clauses; // set of clauses - clause_vector m_learned; // set of learned clauses - clause_vector m_valids; - - unsigned m_num_bool_vars; - atom_vector m_atoms; // bool_var -> atom - svector m_bvalues; // boolean assignment - unsigned_vector m_levels; // bool_var -> level - svector m_justifications; - vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal - bool_vector m_dead; // mark dead boolean variables - id_gen m_bid_gen; - - simplify m_simplify; - - bool_vector m_is_int; // m_is_int[x] is true if variable is integer - vector m_watches; // var -> clauses where variable is maximal - interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. - atom_vector m_var2eq; // var -> to asserted equality - var_vector m_perm; // var -> var permutation of the variables - var_vector m_inv_perm; - // m_perm: internal -> external - // m_inv_perm: external -> internal - struct perm_display_var_proc : public display_var_proc { - var_vector & m_perm; - display_var_proc m_default_display_var; - display_var_proc const * m_proc; // display external var ids - perm_display_var_proc(var_vector & perm): - m_perm(perm), - m_proc(nullptr) { - } - std::ostream& operator()(std::ostream & out, var x) const override { - if (m_proc == nullptr) - m_default_display_var(out, x); - else - (*m_proc)(out, m_perm[x]); - return out; - } - }; - perm_display_var_proc m_display_var; - - display_assumption_proc const* m_display_assumption; - struct display_literal_assumption : public display_assumption_proc { - imp& i; - literal_vector const& lits; - display_literal_assumption(imp& i, literal_vector const& lits): i(i), lits(lits) {} - std::ostream& operator()(std::ostream& out, assumption a) const override { - if (lits.begin() <= a && a < lits.end()) { - out << *((literal const*)a); - } - else if (i.m_display_assumption) { - (*i.m_display_assumption)(out, a); - } - return out; - } - - }; - struct scoped_display_assumptions { - imp& i; - display_assumption_proc const* m_save; - scoped_display_assumptions(imp& i, display_assumption_proc const& p): i(i), m_save(i.m_display_assumption) { - i.m_display_assumption = &p; - } - ~scoped_display_assumptions() { - i.m_display_assumption = m_save; - } - }; - - explain m_explain; - - bool_var m_bk; // current Boolean variable we are processing - var m_xk; // current arith variable we are processing - - unsigned m_scope_lvl; - - struct bvar_assignment {}; - struct stage {}; - struct trail { - enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; - kind m_kind; - union { - bool_var m_b; - interval_set * m_old_set; - atom * m_old_eq; - }; - trail(bool_var b, bvar_assignment):m_kind(BVAR_ASSIGNMENT), m_b(b) {} - trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} - trail(bool s, stage):m_kind(s ? NEW_STAGE : NEW_LEVEL) {} - trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} - }; - svector m_trail; - - anum m_zero; - - // configuration - unsigned long long m_max_memory; - unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts - bool m_simplify_cores; - bool m_reorder; - bool m_randomize; - bool m_random_order; - unsigned m_random_seed; - bool m_inline_vars; - bool m_log_lemmas; - bool m_check_lemmas; - unsigned m_max_conflicts; - unsigned m_lemma_count; - - struct stats { - unsigned m_simplifications; - unsigned m_restarts; - unsigned m_conflicts; - unsigned m_propagations; - unsigned m_decisions; - unsigned m_stages; - unsigned m_irrational_assignments; // number of irrational witnesses - void reset() { memset(this, 0, sizeof(*this)); } - stats() { reset(); } - }; - // statistics - stats m_stats; - - imp(solver& s, ctx& c): - m_ctx(c), - m_solver(s), - m_rlimit(c.m_rlimit), - m_allocator(c.m_allocator), - m_incremental(c.m_incremental), - m_qm(c.m_qm), - m_pm(c.m_pm), - m_cache(m_pm), - m_am(c.m_am), - m_asm(*this, m_allocator), - m_assignment(m_am), m_lo(m_am), m_hi(m_am), - m_evaluator(s, m_assignment, m_pm, m_allocator), - m_ism(m_evaluator.ism()), - m_num_bool_vars(0), - m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), - m_display_var(m_perm), - m_display_assumption(nullptr), - m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()), - m_scope_lvl(0), - m_lemma(s), - m_lazy_clause(s), - m_lemma_assumptions(m_asm) { - updt_params(c.m_params); - reset_statistics(); - mk_true_bvar(); - m_lemma_count = 0; - } - - ~imp() { - clear(); - } - - void mk_true_bvar() { - bool_var b = mk_bool_var(); - SASSERT(b == true_bool_var); - literal true_lit(b, false); - mk_clause(1, &true_lit, false, nullptr); - } - - void updt_params(params_ref const & _p) { - nlsat_params p(_p); - m_max_memory = p.max_memory(); - m_lazy = p.lazy(); - m_simplify_cores = p.simplify_conflicts(); - bool min_cores = p.minimize_conflicts(); - m_reorder = p.reorder(); - m_randomize = p.randomize(); - m_max_conflicts = p.max_conflicts(); - m_random_order = p.shuffle_vars(); - m_random_seed = p.seed(); - m_inline_vars = p.inline_vars(); - m_log_lemmas = p.log_lemmas(); - m_check_lemmas = p.check_lemmas(); - m_ism.set_seed(m_random_seed); - m_explain.set_simplify_cores(m_simplify_cores); - m_explain.set_minimize_cores(min_cores); - m_explain.set_factor(p.factor()); - m_am.updt_params(p.p); - } - - void reset() { - m_explain.reset(); - m_lemma.reset(); - m_lazy_clause.reset(); - undo_until_size(0); - del_clauses(); - del_unref_atoms(); - m_cache.reset(); - m_assignment.reset(); - m_lo.reset(); - m_hi.reset(); - } - - void clear() { - m_explain.reset(); - m_lemma.reset(); - m_lazy_clause.reset(); - undo_until_size(0); - del_clauses(); - del_unref_atoms(); - } - - void checkpoint() { - if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); - if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); - } - - // ----------------------- - // - // Basic - // - // ----------------------- - - unsigned num_bool_vars() const { - return m_num_bool_vars; - } - - unsigned num_vars() const { - return m_is_int.size(); - } - - bool is_int(var x) const { - return m_is_int[x]; - } - - void inc_ref(assumption) {} - - void dec_ref(assumption) {} - - void inc_ref(_assumption_set a) { - if (a != nullptr) m_asm.inc_ref(a); - } - - void dec_ref(_assumption_set a) { - if (a != nullptr) m_asm.dec_ref(a); - } - - void inc_ref(bool_var b) { - if (b == null_bool_var) - return; - atom * a = m_atoms[b]; - if (a == nullptr) - return; - TRACE("ref", display(tout << "inc: " << b << " " << a->ref_count() << " ", *a) << "\n";); - a->inc_ref(); - } - - void inc_ref(literal l) { - inc_ref(l.var()); - } - - void dec_ref(bool_var b) { - if (b == null_bool_var) - return; - atom * a = m_atoms[b]; - if (a == nullptr) - return; - SASSERT(a->ref_count() > 0); - a->dec_ref(); - TRACE("ref", display(tout << "dec: " << b << " " << a->ref_count() << " ", *a) << "\n";); - if (a->ref_count() == 0) - del(a); - } - - void dec_ref(literal l) { - dec_ref(l.var()); - } - - bool is_arith_atom(bool_var b) const { return m_atoms[b] != nullptr; } - - bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } - - var max_var(poly const * p) const { - return m_pm.max_var(p); - } - - var max_var(bool_var b) const { - if (!is_arith_atom(b)) - return null_var; - else - return m_atoms[b]->max_var(); - } - - var max_var(literal l) const { - return max_var(l.var()); - } - - /** - \brief Return the maximum variable occurring in cls. - */ - var max_var(unsigned sz, literal const * cls) const { - var x = null_var; - for (unsigned i = 0; i < sz; i++) { - literal l = cls[i]; - if (is_arith_literal(l)) { - var y = max_var(l); - if (x == null_var || y > x) - x = y; - } - } - return x; - } - - var max_var(clause const & cls) const { - return max_var(cls.size(), cls.data()); - } - - /** - \brief Return the maximum Boolean variable occurring in cls. - */ - bool_var max_bvar(clause const & cls) const { - bool_var b = null_bool_var; - for (literal l : cls) { - if (b == null_bool_var || l.var() > b) - b = l.var(); - } - return b; - } - - /** - \brief Return the degree of the maximal variable of the given atom - */ - unsigned degree(atom const * a) const { - if (a->is_ineq_atom()) { - unsigned max = 0; - unsigned sz = to_ineq_atom(a)->size(); - var x = a->max_var(); - for (unsigned i = 0; i < sz; i++) { - unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); - if (d > max) - max = d; - } - return max; - } - else { - return m_pm.degree(to_root_atom(a)->p(), a->max_var()); - } - } - - /** - \brief Return the degree of the maximal variable in c - */ - unsigned degree(clause const & c) const { - var x = max_var(c); - if (x == null_var) - return 0; - unsigned max = 0; - for (literal l : c) { - atom const * a = m_atoms[l.var()]; - if (a == nullptr) - continue; - unsigned d = degree(a); - if (d > max) - max = d; - } - return max; - } - - // ----------------------- - // - // Variable, Atoms, Clauses & Assumption creation - // - // ----------------------- - - bool_var mk_bool_var_core() { - bool_var b = m_bid_gen.mk(); - m_num_bool_vars++; - m_atoms .setx(b, nullptr, nullptr); - m_bvalues .setx(b, l_undef, l_undef); - m_levels .setx(b, UINT_MAX, UINT_MAX); - m_justifications.setx(b, null_justification, null_justification); - m_bwatches .setx(b, clause_vector(), clause_vector()); - m_dead .setx(b, false, true); - return b; - } - - bool_var mk_bool_var() { - return mk_bool_var_core(); - } - - var mk_var(bool is_int) { - var x = m_pm.mk_var(); - register_var(x, is_int); - return x; - } - void register_var(var x, bool is_int) { - SASSERT(x == num_vars()); - m_is_int. push_back(is_int); - m_watches. push_back(clause_vector()); - m_infeasible.push_back(nullptr); - m_var2eq. push_back(nullptr); - m_perm. push_back(x); - m_inv_perm. push_back(x); - SASSERT(m_is_int.size() == m_watches.size()); - SASSERT(m_is_int.size() == m_infeasible.size()); - SASSERT(m_is_int.size() == m_var2eq.size()); - SASSERT(m_is_int.size() == m_perm.size()); - SASSERT(m_is_int.size() == m_inv_perm.size()); - } - - bool_vector m_found_vars; - void vars(literal l, var_vector& vs) { - vs.reset(); - atom * a = m_atoms[l.var()]; - if (a == nullptr) { - - } - else if (a->is_ineq_atom()) { - unsigned sz = to_ineq_atom(a)->size(); - var_vector new_vs; - for (unsigned j = 0; j < sz; j++) { - m_found_vars.reset(); - m_pm.vars(to_ineq_atom(a)->p(j), new_vs); - for (unsigned i = 0; i < new_vs.size(); ++i) { - if (!m_found_vars.get(new_vs[i], false)) { - m_found_vars.setx(new_vs[i], true, false); - vs.push_back(new_vs[i]); - } - } - } - } - else { - m_pm.vars(to_root_atom(a)->p(), vs); - //vs.erase(max_var(to_root_atom(a)->p())); - vs.push_back(to_root_atom(a)->x()); - } - } - - void deallocate(ineq_atom * a) { - unsigned obj_sz = ineq_atom::get_obj_size(a->size()); - a->~ineq_atom(); - m_allocator.deallocate(obj_sz, a); - } - - void deallocate(root_atom * a) { - a->~root_atom(); - m_allocator.deallocate(sizeof(root_atom), a); - } - - void del(bool_var b) { - SASSERT(m_bwatches[b].empty()); - //SASSERT(m_bvalues[b] == l_undef); - m_num_bool_vars--; - m_dead[b] = true; - m_atoms[b] = nullptr; - m_bvalues[b] = l_undef; - m_bid_gen.recycle(b); - } - - void del(ineq_atom * a) { - CTRACE("nlsat_solver", a->ref_count() > 0, display(tout, *a) << "\n";); - // this triggers in too many benign cases: - // SASSERT(a->ref_count() == 0); - m_ineq_atoms.erase(a); - del(a->bvar()); - unsigned sz = a->size(); - for (unsigned i = 0; i < sz; i++) - m_pm.dec_ref(a->p(i)); - deallocate(a); - } - - void del(root_atom * a) { - SASSERT(a->ref_count() == 0); - m_root_atoms.erase(a); - del(a->bvar()); - m_pm.dec_ref(a->p()); - deallocate(a); - } - - void del(atom * a) { - if (a == nullptr) - return; - TRACE("nlsat_verbose", display(tout << "del: b" << a->m_bool_var << " " << a->ref_count() << " ", *a) << "\n";); - if (a->is_ineq_atom()) - del(to_ineq_atom(a)); - else - del(to_root_atom(a)); - } - - // Delete atoms with ref_count == 0 - void del_unref_atoms() { - for (auto* a : m_atoms) { - del(a); - } - } - - - ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new, bool simplify) { - SASSERT(sz >= 1); - SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); - int sign = 1; - polynomial_ref p(m_pm); - ptr_buffer uniq_ps; - var max = null_var; - for (unsigned i = 0; i < sz; i++) { - p = m_pm.flip_sign_if_lm_neg(ps[i]); - if (p.get() != ps[i] && !is_even[i]) { - sign = -sign; - } - var curr_max = max_var(p.get()); - if (curr_max > max || max == null_var) - max = curr_max; - if (sz == 1 && simplify) { - if (sign < 0) - k = atom::flip(k); - sign = 1; - polynomial::manager::ineq_type t; - switch (k) { - case atom::EQ: t = polynomial::manager::ineq_type::EQ; break; - case atom::LT: t = polynomial::manager::ineq_type::LT; break; - case atom::GT: t = polynomial::manager::ineq_type::GT; break; - default: UNREACHABLE(); break; - } - polynomial::var_vector vars; - m_pm.vars(p, vars); - bool all_int = all_of(vars, [&](var x) { return is_int(x); }); - if (!all_int) - t = polynomial::manager::ineq_type::EQ; - m_pm.gcd_simplify(p, t); - } - uniq_ps.push_back(m_cache.mk_unique(p)); - TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); - //verbose_stream() << "p: " << p.get() << ", uniq: " << uniq_ps.back() << "\n"; - } - void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); - if (sign < 0) - k = atom::flip(k); - ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.data(), is_even, max); - ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); - CTRACE("nlsat_table_bug", tmp_atom != atom, ineq_atom::hash_proc h; - tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var) << "\n";); - CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout << "nonmax: ", *atom, m_display_var) << "\n";); - SASSERT(atom->max_var() == max); - is_new = (atom == tmp_atom); - if (is_new) { - for (unsigned i = 0; i < sz; i++) { - m_pm.inc_ref(atom->p(i)); - } - } - else { - deallocate(tmp_atom); - } - return atom; - } - - bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { - bool is_new = false; - ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new, simplify); - if (!is_new) { - return atom->bvar(); - } - else { - bool_var b = mk_bool_var_core(); - m_atoms[b] = atom; - atom->m_bool_var = b; - TRACE("nlsat_verbose", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); - return b; - } - } - - literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { - SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); - bool is_const = true; - polynomial::manager::scoped_numeral cnst(m_pm.m()); - m_pm.m().set(cnst, 1); - for (unsigned i = 0; i < sz; ++i) { - if (m_pm.is_const(ps[i])) { - if (m_pm.is_zero(ps[i])) { - m_pm.m().set(cnst, 0); - is_const = true; - break; - } - auto const& c = m_pm.coeff(ps[i], 0); - m_pm.m().mul(cnst, c, cnst); - if (is_even[i] && m_pm.m().is_neg(c)) { - m_pm.m().neg(cnst); - } - } - else { - is_const = false; - } - } - if (is_const) { - if (m_pm.m().is_pos(cnst) && k == atom::GT) return true_literal; - if (m_pm.m().is_neg(cnst) && k == atom::LT) return true_literal; - if (m_pm.m().is_zero(cnst) && k == atom::EQ) return true_literal; - return false_literal; - } - return literal(mk_ineq_atom(k, sz, ps, is_even, simplify), false); - } - - bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { - polynomial_ref p1(m_pm), uniq_p(m_pm); - p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. - uniq_p = m_cache.mk_unique(p1); - TRACE("nlsat_solver", tout << x << " " << p1 << " " << uniq_p << "\n";); - SASSERT(i > 0); - SASSERT(x >= max_var(p)); - SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); - - void * mem = m_allocator.allocate(sizeof(root_atom)); - root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); - root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); - SASSERT(old_atom->max_var() == x); - if (old_atom != new_atom) { - deallocate(new_atom); - return old_atom->bvar(); - } - bool_var b = mk_bool_var_core(); - m_atoms[b] = new_atom; - new_atom->m_bool_var = b; - m_pm.inc_ref(new_atom->p()); - return b; - } - - void attach_clause(clause & cls) { - var x = max_var(cls); - if (x != null_var) { - m_watches[x].push_back(&cls); - } - else { - bool_var b = max_bvar(cls); - m_bwatches[b].push_back(&cls); - } - } - - void deattach_clause(clause & cls) { - var x = max_var(cls); - if (x != null_var) { - m_watches[x].erase(&cls); - } - else { - bool_var b = max_bvar(cls); - m_bwatches[b].erase(&cls); - } - } - - void deallocate(clause * cls) { - size_t obj_sz = clause::get_obj_size(cls->size()); - cls->~clause(); - m_allocator.deallocate(obj_sz, cls); - } - - void del_clause(clause * cls) { - deattach_clause(*cls); - m_cid_gen.recycle(cls->id()); - unsigned sz = cls->size(); - for (unsigned i = 0; i < sz; i++) - dec_ref((*cls)[i]); - _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); - dec_ref(a); - deallocate(cls); - } - - void del_clause(clause * cls, clause_vector& clauses) { - clauses.erase(cls); - del_clause(cls); - } - - void del_clauses(ptr_vector & cs) { - for (clause* cp : cs) - del_clause(cp); - cs.reset(); - } - - void del_clauses() { - del_clauses(m_clauses); - del_clauses(m_learned); - del_clauses(m_valids); - } - - // We use a simple heuristic to sort literals - // - bool literals < arith literals - // - sort literals based on max_var - // - sort literal with the same max_var using degree - // break ties using the fact that ineqs are usually cheaper to process than eqs. - struct lit_lt { - imp & m; - lit_lt(imp & _m):m(_m) {} - - bool operator()(literal l1, literal l2) const { - atom * a1 = m.m_atoms[l1.var()]; - atom * a2 = m.m_atoms[l2.var()]; - if (a1 == nullptr && a2 == nullptr) - return l1.index() < l2.index(); - if (a1 == nullptr) - return true; - if (a2 == nullptr) - return false; - var x1 = a1->max_var(); - var x2 = a2->max_var(); - if (x1 < x2) - return true; - if (x1 > x2) - return false; - SASSERT(x1 == x2); - unsigned d1 = m.degree(a1); - unsigned d2 = m.degree(a2); - if (d1 < d2) - return true; - if (d1 > d2) - return false; - if (!a1->is_eq() && a2->is_eq()) - return true; - if (a1->is_eq() && !a2->is_eq()) - return false; - return l1.index() < l2.index(); - } - }; - - class scoped_bool_vars { - imp& s; - svector vec; - public: - scoped_bool_vars(imp& s):s(s) {} - ~scoped_bool_vars() { - for (bool_var v : vec) { - s.dec_ref(v); - } - } - void push_back(bool_var v) { - s.inc_ref(v); - vec.push_back(v); - } - bool_var const* begin() const { return vec.begin(); } - bool_var const* end() const { return vec.end(); } - bool_var operator[](bool_var v) const { return vec[v]; } - }; - - void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { - TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; - display(tout);); - IF_VERBOSE(2, display(verbose_stream() << "check lemma " << (is_valid?"valid: ":"consequence: "), n, cls) << "\n"); - for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); - scoped_suspend_rlimit _limit(m_rlimit); - ctx c(m_rlimit, m_ctx.m_params, m_ctx.m_incremental); - solver solver2(c); - imp& checker = *(solver2.m_imp); - checker.m_check_lemmas = false; - checker.m_log_lemmas = false; - checker.m_inline_vars = false; - - auto pconvert = [&](poly* p) { - return convert(m_pm, p, checker.m_pm); - }; - - // need to translate Boolean variables and literals - scoped_bool_vars tr(checker); - for (var x = 0; x < m_is_int.size(); ++x) { - checker.register_var(x, is_int(x)); - } - bool_var bv = 0; - tr.push_back(bv); - for (bool_var b = 1; b < m_atoms.size(); ++b) { - atom* a = m_atoms[b]; - if (a == nullptr) { - bv = checker.mk_bool_var(); - } - else if (a->is_ineq_atom()) { - ineq_atom& ia = *to_ineq_atom(a); - unsigned sz = ia.size(); - polynomial_ref_vector ps(checker.m_pm); - bool_vector is_even; - for (unsigned i = 0; i < sz; ++i) { - ps.push_back(pconvert(ia.p(i))); - is_even.push_back(ia.is_even(i)); - } - bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.data(), is_even.data()); - } - else if (a->is_root_atom()) { - root_atom& r = *to_root_atom(a); - if (r.x() >= max_var(r.p())) { - // permutation may be reverted after check completes, - // but then root atoms are not used in lemmas. - bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), pconvert(r.p())); - } - } - else { - UNREACHABLE(); - } - tr.push_back(bv); - } - if (!is_valid) { - for (clause* c : m_clauses) { - if (!a && c->assumptions()) { - continue; - } - literal_vector lits; - for (literal lit : *c) { - lits.push_back(literal(tr[lit.var()], lit.sign())); - } - checker.mk_external_clause(lits.size(), lits.data(), nullptr); - } - } - for (unsigned i = 0; i < n; ++i) { - literal lit = cls[i]; - literal nlit(tr[lit.var()], !lit.sign()); - checker.mk_external_clause(1, &nlit, nullptr); - } - lbool r = checker.check(); - if (r == l_true) { - for (bool_var b : tr) { - literal lit(b, false); - IF_VERBOSE(0, checker.display(verbose_stream(), lit) << " := " << checker.value(lit) << "\n"); - TRACE("nlsat", checker.display(tout, lit) << " := " << checker.value(lit) << "\n";); - } - for (clause* c : m_learned) { - bool found = false; - for (literal lit: *c) { - literal tlit(tr[lit.var()], lit.sign()); - found |= checker.value(tlit) == l_true; - } - if (!found) { - IF_VERBOSE(0, display(verbose_stream() << "violdated clause: ", *c) << "\n"); - TRACE("nlsat", display(tout << "violdated clause: ", *c) << "\n";); - } - } - for (clause* c : m_valids) { - bool found = false; - for (literal lit: *c) { - literal tlit(tr[lit.var()], lit.sign()); - found |= checker.value(tlit) == l_true; - } - if (!found) { - IF_VERBOSE(0, display(verbose_stream() << "violdated tautology clause: ", *c) << "\n"); - TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); - } - } - throw default_exception("lemma did not check"); - UNREACHABLE(); - } - } - - void log_lemma(std::ostream& out, clause const& cls) { - log_lemma(out, cls.size(), cls.data(), false); - } - - void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { - ++m_lemma_count; - out << "(set-logic NRA)\n"; - if (is_valid) { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); - } - else - display_smt2(out); - for (unsigned i = 0; i < n; ++i) - display_smt2(out << "(assert ", ~cls[i]) << ")\n"; - display(out << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"; - out << "(check-sat)\n(reset)\n"; - - TRACE("nlsat", display(tout << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"); - } - - clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { - SASSERT(num_lits > 0); - unsigned cid = m_cid_gen.mk(); - void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); - clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); - for (unsigned i = 0; i < num_lits; i++) - inc_ref(lits[i]); - inc_ref(a); - return cls; - } - - clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { - if (num_lits == 0) { - num_lits = 1; - lits = &false_literal; - } - SASSERT(num_lits > 0); - clause * cls = mk_clause_core(num_lits, lits, learned, a); - TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); - std::sort(cls->begin(), cls->end(), lit_lt(*this)); - TRACE("nlsat", display(tout << " after sort:\n", *cls) << "\n";); - if (learned && m_log_lemmas) { - log_lemma(verbose_stream(), *cls); - } - if (learned && m_check_lemmas && false) { - check_lemma(cls->size(), cls->data(), false, cls->assumptions()); - } - if (learned) - m_learned.push_back(cls); - else - m_clauses.push_back(cls); - attach_clause(*cls); - return cls; - } - - void mk_external_clause(unsigned num_lits, literal const * lits, assumption a) { - _assumption_set as = nullptr; - if (a != nullptr) - as = m_asm.mk_leaf(a); - if (num_lits == 0) { - num_lits = 1; - lits = &false_literal; - } - mk_clause(num_lits, lits, false, as); - } - - // ----------------------- - // - // Search - // - // ----------------------- - - void save_assign_trail(bool_var b) { - m_trail.push_back(trail(b, bvar_assignment())); - } - - void save_set_updt_trail(interval_set * old_set) { - m_trail.push_back(trail(old_set)); - } - - void save_updt_eq_trail(atom * old_eq) { - m_trail.push_back(trail(old_eq)); - } - - void save_new_stage_trail() { - m_trail.push_back(trail(true, stage())); - } - - void save_new_level_trail() { - m_trail.push_back(trail(false, stage())); - } - - void undo_bvar_assignment(bool_var b) { - m_bvalues[b] = l_undef; - m_levels[b] = UINT_MAX; - del_jst(m_allocator, m_justifications[b]); - m_justifications[b] = null_justification; - if (m_atoms[b] == nullptr && b < m_bk) - m_bk = b; - } - - void undo_set_updt(interval_set * old_set) { - if (m_xk == null_var) - return; - var x = m_xk; - if (x < m_infeasible.size()) { - m_ism.dec_ref(m_infeasible[x]); - m_infeasible[x] = old_set; - } - } - - void undo_new_stage() { - if (m_xk == 0) { - m_xk = null_var; - } - else if (m_xk != null_var) { - m_xk--; - m_assignment.reset(m_xk); - } - } - - void undo_new_level() { - SASSERT(m_scope_lvl > 0); - m_scope_lvl--; - m_evaluator.pop(1); - } - - void undo_updt_eq(atom * a) { - if (m_var2eq.size() > m_xk) - m_var2eq[m_xk] = a; - } - - template - void undo_until(Predicate const & pred) { - while (pred() && !m_trail.empty()) { - trail & t = m_trail.back(); - switch (t.m_kind) { - case trail::BVAR_ASSIGNMENT: - undo_bvar_assignment(t.m_b); - break; - case trail::INFEASIBLE_UPDT: - undo_set_updt(t.m_old_set); - break; - case trail::NEW_STAGE: - undo_new_stage(); - break; - case trail::NEW_LEVEL: - undo_new_level(); - break; - case trail::UPDT_EQ: - undo_updt_eq(t.m_old_eq); - break; - default: - break; - } - m_trail.pop_back(); - } - } - - struct size_pred { - svector & m_trail; - unsigned m_old_size; - size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} - bool operator()() const { return m_trail.size() > m_old_size; } - }; - - // Keep undoing until trail has the given size - void undo_until_size(unsigned old_size) { - SASSERT(m_trail.size() >= old_size); - undo_until(size_pred(m_trail, old_size)); - } - - struct stage_pred { - var const & m_xk; - var m_target; - stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} - bool operator()() const { return m_xk != m_target; } - }; - - // Keep undoing until stage is new_xk - void undo_until_stage(var new_xk) { - undo_until(stage_pred(m_xk, new_xk)); - } - - struct level_pred { - unsigned const & m_scope_lvl; - unsigned m_new_lvl; - level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} - bool operator()() const { return m_scope_lvl > m_new_lvl; } - }; - - // Keep undoing until level is new_lvl - void undo_until_level(unsigned new_lvl) { - undo_until(level_pred(m_scope_lvl, new_lvl)); - } - - struct unassigned_pred { - bool_var m_b; - svector const & m_bvalues; - unassigned_pred(svector const & bvalues, bool_var b): - m_b(b), - m_bvalues(bvalues) {} - bool operator()() const { return m_bvalues[m_b] != l_undef; } - }; - - // Keep undoing until b is unassigned - void undo_until_unassigned(bool_var b) { - undo_until(unassigned_pred(m_bvalues, b)); - SASSERT(m_bvalues[b] == l_undef); - } - - struct true_pred { - bool operator()() const { return true; } - }; - - void undo_until_empty() { - undo_until(true_pred()); - } - - /** - \brief Create a new scope level - */ - void new_level() { - m_evaluator.push(); - m_scope_lvl++; - save_new_level_trail(); - } - - /** - \brief Return the value of the given literal that was assigned by the search - engine. - */ - lbool assigned_value(literal l) const { - bool_var b = l.var(); - if (l.sign()) - return ~m_bvalues[b]; - else - return m_bvalues[b]; - } - - /** - \brief Assign literal using the given justification - */ - void assign(literal l, justification j) { - TRACE("nlsat_assign", - display(tout << "assigning literal: ", l); - display(tout << " <- ", j);); - - SASSERT(assigned_value(l) == l_undef); - SASSERT(j != null_justification); - SASSERT(!j.is_null()); - if (j.is_decision()) - m_stats.m_decisions++; - else - m_stats.m_propagations++; - bool_var b = l.var(); - m_bvalues[b] = to_lbool(!l.sign()); - m_levels[b] = m_scope_lvl; - m_justifications[b] = j; - save_assign_trail(b); - updt_eq(b, j); - TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << "\n";); - } - - /** - \brief Create a "case-split" - */ - void decide(literal l) { - new_level(); - assign(l, decided_justification); - } - - /** - \brief Return the value of a literal as defined in Dejan and Leo's paper. - */ - lbool value(literal l) { - lbool val = assigned_value(l); - if (val != l_undef) { - TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); - return val; - } - bool_var b = l.var(); - atom * a = m_atoms[b]; - if (a == nullptr) { - TRACE("nlsat_verbose", display(tout << " no atom for ", l) << "\n";); - return l_undef; - } - var max = a->max_var(); - if (!m_assignment.is_assigned(max)) { - TRACE("nlsat_verbose", display(tout << " maximal variable not assigned ", l) << "\n";); - return l_undef; - } - val = to_lbool(m_evaluator.eval(a, l.sign())); - TRACE("nlsat_verbose", display(tout << " evaluated value " << val << " for ", l) << "\n";); - TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; - tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; - display_assignment(tout);); - return val; - } - - /** - \brief Return true if the given clause is already satisfied in the current partial interpretation. - */ - bool is_satisfied(clause const & cls) const { - for (literal l : cls) { - if (const_cast(this)->value(l) == l_true) { - TRACE("value_bug:", tout << l << " := true\n";); - return true; - } - } - return false; - } - - /** - \brief Return true if the given clause is false in the current partial interpretation. - */ - bool is_inconsistent(unsigned sz, literal const * cls) { - for (unsigned i = 0; i < sz; i++) { - if (value(cls[i]) != l_false) { - TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); - return false; - } - } - return true; - } - - /** - \brief Process a clauses that contains only Boolean literals. - */ - bool process_boolean_clause(clause const & cls) { - SASSERT(m_xk == null_var); - unsigned num_undef = 0; - unsigned first_undef = UINT_MAX; - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = cls[i]; - SASSERT(m_atoms[l.var()] == nullptr); - SASSERT(value(l) != l_true); - if (value(l) == l_false) - continue; - SASSERT(value(l) == l_undef); - num_undef++; - if (first_undef == UINT_MAX) - first_undef = i; - } - if (num_undef == 0) - return false; - SASSERT(first_undef != UINT_MAX); - if (num_undef == 1) - assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause - else - decide(cls[first_undef]); - return true; - } - - /** - \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. - */ - literal_vector core; - ptr_vector clauses; - void R_propagate(literal l, interval_set const * s, bool include_l = true) { - m_ism.get_justifications(s, core, clauses); - if (include_l) - core.push_back(~l); - auto j = mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data()); - TRACE("nlsat_resolve", display(tout, j); display_eval(tout << "evaluated:", j)); - assign(l, j); - SASSERT(value(l) == l_true); - } - - /** - \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s - */ - void updt_infeasible(interval_set const * s) { - SASSERT(m_xk != null_var); - interval_set * xk_set = m_infeasible[m_xk]; - save_set_updt_trail(xk_set); - interval_set_ref new_set(m_ism); - TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set) << "\n"; m_ism.display(tout, s) << "\n";); - new_set = m_ism.mk_union(s, xk_set); - TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set) << "\n";); - SASSERT(!m_ism.is_full(new_set)); - m_ism.inc_ref(new_set); - m_infeasible[m_xk] = new_set; - } - - /** - \brief Update m_var2eq mapping. - */ - void updt_eq(bool_var b, justification j) { - if (!m_simplify_cores) - return; - if (m_bvalues[b] != l_true) - return; - atom * a = m_atoms[b]; - if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) - return; - switch (j.get_kind()) { - case justification::CLAUSE: - if (j.get_clause()->assumptions() != nullptr) return; - break; - case justification::LAZY: - if (j.get_lazy()->num_clauses() > 0) return; - if (j.get_lazy()->num_lits() > 0) return; - break; - default: - break; - } - var x = m_xk; - SASSERT(a->max_var() == x); - SASSERT(x != null_var); - if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) - return; // we only update m_var2eq if the new equality has smaller degree - TRACE("nlsat_simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; - tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)) << "\n"; - display(tout, j); - ); - save_updt_eq_trail(m_var2eq[x]); - m_var2eq[x] = a; - } - - /** - \brief Process a clause that contains nonlinear arithmetic literals - - If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 - */ - bool process_arith_clause(clause const & cls, bool satisfy_learned) { - if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { - TRACE("nlsat", tout << "skip learned\n";); - return true; // ignore lemmas in super lazy mode - } - SASSERT(m_xk == max_var(cls)); - unsigned num_undef = 0; // number of undefined literals - unsigned first_undef = UINT_MAX; // position of the first undefined literal - interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal - interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable - SASSERT(!m_ism.is_full(xk_set)); - for (unsigned idx = 0; idx < cls.size(); ++idx) { - literal l = cls[idx]; - checkpoint(); - if (value(l) == l_false) - continue; - if (value(l) == l_true) - return true; // could happen if clause is a tautology - CTRACE("nlsat", max_var(l) != m_xk || value(l) != l_undef, display(tout); - tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l) << "\n"; - display(tout, cls) << "\n";); - SASSERT(value(l) == l_undef); - SASSERT(max_var(l) == m_xk); - bool_var b = l.var(); - atom * a = m_atoms[b]; - SASSERT(a != nullptr); - interval_set_ref curr_set(m_ism); - curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); - TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; - display(tout, cls) << "\n";); - if (m_ism.is_empty(curr_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); - R_propagate(l, nullptr); - SASSERT(is_satisfied(cls)); - return true; - } - if (m_ism.is_full(curr_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); - R_propagate(~l, nullptr); - continue; - } - if (m_ism.subset(curr_set, xk_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); - R_propagate(l, xk_set); - return true; - } - interval_set_ref tmp(m_ism); - tmp = m_ism.mk_union(curr_set, xk_set); - if (m_ism.is_full(tmp)) { - TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; - display(tout, cls) << "\n"; - m_ism.display(tout, tmp); tout << "\n"; - ); - R_propagate(~l, tmp, false); - continue; - } - num_undef++; - if (first_undef == UINT_MAX) { - first_undef = idx; - first_undef_set = curr_set; - } - } - TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); - if (num_undef == 0) - return false; - SASSERT(first_undef != UINT_MAX); - if (num_undef == 1) { - // unit clause - assign(cls[first_undef], mk_clause_jst(&cls)); - updt_infeasible(first_undef_set); - } - else if ( satisfy_learned || - !cls.is_learned() /* must always satisfy input clauses */ || - m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { - decide(cls[first_undef]); - updt_infeasible(first_undef_set); - } - else { - TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() - << ", lazy: " << m_lazy << "\n";); - } - return true; - } - - /** - \brief Try to satisfy the given clause. Return true if succeeded. - - If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 - */ - bool process_clause(clause const & cls, bool satisfy_learned) { - if (is_satisfied(cls)) - return true; - if (m_xk == null_var) - return process_boolean_clause(cls); - else - return process_arith_clause(cls, satisfy_learned); - } - - /** - \brief Try to satisfy the given "set" of clauses. - Return 0, if the set was satisfied, or the violating clause otherwise - */ - clause * process_clauses(clause_vector const & cs) { - for (clause* c : cs) { - if (!process_clause(*c, false)) - return c; - } - return nullptr; // succeeded - } - - /** - \brief Make sure m_bk is the first unassigned pure Boolean variable. - Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. - */ - void peek_next_bool_var() { - while (m_bk < m_atoms.size()) { - if (!m_dead[m_bk] && m_atoms[m_bk] == nullptr && m_bvalues[m_bk] == l_undef) { - return; - } - m_bk++; - } - m_bk = null_bool_var; - } - - /** - \brief Create a new stage. See Dejan and Leo's paper. - */ - void new_stage() { - m_stats.m_stages++; - save_new_stage_trail(); - if (m_xk == null_var) - m_xk = 0; - else - m_xk++; - } - - /** - \brief Assign m_xk - */ - void select_witness() { - scoped_anum w(m_am); - SASSERT(!m_ism.is_full(m_infeasible[m_xk])); - m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); - TRACE("nlsat", - tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; - tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); - TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); - if (!m_am.is_rational(w)) - m_stats.m_irrational_assignments++; - m_assignment.set_core(m_xk, w); - } - - - - bool is_satisfied() { - if (m_bk == null_bool_var && m_xk >= num_vars()) { - TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); - fix_patch(); - SASSERT(check_satisfied(m_clauses)); - return true; // all variables were assigned, and all clauses were satisfied. - } - else { - return false; - } - } - - - /** - \brief main procedure - */ - lbool search() { - TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); - TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); - TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); - TRACE("nlsat_mathematica", display_mathematica(tout);); - TRACE("nlsat", display_smt2(tout);); - m_bk = 0; - m_xk = null_var; - - while (true) { - if (should_reorder()) - do_reorder(); - -#if 0 - if (should_gc()) - do_gc(); -#endif - - if (should_simplify()) - do_simplify(); - - CASSERT("nlsat", check_satisfied()); - if (m_xk == null_var) { - peek_next_bool_var(); - if (m_bk == null_bool_var) - new_stage(); // move to arith vars - } - else { - new_stage(); // peek next arith var - } - TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); - if (is_satisfied()) { - return l_true; - } - while (true) { - TRACE("nlsat_verbose", tout << "processing variable "; - if (m_xk != null_var) { - m_display_var(tout, m_xk); tout << " " << m_watches[m_xk].size(); - } - else { - tout << m_bwatches[m_bk].size() << " boolean b" << m_bk; - } - tout << "\n";); - checkpoint(); - clause * conflict_clause; - if (m_xk == null_var) - conflict_clause = process_clauses(m_bwatches[m_bk]); - else - conflict_clause = process_clauses(m_watches[m_xk]); - if (conflict_clause == nullptr) - break; - if (!resolve(*conflict_clause)) - return l_false; - if (m_stats.m_conflicts >= m_max_conflicts) - return l_undef; - log(); - } - - if (m_xk == null_var) { - if (m_bvalues[m_bk] == l_undef) { - decide(literal(m_bk, true)); - m_bk++; - } - } - else { - select_witness(); - } - } - } - - void gc() { - if (m_learned.size() <= 4*m_clauses.size()) - return; - reset_watches(); - reinit_cache(); - unsigned j = 0; - for (unsigned i = 0; i < m_learned.size(); ++i) { - auto cls = m_learned[i]; - if (i - j < m_clauses.size() && cls->size() > 1 && !cls->is_active()) - del_clause(cls); - else { - m_learned[j++] = cls; - cls->set_active(false); - } - } - m_learned.shrink(j); - reattach_arith_clauses(m_clauses); - reattach_arith_clauses(m_learned); - } - - - bool should_gc() { - return m_learned.size() > 10 * m_clauses.size(); - } - - void do_gc() { - undo_to_base(); - gc(); - } - - void undo_to_base() { - init_search(); - m_bk = 0; - m_xk = null_var; - } - - unsigned m_restart_threshold = 10000; - bool should_reorder() { - return m_stats.m_conflicts > 0 && m_stats.m_conflicts % m_restart_threshold == 0; - } - - void do_reorder() { - undo_to_base(); - m_stats.m_restarts++; - m_stats.m_conflicts++; - if (m_reordered) - restore_order(); - apply_reorder(); - } - - bool m_did_simplify = false; - bool should_simplify() { - return - !m_did_simplify && m_inline_vars && - !m_incremental && m_stats.m_conflicts > 100; - } - - void do_simplify() { - undo_to_base(); - m_did_simplify = true; - m_simplify(); - } - - unsigned m_next_conflict = 100; - void log() { - if (m_stats.m_conflicts != 1 && m_stats.m_conflicts < m_next_conflict) - return; - m_next_conflict += 100; - IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_stats.m_conflicts - << " :decisions " << m_stats.m_decisions - << " :propagations " << m_stats.m_propagations - << " :clauses " << m_clauses.size() - << " :learned " << m_learned.size() << ")\n"); - } - - - lbool search_check() { - lbool r = l_undef; - m_stats.m_conflicts = 0; - m_stats.m_restarts = 0; - m_next_conflict = 0; - while (true) { - r = search(); - if (r != l_true) - break; - ++m_stats.m_restarts; - vector> bounds; - - for (var x = 0; x < num_vars(); x++) { - if (is_int(x) && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { - scoped_anum v(m_am), vlo(m_am); - v = m_assignment.value(x); - rational lo; - m_am.int_lt(v, vlo); - if (!m_am.is_int(vlo)) - continue; - m_am.to_rational(vlo, lo); - // derive tight bounds. - while (true) { - lo++; - if (!m_am.gt(v, lo.to_mpq())) { - lo--; - break; - } - } - bounds.push_back(std::make_pair(x, lo)); - } - } - if (bounds.empty()) - break; - - gc(); - if (m_stats.m_restarts % 10 == 0) { - if (m_reordered) - restore_order(); - apply_reorder(); - } - - init_search(); - IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts - << " :decisions " << m_stats.m_decisions - << " :propagations " << m_stats.m_propagations - << " :clauses " << m_clauses.size() - << " :learned " << m_learned.size() << ")\n"); - for (auto const& b : bounds) { - var x = b.first; - rational lo = b.second; - rational hi = lo + 1; // rational::one(); - bool is_even = false; - polynomial_ref p(m_pm); - rational one(1); - m_lemma.reset(); - p = m_pm.mk_linear(1, &one, &x, -lo); - poly* p1 = p.get(); - m_lemma.push_back(~mk_ineq_literal(atom::GT, 1, &p1, &is_even)); - p = m_pm.mk_linear(1, &one, &x, -hi); - poly* p2 = p.get(); - m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); - - // perform branch and bound - clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); - IF_VERBOSE(4, display(verbose_stream(), *cls) << "\n"); - if (cls) { - TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); - } - } - } - return r; - } - - bool m_reordered = false; - - void apply_reorder() { - m_reordered = false; - if (!can_reorder()) - ; - else if (m_random_order) { - shuffle_vars(); - m_reordered = true; - } - else if (m_reorder) { - heuristic_reorder(); - m_reordered = true; - } - } - - lbool check() { - TRACE("nlsat_smt2", display_smt2(tout);); - TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); - init_search(); - m_explain.set_full_dimensional(is_full_dimensional()); - - apply_reorder(); - -#if 0 - if (!m_incremental && m_inline_vars) { - if (!m_simplify()) - return l_false; - } -#endif - IF_VERBOSE(3, verbose_stream() << "search\n"); - sort_watched_clauses(); - lbool r = search_check(); - CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); - if (m_reordered) - restore_order(); - CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); - CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); - SASSERT(r != l_true || check_satisfied(m_clauses)); - return r; - } - - void init_search() { - undo_until_empty(); - while (m_scope_lvl > 0) { - undo_new_level(); - } - m_xk = null_var; - for (unsigned i = 0; i < m_bvalues.size(); ++i) { - m_bvalues[i] = l_undef; - } - m_assignment.reset(); - } - - lbool check(literal_vector& assumptions) { - literal_vector result; - unsigned sz = assumptions.size(); - literal const* ptr = assumptions.data(); - for (unsigned i = 0; i < sz; ++i) { - mk_external_clause(1, ptr+i, (assumption)(ptr+i)); - } - display_literal_assumption dla(*this, assumptions); - scoped_display_assumptions _scoped_display(*this, dla); - lbool r = check(); - - if (r == l_false) { - // collect used literals from m_lemma_assumptions - vector deps; - get_core(deps); - for (unsigned i = 0; i < deps.size(); ++i) { - literal const* lp = (literal const*)(deps[i]); - if (ptr <= lp && lp < ptr + sz) { - result.push_back(*lp); - } - } - } - collect(assumptions, m_clauses); - collect(assumptions, m_learned); - del_clauses(m_valids); - if (m_check_lemmas) { - for (clause* c : m_learned) { - check_lemma(c->size(), c->data(), false, nullptr); - } - } - -#if 0 - for (clause* c : m_learned) { - IF_VERBOSE(0, display(verbose_stream() << "KEEP: ", c->size(), c->c_ptr()) << "\n"); - } -#endif - assumptions.reset(); - assumptions.append(result); - return r; - } - - void get_core(vector& deps) { - m_asm.linearize(m_lemma_assumptions.get(), deps); - } - - void collect(literal_vector const& assumptions, clause_vector& clauses) { - unsigned j = 0; - for (clause * c : clauses) { - if (collect(assumptions, *c)) { - del_clause(c); - } - else { - clauses[j++] = c; - } - } - clauses.shrink(j); - } - - bool collect(literal_vector const& assumptions, clause const& c) { - unsigned sz = assumptions.size(); - literal const* ptr = assumptions.data(); - _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); - if (asms == nullptr) { - return false; - } - vector deps; - m_asm.linearize(asms, deps); - for (auto dep : deps) { - if (ptr <= dep && dep < ptr + sz) { - return true; - } - } - return false; - } - - // ----------------------- - // - // Conflict Resolution - // - // ----------------------- - svector m_marks; // bool_var -> bool temp mark used during conflict resolution - unsigned m_num_marks; - scoped_literal_vector m_lemma; - scoped_literal_vector m_lazy_clause; - assumption_set_ref m_lemma_assumptions; // assumption tracking - - // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. - - bool check_marks() { - for (unsigned m : m_marks) { - (void)m; - SASSERT(m == 0); - } - return true; - } - - unsigned scope_lvl() const { return m_scope_lvl; } - - bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } - - void mark(bool_var b) { m_marks.setx(b, 1, 0); } - - void reset_mark(bool_var b) { m_marks[b] = 0; } - - void reset_marks() { - for (auto const& l : m_lemma) { - reset_mark(l.var()); - } - } - - void process_antecedent(literal antecedent) { - checkpoint(); - bool_var b = antecedent.var(); - TRACE("nlsat_resolve", display(tout << "resolving antecedent: ", antecedent) << "\n";); - if (assigned_value(antecedent) == l_undef) { - checkpoint(); - // antecedent must be false in the current arith interpretation - SASSERT(value(antecedent) == l_false || m_rlimit.is_canceled()); - if (!is_marked(b)) { - SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage - TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); - mark(b); - m_lemma.push_back(antecedent); - } - return; - } - - unsigned b_lvl = m_levels[b]; - TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); - if (!is_marked(b)) { - mark(b); - if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { - TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); - m_num_marks++; - } - else { - TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; - tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); - m_lemma.push_back(antecedent); - } - } - } - - void resolve_clause(bool_var b, unsigned sz, literal const * c) { - TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b) << "\n"; display(tout, sz, c); tout << "\n";); - TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); - - for (unsigned i = 0; i < sz; i++) { - if (c[i].var() != b) - process_antecedent(c[i]); - } - } - - void resolve_clause(bool_var b, clause & c) { - TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); - c.set_active(true); - resolve_clause(b, c.size(), c.data()); - m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); - } - - void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { - TRACE("nlsat_resolve", tout << "resolving lazy_justification for b" << b << "\n";); - unsigned sz = jst.num_lits(); - - // Dump lemma as Mathematica formula that must be true, - // if the current interpretation (really) makes the core in jst infeasible. - TRACE("nlsat_mathematica", - tout << "assignment lemma\n"; - literal_vector core; - for (unsigned i = 0; i < sz; i++) { - core.push_back(~jst.lit(i)); - } - display_mathematica_lemma(tout, core.size(), core.data(), true);); - - m_lazy_clause.reset(); - m_explain(jst.num_lits(), jst.lits(), m_lazy_clause); - for (unsigned i = 0; i < sz; i++) - m_lazy_clause.push_back(~jst.lit(i)); - - // lazy clause is a valid clause - TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); - TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.data()); tout << "\n";); - TRACE("nlsat_resolve", - tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; - tout << "new valid clause:\n"; - display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";); - - - if (m_log_lemmas) - log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true); - - if (m_check_lemmas) { - check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), true, nullptr); - m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr)); - } - -#ifdef Z3DEBUG - { - unsigned sz = m_lazy_clause.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = m_lazy_clause[i]; - if (l.var() != b) { - if (value(l) != l_false) - display(verbose_stream() << value(l) << " ", 1, &l); - SASSERT(value(l) == l_false || m_rlimit.is_canceled()); - } - else { - SASSERT(value(l) == l_true || m_rlimit.is_canceled()); - SASSERT(!l.sign() || m_bvalues[b] == l_false); - SASSERT(l.sign() || m_bvalues[b] == l_true); - } - } - } -#endif - checkpoint(); - resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.data()); - - for (unsigned i = 0; i < jst.num_clauses(); ++i) { - clause const& c = jst.clause(i); - TRACE("nlsat", display(tout << "adding clause assumptions ", c) << "\n";); - m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); - } - } - - /** - \brief Return true if all literals in ls are from previous stages. - */ - bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { - for (unsigned i = 0; i < num; i++) { - if (max_var(ls[i]) == m_xk) - return false; - } - return true; - } - - /** - \brief Return the maximum scope level in ls. - - \pre This method assumes value(ls[i]) is l_false for i in [0, num) - */ - unsigned max_scope_lvl(unsigned num, literal const * ls) { - unsigned max = 0; - for (unsigned i = 0; i < num; i++) { - literal l = ls[i]; - bool_var b = l.var(); - SASSERT(value(ls[i]) == l_false); - if (assigned_value(l) == l_false) { - unsigned lvl = m_levels[b]; - if (lvl > max) - max = lvl; - } - else { - // l must be a literal from a previous stage that is false in the current interpretation - SASSERT(assigned_value(l) == l_undef); - SASSERT(max_var(b) != null_var); - SASSERT(m_xk != null_var); - SASSERT(max_var(b) < m_xk); - } - } - return max; - } - - /** - \brief Remove literals of the given lvl (that are in the current stage) from lemma. - - \pre This method assumes value(ls[i]) is l_false for i in [0, num) - */ - void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { - TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); - unsigned sz = lemma.size(); - unsigned j = 0; - for (unsigned i = 0; i < sz; i++) { - literal l = lemma[i]; - bool_var b = l.var(); - SASSERT(is_marked(b)); - SASSERT(value(lemma[i]) == l_false); - if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { - m_num_marks++; - continue; - } - lemma.set(j, l); - j++; - } - lemma.shrink(j); - } - - /** - \brief Return true if it is a Boolean lemma. - */ - bool is_bool_lemma(unsigned sz, literal const * ls) const { - for (unsigned i = 0; i < sz; i++) { - if (m_atoms[ls[i].var()] != nullptr) - return false; - } - return true; - } - - - /** - Return the maximal decision level in lemma for literals in the first sz-1 positions that - are at the same stage. If all these literals are from previous stages, - we just backtrack the current level. - */ - unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { - SASSERT(!is_bool_lemma(sz, lemma)); - unsigned new_lvl = 0; - bool found_lvl = false; - for (unsigned i = 0; i < sz - 1; i++) { - literal l = lemma[i]; - if (max_var(l) == m_xk) { - bool_var b = l.var(); - if (!found_lvl) { - found_lvl = true; - new_lvl = m_levels[b]; - } - else { - if (m_levels[b] > new_lvl) - new_lvl = m_levels[b]; - } - } - } - SASSERT(!found_lvl || new_lvl < scope_lvl()); - if (!found_lvl) { - TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); - new_lvl = scope_lvl() - 1; - } - return new_lvl; - } - - struct scoped_reset_marks { - imp& i; - scoped_reset_marks(imp& i):i(i) {} - ~scoped_reset_marks() { if (i.m_num_marks > 0) { i.m_num_marks = 0; for (char& m : i.m_marks) m = 0; } } - }; - - - /** - \brief Return true if the conflict was solved. - */ - bool resolve(clause & conflict) { - clause * conflict_clause = &conflict; - m_lemma_assumptions = nullptr; - start: - SASSERT(check_marks()); - TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); - TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); - m_stats.m_conflicts++; - TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; - tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; - tout << "scope_lvl: " << scope_lvl() << "\n"; - tout << "current assignment\n"; display_assignment(tout);); - - m_num_marks = 0; - m_lemma.reset(); - m_lemma_assumptions = nullptr; - scoped_reset_marks _sr(*this); - resolve_clause(null_bool_var, *conflict_clause); - - unsigned top = m_trail.size(); - bool found_decision; - while (true) { - found_decision = false; - while (m_num_marks > 0) { - checkpoint(); - SASSERT(top > 0); - trail & t = m_trail[top-1]; - SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage - if (t.m_kind == trail::BVAR_ASSIGNMENT) { - bool_var b = t.m_b; - if (is_marked(b)) { - TRACE("nlsat_resolve", tout << "found marked: b" << b << "\n"; display_atom(tout, b) << "\n";); - m_num_marks--; - reset_mark(b); - justification jst = m_justifications[b]; - switch (jst.get_kind()) { - case justification::CLAUSE: - resolve_clause(b, *(jst.get_clause())); - break; - case justification::LAZY: - resolve_lazy_justification(b, *(jst.get_lazy())); - break; - case justification::DECISION: - SASSERT(m_num_marks == 0); - found_decision = true; - TRACE("nlsat_resolve", tout << "found decision\n";); - m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); - break; - default: - UNREACHABLE(); - break; - } - } - } - top--; - } - - // m_lemma is an implicating clause after backtracking current scope level. - if (found_decision) - break; - - // If lemma only contains literals from previous stages, then we can stop. - // We make progress by returning to a previous stage with additional information (new lemma) - // that forces us to select a new partial interpretation - if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.data())) - break; - - // Conflict does not depend on the current decision, and it is still in the current stage. - // We should find - // - the maximal scope level in the lemma - // - remove literal assigned in the scope level from m_lemma - // - backtrack to this level - // - and continue conflict resolution from there - // - we must bump m_num_marks for literals removed from m_lemma - unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.data()); - TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); - SASSERT(max_lvl < scope_lvl()); - remove_literals_from_lvl(m_lemma, max_lvl); - undo_until_level(max_lvl); - top = m_trail.size(); - TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); - SASSERT(scope_lvl() == max_lvl); - } - - TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); - TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); - - if (m_lemma.empty()) { - TRACE("nlsat", tout << "empty clause generated\n";); - return false; // problem is unsat, empty clause was generated - } - - reset_marks(); // remove marks from the literals in m_lemmas. - TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n"; - tout << "found_decision: " << found_decision << "\n";); - - if (m_check_lemmas) { - check_lemma(m_lemma.size(), m_lemma.data(), false, m_lemma_assumptions.get()); - } - - if (m_log_lemmas) - log_lemma(verbose_stream(), m_lemma.size(), m_lemma.data(), false); - - // There are two possibilities: - // 1) m_lemma contains only literals from previous stages, and they - // are false in the current interpretation. We make progress - // by returning to a previous stage with additional information (new clause) - // that forces us to select a new partial interpretation - // >>> Return to some previous stage (we may also backjump many decisions and stages). - // - // 2) m_lemma contains at most one literal from the current level (the last literal). - // Moreover, this literal was a decision, but the new lemma forces it to - // be assigned to a different value. - // >>> In this case, we remain in the same stage but, we add a new asserted literal - // in a previous scope level. We may backjump many decisions. - // - unsigned sz = m_lemma.size(); - clause * new_cls = nullptr; - if (!found_decision) { - // Case 1) - // We just have to find the maximal variable in m_lemma, and return to that stage - // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; - var new_max_var = max_var(sz, m_lemma.data()); - TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); - undo_until_stage(new_max_var); - SASSERT(m_xk == new_max_var); - new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); - TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << "\n"; - if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); - } - else { - SASSERT(scope_lvl() >= 1); - // Case 2) - if (is_bool_lemma(m_lemma.size(), m_lemma.data())) { - // boolean lemma, we just backtrack until the last literal is unassigned. - bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); - undo_until_unassigned(max_bool_var); - } - else { - // We must find the maximal decision level in literals in the first sz-1 positions that - // are at the same stage. If all these literals are from previous stages, - // we just backtrack the current level. - unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.data()); - TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); - undo_until_level(new_lvl); - } - - if (lemma_is_clause(*conflict_clause)) { - TRACE("nlsat", tout << "found decision literal in conflict clause\n";); - VERIFY(process_clause(*conflict_clause, true)); - return true; - } - new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); - } - NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); - if (!process_clause(*new_cls, true)) { - TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n"; - display(tout, *new_cls) << "\n"; - ); - // we are still in conflict - conflict_clause = new_cls; - goto start; - } - TRACE("nlsat_resolve_done", display_assignment(tout);); - return true; - } - - bool lemma_is_clause(clause const& cls) const { - bool same = (m_lemma.size() == cls.size()); - for (unsigned i = 0; same && i < m_lemma.size(); ++i) { - same = m_lemma[i] == cls[i]; - } - return same; - } - - - // ----------------------- - // - // Debugging - // - // ----------------------- - - bool check_watches() const { -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - clause_vector const & cs = m_watches[x]; - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - SASSERT(max_var(*(cs[i])) == x); - } - } -#endif - return true; - } - - bool check_bwatches() const { -#ifdef Z3DEBUG - for (bool_var b = 0; b < m_bwatches.size(); b++) { - clause_vector const & cs = m_bwatches[b]; - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause const & c = *(cs[i]); - SASSERT(max_var(c) == null_var); - SASSERT(max_bvar(c) == b); - } - } -#endif - return true; - } - - bool check_invariant() const { - SASSERT(check_watches()); - SASSERT(check_bwatches()); - return true; - } - - bool check_satisfied(clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause const & c = *(cs[i]); - if (!is_satisfied(c)) { - TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); - return false; - } - } - return true; - } - - bool check_satisfied() const { - TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); - unsigned num = m_atoms.size(); - if (m_bk != null_bool_var) - num = m_bk; - for (bool_var b = 0; b < num; b++) { - if (!check_satisfied(m_bwatches[b])) { - UNREACHABLE(); - return false; - } - } - if (m_xk != null_var) { - for (var x = 0; x < m_xk; x++) { - if (!check_satisfied(m_watches[x])) { - UNREACHABLE(); - return false; - } - } - } - return true; - } - - // ----------------------- - // - // Statistics - // - // ----------------------- - - void collect_statistics(statistics & st) { - st.update("nlsat conflicts", m_stats.m_conflicts); - st.update("nlsat propagations", m_stats.m_propagations); - st.update("nlsat decisions", m_stats.m_decisions); - st.update("nlsat restarts", m_stats.m_restarts); - st.update("nlsat stages", m_stats.m_stages); - st.update("nlsat simplifications", m_stats.m_simplifications); - st.update("nlsat irrational assignments", m_stats.m_irrational_assignments); - } - - void reset_statistics() { - m_stats.reset(); - } - - // ----------------------- - // - // Variable reordering - // - // ----------------------- - - struct var_info_collector { - pmanager & pm; - atom_vector const & m_atoms; - var_vector m_shuffle; - unsigned_vector m_max_degree; - unsigned_vector m_num_occs; - - var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): - pm(_pm), - m_atoms(atoms) { - m_max_degree.resize(num_vars, 0); - m_num_occs.resize(num_vars, 0); - } - - var_vector m_vars; - void collect(poly * p) { - m_vars.reset(); - pm.vars(p, m_vars); - unsigned sz = m_vars.size(); - for (unsigned i = 0; i < sz; i++) { - var x = m_vars[i]; - unsigned k = pm.degree(p, x); - m_num_occs[x]++; - if (k > m_max_degree[x]) - m_max_degree[x] = k; - } - } - - void collect(literal l) { - bool_var b = l.var(); - atom * a = m_atoms[b]; - if (a == nullptr) - return; - if (a->is_ineq_atom()) { - unsigned sz = to_ineq_atom(a)->size(); - for (unsigned i = 0; i < sz; i++) { - collect(to_ineq_atom(a)->p(i)); - } - } - else { - collect(to_root_atom(a)->p()); - } - } - - void collect(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - collect(c[i]); - } - - void collect(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) - collect(*(cs[i])); - } - - std::ostream& display(std::ostream & out, display_var_proc const & proc) { - unsigned sz = m_num_occs.size(); - for (unsigned i = 0; i < sz; i++) { - proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; - } - return out; - } - }; - - struct reorder_lt { - var_info_collector const & m_info; - reorder_lt(var_info_collector const & info):m_info(info) {} - bool operator()(var x, var y) const { - // high degree first - if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) - return false; - if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) - return true; - // more constrained first - if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) - return false; - if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) - return true; - return m_info.m_shuffle[x] < m_info.m_shuffle[y]; - } - }; - - // Order variables by degree and number of occurrences - void heuristic_reorder() { - unsigned num = num_vars(); - var_info_collector collector(m_pm, m_atoms, num); - collector.collect(m_clauses); - collector.collect(m_learned); - init_shuffle(collector.m_shuffle); - TRACE("nlsat_reorder", collector.display(tout, m_display_var);); - var_vector new_order; - for (var x = 0; x < num; x++) - new_order.push_back(x); - - std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); - TRACE("nlsat_reorder", - tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); - var_vector perm; - perm.resize(num, 0); - for (var x = 0; x < num; x++) { - perm[new_order[x]] = x; - } - reorder(perm.size(), perm.data()); - SASSERT(check_invariant()); - } - - void init_shuffle(var_vector& p) { - unsigned num = num_vars(); - for (var x = 0; x < num; x++) - p.push_back(x); - - random_gen r(++m_random_seed); - shuffle(p.size(), p.data(), r); - } - - void shuffle_vars() { - var_vector p; - init_shuffle(p); - reorder(p.size(), p.data()); - } - - bool can_reorder() const { - return all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) - && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); - } - - /** - \brief Reorder variables using the giving permutation. - p maps internal variables to their new positions - */ - - - void reorder(unsigned sz, var const * p) { - - remove_learned_roots(); - SASSERT(can_reorder()); - TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); - display_vars(tout); - tout << "\npermutation:\n"; - for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; - ); - // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; - reinit_cache(); - SASSERT(num_vars() == sz); - TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); - reset_watches(); - assignment new_assignment(m_am); - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) - new_assignment.set(p[x], m_assignment.value(x)); - } - var_vector new_inv_perm; - new_inv_perm.resize(sz); - // the undo_until_size(0) statement erases the Boolean assignment. - // undo_until_size(0) - undo_until_stage(null_var); - m_cache.reset(); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_watches[x].empty()); - } -#endif - // update m_perm mapping - for (unsigned ext_x = 0; ext_x < sz; ext_x++) { - // p: internal -> new pos - // m_perm: internal -> external - // m_inv_perm: external -> internal - new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; - m_perm.set(new_inv_perm[ext_x], ext_x); - } - bool_vector is_int; - is_int.swap(m_is_int); - for (var x = 0; x < sz; x++) { - m_is_int.setx(p[x], is_int[x], false); - SASSERT(m_infeasible[x] == 0); - } - m_inv_perm.swap(new_inv_perm); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(x == m_inv_perm[m_perm[x]]); - SASSERT(m_watches[x].empty()); - } -#endif - m_pm.rename(sz, p); - for (auto& b : m_bounds) - b.x = p[b.x]; - TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); - reinit_cache(); - m_assignment.swap(new_assignment); - reattach_arith_clauses(m_clauses); - reattach_arith_clauses(m_learned); - TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); - } - - - /** - \brief Restore variable order. - */ - void restore_order() { - // m_perm: internal -> external - // m_inv_perm: external -> internal - var_vector p; - p.append(m_perm); - reorder(p.size(), p.data()); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_perm[x] == x); - SASSERT(m_inv_perm[x] == x); - } -#endif - } - - /** - \brief After variable reordering some lemmas containing root atoms may be ill-formed. - */ - void remove_learned_roots() { - unsigned j = 0; - for (clause* c : m_learned) { - if (has_root_atom(*c)) { - del_clause(c); - } - else { - m_learned[j++] = c; - } - } - m_learned.shrink(j); - } - - /** - \brief Return true if the clause contains an ill formed root atom - */ - bool has_root_atom(clause const & c) const { - for (literal lit : c) { - bool_var b = lit.var(); - atom * a = m_atoms[b]; - if (a && a->is_root_atom()) - return true; - } - return false; - } - - /** - \brief reinsert all polynomials in the unique cache - */ - void reinit_cache() { - reinit_cache(m_clauses); - reinit_cache(m_learned); - for (atom* a : m_atoms) - reinit_cache(a); - } - void reinit_cache(clause_vector const & cs) { - for (clause* c : cs) - reinit_cache(*c); - } - void reinit_cache(clause const & c) { - for (literal l : c) - reinit_cache(l); - } - void reinit_cache(literal l) { - bool_var b = l.var(); - reinit_cache(m_atoms[b]); - } - void reinit_cache(atom* a) { - if (a == nullptr) { - - } - else if (a->is_ineq_atom()) { - var max = 0; - unsigned sz = to_ineq_atom(a)->size(); - for (unsigned i = 0; i < sz; i++) { - poly * p = to_ineq_atom(a)->p(i); - VERIFY(m_cache.mk_unique(p) == p); - var x = m_pm.max_var(p); - if (x > max) - max = x; - } - a->m_max_var = max; - } - else { - poly * p = to_root_atom(a)->p(); - VERIFY(m_cache.mk_unique(p) == p); - a->m_max_var = m_pm.max_var(p); - } - } - - void reset_watches() { - unsigned num = num_vars(); - for (var x = 0; x < num; x++) { - m_watches[x].reset(); - } - } - - void reattach_arith_clauses(clause_vector const & cs) { - for (clause* cp : cs) { - var x = max_var(*cp); - if (x != null_var) - m_watches[x].push_back(cp); - } - } - - // ----------------------- - // - // Solver initialization - // - // ----------------------- - - struct degree_lt { - unsigned_vector & m_degrees; - degree_lt(unsigned_vector & ds):m_degrees(ds) {} - bool operator()(unsigned i1, unsigned i2) const { - if (m_degrees[i1] < m_degrees[i2]) - return true; - if (m_degrees[i1] > m_degrees[i2]) - return false; - return i1 < i2; - } - }; - - unsigned_vector m_cs_degrees; - unsigned_vector m_cs_p; - void sort_clauses_by_degree(unsigned sz, clause ** cs) { - if (sz <= 1) - return; - TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - m_cs_degrees.reset(); - m_cs_p.reset(); - for (unsigned i = 0; i < sz; i++) { - m_cs_p.push_back(i); - m_cs_degrees.push_back(degree(*(cs[i]))); - } - std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); - TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); - apply_permutation(sz, cs, m_cs_p.data()); - TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - } - - void sort_watched_clauses() { - unsigned num = num_vars(); - for (unsigned i = 0; i < num; i++) { - clause_vector & ws = m_watches[i]; - sort_clauses_by_degree(ws.size(), ws.data()); - } - } - - // ----------------------- - // - // Full dimensional - // - // A problem is in the full dimensional fragment if it does - // not contain equalities or non-strict inequalities. - // - // ----------------------- - - bool is_full_dimensional(literal l) const { - atom * a = m_atoms[l.var()]; - if (a == nullptr) - return true; - switch (a->get_kind()) { - case atom::EQ: return l.sign(); - case atom::LT: return !l.sign(); - case atom::GT: return !l.sign(); - case atom::ROOT_EQ: return l.sign(); - case atom::ROOT_LT: return !l.sign(); - case atom::ROOT_GT: return !l.sign(); - case atom::ROOT_LE: return l.sign(); - case atom::ROOT_GE: return l.sign(); - default: - UNREACHABLE(); - return false; - } - } - - bool is_full_dimensional(clause const & c) const { - for (literal l : c) { - if (!is_full_dimensional(l)) - return false; - } - return true; - } - - bool is_full_dimensional(clause_vector const & cs) const { - for (clause* c : cs) { - if (!is_full_dimensional(*c)) - return false; - } - return true; - } - - bool is_full_dimensional() const { - return is_full_dimensional(m_clauses); - } - - - // ----------------------- - // - // Simplification - // - // ----------------------- - - // solve simple equalities - // TBD WU-Reit decomposition? - - // - elim_unconstrained - // - solve_eqs - // - fm - - /** - \brief isolate variables in unit equalities. - Assume a clause is c == v*p + q - and the context implies p > 0 - - replace v by -q/p - remove clause c, - The for other occurrences of v, - replace v*r + v*v*r' > 0 by - by p*p*v*r + p*p*v*v*r' > 0 - by p*q*r + q*q*r' > 0 - - The method ignores lemmas and assumes constraints don't use roots. - */ - - - - // Eliminated variables are tracked in m_bounds. - // Each element in m_bounds tracks the eliminated variable and an upper or lower bound - // that has to be satisfied. Variables that are eliminated through equalities are tracked - // by non-strict bounds. A satisfiable solution is required to provide an evaluation that - // is consistent with the bounds. For equalities, the non-strict lower or upper bound can - // always be assigned as a value to the variable. - - void fix_patch() { - m_lo.reset(); m_hi.reset(); - for (auto& b : m_bounds) - m_assignment.reset(b.x); - for (unsigned i = m_bounds.size(); i-- > 0; ) - fix_patch(m_bounds[i]); - } - - // x is unassigned, lo < x -> x <- lo + 1 - // x is unassigned, x < hi -> x <- hi - 1 - // x is unassigned, lo <= x -> x <- lo - // x is unassigned, x <= hi -> x <- hi - // x is assigned above hi, lo is strict lo < x < hi -> set x <- (lo + hi)/2 - // x is assigned below hi, above lo -> no-op - // x is assigned below lo, hi is strict lo < x < hi -> set x <-> (lo + hi)/2 - // x is assigned above hi, x <= hi -> x <- hi - // x is assigned blow lo, lo <= x -> x <- lo - void fix_patch(bound_constraint& b) { - var x = b.x; - scoped_anum Av(m_am), Bv(m_am), val(m_am); - m_pm.eval(b.A, m_assignment, Av); - m_pm.eval(b.B, m_assignment, Bv); - m_am.neg(Bv); - val = Bv / Av; - // Ax >= B - // is-lower : A > 0 - // is-upper: A < 0 - // x <- B / A - bool is_lower = m_am.is_pos(Av); - TRACE("nlsat", - m_display_var(tout << "patch v" << x << " ", x) << "\n"; - if (m_assignment.is_assigned(x)) m_am.display(tout << "previous value: ", m_assignment.value(x)); tout << "\n"; - m_am.display(tout << "updated value: ", val); tout << "\n"; - ); - - if (!m_assignment.is_assigned(x)) { - if (!b.is_strict) - m_assignment.set_core(x, val); - else if (is_lower) - m_assignment.set_core(x, val + 1); - else - m_assignment.set_core(x, val - 1); - } - else { - auto& aval = m_assignment.value(x); - if (is_lower) { - // lo < value(x), lo < x -> x is unchanged - if (b.is_strict && m_am.lt(val, aval)) - ; - else if (!b.is_strict && m_am.le(val, aval)) - ; - else if (!b.is_strict) - m_assignment.set_core(x, val); - // aval < lo < x, hi is unassigned: x <- lo + 1 - else if (!m_hi.is_assigned(x)) - m_assignment.set_core(x, val + 1); - // aval < lo < x, hi is assigned: x <- (lo + hi) / 2 - else { - scoped_anum mid(m_am); - m_am.add(m_hi.value(x), val, mid); - mid = mid / 2; - m_assignment.set_core(x, mid); - } - } - else { - // dual to lower bounds - if (b.is_strict && m_am.lt(aval, val)) - ; - else if (!b.is_strict && m_am.le(aval, val)) - ; - else if (!b.is_strict) - m_assignment.set_core(x, val); - else if (!m_lo.is_assigned(x)) - m_assignment.set_core(x, val - 1); - else { - scoped_anum mid(m_am); - m_am.add(m_lo.value(x), val, mid); - mid = mid / 2; - m_assignment.set_core(x, mid); - } - } - } - - if (is_lower) { - if (!m_lo.is_assigned(x) || m_am.lt(m_lo.value(x), val)) - m_lo.set_core(x, val); - } - else { - if (!m_hi.is_assigned(x) || m_am.gt(m_hi.value(x), val)) - m_hi.set_core(x, val); - } - } - - bool is_unit_ineq(clause const& c) const { - return - c.size() == 1 && - m_atoms[c[0].var()] && - m_atoms[c[0].var()]->is_ineq_atom(); - } - - bool is_unit_eq(clause const& c) const { - return - is_unit_ineq(c) && - !c[0].sign() && - m_atoms[c[0].var()]->is_eq(); - } - - /** - \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. - */ - lbool is_cmp0(clause const& c, var& v) { - if (!is_unit_ineq(c)) - return l_undef; - literal lit = c[0]; - ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); - bool sign = lit.sign(); - poly * p0; - if (!is_single_poly(a, p0)) - return l_undef; - if (m_pm.is_var(p0, v)) { - if (!sign && a.get_kind() == atom::GT) { - return l_true; - } - if (!sign && a.get_kind() == atom::LT) { - return l_false; - } - return l_undef; - } - polynomial::scoped_numeral n(m_pm.m()); - if (m_pm.is_var_num(p0, v, n)) { - // x - k > 0 - if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { - return l_true; - } - // x + k < 0 - if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { - return l_false; - } - // !(x + k > 0) - if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { - return l_false; - } - // !(x - k < 0) - if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { - return l_true; - } - } - return l_undef; - } - - bool is_single_poly(ineq_atom const& a, poly*& p) { - unsigned sz = a.size(); - return sz == 1 && a.is_odd(0) && (p = a.p(0), true); - } - - bool is_unit(polynomial_ref const& p) { - if (!m_pm.is_const(p)) - return false; - auto const& c = m_pm.coeff(p, 0); - return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); - } - - // ----------------------- - // - // Pretty printing - // - // ----------------------- - - std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) { - proc(out, x); - out << " -> "; - m_am.display_decimal(out, m_assignment.value(x)); - out << "\n"; - } - } - return out; - } - - std::ostream& display_bool_assignment(std::ostream & out) const { - unsigned sz = m_atoms.size(); - for (bool_var b = 0; b < sz; b++) { - if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { - out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; - } - else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { - display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; - } - } - TRACE("nlsat_bool_assignment", - for (bool_var b = 0; b < sz; b++) { - out << "b" << b << " -> " << m_bvalues[b] << " "; - if (m_atoms[b]) display(out, *m_atoms[b]); - out << "\n"; - }); - return out; - } - - bool display_mathematica_assignment(std::ostream & out) const { - bool first = true; - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) { - if (first) - first = false; - else - out << " && "; - out << "x" << x << " == "; - m_am.display_mathematica(out, m_assignment.value(x)); - } - } - return !first; - } - - std::ostream& display_num_assignment(std::ostream & out) const { - return display_num_assignment(out, m_display_var); - } - - std::ostream& display_assignment(std::ostream& out) const { - display_bool_assignment(out); - display_num_assignment(out); - return out; - } - - std::ostream& display(std::ostream& out, justification j) const { - switch (j.get_kind()) { - case justification::CLAUSE: - display(out, *j.get_clause()) << "\n"; - break; - case justification::LAZY: { - lazy_justification const& lz = *j.get_lazy(); - display_not(out, lz.num_lits(), lz.lits()) << "\n"; - for (unsigned i = 0; i < lz.num_clauses(); ++i) { - display(out, lz.clause(i)) << "\n"; - } - break; - } - default: - out << j.get_kind() << "\n"; - break; - } - return out; - } - - bool m_display_eval = false; - std::ostream& display_eval(std::ostream& out, justification j) { - flet _display(m_display_eval, true); - return display(out, j); - } - - std::ostream& display_ineq(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { - unsigned sz = a.size(); - for (unsigned i = 0; i < sz; i++) { - if (use_star && i > 0) - out << "*"; - bool is_even = a.is_even(i); - if (is_even || sz > 1) - out << "("; - display_polynomial(out, a.p(i), proc, use_star); - if (is_even || sz > 1) - out << ")"; - if (is_even) - out << "^2"; - } - switch (a.get_kind()) { - case atom::LT: out << " < 0"; break; - case atom::GT: out << " > 0"; break; - case atom::EQ: out << " = 0"; break; - default: UNREACHABLE(); break; - } - return out; - } - - std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { - unsigned sz = a.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) - out << "*"; - bool is_even = a.is_even(i); - if (sz > 1) - out << "("; - if (is_even) - out << "("; - m_pm.display(out, a.p(i), display_var_proc(), true); - if (is_even) - out << "^2)"; - if (sz > 1) - out << ")"; - } - switch (a.get_kind()) { - case atom::LT: out << " < 0"; break; - case atom::GT: out << " > 0"; break; - case atom::EQ: out << " == 0"; break; - default: UNREACHABLE(); break; - } - return out; - } - - std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { - return m_pm.display_smt2(out, p, proc); - } - - std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { - switch (a.get_kind()) { - case atom::LT: out << "(< "; break; - case atom::GT: out << "(> "; break; - case atom::EQ: out << "(= "; break; - default: UNREACHABLE(); break; - } - unsigned sz = a.size(); - if (sz > 1) - out << "(* "; - for (unsigned i = 0; i < sz; i++) { - if (i > 0) out << " "; - if (a.is_even(i)) { - out << "(* "; - display_polynomial_smt2(out, a.p(i), proc); - out << " "; - display_polynomial_smt2(out, a.p(i), proc); - out << ")"; - } - else { - display_polynomial_smt2(out, a.p(i), proc); - } - } - if (sz > 1) - out << ")"; - out << " 0)"; - return out; - } - - std::ostream& display_poly_root(std::ostream& out, char const* y, root_atom const& a, display_var_proc const& proc) const { - out << "(exists (("; proc(out,a.x()); out << " Real))\n"; - out << "(and (= " << y << " "; - proc(out, a.x()); - out << ") (= 0 "; - display_polynomial_smt2(out, a.p(), proc); - out << ")))\n"; - return out; - } - - std::ostream& display_binary_smt2(std::ostream& out, poly const* p1, char const* rel, poly const* p2, display_var_proc const& proc) const { - out << "(" << rel << " "; - display_polynomial_smt2(out, p1, proc); - out << " "; - display_polynomial_smt2(out, p2, proc); - out << ")"; - return out; - } - - - std::ostream& display_linear_root_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { - polynomial_ref A(m_pm), B(m_pm), Z(m_pm), Ax(m_pm); - polynomial::scoped_numeral zero(m_qm); - m_pm.m().set(zero, 0); - A = m_pm.derivative(a.p(), a.x()); - B = m_pm.neg(m_pm.substitute(a.p(), a.x(), zero)); - Z = m_pm.mk_zero(); - - Ax = m_pm.mul(m_pm.mk_polynomial(a.x()), A); - - // x < root[1](ax + b) == (a > 0 => ax + b < 0) & (a < 0 => ax + b > 0) - // x < root[1](ax + b) == (a > 0 => ax < -b) & (a < 0 => ax > -b) - - char const* rel1 = "<", *rel2 = ">"; - switch (a.get_kind()) { - case atom::ROOT_LT: rel1 = "<"; rel2 = ">"; break; - case atom::ROOT_GT: rel1 = ">"; rel2 = "<"; break; - case atom::ROOT_LE: rel1 = "<="; rel2 = ">="; break; - case atom::ROOT_GE: rel1 = ">="; rel2 = "<="; break; - case atom::ROOT_EQ: rel1 = rel2 = "="; break; - default: UNREACHABLE(); break; - } - - out << "(and "; - out << "(=> "; display_binary_smt2(out, A, ">", Z, proc); display_binary_smt2(out, Ax, rel1, B, proc); out << ") "; - out << "(=> "; display_binary_smt2(out, A, "<", Z, proc); display_binary_smt2(out, Ax, rel2, B, proc); out << ") "; - out << ")"; - - return out; - } - - - std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { - if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) - return display_linear_root_smt2(out, a, proc); -#if 1 - out << "(exists ("; - for (unsigned j = 0; j < a.i(); ++j) { - std::string y = std::string("y") + std::to_string(j); - out << "(" << y << " Real) "; - } - out << ")\n"; - out << "(and\n"; - for (unsigned j = 0; j < a.i(); ++j) { - std::string y = std::string("y") + std::to_string(j); - display_poly_root(out, y.c_str(), a, proc); - } - for (unsigned j = 0; j + 1 < a.i(); ++j) { - std::string y1 = std::string("y") + std::to_string(j); - std::string y2 = std::string("y") + std::to_string(j+1); - out << "(< " << y1 << " " << y2 << ")\n"; - } - - std::string yn = "y" + std::to_string(a.i() - 1); - - // TODO we need (forall z : z < yn . p(z) => z = y1 or ... z = y_{n-1}) - // to say y1, .., yn are the first n distinct roots. - // - out << "(forall ((z Real)) (=> (and (< z " << yn << ") "; display_poly_root(out, "z", a, proc) << ") "; - if (a.i() == 1) { - out << "false))\n"; - } - else { - out << "(or "; - for (unsigned j = 0; j + 1 < a.i(); ++j) { - std::string y1 = std::string("y") + std::to_string(j); - out << "(= z " << y1 << ") "; - } - out << ")))\n"; - } - switch (a.get_kind()) { - case atom::ROOT_LT: out << "(< "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_GT: out << "(> "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; - default: UNREACHABLE(); break; - } - out << "))"; - return out; -#endif - - - return display_root(out, a, proc); - } - - std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { - proc(out, a.x()); - switch (a.get_kind()) { - case atom::ROOT_LT: out << " < "; break; - case atom::ROOT_GT: out << " > "; break; - case atom::ROOT_LE: out << " <= "; break; - case atom::ROOT_GE: out << " >= "; break; - case atom::ROOT_EQ: out << " = "; break; - default: UNREACHABLE(); break; - } - out << "root[" << a.i() << "]("; - display_polynomial(out, a.p(), proc); - out << ")"; - return out; - } - - struct mathematica_var_proc : public display_var_proc { - var m_x; - public: - mathematica_var_proc(var x):m_x(x) {} - std::ostream& operator()(std::ostream & out, var x) const override { - if (m_x == x) - return out << "#1"; - else - return out << "x" << x; - } - }; - - std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { - out << "x" << a.x(); - switch (a.get_kind()) { - case atom::ROOT_LT: out << " < "; break; - case atom::ROOT_GT: out << " > "; break; - case atom::ROOT_LE: out << " <= "; break; - case atom::ROOT_GE: out << " >= "; break; - case atom::ROOT_EQ: out << " == "; break; - default: UNREACHABLE(); break; - } - out << "Root["; - display_polynomial(out, a.p(), mathematica_var_proc(a.x()), true); - out << " &, " << a.i() << "]"; - return out; - } - - std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { - if (a.is_ineq_atom()) - return display_ineq(out, static_cast(a), proc); - else - return display_root(out, static_cast(a), proc); - } - - std::ostream& display(std::ostream & out, atom const & a) const { - return display(out, a, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, atom const & a) const { - if (a.is_ineq_atom()) - return display_mathematica(out, static_cast(a)); - else - return display_mathematica(out, static_cast(a)); - } - - std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { - if (a.is_ineq_atom()) - return display_ineq_smt2(out, static_cast(a), proc); - else - return display_root_smt2(out, static_cast(a), proc); - } - - std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { - if (b == 0) - out << "true"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display(out, *(m_atoms[b]), proc); - return out; - } - - std::ostream& display_atom(std::ostream & out, bool_var b) const { - return display_atom(out, b, m_display_var); - } - - std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { - if (b == 0) - out << "(0 < 1)"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display_mathematica(out, *(m_atoms[b])); - return out; - } - - std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { - if (b == 0) - out << "true"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display_smt2(out, *(m_atoms[b]), proc); - return out; - } - - std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (m_atoms[b] != 0) - out << "("; - display_atom(out, b, proc); - if (m_atoms[b] != 0) - out << ")"; - } - else { - display_atom(out, l.var(), proc); - } - return out; - } - - std::ostream& display(std::ostream & out, literal l) const { - return display(out, l, m_display_var); - } - - std::ostream& display_smt2(std::ostream & out, literal l) const { - return display_smt2(out, l, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, literal l) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (m_atoms[b] != 0) - out << "("; - display_mathematica_atom(out, b); - if (m_atoms[b] != 0) - out << ")"; - } - else { - display_mathematica_atom(out, l.var()); - } - return out; - } - - std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { - if (l.sign()) { - bool_var b = l.var(); - out << "(not "; - display_smt2_atom(out, b, proc); - out << ")"; - } - else { - display_smt2_atom(out, l.var(), proc); - } - return out; - } - - std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { - if (!m_display_assumption) - return out; - vector deps; - m_asm.linearize(s, deps); - bool first = true; - for (auto dep : deps) { - if (first) first = false; else out << " "; - (*m_display_assumption)(out, dep); - } - return out; - } - - std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display(out, ls[i], proc); - } - return out; - } - - std::ostream& display(std::ostream & out, unsigned num, literal const * ls) const { - return display(out, num, ls, m_display_var); - } - - std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display(out, ~ls[i], proc); - } - return out; - } - - std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls) const { - return display_not(out, num, ls, m_display_var); - } - - std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { - return display(out, cs.size(), cs.data(), m_display_var); - } - - std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { - if (c.assumptions() != nullptr) { - display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); - out << " |- "; - } - return display(out, c.size(), c.data(), proc); - } - - std::ostream& display(std::ostream & out, clause const & c) const { - return display(out, c, m_display_var); - } - - - std::ostream& display_polynomial(std::ostream& out, poly* p, display_var_proc const & proc, bool use_star = false) const { - if (m_display_eval) { - polynomial_ref q(m_pm); - q = p; - for (var x = 0; x < num_vars(); x++) - if (m_assignment.is_assigned(x)) { - auto& a = m_assignment.value(x); - if (!m_am.is_rational(a)) - continue; - mpq r; - m_am.to_rational(a, r); - q = m_pm.substitute(q, 1, &x, &r); - } - m_pm.display(out, q, proc, use_star); - } - else - m_pm.display(out, p, proc, use_star); - return out; - } - - // -- - - std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const { - return display_smt2(out, n, ls, display_var_proc()); - } - - - std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - if (num == 0) { - out << "false"; - } - else if (num == 1) { - display_smt2(out, ls[0], proc); - } - else { - out << "(or"; - for (unsigned i = 0; i < num; i++) { - out << " "; - display_smt2(out, ls[i], proc); - } - out << ")"; - } - return out; - } - - std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { - return display_smt2(out, c.size(), c.data(), proc); - } - - std::ostream& display_abst(std::ostream & out, literal l) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (b == true_bool_var) - out << "true"; - else - out << "b" << b; - } - else { - out << "b" << l.var(); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display_abst(out, ls[i]); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { - return display_abst(out, cs.size(), cs.data()); - } - - std::ostream& display_abst(std::ostream & out, clause const & c) const { - return display_abst(out, c.size(), c.data()); - } - - std::ostream& display_mathematica(std::ostream & out, clause const & c) const { - out << "("; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) - out << " || "; - display_mathematica(out, c[i]); - } - out << ")"; - return out; - } - - // Debugging support: - // Display generated lemma in Mathematica format. - // Mathematica must reduce lemma to True (modulo resource constraints). - std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { - out << "Resolve[ForAll[{"; - // var definition - for (unsigned i = 0; i < num_vars(); i++) { - if (i > 0) - out << ", "; - out << "x" << i; - } - out << "}, "; - if (include_assignment) { - out << "!("; - if (!display_mathematica_assignment(out)) - out << "0 < 1"; // nothing was printed - out << ") || "; - } - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " || "; - display_mathematica(out, ls[i]); - } - out << "], Reals]\n"; // end of exists - return out; - } - - std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { - for (clause* c : cs) { - display(out, *c, proc) << "\n"; - } - return out; - } - - std::ostream& display(std::ostream & out, clause_vector const & cs) const { - return display(out, cs, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) out << ",\n"; - display_mathematica(out << " ", *(cs[i])); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { - for (clause* c : cs) { - display_abst(out, *c) << "\n"; - } - return out; - } - - std::ostream& display(std::ostream & out, display_var_proc const & proc) const { - display(out, m_clauses, proc); - if (!m_learned.empty()) { - display(out << "Lemmas:\n", m_learned, proc); - } - return out; - } - - std::ostream& display_mathematica(std::ostream & out) const { - return display_mathematica(out << "{\n", m_clauses) << "}\n"; - } - - std::ostream& display_abst(std::ostream & out) const { - display_abst(out, m_clauses); - if (!m_learned.empty()) { - display_abst(out << "Lemmas:\n", m_learned); - } - return out; - } - - std::ostream& display(std::ostream & out) const { - display(out, m_display_var); - display_assignment(out << "assignment:\n"); - return out << "---\n"; - } - - std::ostream& display_vars(std::ostream & out) const { - for (unsigned i = 0; i < num_vars(); i++) { - out << i << " -> "; m_display_var(out, i); out << "\n"; - } - return out; - } - - std::ostream& display_smt2_arith_decls(std::ostream & out) const { - unsigned sz = m_is_int.size(); - for (unsigned i = 0; i < sz; i++) { - if (is_int(i)) { - out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; - } - else { - out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; - } - } - return out; - } - - std::ostream& display_smt2_bool_decls(std::ostream & out) const { - unsigned sz = m_atoms.size(); - for (unsigned i = 0; i < sz; i++) { - if (m_atoms[i] == nullptr) - out << "(declare-fun b" << i << " () Bool)\n"; - } - return out; - } - - std::ostream& display_smt2(std::ostream & out) const { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); - out << "(assert (and true\n"; - for (clause* c : m_clauses) { - display_smt2(out, *c, m_display_var) << "\n"; - } - out << "))\n" << std::endl; - return out; - } - }; - - solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { - m_ctx = alloc(ctx, rlim, p, incremental); - m_imp = alloc(imp, *this, *m_ctx); - } - - solver::solver(ctx& ctx) { - m_ctx = nullptr; - m_imp = alloc(imp, *this, ctx); - } - - solver::~solver() { - dealloc(m_imp); - dealloc(m_ctx); - } - - lbool solver::check() { - return m_imp->check(); - } - - lbool solver::check(literal_vector& assumptions) { - return m_imp->check(assumptions); - } - - void solver::get_core(vector& assumptions) { - return m_imp->get_core(assumptions); - } - - void solver::reset() { - m_imp->reset(); - } - - - void solver::updt_params(params_ref const & p) { - m_imp->updt_params(p); - } - - - void solver::collect_param_descrs(param_descrs & d) { - algebraic_numbers::manager::collect_param_descrs(d); - nlsat_params::collect_param_descrs(d); - } - - unsynch_mpq_manager & solver::qm() { - return m_imp->m_qm; - } - - anum_manager & solver::am() { - return m_imp->m_am; - } - - pmanager & solver::pm() { - return m_imp->m_pm; - } - - void solver::set_display_var(display_var_proc const & proc) { - m_imp->m_display_var.m_proc = &proc; - } - - void solver::set_display_assumption(display_assumption_proc const& proc) { - m_imp->m_display_assumption = &proc; - } - - - unsigned solver::num_vars() const { - return m_imp->num_vars(); - } - - bool solver::is_int(var x) const { - return m_imp->is_int(x); - } - - bool_var solver::mk_bool_var() { - return m_imp->mk_bool_var(); - } - - literal solver::mk_true() { - return literal(0, false); - } - - atom * solver::bool_var2atom(bool_var b) { - return m_imp->m_atoms[b]; - } - - void solver::vars(literal l, var_vector& vs) { - m_imp->vars(l, vs); - } - - atom_vector const& solver::get_atoms() { - return m_imp->m_atoms; - } - - atom_vector const& solver::get_var2eq() { - return m_imp->m_var2eq; - } - - evaluator& solver::get_evaluator() { - return m_imp->m_evaluator; - } - - explain& solver::get_explain() { - return m_imp->m_explain; - } - - void solver::reorder(unsigned sz, var const* p) { - m_imp->reorder(sz, p); - } - - void solver::restore_order() { - m_imp->restore_order(); - } - - void solver::set_rvalues(assignment const& as) { - m_imp->m_assignment.copy(as); - } - - void solver::get_rvalues(assignment& as) { - as.copy(m_imp->m_assignment); - } - - void solver::get_bvalues(svector const& bvars, svector& vs) { - vs.reset(); - for (bool_var b : bvars) { - vs.reserve(b + 1, l_undef); - if (!m_imp->m_atoms[b]) { - vs[b] = m_imp->m_bvalues[b]; - } - } - TRACE("nlsat", display(tout);); - } - - void solver::set_bvalues(svector const& vs) { - TRACE("nlsat", display(tout);); - for (bool_var b = 0; b < vs.size(); ++b) { - if (vs[b] != l_undef) { - m_imp->m_bvalues[b] = vs[b]; - SASSERT(!m_imp->m_atoms[b]); - } - } -#if 0 - m_imp->m_bvalues.reset(); - m_imp->m_bvalues.append(vs); - m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); - for (unsigned i = 0; i < m_imp->m_atoms.size(); ++i) { - atom* a = m_imp->m_atoms[i]; - SASSERT(!a); - if (a) { - m_imp->m_bvalues[i] = to_lbool(m_imp->m_evaluator.eval(a, false)); - } - } -#endif - TRACE("nlsat", display(tout);); - } - - void solver::del_clause(clause* c) { - m_imp->del_clause(c); - } - - var solver::mk_var(bool is_int) { - return m_imp->mk_var(is_int); - } - - bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { - return m_imp->mk_ineq_atom(k, sz, ps, is_even); - } - - literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify) { - return m_imp->mk_ineq_literal(k, sz, ps, is_even, simplify); - } - - bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { - return m_imp->mk_root_atom(k, x, i, p); - } - - void solver::inc_ref(bool_var b) { - m_imp->inc_ref(b); - } - - void solver::dec_ref(bool_var b) { - m_imp->dec_ref(b); - } - - void solver::inc_ref(assumption a) { - m_imp->inc_ref(static_cast(a)); - } - - void solver::dec_ref(assumption a) { - m_imp->dec_ref(static_cast(a)); - } - - void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { - return m_imp->mk_external_clause(num_lits, lits, a); - } - - std::ostream& solver::display(std::ostream & out) const { - return m_imp->display(out); - } - - std::ostream& solver::display(std::ostream & out, literal l) const { - return m_imp->display(out, l); - } - - std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { - for (unsigned i = 0; i < n; ++i) { - display(out, ls[i]); - out << "; "; - } - return out; - } - - std::ostream& solver::display(std::ostream & out, literal_vector const& ls) const { - return display(out, ls.size(), ls.data()); - } - - std::ostream& solver::display_smt2(std::ostream & out, literal l) const { - return m_imp->display_smt2(out, l); - } - - std::ostream& solver::display_smt2(std::ostream & out, unsigned n, literal const* ls) const { - for (unsigned i = 0; i < n; ++i) { - display_smt2(out, ls[i]); - out << " "; - } - return out; - } - - std::ostream& solver::display(std::ostream& out, clause const& c) const { - return m_imp->display(out, c); - } - - std::ostream& solver::display_smt2(std::ostream & out) const { - return m_imp->display_smt2(out); - } - - std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { - return display_smt2(out, ls.size(), ls.data()); - } - - std::ostream& solver::display(std::ostream & out, var x) const { - return m_imp->m_display_var(out, x); - } - - std::ostream& solver::display(std::ostream & out, atom const& a) const { - return m_imp->display(out, a, m_imp->m_display_var); - } - - display_var_proc const & solver::display_proc() const { - return m_imp->m_display_var; - } - - anum const & solver::value(var x) const { - if (m_imp->m_assignment.is_assigned(x)) - return m_imp->m_assignment.value(x); - return m_imp->m_zero; - } - - lbool solver::bvalue(bool_var b) const { - return m_imp->m_bvalues[b]; - } - - lbool solver::value(literal l) const { - return m_imp->value(l); - } - - bool solver::is_interpreted(bool_var b) const { - return m_imp->m_atoms[b] != 0; - } - - void solver::reset_statistics() { - return m_imp->reset_statistics(); - } - - void solver::collect_statistics(statistics & st) { - return m_imp->collect_statistics(st); - } - - clause* solver::mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a) { - return m_imp->mk_clause(n, lits, learned, static_cast(a)); - } - - void solver::inc_simplify() { - m_imp->m_stats.m_simplifications++; - } - - bool solver::has_root_atom(clause const& c) const { - return m_imp->has_root_atom(c); - } - - void solver::add_bound(bound_constraint const& c) { - m_imp->m_bounds.push_back(c); - } - - assumption solver::join(assumption a, assumption b) { - return (m_imp->m_asm.mk_join(static_cast(a), static_cast(b))); - } - -}; diff --git a/src/nlsat/nlsat_symmetry_checker.cpp b/src/nlsat/nlsat_symmetry_checker.cpp new file mode 100644 index 00000000000..8d0ee87663d --- /dev/null +++ b/src/nlsat/nlsat_symmetry_checker.cpp @@ -0,0 +1,356 @@ +#include "nlsat/nlsat_symmetry_checker.h" + +struct Debug_Tracer { + std::string tag_str; + Debug_Tracer(std::string _tag_str) { + tag_str = _tag_str; + TRACE("linxi_symmetry_checker", + tout << "Debug_Tracer begin\n"; + tout << tag_str << "\n"; + ); + } + ~Debug_Tracer() { + TRACE("linxi_symmetry_checker", + tout << "Debug_Tracer end\n"; + tout << tag_str << "\n"; + ); + } +}; + +// #define _LINXI_DEBUG + +#ifdef _LINXI_DEBUG +#define LINXI_DEBUG_CORE(x) std::stringstream DEBUG_ss_##x; DEBUG_ss_##x << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__; Debug_Tracer DEBUG_dt_##x(DEBUG_ss_##x.str()); +#define LINXI_DEBUG_TRANS(x) LINXI_DEBUG_CORE(x); +#define LINXI_DEBUG LINXI_DEBUG_TRANS(__LINE__); +#define LINXI_HERE TRACE("linxi_symmetry_checker", tout << "here\n";); +#else +#define LINXI_DEBUG { }((void) 0 ); +#define LINXI_HERE { }((void) 0 ); +#endif + + + +namespace nlsat { + struct Symmetry_Checker::imp { + // solver / + pmanager ± + unsynch_mpq_manager &qm; + const clause_vector &clauses; + const atom_vector &atoms; + const bool_vector &is_int; + const unsigned arith_var_num; + // vector vars_hash; + vector> vars_occur; + bool is_check; + var tx, ty; + struct Variable_Information { + unsigned long long hash_value; + unsigned deg; + var x; + bool is_int; + bool operator< (const Variable_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Variable_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (deg != rhs.deg) + return false; + if (x != rhs.x) + return false; + if (is_int != rhs.is_int) + return false; + return true; + } + bool operator!= (const Variable_Information &rhs) const { + return !(*this == rhs); + } + }; + unsigned long long VAR_BASE = 601; + void collect_var_info(Variable_Information &var_info, var x, unsigned deg) { + LINXI_DEBUG; + if (is_check) { + if (x == tx) { + x = ty; + } + else if (x == ty) { + x = tx; + } + else { + // do nothing + } + } + else { + vars_occur[x].push_back(deg); + } + var_info.deg = deg; + var_info.x = x; + var_info.is_int = is_int[x]; + var_info.hash_value = 0; + for (unsigned i = 0; i < deg; ++i) { + var_info.hash_value = var_info.hash_value*VAR_BASE + (unsigned long long)x; + } + var_info.hash_value = var_info.hash_value*VAR_BASE + (unsigned long long)var_info.is_int; + } + struct Monomial_Information { + unsigned long long hash_value; + unsigned long long coef; + vector vars_info; + bool operator< (const Monomial_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Monomial_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (coef != rhs.coef) + return false; + if (vars_info.size() != rhs.vars_info.size()) + return false; + for (unsigned i = 0, sz = vars_info.size(); i < sz; ++i) { + if (vars_info[i] != rhs.vars_info[i]) + return false; + } + return true; + } + bool operator!= (const Monomial_Information &rhs) const { + return !(*this == rhs); + } + }; + unsigned long long MONO_BASE = 99991; + + void collect_mono_info(Monomial_Information &mono_info, monomial *m, unsigned long long coef) { + LINXI_DEBUG; + unsigned sz = pm.size(m); + mono_info.vars_info.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + collect_var_info(mono_info.vars_info[i], pm.get_var(m, i), pm.degree(m, i)); + } + mono_info.coef = coef; + mono_info.hash_value = coef; + std::sort(mono_info.vars_info.begin(), mono_info.vars_info.end()); + for (unsigned i = 0; i < sz; ++i) { + mono_info.hash_value = mono_info.hash_value*MONO_BASE + mono_info.vars_info[i].hash_value; + } + } + struct Polynomial_Information { + unsigned long long hash_value; + bool is_even; + vector monos_info; + bool operator< (const Polynomial_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Polynomial_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (is_even != rhs.is_even) + return false; + if (monos_info.size() != rhs.monos_info.size()) + return false; + for (unsigned i = 0, sz = monos_info.size(); i < sz; ++i) { + if (monos_info[i] != rhs.monos_info[i]) + return false; + } + return true; + } + bool operator!= (const Polynomial_Information &rhs) const { + return !(*this == rhs); + } + }; + unsigned long long POLY_BASE = 99991; + void collect_poly_info(Polynomial_Information &poly_info, poly *p, bool is_even) { + LINXI_DEBUG; + unsigned sz = pm.size(p); + poly_info.monos_info.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + collect_mono_info(poly_info.monos_info[i], pm.get_monomial(p, i), qm.get_uint64(pm.coeff(p, i))); + } + poly_info.hash_value = 0; + std::sort(poly_info.monos_info.begin(), poly_info.monos_info.end()); + for (unsigned i = 0; i < sz; ++i) { + poly_info.hash_value = poly_info.hash_value*POLY_BASE + poly_info.monos_info[i].hash_value; + } + poly_info.is_even = is_even; + if (is_even) { + poly_info.hash_value = poly_info.hash_value*poly_info.hash_value; + } + } + struct Atom_Information { + unsigned long long hash_value; + atom::kind akd; + vector polys_info; + bool operator< (const Atom_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Atom_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (akd != rhs.akd) + return false; + if (polys_info.size() != rhs.polys_info.size()) + return false; + for (unsigned i = 0, sz = polys_info.size(); i < sz; ++i) { + if (polys_info[i] != rhs.polys_info[i]) + return false; + } + return true; + } + bool operator!= (const Atom_Information &rhs) const { + return !(*this == rhs); + } + }; + unsigned long long ATOM_BASE = 233; + void collect_atom_info(Atom_Information &atom_info, ineq_atom *iat) { + LINXI_DEBUG; + unsigned sz = iat->size(); + atom_info.polys_info.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + collect_poly_info(atom_info.polys_info[i], iat->p(i), iat->is_even(i)); + } + atom_info.hash_value = 0; + std::sort(atom_info.polys_info.begin(), atom_info.polys_info.end()); + for (unsigned i = 0; i < sz; ++i) { + atom_info.hash_value = atom_info.hash_value*ATOM_BASE + atom_info.polys_info[i].hash_value; + } + atom_info.akd = iat->get_kind(); + atom_info.hash_value = atom_info.hash_value*ATOM_BASE + (unsigned long long)atom_info.akd; + } + struct Literal_Information { + unsigned long long hash_value; + vector atom_info; // not atoms + bool operator< (const Literal_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Literal_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (atom_info.size() != rhs.atom_info.size()) + return false; + for (unsigned i = 0, sz = atom_info.size(); i < sz; ++i) { + if (atom_info[i] != rhs.atom_info[i]) + return false; + } + return true; + } + bool operator!= (const Literal_Information &rhs) const { + return !(*this == rhs); + } + }; + void collect_lit_info(Literal_Information &lit_info, literal lit) { + LINXI_DEBUG; + atom *at = atoms[lit.var()]; + if (at == nullptr || !at->is_ineq_atom()) { + lit_info.hash_value = lit.to_uint(); + } + else { + lit_info.atom_info.resize(1); + collect_atom_info(lit_info.atom_info[0], to_ineq_atom(at)); + lit_info.hash_value = lit_info.atom_info[0].hash_value; + } + } + struct Clause_Information { + unsigned long long hash_value; + vector lits_info; + bool operator< (const Clause_Information &rhs) const { + return hash_value < rhs.hash_value; + } + bool operator== (const Clause_Information &rhs) const { + if (hash_value != rhs.hash_value) + return false; + if (lits_info.size() != rhs.lits_info.size()) + return false; + for (unsigned i = 0, sz = lits_info.size(); i < sz; ++i) { + if (lits_info[i] != rhs.lits_info[i]) + return false; + } + return true; + } + bool operator!= (const Clause_Information &rhs) const { + return !(*this == rhs); + } + }; + unsigned long long CLA_BASE = 9973; + void collect_cla_info(Clause_Information &cla_info, clause *cla) { + LINXI_DEBUG; + unsigned sz = cla->size(); + cla_info.lits_info.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + literal lit = (*cla)[i]; + collect_lit_info(cla_info.lits_info[i], lit); + } + cla_info.hash_value = 0; + std::sort(cla_info.lits_info.begin(), cla_info.lits_info.end()); + for (unsigned i = 0; i < sz; ++i) { + cla_info.hash_value = cla_info.hash_value*CLA_BASE + cla_info.lits_info[i].hash_value; + } + } + + void collect_clas_info(vector &clas_info) { + LINXI_DEBUG; + unsigned sz = clauses.size(); + clas_info.resize(sz); + for (unsigned i = 0; i < sz; ++i) { + collect_cla_info(clas_info[i], clauses[i]); + } + std::sort(clas_info.begin(), clas_info.end()); + if (!is_check) { + for (unsigned i = 0; i < arith_var_num; ++i) { + std::sort(vars_occur[i].begin(), vars_occur[i].begin()); + } + } + } + vector ori_clas_info; + imp(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num) : + // sol(_sol), + pm(_pm), + qm(_qm), + clauses(_clauses), + atoms(_atoms), + is_int(_is_int), + arith_var_num(_arith_var_num), + is_check(false) { + vars_occur.resize(arith_var_num); + collect_clas_info(ori_clas_info); + // vars_hash.resize(arith_var_num, 0); + } + vector check_clas_info; + bool check_occur_same(var x, var y) { + if (vars_occur[x].size() != vars_occur[y].size()) + return false; + for (unsigned i = 0, sz = vars_occur[x].size(); i < sz; ++i) { + if (vars_occur[x][i] != vars_occur[y][i]) + return false; + } + return true; + } + bool check_symmetry(var x, var y) { + if (!check_occur_same(x, y)) { + return false; + } + is_check = true; + tx = x, ty = y; + collect_clas_info(check_clas_info); + for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { + if (ori_clas_info[i] != check_clas_info[i]) + return false; + } + return true; + } + }; + Symmetry_Checker::Symmetry_Checker(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num) { + LINXI_DEBUG; + // m_imp = alloc(imp, _sol, _pm, _am, _clauses, _learned, _atoms, _arith_var_num); + m_imp = alloc(imp, _pm, _qm, _clauses, _atoms, _is_int, _arith_var_num); + } + Symmetry_Checker::~Symmetry_Checker() { + LINXI_DEBUG; + dealloc(m_imp); + } + // bool Symmetry_Checker::operator()() { + // LINXI_DEBUG; + + // } + bool Symmetry_Checker::check_symmetry(var x, var y) { + return m_imp->check_symmetry(x, y); + } +} \ No newline at end of file diff --git a/src/nlsat/nlsat_symmetry_checker.h b/src/nlsat/nlsat_symmetry_checker.h new file mode 100644 index 00000000000..1db5cb24be7 --- /dev/null +++ b/src/nlsat/nlsat_symmetry_checker.h @@ -0,0 +1,13 @@ +#include "nlsat/nlsat_clause.h" + +namespace nlsat { + class Symmetry_Checker { + struct imp; + imp * m_imp; + public: + // Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num); + Symmetry_Checker(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num); + ~Symmetry_Checker(); + bool check_symmetry(var x, var y); + }; +} \ No newline at end of file diff --git a/src/nlsat/nlsat_variable_ordering_strategy.cpp b/src/nlsat/nlsat_variable_ordering_strategy.cpp new file mode 100644 index 00000000000..8c4723fd309 --- /dev/null +++ b/src/nlsat/nlsat_variable_ordering_strategy.cpp @@ -0,0 +1,282 @@ +#include "nlsat/nlsat_variable_ordering_strategy.h" + +namespace nlsat { + struct VOS_Var_Info_Collector::imp { + pmanager & pm; + atom_vector const & m_atoms; + unsigned num_vars; + Variable_Ordering_Strategy_Type m_vos_type; + + /** Maximum degree of this variable. */ + unsigned_vector m_max_degree; + /** Sum of degrees of this variable within all polynomials. */ + unsigned_vector m_sum_poly_degree; + /** Number of polynomials that contain this variable. */ + unsigned_vector m_num_polynomials; + + /** Maximum degree of the leading coefficient of this variable. */ + unsigned_vector m_max_lc_degree; + /** Maximum of total degrees of terms that contain this variable. */ + unsigned_vector m_max_terms_tdegree; + /** Sum of degrees of this variable within all terms. */ + unsigned_vector m_sum_term_degree; + /** Number of terms that contain this variable. */ + unsigned_vector m_num_terms; + + + unsigned_vector m_num_uni; + numeral_vector m_coeffs; + + + imp(pmanager & _pm, atom_vector const & atoms, unsigned _num_vars, unsigned _vos_type): + pm(_pm), + m_atoms(atoms), + num_vars(_num_vars), + m_vos_type(Variable_Ordering_Strategy_Type(_vos_type)) { + + m_max_degree.resize(num_vars, 0); + m_sum_poly_degree.resize(num_vars, 0); + m_num_polynomials.resize(num_vars, 0); + + if (m_vos_type != ONLYPOLY) { + m_max_lc_degree.resize(num_vars, 0); + m_max_terms_tdegree.resize(num_vars, 0); + m_sum_term_degree.resize(num_vars, 0); + m_num_terms.resize(num_vars, 0); + + + m_num_uni.resize(num_vars, 0); + m_coeffs.resize(num_vars, 0); + + } + } + + void collect(monomial * m) { + unsigned mdeg = 0; + for (unsigned i = 0, sz = pm.size(m); i < sz; ++i) { + var x = pm.get_var(m, i); + mdeg += pm.degree_of(m, x); + ++m_num_terms[x]; + } + + for (unsigned i = 0, sz = pm.size(m); i < sz; ++i) { + var x = pm.get_var(m, i); + m_sum_term_degree[x] += mdeg; + if (mdeg > m_max_terms_tdegree[x]) + m_max_terms_tdegree[x] = mdeg; + unsigned lc_deg = mdeg - pm.degree_of(m, x); + if (lc_deg > m_max_lc_degree[x]) + m_max_lc_degree[x] = lc_deg; + } + } + + void collect(poly * p) { + var_vector vec_vars; + pm.vars(p, vec_vars); + + if (m_vos_type == UNIVARIATE) { + if (vec_vars.size() == 1) + ++m_num_uni[vec_vars[0]]; + } + + for (unsigned i = 0, sz = vec_vars.size(); i < sz; ++i) { + var x = vec_vars[i]; + unsigned k = pm.degree(p, x); + ++m_num_polynomials[x]; + m_sum_poly_degree[x] += k; + if (k > m_max_degree[x]) + m_max_degree[x] = k; + + if (m_vos_type == FEATURE){ + for (unsigned kl = 0; kl <= k; kl++) { + scoped_numeral curr(pm.m()); + if (pm.const_coeff(p, x, kl, curr)) { + pm.m().abs(curr); + if (pm.m().gt(curr, m_coeffs[x])) { + pm.m().set(m_coeffs[x], curr); + } + } + } + } + + } + + if (m_vos_type != ONLYPOLY && m_vos_type != UNIVARIATE){ + for (unsigned i = 0, sz = pm.size(p); i < sz; ++i) { + collect(pm.get_monomial(p, i)); + } + } + } + + void collect(literal l) { + bool_var b = l.var(); + atom * a = m_atoms[b]; + if (a == nullptr) + return; + if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; i++) { + collect(to_ineq_atom(a)->p(i)); + } + } + else { + collect(to_root_atom(a)->p()); + } + } + + void collect(clause const & c) { + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) + collect(c[i]); + } + + void collect(clause_vector const & cs) { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) + collect(*(cs[i])); + } + + + struct univariate_reorder_lt { + VOS_Var_Info_Collector::imp const *m_info; + univariate_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + bool operator()(var x, var y) const { + if (m_info->m_num_uni[x] != m_info->m_num_uni[y]) + return m_info->m_num_uni[x] > m_info->m_num_uni[y]; + return x < y; + } + }; + + struct feature_reorder_lt { + VOS_Var_Info_Collector::imp const *m_info; + feature_reorder_lt(VOS_Var_Info_Collector::imp const * info): m_info(info){} + bool operator()(var x, var y) const { + if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) + return m_info->m_max_degree[x] > m_info->m_max_degree[y]; + if (m_info->m_max_terms_tdegree[x] != m_info->m_max_terms_tdegree[y]) + return m_info->m_max_terms_tdegree[x] > m_info->m_max_terms_tdegree[y]; + if (!m_info->pm.m().eq(m_info->m_coeffs[x], m_info->m_coeffs[y])) { + return m_info->pm.m().lt(m_info->m_coeffs[x], m_info->m_coeffs[y]); + } + return x < y; + } + }; + struct brown_reorder_lt { + VOS_Var_Info_Collector::imp const *m_info; + brown_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + bool operator()(var x, var y) const { + // if (a.max_degree != b.max_degree) + // return a.max_degree > b.max_degree; + // if (a.max_terms_tdegree != b.max_terms_tdegree) + // return a.max_terms_tdegree > b.max_terms_tdegree; + // return a.num_terms > b.num_terms; + if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) + return m_info->m_max_degree[x] > m_info->m_max_degree[y]; + if (m_info->m_max_terms_tdegree[x] != m_info->m_max_terms_tdegree[y]) + return m_info->m_max_terms_tdegree[x] > m_info->m_max_terms_tdegree[y]; + if (m_info->m_num_terms[x] != m_info->m_num_terms[y]) + return m_info->m_num_terms[x] > m_info->m_num_terms[y]; + return x < y; + } + }; + struct triangular_reorder_lt { + const VOS_Var_Info_Collector::imp *m_info; + triangular_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + bool operator()(var x, var y) const { + // if (a.max_degree != b.max_degree) + // return a.max_degree > b.max_degree; + // if (a.max_lc_degree != b.max_lc_degree) + // return a.max_lc_degree > b.max_lc_degree; + // return a.sum_poly_degree > b.sum_poly_degree; + if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) + return m_info->m_max_degree[x] > m_info->m_max_degree[y]; + if (m_info->m_max_lc_degree[x] != m_info->m_max_lc_degree[y]) + return m_info->m_max_lc_degree[x] > m_info->m_max_lc_degree[y]; + if (m_info->m_sum_poly_degree[x] != m_info->m_sum_poly_degree[y]) + return m_info->m_sum_poly_degree[x] > m_info->m_sum_poly_degree[y]; + return x < y; + } + }; + struct onlypoly_reorder_lt { + const VOS_Var_Info_Collector::imp *m_info; + onlypoly_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + bool operator()(var x, var y) const { + // high degree first + if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) + return m_info->m_max_degree[x] > m_info->m_max_degree[y]; + // + if (m_info->m_sum_poly_degree[x] != m_info->m_sum_poly_degree[y]) + return m_info->m_sum_poly_degree[x] > m_info->m_sum_poly_degree[y]; + // more constrained first + if (m_info->m_num_polynomials[x] != m_info->m_num_polynomials[y]) + return m_info->m_num_polynomials[x] > m_info->m_num_polynomials[y]; + return x < y; + } + }; + bool check_invariant() const {return true;} // what is the invariant + void operator()(var_vector &perm) { + var_vector new_order; + for (var x = 0; x < num_vars; x++) { + new_order.push_back(x); + } + if (m_vos_type == BROWN) { + std::sort(new_order.begin(), new_order.end(), brown_reorder_lt(this)); + } + else if (m_vos_type == TRIANGULAR) { + std::sort(new_order.begin(), new_order.end(), triangular_reorder_lt(this)); + } + else if (m_vos_type == ONLYPOLY) { + std::sort(new_order.begin(), new_order.end(), onlypoly_reorder_lt(this)); + } + + else if(m_vos_type == UNIVARIATE){ + std::sort(new_order.begin(), new_order.end(), univariate_reorder_lt(this)); + } + else if(m_vos_type == FEATURE){ + std::sort(new_order.begin(), new_order.end(), feature_reorder_lt(this)); + } + + else { + UNREACHABLE(); + } + TRACE("linxi_reorder", + tout << "new order: "; + for (unsigned i = 0; i < num_vars; i++) + tout << new_order[i] << " "; + tout << "\n"; + ); + perm.resize(num_vars, 0); + for (var x = 0; x < num_vars; x++) { + perm[new_order[x]] = x; + } + + SASSERT(check_invariant()); + } + // std::ostream& display(std::ostream & out, display_var_proc const & proc) { + // unsigned sz = m_num_occs.size(); + // for (unsigned i = 0; i < sz; i++) { + // proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; + // } + // return out; + // } + + // std::ostream& display(std::ostream & out, display_var_proc const & proc) { + // for (unsigned i = 0; i < num_vars; ++i) { + // proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_sum_poly_degree[i] << "\n"; + // } + // return out; + // } + }; + VOS_Var_Info_Collector::VOS_Var_Info_Collector(pmanager & _pm, atom_vector const & _atoms, unsigned _num_vars, unsigned _vos_type) { + m_imp = alloc(imp, _pm, _atoms, _num_vars, _vos_type); + } + VOS_Var_Info_Collector::~VOS_Var_Info_Collector() { + dealloc(m_imp); + } + void VOS_Var_Info_Collector::collect(clause_vector const & cs) { + m_imp->collect(cs); + } + void VOS_Var_Info_Collector::operator()(var_vector &perm) { + m_imp->operator()(perm); + } +} diff --git a/src/nlsat/nlsat_variable_ordering_strategy.h b/src/nlsat/nlsat_variable_ordering_strategy.h new file mode 100644 index 00000000000..6e01825c391 --- /dev/null +++ b/src/nlsat/nlsat_variable_ordering_strategy.h @@ -0,0 +1,27 @@ +#include "nlsat/nlsat_clause.h" + + +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial.h" + + +namespace nlsat { + + typedef polynomial::manager::scoped_numeral scoped_numeral; + typedef polynomial::manager::numeral_vector numeral_vector; + + + // enum Variable_Ordering_Strategy_Type {NONE = 0, BROWN, TRIANGULAR, ONLYPOLY}; + + enum Variable_Ordering_Strategy_Type {NONE = 0, BROWN, TRIANGULAR, ONLYPOLY, UNIVARIATE, FEATURE, ROOT}; + + class VOS_Var_Info_Collector { + struct imp; + imp * m_imp; + public: + VOS_Var_Info_Collector(pmanager & _pm, atom_vector const & atoms, unsigned _num_vars, unsigned _vos_type); + ~VOS_Var_Info_Collector(); + void operator()(var_vector &perm); + void collect(clause_vector const & cs); + }; +} \ No newline at end of file diff --git a/src/tactic/smtlogics/qfnra_tactic.cpp b/src/tactic/smtlogics/qfnra_tactic.cpp index b9fec436600..5ecfa242652 100644 --- a/src/tactic/smtlogics/qfnra_tactic.cpp +++ b/src/tactic/smtlogics/qfnra_tactic.cpp @@ -23,6 +23,8 @@ Module Name: #include "nlsat/tactic/qfnra_nlsat_tactic.h" #include "tactic/smtlogics/smt_tactic.h" +#include "tactic/smtlogics/qflra_tactic.h" + static tactic * mk_qfnra_sat_solver(ast_manager& m, params_ref const& p, unsigned bv_size) { params_ref nra2sat_p = p; nra2sat_p.set_uint("nla2bv_max_bv_size", p.get_uint("nla2bv_max_bv_size", bv_size)); @@ -32,24 +34,305 @@ static tactic * mk_qfnra_sat_solver(ast_manager& m, params_ref const& p, unsigne mk_fail_if_undecided_tactic()); } +tactic * mk_multilinear_ls_tactic(ast_manager & m, params_ref const & p, unsigned ls_time = 60) { + params_ref p_mls = p; + p_mls.set_bool("use_ls", true); + p_mls.set_uint("ls_time",ls_time); + return using_params(mk_smt_tactic(m), p_mls); +} + +tactic * linxi_mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) { + ptr_vector ts; + { + params_ref p_sc = p; + p_sc.set_bool("linxi_simple_check", true); + // p_sc.set_uint("seed", 997); + ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 10 * 1000)); + } + { + params_ref p_heuristic = p; + // p_heuristic.set_uint("seed", 233); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_heuristic), 4 * 1000)); + + params_ref p_order_4 = p; + p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 4 * 1000)); + + params_ref p_order_3 = p; + p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + // p_order_3.set_uint("seed", 17); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 6 * 1000)); + + params_ref p_order_1 = p; + p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 8 * 1000)); + + params_ref p_order_5 = p; + p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 8 * 1000)); + + params_ref p_order_2 = p; + p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 10 * 1000)); + } + { + ts.push_back(mk_multilinear_ls_tactic(m, p, 60)); + } + { + params_ref p_l = p; + p_l.set_bool("arith.greatest_error_pivot", true); + ts.push_back(and_then(try_for(using_params(mk_smt_tactic(m), p_l), 300 * 1000), mk_fail_if_undecided_tactic())); + } + for (unsigned i = 0; i < 200; ++i) { // 3s * 200 = 600s + params_ref p_i = p; + p_i.set_uint("seed", i); + p_i.set_bool("shuffle_vars", true); + // if ((i & 1) == 0) + // p_i.set_bool("randomize", false); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 3 * 1000)); + } + { + ts.push_back(mk_qfnra_nlsat_tactic(m, p)); + } + return or_else(ts.size(), ts.data()); +} + +tactic * linxi_mk_qfnra_small_solver(ast_manager& m, params_ref const& p) { + ptr_vector ts; + { + params_ref p_sc = p; + p_sc.set_bool("linxi_simple_check", true); + // p_sc.set_uint("seed", 997); + ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 20 * 1000)); + } + { + params_ref p_heuristic = p; + // p_heuristic.set_uint("seed", 233); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_heuristic), 5 * 1000)); + + params_ref p_order_4 = p; + p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 5 * 1000)); + + params_ref p_order_3 = p; + p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + // p_order_3.set_uint("seed", 17); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 10 * 1000)); + + params_ref p_order_1 = p; + p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 15 * 1000)); + + + params_ref p_order_5 = p; + p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 15 * 1000)); + + + params_ref p_order_2 = p; + p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 20 * 1000)); + } + { + ts.push_back(mk_multilinear_ls_tactic(m, p, 70)); + } + { + params_ref p_l = p; + p_l.set_bool("arith.greatest_error_pivot", true); + ts.push_back(and_then(try_for(using_params(mk_smt_tactic(m), p_l), 350 * 1000), mk_fail_if_undecided_tactic())); + } + for (unsigned i = 0; i < 100; ++i) { // 5s * 100 = 500s + params_ref p_i = p; + p_i.set_uint("seed", i); + p_i.set_bool("shuffle_vars", true); + // if ((i & 1) == 0) + // p_i.set_bool("randomize", false); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 5 * 1000)); + } + { + ts.push_back(mk_qfnra_nlsat_tactic(m, p)); + } + return or_else(ts.size(), ts.data()); +} + +tactic * linxi_mk_qfnra_middle_solver(ast_manager& m, params_ref const& p) { + ptr_vector ts; + { + params_ref p_sc = p; + p_sc.set_bool("linxi_simple_check", true); + // p_sc.set_uint("seed", 997); + ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 30 * 1000)); + } + { + params_ref p_heuristic = p; + // p_heuristic.set_uint("seed", 233); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_heuristic), 10 * 1000)); + + + params_ref p_order_4 = p; + p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 15 * 1000)); + + + params_ref p_order_3 = p; + p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + // p_order_3.set_uint("seed", 17); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 15 * 1000)); + + params_ref p_order_1 = p; + p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 20 * 1000)); + + + params_ref p_order_5 = p; + p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 20 * 1000)); + + + params_ref p_order_2 = p; + p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 25 * 1000)); + } + { + ts.push_back(mk_multilinear_ls_tactic(m, p, 80)); + } + { + params_ref p_l = p; + p_l.set_bool("arith.greatest_error_pivot", true); + ts.push_back(and_then(try_for(using_params(mk_smt_tactic(m), p_l), 375 * 1000), mk_fail_if_undecided_tactic())); + } + for (unsigned i = 0; i < 40; ++i) { // 10s * 40 = 400s + params_ref p_i = p; + p_i.set_uint("seed", i); + p_i.set_bool("shuffle_vars", true); + // if ((i & 1) == 0) + // p_i.set_bool("randomize", false); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 10 * 1000)); + } + { + ts.push_back(mk_qfnra_nlsat_tactic(m, p)); + } + return or_else(ts.size(), ts.data()); +} + +tactic * linxi_mk_qfnra_large_solver(ast_manager& m, params_ref const& p) { + ptr_vector ts; + { + params_ref p_sc = p; + p_sc.set_bool("linxi_simple_check", true); + // p_sc.set_uint("seed", 997); + ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 50 * 1000)); + } + { + + params_ref p_order_4 = p; + p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 15 * 1000)); + + + params_ref p_order_3 = p; + p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + // p_order_3.set_uint("seed", 17); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 30 * 1000)); + + params_ref p_order_1 = p; + p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 40 * 1000)); + + + params_ref p_order_5 = p; + p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 40 * 1000)); + + + params_ref p_order_2 = p; + p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 50 * 1000)); + } + { + ts.push_back(mk_multilinear_ls_tactic(m, p, 90)); + } + { + params_ref p_l = p; + p_l.set_bool("arith.greatest_error_pivot", true); + ts.push_back(and_then(try_for(using_params(mk_smt_tactic(m), p_l), 400 * 1000), mk_fail_if_undecided_tactic())); + } + for (unsigned i = 0; i < 10; ++i) { // 20s * 10 = 200s + params_ref p_i = p; + p_i.set_uint("seed", i); + p_i.set_bool("shuffle_vars", true); + // if ((i & 1) == 0) + // p_i.set_bool("randomize", false); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 20 * 1000)); + } + { + ts.push_back(mk_qfnra_nlsat_tactic(m, p)); + } + return or_else(ts.size(), ts.data()); +} + +tactic * linxi_mk_qfnra_very_large_solver(ast_manager& m, params_ref const& p) { + ptr_vector ts; + { + params_ref p_sc = p; + p_sc.set_bool("linxi_simple_check", true); + // p_sc.set_uint("seed", 997); + ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 100 * 1000)); + } + { + params_ref p_order_1 = p; + p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 80 * 1000)); + + + params_ref p_order_5 = p; + p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 80 * 1000)); + + + params_ref p_order_2 = p; + p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 100 * 1000)); + } + { + ts.push_back(mk_multilinear_ls_tactic(m, p, 100)); + } + { + params_ref p_l = p; + p_l.set_bool("arith.greatest_error_pivot", true); + ts.push_back(and_then(try_for(using_params(mk_smt_tactic(m), p_l), 425 * 1000), mk_fail_if_undecided_tactic())); + } + { + ts.push_back(mk_qfnra_nlsat_tactic(m, p)); + } + return or_else(ts.size(), ts.data()); +} + +const double VERY_SMALL_THRESHOLD = 30.0; +const double SMALL_THRESHOLD = 80.0; +const double MIDDLE_THRESHOLD = 300.0; +const double LARGE_THRESHOLD = 600.0; +tactic * linxi_mk_qfnra_mixed_solver(ast_manager& m, params_ref const& p) { + return cond(mk_lt(mk_memory_probe(), mk_const_probe(VERY_SMALL_THRESHOLD)), + linxi_mk_qfnra_very_small_solver(m, p), + cond(mk_lt(mk_memory_probe(), mk_const_probe(SMALL_THRESHOLD)), + linxi_mk_qfnra_small_solver(m, p), + cond(mk_lt(mk_memory_probe(), mk_const_probe(MIDDLE_THRESHOLD)), + linxi_mk_qfnra_middle_solver(m, p), + cond(mk_lt(mk_memory_probe(), mk_const_probe(LARGE_THRESHOLD)), + linxi_mk_qfnra_large_solver(m, p), + linxi_mk_qfnra_very_large_solver(m, p) + ) + ) + ) + ); +} + tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { - params_ref p0 = p; - p0.set_bool("inline_vars", true); - params_ref p1 = p; - p1.set_uint("seed", 11); - p1.set_bool("factor", false); - params_ref p2 = p; - p2.set_uint("seed", 13); - p2.set_bool("factor", false); return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), - or_else(try_for(mk_qfnra_nlsat_tactic(m, p0), 5000), - try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), - mk_qfnra_sat_solver(m, p, 4), - and_then(try_for(mk_smt_tactic(m), 5000), mk_fail_if_undecided_tactic()), - mk_qfnra_sat_solver(m, p, 6), - mk_qfnra_nlsat_tactic(m, p2))); + // mk_multilinear_ls_tactic(m, p) + linxi_mk_qfnra_mixed_solver(m, p) + ); } - - From 4b3a06a3c5175637aa0dd0de22392f32378302f8 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 8 Aug 2024 12:16:41 -1000 Subject: [PATCH 141/414] port hybridSMT Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_solver.cpp | 4219 ++++++++++++++++++++++++++++++++++++ 1 file changed, 4219 insertions(+) create mode 100644 src/nlsat/nlsat_solver.cpp diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp new file mode 100644 index 00000000000..d5075246064 --- /dev/null +++ b/src/nlsat/nlsat_solver.cpp @@ -0,0 +1,4219 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + nlsat_solver.cpp + +Abstract: + + Nonlinear arithmetic satisfiability procedure. The procedure is + complete for nonlinear real arithmetic, but it also has limited + support for integers. + +Author: + + Leonardo de Moura (leonardo) 2012-01-02. + +Revision History: + +--*/ +#include "util/z3_exception.h" +#include "util/chashtable.h" +#include "util/id_gen.h" +#include "util/map.h" +#include "util/dependency.h" +#include "util/permutation.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial_cache.h" +#include "nlsat/nlsat_solver.h" +#include "nlsat/nlsat_clause.h" +#include "nlsat/nlsat_assignment.h" +#include "nlsat/nlsat_justification.h" +#include "nlsat/nlsat_evaluator.h" +#include "nlsat/nlsat_explain.h" +#include "nlsat/nlsat_params.hpp" +#include "nlsat/nlsat_simplify.h" +#include "nlsat/nlsat_simple_checker.h" +#include "nlsat/nlsat_variable_ordering_strategy.h" +#include "nlsat/nlsat_symmetry_checker.h" + +#define NLSAT_EXTRA_VERBOSE + +#ifdef NLSAT_EXTRA_VERBOSE +#define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) +#else +#define NLSAT_VERBOSE(CODE) ((void)0) +#endif + +namespace nlsat { + + + typedef chashtable ineq_atom_table; + typedef chashtable root_atom_table; + + // for apply_permutation procedure + void swap(clause * & c1, clause * & c2) noexcept { + std::swap(c1, c2); + } + + struct solver::ctx { + params_ref m_params; + reslimit& m_rlimit; + small_object_allocator m_allocator; + unsynch_mpq_manager m_qm; + pmanager m_pm; + anum_manager m_am; + bool m_incremental; + ctx(reslimit& rlim, params_ref const & p, bool incremental): + m_params(p), + m_rlimit(rlim), + m_allocator("nlsat"), + m_pm(rlim, m_qm, &m_allocator), + m_am(rlim, m_qm, p, &m_allocator), + m_incremental(incremental) + {} + }; + + struct solver::imp { + + + struct dconfig { + typedef imp value_manager; + typedef small_object_allocator allocator; + typedef void* value; + static const bool ref_count = false; + }; + + typedef dependency_manager assumption_manager; + typedef assumption_manager::dependency* _assumption_set; + + typedef obj_ref assumption_set_ref; + + + typedef polynomial::cache cache; + typedef ptr_vector interval_set_vector; + + + + ctx& m_ctx; + solver& m_solver; + reslimit& m_rlimit; + small_object_allocator& m_allocator; + bool m_incremental; + unsynch_mpq_manager& m_qm; + pmanager& m_pm; + cache m_cache; + anum_manager& m_am; + mutable assumption_manager m_asm; + assignment m_assignment, m_lo, m_hi; // partial interpretation + evaluator m_evaluator; + interval_set_manager & m_ism; + ineq_atom_table m_ineq_atoms; + root_atom_table m_root_atoms; + + + vector m_bounds; + + id_gen m_cid_gen; + clause_vector m_clauses; // set of clauses + clause_vector m_learned; // set of learned clauses + clause_vector m_valids; + + unsigned m_num_bool_vars; + atom_vector m_atoms; // bool_var -> atom + svector m_bvalues; // boolean assignment + unsigned_vector m_levels; // bool_var -> level + svector m_justifications; + vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal + bool_vector m_dead; // mark dead boolean variables + id_gen m_bid_gen; + + simplify m_simplify; + + bool_vector m_is_int; // m_is_int[x] is true if variable is integer + vector m_watches; // var -> clauses where variable is maximal + interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. + atom_vector m_var2eq; // var -> to asserted equality + var_vector m_perm; // var -> var permutation of the variables + var_vector m_inv_perm; + // m_perm: internal -> external + // m_inv_perm: external -> internal + struct perm_display_var_proc : public display_var_proc { + var_vector & m_perm; + display_var_proc m_default_display_var; + display_var_proc const * m_proc; // display external var ids + perm_display_var_proc(var_vector & perm): + m_perm(perm), + m_proc(nullptr) { + } + std::ostream& operator()(std::ostream & out, var x) const override { + if (m_proc == nullptr) + m_default_display_var(out, x); + else + (*m_proc)(out, m_perm[x]); + return out; + } + }; + perm_display_var_proc m_display_var; + + display_assumption_proc const* m_display_assumption; + struct display_literal_assumption : public display_assumption_proc { + imp& i; + literal_vector const& lits; + display_literal_assumption(imp& i, literal_vector const& lits): i(i), lits(lits) {} + std::ostream& operator()(std::ostream& out, assumption a) const override { + if (lits.begin() <= a && a < lits.end()) { + out << *((literal const*)a); + } + else if (i.m_display_assumption) { + (*i.m_display_assumption)(out, a); + } + return out; + } + + }; + struct scoped_display_assumptions { + imp& i; + display_assumption_proc const* m_save; + scoped_display_assumptions(imp& i, display_assumption_proc const& p): i(i), m_save(i.m_display_assumption) { + i.m_display_assumption = &p; + } + ~scoped_display_assumptions() { + i.m_display_assumption = m_save; + } + }; + + explain m_explain; + + bool_var m_bk; // current Boolean variable we are processing + var m_xk; // current arith variable we are processing + + unsigned m_scope_lvl; + + struct bvar_assignment {}; + struct stage {}; + struct trail { + enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; + kind m_kind; + union { + bool_var m_b; + interval_set * m_old_set; + atom * m_old_eq; + }; + trail(bool_var b, bvar_assignment):m_kind(BVAR_ASSIGNMENT), m_b(b) {} + trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} + trail(bool s, stage):m_kind(s ? NEW_STAGE : NEW_LEVEL) {} + trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} + }; + svector m_trail; + + anum m_zero; + + // configuration + unsigned long long m_max_memory; + unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts + bool m_simplify_cores; + bool m_reorder; + bool m_randomize; + bool m_random_order; + unsigned m_random_seed; + bool m_inline_vars; + bool m_log_lemmas; + bool m_check_lemmas; + unsigned m_max_conflicts; + unsigned m_lemma_count; +//#linxi begin + bool m_linxi_simple_check; + unsigned m_linxi_variable_ordering_strategy; + bool m_linxi_symmetry_check; + bool m_linxi_set_0_more; + bool m_cell_sample; +//#linxi end + + struct stats { + unsigned m_simplifications; + unsigned m_restarts; + unsigned m_conflicts; + unsigned m_propagations; + unsigned m_decisions; + unsigned m_stages; + unsigned m_irrational_assignments; // number of irrational witnesses + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; + // statistics + stats m_stats; + + imp(solver& s, ctx& c): + m_ctx(c), + m_solver(s), + m_rlimit(c.m_rlimit), + m_allocator(c.m_allocator), + m_incremental(c.m_incremental), + m_qm(c.m_qm), + m_pm(c.m_pm), + m_cache(m_pm), + m_am(c.m_am), + m_asm(*this, m_allocator), + m_assignment(m_am), m_lo(m_am), m_hi(m_am), + m_evaluator(s, m_assignment, m_pm, m_allocator), + m_ism(m_evaluator.ism()), + m_num_bool_vars(0), + m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), + m_display_var(m_perm), + m_display_assumption(nullptr), + m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()), + m_scope_lvl(0), + m_lemma(s), + m_lazy_clause(s), + m_lemma_assumptions(m_asm) { + updt_params(c.m_params); + reset_statistics(); + mk_true_bvar(); + m_lemma_count = 0; + } + + ~imp() { + clear(); + } + + void mk_true_bvar() { + bool_var b = mk_bool_var(); + SASSERT(b == true_bool_var); + literal true_lit(b, false); + mk_clause(1, &true_lit, false, nullptr); + } + + void updt_params(params_ref const & _p) { + nlsat_params p(_p); + m_max_memory = p.max_memory(); + m_lazy = p.lazy(); + m_simplify_cores = p.simplify_conflicts(); + bool min_cores = p.minimize_conflicts(); + m_reorder = p.reorder(); + m_randomize = p.randomize(); + m_max_conflicts = p.max_conflicts(); + m_random_order = p.shuffle_vars(); + m_random_seed = p.seed(); + m_inline_vars = p.inline_vars(); + m_log_lemmas = p.log_lemmas(); + m_check_lemmas = p.check_lemmas(); +//#linxi begin + m_linxi_simple_check = p.linxi_simple_check(); + m_linxi_variable_ordering_strategy = p.linxi_variable_ordering_strategy(); + m_linxi_symmetry_check = p.linxi_symmetry_check(); +//#linxi end + + + m_cell_sample = p.cell_sample(); + + + m_ism.set_seed(m_random_seed); + m_explain.set_simplify_cores(m_simplify_cores); + m_explain.set_minimize_cores(min_cores); + m_explain.set_factor(p.factor()); + m_am.updt_params(p.p); + } + + void reset() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + m_cache.reset(); + m_assignment.reset(); + m_lo.reset(); + m_hi.reset(); + } + + void clear() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + } + + void checkpoint() { + if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); + if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); + } + + // ----------------------- + // + // Basic + // + // ----------------------- + + unsigned num_bool_vars() const { + return m_num_bool_vars; + } + + unsigned num_vars() const { + return m_is_int.size(); + } + + bool is_int(var x) const { + return m_is_int[x]; + } + + void inc_ref(assumption) {} + + void dec_ref(assumption) {} + + void inc_ref(_assumption_set a) { + if (a != nullptr) m_asm.inc_ref(a); + } + + void dec_ref(_assumption_set a) { + if (a != nullptr) m_asm.dec_ref(a); + } + + void inc_ref(bool_var b) { + if (b == null_bool_var) + return; + atom * a = m_atoms[b]; + if (a == nullptr) + return; + TRACE("ref", display(tout << "inc: " << b << " " << a->ref_count() << " ", *a) << "\n";); + a->inc_ref(); + } + + void inc_ref(literal l) { + inc_ref(l.var()); + } + + void dec_ref(bool_var b) { + if (b == null_bool_var) + return; + atom * a = m_atoms[b]; + if (a == nullptr) + return; + SASSERT(a->ref_count() > 0); + a->dec_ref(); + TRACE("ref", display(tout << "dec: " << b << " " << a->ref_count() << " ", *a) << "\n";); + if (a->ref_count() == 0) + del(a); + } + + void dec_ref(literal l) { + dec_ref(l.var()); + } + + bool is_arith_atom(bool_var b) const { return m_atoms[b] != nullptr; } + + bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } + + var max_var(poly const * p) const { + return m_pm.max_var(p); + } + + var max_var(bool_var b) const { + if (!is_arith_atom(b)) + return null_var; + else + return m_atoms[b]->max_var(); + } + + var max_var(literal l) const { + return max_var(l.var()); + } + + /** + \brief Return the maximum variable occurring in cls. + */ + var max_var(unsigned sz, literal const * cls) const { + var x = null_var; + for (unsigned i = 0; i < sz; i++) { + literal l = cls[i]; + if (is_arith_literal(l)) { + var y = max_var(l); + if (x == null_var || y > x) + x = y; + } + } + return x; + } + + var max_var(clause const & cls) const { + return max_var(cls.size(), cls.data()); + } + + /** + \brief Return the maximum Boolean variable occurring in cls. + */ + bool_var max_bvar(clause const & cls) const { + bool_var b = null_bool_var; + for (literal l : cls) { + if (b == null_bool_var || l.var() > b) + b = l.var(); + } + return b; + } + + /** + \brief Return the degree of the maximal variable of the given atom + */ + unsigned degree(atom const * a) const { + if (a->is_ineq_atom()) { + unsigned max = 0; + unsigned sz = to_ineq_atom(a)->size(); + var x = a->max_var(); + for (unsigned i = 0; i < sz; i++) { + unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); + if (d > max) + max = d; + } + return max; + } + else { + return m_pm.degree(to_root_atom(a)->p(), a->max_var()); + } + } + + /** + \brief Return the degree of the maximal variable in c + */ + unsigned degree(clause const & c) const { + var x = max_var(c); + if (x == null_var) + return 0; + unsigned max = 0; + for (literal l : c) { + atom const * a = m_atoms[l.var()]; + if (a == nullptr) + continue; + unsigned d = degree(a); + if (d > max) + max = d; + } + return max; + } + + // ----------------------- + // + // Variable, Atoms, Clauses & Assumption creation + // + // ----------------------- + + bool_var mk_bool_var_core() { + bool_var b = m_bid_gen.mk(); + m_num_bool_vars++; + m_atoms .setx(b, nullptr, nullptr); + m_bvalues .setx(b, l_undef, l_undef); + m_levels .setx(b, UINT_MAX, UINT_MAX); + m_justifications.setx(b, null_justification, null_justification); + m_bwatches .setx(b, clause_vector(), clause_vector()); + m_dead .setx(b, false, true); + return b; + } + + bool_var mk_bool_var() { + return mk_bool_var_core(); + } + + var mk_var(bool is_int) { + var x = m_pm.mk_var(); + register_var(x, is_int); + return x; + } + void register_var(var x, bool is_int) { + SASSERT(x == num_vars()); + m_is_int. push_back(is_int); + m_watches. push_back(clause_vector()); + m_infeasible.push_back(nullptr); + m_var2eq. push_back(nullptr); + m_perm. push_back(x); + m_inv_perm. push_back(x); + SASSERT(m_is_int.size() == m_watches.size()); + SASSERT(m_is_int.size() == m_infeasible.size()); + SASSERT(m_is_int.size() == m_var2eq.size()); + SASSERT(m_is_int.size() == m_perm.size()); + SASSERT(m_is_int.size() == m_inv_perm.size()); + } + + bool_vector m_found_vars; + void vars(literal l, var_vector& vs) { + vs.reset(); + atom * a = m_atoms[l.var()]; + if (a == nullptr) { + + } + else if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + var_vector new_vs; + for (unsigned j = 0; j < sz; j++) { + m_found_vars.reset(); + m_pm.vars(to_ineq_atom(a)->p(j), new_vs); + for (unsigned i = 0; i < new_vs.size(); ++i) { + if (!m_found_vars.get(new_vs[i], false)) { + m_found_vars.setx(new_vs[i], true, false); + vs.push_back(new_vs[i]); + } + } + } + } + else { + m_pm.vars(to_root_atom(a)->p(), vs); + //vs.erase(max_var(to_root_atom(a)->p())); + vs.push_back(to_root_atom(a)->x()); + } + } + + void deallocate(ineq_atom * a) { + unsigned obj_sz = ineq_atom::get_obj_size(a->size()); + a->~ineq_atom(); + m_allocator.deallocate(obj_sz, a); + } + + void deallocate(root_atom * a) { + a->~root_atom(); + m_allocator.deallocate(sizeof(root_atom), a); + } + + void del(bool_var b) { + SASSERT(m_bwatches[b].empty()); + //SASSERT(m_bvalues[b] == l_undef); + m_num_bool_vars--; + m_dead[b] = true; + m_atoms[b] = nullptr; + m_bvalues[b] = l_undef; + m_bid_gen.recycle(b); + } + + void del(ineq_atom * a) { + CTRACE("nlsat_solver", a->ref_count() > 0, display(tout, *a) << "\n";); + // this triggers in too many benign cases: + // SASSERT(a->ref_count() == 0); + m_ineq_atoms.erase(a); + del(a->bvar()); + unsigned sz = a->size(); + for (unsigned i = 0; i < sz; i++) + m_pm.dec_ref(a->p(i)); + deallocate(a); + } + + void del(root_atom * a) { + SASSERT(a->ref_count() == 0); + m_root_atoms.erase(a); + del(a->bvar()); + m_pm.dec_ref(a->p()); + deallocate(a); + } + + void del(atom * a) { + if (a == nullptr) + return; + TRACE("nlsat_verbose", display(tout << "del: b" << a->m_bool_var << " " << a->ref_count() << " ", *a) << "\n";); + if (a->is_ineq_atom()) + del(to_ineq_atom(a)); + else + del(to_root_atom(a)); + } + + // Delete atoms with ref_count == 0 + void del_unref_atoms() { + for (auto* a : m_atoms) { + del(a); + } + } + + + ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new, bool simplify) { + SASSERT(sz >= 1); + SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); + int sign = 1; + polynomial_ref p(m_pm); + ptr_buffer uniq_ps; + var max = null_var; + for (unsigned i = 0; i < sz; i++) { + p = m_pm.flip_sign_if_lm_neg(ps[i]); + if (p.get() != ps[i] && !is_even[i]) { + sign = -sign; + } + var curr_max = max_var(p.get()); + if (curr_max > max || max == null_var) + max = curr_max; + if (sz == 1 && simplify) { + if (sign < 0) + k = atom::flip(k); + sign = 1; + polynomial::manager::ineq_type t; + switch (k) { + case atom::EQ: t = polynomial::manager::ineq_type::EQ; break; + case atom::LT: t = polynomial::manager::ineq_type::LT; break; + case atom::GT: t = polynomial::manager::ineq_type::GT; break; + default: UNREACHABLE(); break; + } + polynomial::var_vector vars; + m_pm.vars(p, vars); + bool all_int = all_of(vars, [&](var x) { return is_int(x); }); + if (!all_int) + t = polynomial::manager::ineq_type::EQ; + m_pm.gcd_simplify(p, t); + } + uniq_ps.push_back(m_cache.mk_unique(p)); + TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); + //verbose_stream() << "p: " << p.get() << ", uniq: " << uniq_ps.back() << "\n"; + } + void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); + if (sign < 0) + k = atom::flip(k); + ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.data(), is_even, max); + ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); + CTRACE("nlsat_table_bug", tmp_atom != atom, ineq_atom::hash_proc h; + tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var) << "\n";); + CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout << "nonmax: ", *atom, m_display_var) << "\n";); + SASSERT(atom->max_var() == max); + is_new = (atom == tmp_atom); + if (is_new) { + for (unsigned i = 0; i < sz; i++) { + m_pm.inc_ref(atom->p(i)); + } + } + else { + deallocate(tmp_atom); + } + return atom; + } + + bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { + bool is_new = false; + ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new, simplify); + if (!is_new) { + return atom->bvar(); + } + else { + bool_var b = mk_bool_var_core(); + m_atoms[b] = atom; + atom->m_bool_var = b; + TRACE("nlsat_verbose", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); + return b; + } + } + + literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { + SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); + bool is_const = true; + polynomial::manager::scoped_numeral cnst(m_pm.m()); + m_pm.m().set(cnst, 1); + for (unsigned i = 0; i < sz; ++i) { + if (m_pm.is_const(ps[i])) { + if (m_pm.is_zero(ps[i])) { + m_pm.m().set(cnst, 0); + is_const = true; + break; + } + auto const& c = m_pm.coeff(ps[i], 0); + m_pm.m().mul(cnst, c, cnst); + if (is_even[i] && m_pm.m().is_neg(c)) { + m_pm.m().neg(cnst); + } + } + else { + is_const = false; + } + } + if (is_const) { + if (m_pm.m().is_pos(cnst) && k == atom::GT) return true_literal; + if (m_pm.m().is_neg(cnst) && k == atom::LT) return true_literal; + if (m_pm.m().is_zero(cnst) && k == atom::EQ) return true_literal; + return false_literal; + } + return literal(mk_ineq_atom(k, sz, ps, is_even, simplify), false); + } + + bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { + polynomial_ref p1(m_pm), uniq_p(m_pm); + p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. + uniq_p = m_cache.mk_unique(p1); + TRACE("nlsat_solver", tout << x << " " << p1 << " " << uniq_p << "\n";); + SASSERT(i > 0); + SASSERT(x >= max_var(p)); + SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); + + void * mem = m_allocator.allocate(sizeof(root_atom)); + root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); + root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); + SASSERT(old_atom->max_var() == x); + if (old_atom != new_atom) { + deallocate(new_atom); + return old_atom->bvar(); + } + bool_var b = mk_bool_var_core(); + m_atoms[b] = new_atom; + new_atom->m_bool_var = b; + m_pm.inc_ref(new_atom->p()); + return b; + } + + void attach_clause(clause & cls) { + var x = max_var(cls); + if (x != null_var) { + m_watches[x].push_back(&cls); + } + else { + bool_var b = max_bvar(cls); + m_bwatches[b].push_back(&cls); + } + } + + void deattach_clause(clause & cls) { + var x = max_var(cls); + if (x != null_var) { + m_watches[x].erase(&cls); + } + else { + bool_var b = max_bvar(cls); + m_bwatches[b].erase(&cls); + } + } + + void deallocate(clause * cls) { + size_t obj_sz = clause::get_obj_size(cls->size()); + cls->~clause(); + m_allocator.deallocate(obj_sz, cls); + } + + void del_clause(clause * cls) { + deattach_clause(*cls); + m_cid_gen.recycle(cls->id()); + unsigned sz = cls->size(); + for (unsigned i = 0; i < sz; i++) + dec_ref((*cls)[i]); + _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); + dec_ref(a); + deallocate(cls); + } + + void del_clause(clause * cls, clause_vector& clauses) { + clauses.erase(cls); + del_clause(cls); + } + + void del_clauses(ptr_vector & cs) { + for (clause* cp : cs) + del_clause(cp); + cs.reset(); + } + + void del_clauses() { + del_clauses(m_clauses); + del_clauses(m_learned); + del_clauses(m_valids); + } + + // We use a simple heuristic to sort literals + // - bool literals < arith literals + // - sort literals based on max_var + // - sort literal with the same max_var using degree + // break ties using the fact that ineqs are usually cheaper to process than eqs. + struct lit_lt { + imp & m; + lit_lt(imp & _m):m(_m) {} + + bool operator()(literal l1, literal l2) const { + atom * a1 = m.m_atoms[l1.var()]; + atom * a2 = m.m_atoms[l2.var()]; + if (a1 == nullptr && a2 == nullptr) + return l1.index() < l2.index(); + if (a1 == nullptr) + return true; + if (a2 == nullptr) + return false; + var x1 = a1->max_var(); + var x2 = a2->max_var(); + if (x1 < x2) + return true; + if (x1 > x2) + return false; + SASSERT(x1 == x2); + unsigned d1 = m.degree(a1); + unsigned d2 = m.degree(a2); + if (d1 < d2) + return true; + if (d1 > d2) + return false; + if (!a1->is_eq() && a2->is_eq()) + return true; + if (a1->is_eq() && !a2->is_eq()) + return false; + return l1.index() < l2.index(); + } + }; + + class scoped_bool_vars { + imp& s; + svector vec; + public: + scoped_bool_vars(imp& s):s(s) {} + ~scoped_bool_vars() { + for (bool_var v : vec) { + s.dec_ref(v); + } + } + void push_back(bool_var v) { + s.inc_ref(v); + vec.push_back(v); + } + bool_var const* begin() const { return vec.begin(); } + bool_var const* end() const { return vec.end(); } + bool_var operator[](bool_var v) const { return vec[v]; } + }; + + void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { + TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; + display(tout);); + IF_VERBOSE(2, display(verbose_stream() << "check lemma " << (is_valid?"valid: ":"consequence: "), n, cls) << "\n"); + for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); + scoped_suspend_rlimit _limit(m_rlimit); + ctx c(m_rlimit, m_ctx.m_params, m_ctx.m_incremental); + solver solver2(c); + imp& checker = *(solver2.m_imp); + checker.m_check_lemmas = false; + checker.m_log_lemmas = false; + checker.m_inline_vars = false; + + auto pconvert = [&](poly* p) { + return convert(m_pm, p, checker.m_pm); + }; + + // need to translate Boolean variables and literals + scoped_bool_vars tr(checker); + for (var x = 0; x < m_is_int.size(); ++x) { + checker.register_var(x, is_int(x)); + } + bool_var bv = 0; + tr.push_back(bv); + for (bool_var b = 1; b < m_atoms.size(); ++b) { + atom* a = m_atoms[b]; + if (a == nullptr) { + bv = checker.mk_bool_var(); + } + else if (a->is_ineq_atom()) { + ineq_atom& ia = *to_ineq_atom(a); + unsigned sz = ia.size(); + polynomial_ref_vector ps(checker.m_pm); + bool_vector is_even; + for (unsigned i = 0; i < sz; ++i) { + ps.push_back(pconvert(ia.p(i))); + is_even.push_back(ia.is_even(i)); + } + bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.data(), is_even.data()); + } + else if (a->is_root_atom()) { + root_atom& r = *to_root_atom(a); + if (r.x() >= max_var(r.p())) { + // permutation may be reverted after check completes, + // but then root atoms are not used in lemmas. + bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), pconvert(r.p())); + } + } + else { + UNREACHABLE(); + } + tr.push_back(bv); + } + if (!is_valid) { + for (clause* c : m_clauses) { + if (!a && c->assumptions()) { + continue; + } + literal_vector lits; + for (literal lit : *c) { + lits.push_back(literal(tr[lit.var()], lit.sign())); + } + checker.mk_external_clause(lits.size(), lits.data(), nullptr); + } + } + for (unsigned i = 0; i < n; ++i) { + literal lit = cls[i]; + literal nlit(tr[lit.var()], !lit.sign()); + checker.mk_external_clause(1, &nlit, nullptr); + } + lbool r = checker.check(); + if (r == l_true) { + for (bool_var b : tr) { + literal lit(b, false); + IF_VERBOSE(0, checker.display(verbose_stream(), lit) << " := " << checker.value(lit) << "\n"); + TRACE("nlsat", checker.display(tout, lit) << " := " << checker.value(lit) << "\n";); + } + for (clause* c : m_learned) { + bool found = false; + for (literal lit: *c) { + literal tlit(tr[lit.var()], lit.sign()); + found |= checker.value(tlit) == l_true; + } + if (!found) { + IF_VERBOSE(0, display(verbose_stream() << "violdated clause: ", *c) << "\n"); + TRACE("nlsat", display(tout << "violdated clause: ", *c) << "\n";); + } + } + for (clause* c : m_valids) { + bool found = false; + for (literal lit: *c) { + literal tlit(tr[lit.var()], lit.sign()); + found |= checker.value(tlit) == l_true; + } + if (!found) { + IF_VERBOSE(0, display(verbose_stream() << "violdated tautology clause: ", *c) << "\n"); + TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); + } + } + throw default_exception("lemma did not check"); + UNREACHABLE(); + } + } + + void log_lemma(std::ostream& out, clause const& cls) { + log_lemma(out, cls.size(), cls.data(), false); + } + + void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { + ++m_lemma_count; + out << "(set-logic NRA)\n"; + if (is_valid) { + display_smt2_bool_decls(out); + display_smt2_arith_decls(out); + } + else + display_smt2(out); + for (unsigned i = 0; i < n; ++i) + display_smt2(out << "(assert ", ~cls[i]) << ")\n"; + display(out << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"; + out << "(check-sat)\n(reset)\n"; + + TRACE("nlsat", display(tout << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"); + } + + clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { + SASSERT(num_lits > 0); + unsigned cid = m_cid_gen.mk(); + void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); + clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); + for (unsigned i = 0; i < num_lits; i++) + inc_ref(lits[i]); + inc_ref(a); + return cls; + } + + clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { + if (num_lits == 0) { + num_lits = 1; + lits = &false_literal; + } + SASSERT(num_lits > 0); + clause * cls = mk_clause_core(num_lits, lits, learned, a); + TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); + std::sort(cls->begin(), cls->end(), lit_lt(*this)); + TRACE("nlsat", display(tout << " after sort:\n", *cls) << "\n";); + if (learned && m_log_lemmas) { + log_lemma(verbose_stream(), *cls); + } + if (learned && m_check_lemmas && false) { + check_lemma(cls->size(), cls->data(), false, cls->assumptions()); + } + if (learned) + m_learned.push_back(cls); + else + m_clauses.push_back(cls); + attach_clause(*cls); + return cls; + } + + void mk_external_clause(unsigned num_lits, literal const * lits, assumption a) { + _assumption_set as = nullptr; + if (a != nullptr) + as = m_asm.mk_leaf(a); + if (num_lits == 0) { + num_lits = 1; + lits = &false_literal; + } + mk_clause(num_lits, lits, false, as); + } + + // ----------------------- + // + // Search + // + // ----------------------- + + void save_assign_trail(bool_var b) { + m_trail.push_back(trail(b, bvar_assignment())); + } + + void save_set_updt_trail(interval_set * old_set) { + m_trail.push_back(trail(old_set)); + } + + void save_updt_eq_trail(atom * old_eq) { + m_trail.push_back(trail(old_eq)); + } + + void save_new_stage_trail() { + m_trail.push_back(trail(true, stage())); + } + + void save_new_level_trail() { + m_trail.push_back(trail(false, stage())); + } + + void undo_bvar_assignment(bool_var b) { + m_bvalues[b] = l_undef; + m_levels[b] = UINT_MAX; + del_jst(m_allocator, m_justifications[b]); + m_justifications[b] = null_justification; + if (m_atoms[b] == nullptr && b < m_bk) + m_bk = b; + } + + void undo_set_updt(interval_set * old_set) { + if (m_xk == null_var) + return; + var x = m_xk; + if (x < m_infeasible.size()) { + m_ism.dec_ref(m_infeasible[x]); + m_infeasible[x] = old_set; + } + } + + void undo_new_stage() { + if (m_xk == 0) { + m_xk = null_var; + } + else if (m_xk != null_var) { + m_xk--; + m_assignment.reset(m_xk); + } + } + + void undo_new_level() { + SASSERT(m_scope_lvl > 0); + m_scope_lvl--; + m_evaluator.pop(1); + } + + void undo_updt_eq(atom * a) { + if (m_var2eq.size() > m_xk) + m_var2eq[m_xk] = a; + } + + template + void undo_until(Predicate const & pred) { + while (pred() && !m_trail.empty()) { + trail & t = m_trail.back(); + switch (t.m_kind) { + case trail::BVAR_ASSIGNMENT: + undo_bvar_assignment(t.m_b); + break; + case trail::INFEASIBLE_UPDT: + undo_set_updt(t.m_old_set); + break; + case trail::NEW_STAGE: + undo_new_stage(); + break; + case trail::NEW_LEVEL: + undo_new_level(); + break; + case trail::UPDT_EQ: + undo_updt_eq(t.m_old_eq); + break; + default: + break; + } + m_trail.pop_back(); + } + } + + struct size_pred { + svector & m_trail; + unsigned m_old_size; + size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} + bool operator()() const { return m_trail.size() > m_old_size; } + }; + + // Keep undoing until trail has the given size + void undo_until_size(unsigned old_size) { + SASSERT(m_trail.size() >= old_size); + undo_until(size_pred(m_trail, old_size)); + } + + struct stage_pred { + var const & m_xk; + var m_target; + stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} + bool operator()() const { return m_xk != m_target; } + }; + + // Keep undoing until stage is new_xk + void undo_until_stage(var new_xk) { + undo_until(stage_pred(m_xk, new_xk)); + } + + struct level_pred { + unsigned const & m_scope_lvl; + unsigned m_new_lvl; + level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} + bool operator()() const { return m_scope_lvl > m_new_lvl; } + }; + + // Keep undoing until level is new_lvl + void undo_until_level(unsigned new_lvl) { + undo_until(level_pred(m_scope_lvl, new_lvl)); + } + + struct unassigned_pred { + bool_var m_b; + svector const & m_bvalues; + unassigned_pred(svector const & bvalues, bool_var b): + m_b(b), + m_bvalues(bvalues) {} + bool operator()() const { return m_bvalues[m_b] != l_undef; } + }; + + // Keep undoing until b is unassigned + void undo_until_unassigned(bool_var b) { + undo_until(unassigned_pred(m_bvalues, b)); + SASSERT(m_bvalues[b] == l_undef); + } + + struct true_pred { + bool operator()() const { return true; } + }; + + void undo_until_empty() { + undo_until(true_pred()); + } + + /** + \brief Create a new scope level + */ + void new_level() { + m_evaluator.push(); + m_scope_lvl++; + save_new_level_trail(); + } + + /** + \brief Return the value of the given literal that was assigned by the search + engine. + */ + lbool assigned_value(literal l) const { + bool_var b = l.var(); + if (l.sign()) + return ~m_bvalues[b]; + else + return m_bvalues[b]; + } + + /** + \brief Assign literal using the given justification + */ + void assign(literal l, justification j) { + TRACE("nlsat_assign", + display(tout << "assigning literal: ", l); + display(tout << " <- ", j);); + + SASSERT(assigned_value(l) == l_undef); + SASSERT(j != null_justification); + SASSERT(!j.is_null()); + if (j.is_decision()) + m_stats.m_decisions++; + else + m_stats.m_propagations++; + bool_var b = l.var(); + m_bvalues[b] = to_lbool(!l.sign()); + m_levels[b] = m_scope_lvl; + m_justifications[b] = j; + save_assign_trail(b); + updt_eq(b, j); + TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << "\n";); + } + + /** + \brief Create a "case-split" + */ + void decide(literal l) { + new_level(); + assign(l, decided_justification); + } + + /** + \brief Return the value of a literal as defined in Dejan and Leo's paper. + */ + lbool value(literal l) { + lbool val = assigned_value(l); + if (val != l_undef) { + TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); + return val; + } + bool_var b = l.var(); + atom * a = m_atoms[b]; + if (a == nullptr) { + TRACE("nlsat_verbose", display(tout << " no atom for ", l) << "\n";); + return l_undef; + } + var max = a->max_var(); + if (!m_assignment.is_assigned(max)) { + TRACE("nlsat_verbose", display(tout << " maximal variable not assigned ", l) << "\n";); + return l_undef; + } + val = to_lbool(m_evaluator.eval(a, l.sign())); + TRACE("nlsat_verbose", display(tout << " evaluated value " << val << " for ", l) << "\n";); + TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; + tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; + display_assignment(tout);); + return val; + } + + /** + \brief Return true if the given clause is already satisfied in the current partial interpretation. + */ + bool is_satisfied(clause const & cls) const { + for (literal l : cls) { + if (const_cast(this)->value(l) == l_true) { + TRACE("value_bug:", tout << l << " := true\n";); + return true; + } + } + return false; + } + + /** + \brief Return true if the given clause is false in the current partial interpretation. + */ + bool is_inconsistent(unsigned sz, literal const * cls) { + for (unsigned i = 0; i < sz; i++) { + if (value(cls[i]) != l_false) { + TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); + return false; + } + } + return true; + } + + /** + \brief Process a clauses that contains only Boolean literals. + */ + bool process_boolean_clause(clause const & cls) { + SASSERT(m_xk == null_var); + unsigned num_undef = 0; + unsigned first_undef = UINT_MAX; + unsigned sz = cls.size(); + for (unsigned i = 0; i < sz; i++) { + literal l = cls[i]; + SASSERT(m_atoms[l.var()] == nullptr); + SASSERT(value(l) != l_true); + if (value(l) == l_false) + continue; + SASSERT(value(l) == l_undef); + num_undef++; + if (first_undef == UINT_MAX) + first_undef = i; + } + if (num_undef == 0) + return false; + SASSERT(first_undef != UINT_MAX); + if (num_undef == 1) + assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause + else + decide(cls[first_undef]); + return true; + } + + /** + \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. + */ + literal_vector core; + ptr_vector clauses; + void R_propagate(literal l, interval_set const * s, bool include_l = true) { + m_ism.get_justifications(s, core, clauses); + if (include_l) + core.push_back(~l); + auto j = mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data()); + TRACE("nlsat_resolve", display(tout, j); display_eval(tout << "evaluated:", j)); + assign(l, j); + SASSERT(value(l) == l_true); + } + + /** + \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s + */ + void updt_infeasible(interval_set const * s) { + SASSERT(m_xk != null_var); + interval_set * xk_set = m_infeasible[m_xk]; + save_set_updt_trail(xk_set); + interval_set_ref new_set(m_ism); + TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set) << "\n"; m_ism.display(tout, s) << "\n";); + new_set = m_ism.mk_union(s, xk_set); + TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set) << "\n";); + SASSERT(!m_ism.is_full(new_set)); + m_ism.inc_ref(new_set); + m_infeasible[m_xk] = new_set; + } + + /** + \brief Update m_var2eq mapping. + */ + void updt_eq(bool_var b, justification j) { + if (!m_simplify_cores) + return; + if (m_bvalues[b] != l_true) + return; + atom * a = m_atoms[b]; + if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) + return; + switch (j.get_kind()) { + case justification::CLAUSE: + if (j.get_clause()->assumptions() != nullptr) return; + break; + case justification::LAZY: + if (j.get_lazy()->num_clauses() > 0) return; + if (j.get_lazy()->num_lits() > 0) return; + break; + default: + break; + } + var x = m_xk; + SASSERT(a->max_var() == x); + SASSERT(x != null_var); + if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) + return; // we only update m_var2eq if the new equality has smaller degree + TRACE("nlsat_simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; + tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)) << "\n"; + display(tout, j); + ); + save_updt_eq_trail(m_var2eq[x]); + m_var2eq[x] = a; + } + + /** + \brief Process a clause that contains nonlinear arithmetic literals + + If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 + */ + bool process_arith_clause(clause const & cls, bool satisfy_learned) { + if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { + TRACE("nlsat", tout << "skip learned\n";); + return true; // ignore lemmas in super lazy mode + } + SASSERT(m_xk == max_var(cls)); + unsigned num_undef = 0; // number of undefined literals + unsigned first_undef = UINT_MAX; // position of the first undefined literal + interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal + interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable + SASSERT(!m_ism.is_full(xk_set)); + for (unsigned idx = 0; idx < cls.size(); ++idx) { + literal l = cls[idx]; + checkpoint(); + if (value(l) == l_false) + continue; + if (value(l) == l_true) + return true; // could happen if clause is a tautology + CTRACE("nlsat", max_var(l) != m_xk || value(l) != l_undef, display(tout); + tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l) << "\n"; + display(tout, cls) << "\n";); + SASSERT(value(l) == l_undef); + SASSERT(max_var(l) == m_xk); + bool_var b = l.var(); + atom * a = m_atoms[b]; + SASSERT(a != nullptr); + interval_set_ref curr_set(m_ism); + curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); + TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; + display(tout, cls) << "\n";); + if (m_ism.is_empty(curr_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); + R_propagate(l, nullptr); + SASSERT(is_satisfied(cls)); + return true; + } + if (m_ism.is_full(curr_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); + R_propagate(~l, nullptr); + continue; + } + if (m_ism.subset(curr_set, xk_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); + R_propagate(l, xk_set); + return true; + } + interval_set_ref tmp(m_ism); + tmp = m_ism.mk_union(curr_set, xk_set); + if (m_ism.is_full(tmp)) { + TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; + display(tout, cls) << "\n"; + m_ism.display(tout, tmp); tout << "\n"; + ); + R_propagate(~l, tmp, false); + continue; + } + num_undef++; + if (first_undef == UINT_MAX) { + first_undef = idx; + first_undef_set = curr_set; + } + } + TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); + if (num_undef == 0) + return false; + SASSERT(first_undef != UINT_MAX); + if (num_undef == 1) { + // unit clause + assign(cls[first_undef], mk_clause_jst(&cls)); + updt_infeasible(first_undef_set); + } + else if ( satisfy_learned || + !cls.is_learned() /* must always satisfy input clauses */ || + m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { + decide(cls[first_undef]); + updt_infeasible(first_undef_set); + } + else { + TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() + << ", lazy: " << m_lazy << "\n";); + } + return true; + } + + /** + \brief Try to satisfy the given clause. Return true if succeeded. + + If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 + */ + bool process_clause(clause const & cls, bool satisfy_learned) { + if (is_satisfied(cls)) + return true; + if (m_xk == null_var) + return process_boolean_clause(cls); + else + return process_arith_clause(cls, satisfy_learned); + } + + /** + \brief Try to satisfy the given "set" of clauses. + Return 0, if the set was satisfied, or the violating clause otherwise + */ + clause * process_clauses(clause_vector const & cs) { + for (clause* c : cs) { + if (!process_clause(*c, false)) + return c; + } + return nullptr; // succeeded + } + + /** + \brief Make sure m_bk is the first unassigned pure Boolean variable. + Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. + */ + void peek_next_bool_var() { + while (m_bk < m_atoms.size()) { + if (!m_dead[m_bk] && m_atoms[m_bk] == nullptr && m_bvalues[m_bk] == l_undef) { + return; + } + m_bk++; + } + m_bk = null_bool_var; + } + + /** + \brief Create a new stage. See Dejan and Leo's paper. + */ + void new_stage() { + m_stats.m_stages++; + save_new_stage_trail(); + if (m_xk == null_var) + m_xk = 0; + else + m_xk++; + } + + /** + \brief Assign m_xk + */ + void select_witness() { + scoped_anum w(m_am); + SASSERT(!m_ism.is_full(m_infeasible[m_xk])); + m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); + TRACE("nlsat", + tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; + tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); + TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); + if (!m_am.is_rational(w)) + m_stats.m_irrational_assignments++; + m_assignment.set_core(m_xk, w); + } + + + + bool is_satisfied() { + if (m_bk == null_bool_var && m_xk >= num_vars()) { + TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); + fix_patch(); + SASSERT(check_satisfied(m_clauses)); + return true; // all variables were assigned, and all clauses were satisfied. + } + else { + return false; + } + } + + + /** + \brief main procedure + */ + lbool search() { + TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); + TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); + TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); + TRACE("nlsat_mathematica", display_mathematica(tout);); + TRACE("nlsat", display_smt2(tout);); + m_bk = 0; + m_xk = null_var; + + while (true) { + if (should_reorder()) + do_reorder(); + +#if 0 + if (should_gc()) + do_gc(); +#endif + + if (should_simplify()) + do_simplify(); + + CASSERT("nlsat", check_satisfied()); + if (m_xk == null_var) { + peek_next_bool_var(); + if (m_bk == null_bool_var) + new_stage(); // move to arith vars + } + else { + new_stage(); // peek next arith var + } + TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); + if (is_satisfied()) { + return l_true; + } + while (true) { + TRACE("nlsat_verbose", tout << "processing variable "; + if (m_xk != null_var) { + m_display_var(tout, m_xk); tout << " " << m_watches[m_xk].size(); + } + else { + tout << m_bwatches[m_bk].size() << " boolean b" << m_bk; + } + tout << "\n";); + checkpoint(); + clause * conflict_clause; + if (m_xk == null_var) + conflict_clause = process_clauses(m_bwatches[m_bk]); + else + conflict_clause = process_clauses(m_watches[m_xk]); + if (conflict_clause == nullptr) + break; + if (!resolve(*conflict_clause)) + return l_false; + if (m_stats.m_conflicts >= m_max_conflicts) + return l_undef; + log(); + } + + if (m_xk == null_var) { + if (m_bvalues[m_bk] == l_undef) { + decide(literal(m_bk, true)); + m_bk++; + } + } + else { + select_witness(); + } + } + } + + void gc() { + if (m_learned.size() <= 4*m_clauses.size()) + return; + reset_watches(); + reinit_cache(); + unsigned j = 0; + for (unsigned i = 0; i < m_learned.size(); ++i) { + auto cls = m_learned[i]; + if (i - j < m_clauses.size() && cls->size() > 1 && !cls->is_active()) + del_clause(cls); + else { + m_learned[j++] = cls; + cls->set_active(false); + } + } + m_learned.shrink(j); + reattach_arith_clauses(m_clauses); + reattach_arith_clauses(m_learned); + } + + + bool should_gc() { + return m_learned.size() > 10 * m_clauses.size(); + } + + void do_gc() { + undo_to_base(); + gc(); + } + + void undo_to_base() { + init_search(); + m_bk = 0; + m_xk = null_var; + } + + unsigned m_restart_threshold = 10000; + bool should_reorder() { + return m_stats.m_conflicts > 0 && m_stats.m_conflicts % m_restart_threshold == 0; + } + + void do_reorder() { + undo_to_base(); + m_stats.m_restarts++; + m_stats.m_conflicts++; + if (m_reordered) + restore_order(); + apply_reorder(); + } + + bool m_did_simplify = false; + bool should_simplify() { + return + !m_did_simplify && m_inline_vars && + !m_incremental && m_stats.m_conflicts > 100; + } + + void do_simplify() { + undo_to_base(); + m_did_simplify = true; + m_simplify(); + } + + unsigned m_next_conflict = 100; + void log() { + if (m_stats.m_conflicts != 1 && m_stats.m_conflicts < m_next_conflict) + return; + m_next_conflict += 100; + IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); + } + + + lbool search_check() { + lbool r = l_undef; + m_stats.m_conflicts = 0; + m_stats.m_restarts = 0; + m_next_conflict = 0; + while (true) { + r = search(); + if (r != l_true) + break; + ++m_stats.m_restarts; + vector> bounds; + + for (var x = 0; x < num_vars(); x++) { + if (is_int(x) && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { + scoped_anum v(m_am), vlo(m_am); + v = m_assignment.value(x); + rational lo; + m_am.int_lt(v, vlo); + if (!m_am.is_int(vlo)) + continue; + m_am.to_rational(vlo, lo); + // derive tight bounds. + while (true) { + lo++; + if (!m_am.gt(v, lo.to_mpq())) { + lo--; + break; + } + } + bounds.push_back(std::make_pair(x, lo)); + } + } + if (bounds.empty()) + break; + + gc(); + if (m_stats.m_restarts % 10 == 0) { + if (m_reordered) + restore_order(); + apply_reorder(); + } + + init_search(); + IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); + for (auto const& b : bounds) { + var x = b.first; + rational lo = b.second; + rational hi = lo + 1; // rational::one(); + bool is_even = false; + polynomial_ref p(m_pm); + rational one(1); + m_lemma.reset(); + p = m_pm.mk_linear(1, &one, &x, -lo); + poly* p1 = p.get(); + m_lemma.push_back(~mk_ineq_literal(atom::GT, 1, &p1, &is_even)); + p = m_pm.mk_linear(1, &one, &x, -hi); + poly* p2 = p.get(); + m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); + + // perform branch and bound + clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); + IF_VERBOSE(4, display(verbose_stream(), *cls) << "\n"); + if (cls) { + TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); + } + } + } + return r; + } + + bool m_reordered = false; +//#linxi begin Simple Check +// test + void test_anum() { + scoped_anum x(m_am), y(m_am); + m_am.set(x, 3); + m_am.set(y, 5); + TRACE("linxi_simple_checker", + tout << x << " " << y << std::endl; + ); + } + bool simple_check() { + // test_anum(); + literal_vector learned_unit; + // Simple_Checker checker(m_solver, m_pm, m_am, m_clauses, m_learned, m_atoms, m_is_int.size()); + Simple_Checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); + // TRACE("linxi_simple_checker", + // tout << "here" << std::endl; + // ); + if (!checker()) + return false; + for (unsigned i = 0, sz = learned_unit.size(); i < sz; ++i) { + clause *cla = mk_clause(1, &learned_unit[i], true, nullptr); + if (m_atoms[learned_unit[i].var()] == nullptr) { + assign(learned_unit[i], mk_clause_jst(cla)); + } + // decide(learned_unit[i]); + } + return true; + } + +//#linxi end Simple Check + +//#linxi begin Variable Ordering Strategy + void run_variable_ordering_strategy() { + TRACE("linxi_reorder", tout << "runing vos: " << m_linxi_variable_ordering_strategy << '\n';); + + unsigned num = num_vars(); + VOS_Var_Info_Collector vos_collector(m_pm, m_atoms, num, m_linxi_variable_ordering_strategy); + vos_collector.collect(m_clauses); + vos_collector.collect(m_learned); + // TRACE("linxi_reorder", vos_collector.display(tout, m_display_var);); + + var_vector perm; + vos_collector(perm); + reorder(perm.size(), perm.data()); + } + +//#linxi end Variable Ordering Strategy + +//#linxi begin symmetry check + void symmetry_check() { + std::cout << "sym called\n"; + unsigned arith_num = m_is_int.size(); + if (arith_num > 10000) + return ; + Symmetry_Checker checker(m_pm, m_qm, m_clauses, m_atoms, m_is_int, arith_num); + for (var x = 0; x < arith_num; ++x) { + for (var y = x + 1; y < arith_num; ++y) { + if (checker.check_symmetry(x, y)) { + TRACE("linxi_symmetry_checker", + tout << "symmetry: " << x << ", " << y << "\n"; + ); + + rational zero(0); + vector as; + vector xs; + as.push_back(rational(1)); + xs.push_back(x); + as.push_back(rational(-1)); + xs.push_back(y); + polynomial_ref pr(m_pm); + pr = m_pm.mk_linear(2, as.data(), xs.data(), zero); + poly* p = pr.get(); + bool is_even = false; + literal lit = ~mk_ineq_literal(atom::GT, 1, &p, &is_even); + clause *cla = mk_clause(1, &lit, true, nullptr); + } + } + } + TRACE("linxi_symmetry_checker", + display(tout); + ); + + } +//#linxi end symmetry check + void apply_reorder() { + m_reordered = false; + if (!can_reorder()) + ; + else if (m_random_order) { + shuffle_vars(); + m_reordered = true; + } + else if (m_reorder) { + heuristic_reorder(); + m_reordered = true; + } + } + + lbool check() { + +//#linxi begin symmetry check + if (m_linxi_symmetry_check) { + symmetry_check(); + } + // exit(0); +//#linxi end symmetry check + +//#linxi begin simple check + if (m_linxi_simple_check) { + if (!simple_check()) { + TRACE("linxi_simple_check", tout << "real unsat\n";); + return l_false; + } + TRACE("linxi_simple_checker_learned", + tout << "simple check done\n"; + ); + // exit(0); + // return l_undef; + } + // exit(0); + // return l_false; +//#linxi end simple check + + TRACE("nlsat_smt2", display_smt2(tout);); + TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); + init_search(); + m_explain.set_full_dimensional(is_full_dimensional()); + bool reordered = false; + + + if (!can_reorder()) { + + } +//#linxi begin Variable Ordering Strategy + else if (m_linxi_variable_ordering_strategy > 0) { + std::cout << "m_linxi_variable_ordering_strategy=" << m_linxi_variable_ordering_strategy << "\n"; + run_variable_ordering_strategy(); + reordered = true; + } +//#linxi end Variable Ordering Strategy + else if (m_random_order) { + shuffle_vars(); + reordered = true; + } + else if (m_reorder) { + heuristic_reorder(); + reordered = true; + } + sort_watched_clauses(); + lbool r = search_check(); + CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); + if (reordered) { + restore_order(); + } + CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); + CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); + SASSERT(r != l_true || check_satisfied(m_clauses)); + return r; + } + + void init_search() { + undo_until_empty(); + while (m_scope_lvl > 0) { + undo_new_level(); + } + m_xk = null_var; + for (unsigned i = 0; i < m_bvalues.size(); ++i) { + m_bvalues[i] = l_undef; + } + m_assignment.reset(); + } + + lbool check(literal_vector& assumptions) { + literal_vector result; + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.data(); + for (unsigned i = 0; i < sz; ++i) { + mk_external_clause(1, ptr+i, (assumption)(ptr+i)); + } + display_literal_assumption dla(*this, assumptions); + scoped_display_assumptions _scoped_display(*this, dla); + lbool r = check(); + + if (r == l_false) { + // collect used literals from m_lemma_assumptions + vector deps; + get_core(deps); + for (unsigned i = 0; i < deps.size(); ++i) { + literal const* lp = (literal const*)(deps[i]); + if (ptr <= lp && lp < ptr + sz) { + result.push_back(*lp); + } + } + } + collect(assumptions, m_clauses); + collect(assumptions, m_learned); + del_clauses(m_valids); + if (m_check_lemmas) { + for (clause* c : m_learned) { + check_lemma(c->size(), c->data(), false, nullptr); + } + } + +#if 0 + for (clause* c : m_learned) { + IF_VERBOSE(0, display(verbose_stream() << "KEEP: ", c->size(), c->c_ptr()) << "\n"); + } +#endif + assumptions.reset(); + assumptions.append(result); + return r; + } + + void get_core(vector& deps) { + m_asm.linearize(m_lemma_assumptions.get(), deps); + } + + void collect(literal_vector const& assumptions, clause_vector& clauses) { + unsigned j = 0; + for (clause * c : clauses) { + if (collect(assumptions, *c)) { + del_clause(c); + } + else { + clauses[j++] = c; + } + } + clauses.shrink(j); + } + + bool collect(literal_vector const& assumptions, clause const& c) { + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.data(); + _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); + if (asms == nullptr) { + return false; + } + vector deps; + m_asm.linearize(asms, deps); + for (auto dep : deps) { + if (ptr <= dep && dep < ptr + sz) { + return true; + } + } + return false; + } + + // ----------------------- + // + // Conflict Resolution + // + // ----------------------- + svector m_marks; // bool_var -> bool temp mark used during conflict resolution + unsigned m_num_marks; + scoped_literal_vector m_lemma; + scoped_literal_vector m_lazy_clause; + assumption_set_ref m_lemma_assumptions; // assumption tracking + + // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. + + bool check_marks() { + for (unsigned m : m_marks) { + (void)m; + SASSERT(m == 0); + } + return true; + } + + unsigned scope_lvl() const { return m_scope_lvl; } + + bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } + + void mark(bool_var b) { m_marks.setx(b, 1, 0); } + + void reset_mark(bool_var b) { m_marks[b] = 0; } + + void reset_marks() { + for (auto const& l : m_lemma) { + reset_mark(l.var()); + } + } + + void process_antecedent(literal antecedent) { + checkpoint(); + bool_var b = antecedent.var(); + TRACE("nlsat_resolve", display(tout << "resolving antecedent: ", antecedent) << "\n";); + if (assigned_value(antecedent) == l_undef) { + checkpoint(); + // antecedent must be false in the current arith interpretation + SASSERT(value(antecedent) == l_false || m_rlimit.is_canceled()); + if (!is_marked(b)) { + SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage + TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); + mark(b); + m_lemma.push_back(antecedent); + } + return; + } + + unsigned b_lvl = m_levels[b]; + TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); + if (!is_marked(b)) { + mark(b); + if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { + TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); + m_num_marks++; + } + else { + TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; + tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); + m_lemma.push_back(antecedent); + } + } + } + + void resolve_clause(bool_var b, unsigned sz, literal const * c) { + TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b) << "\n"; display(tout, sz, c); tout << "\n";); + TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); + + for (unsigned i = 0; i < sz; i++) { + if (c[i].var() != b) + process_antecedent(c[i]); + } + } + + void resolve_clause(bool_var b, clause & c) { + TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); + c.set_active(true); + resolve_clause(b, c.size(), c.data()); + m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); + } + + void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { + TRACE("nlsat_resolve", tout << "resolving lazy_justification for b" << b << "\n";); + unsigned sz = jst.num_lits(); + + // Dump lemma as Mathematica formula that must be true, + // if the current interpretation (really) makes the core in jst infeasible. + TRACE("nlsat_mathematica", + tout << "assignment lemma\n"; + literal_vector core; + for (unsigned i = 0; i < sz; i++) { + core.push_back(~jst.lit(i)); + } + display_mathematica_lemma(tout, core.size(), core.data(), true);); + + m_lazy_clause.reset(); + m_explain(jst.num_lits(), jst.lits(), m_lazy_clause); + for (unsigned i = 0; i < sz; i++) + m_lazy_clause.push_back(~jst.lit(i)); + + // lazy clause is a valid clause + TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); + TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.data()); tout << "\n";); + TRACE("nlsat_resolve", + tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; + tout << "new valid clause:\n"; + display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";); + + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true); + + if (m_check_lemmas) { + check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), true, nullptr); + m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr)); + } + +#ifdef Z3DEBUG + { + unsigned sz = m_lazy_clause.size(); + for (unsigned i = 0; i < sz; i++) { + literal l = m_lazy_clause[i]; + if (l.var() != b) { + if (value(l) != l_false) + display(verbose_stream() << value(l) << " ", 1, &l); + SASSERT(value(l) == l_false || m_rlimit.is_canceled()); + } + else { + SASSERT(value(l) == l_true || m_rlimit.is_canceled()); + SASSERT(!l.sign() || m_bvalues[b] == l_false); + SASSERT(l.sign() || m_bvalues[b] == l_true); + } + } + } +#endif + checkpoint(); + resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.data()); + + for (unsigned i = 0; i < jst.num_clauses(); ++i) { + clause const& c = jst.clause(i); + TRACE("nlsat", display(tout << "adding clause assumptions ", c) << "\n";); + m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); + } + } + + /** + \brief Return true if all literals in ls are from previous stages. + */ + bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { + for (unsigned i = 0; i < num; i++) { + if (max_var(ls[i]) == m_xk) + return false; + } + return true; + } + + /** + \brief Return the maximum scope level in ls. + + \pre This method assumes value(ls[i]) is l_false for i in [0, num) + */ + unsigned max_scope_lvl(unsigned num, literal const * ls) { + unsigned max = 0; + for (unsigned i = 0; i < num; i++) { + literal l = ls[i]; + bool_var b = l.var(); + SASSERT(value(ls[i]) == l_false); + if (assigned_value(l) == l_false) { + unsigned lvl = m_levels[b]; + if (lvl > max) + max = lvl; + } + else { + // l must be a literal from a previous stage that is false in the current interpretation + SASSERT(assigned_value(l) == l_undef); + SASSERT(max_var(b) != null_var); + SASSERT(m_xk != null_var); + SASSERT(max_var(b) < m_xk); + } + } + return max; + } + + /** + \brief Remove literals of the given lvl (that are in the current stage) from lemma. + + \pre This method assumes value(ls[i]) is l_false for i in [0, num) + */ + void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { + TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); + unsigned sz = lemma.size(); + unsigned j = 0; + for (unsigned i = 0; i < sz; i++) { + literal l = lemma[i]; + bool_var b = l.var(); + SASSERT(is_marked(b)); + SASSERT(value(lemma[i]) == l_false); + if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { + m_num_marks++; + continue; + } + lemma.set(j, l); + j++; + } + lemma.shrink(j); + } + + /** + \brief Return true if it is a Boolean lemma. + */ + bool is_bool_lemma(unsigned sz, literal const * ls) const { + for (unsigned i = 0; i < sz; i++) { + if (m_atoms[ls[i].var()] != nullptr) + return false; + } + return true; + } + + + /** + Return the maximal decision level in lemma for literals in the first sz-1 positions that + are at the same stage. If all these literals are from previous stages, + we just backtrack the current level. + */ + unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { + SASSERT(!is_bool_lemma(sz, lemma)); + unsigned new_lvl = 0; + bool found_lvl = false; + for (unsigned i = 0; i < sz - 1; i++) { + literal l = lemma[i]; + if (max_var(l) == m_xk) { + bool_var b = l.var(); + if (!found_lvl) { + found_lvl = true; + new_lvl = m_levels[b]; + } + else { + if (m_levels[b] > new_lvl) + new_lvl = m_levels[b]; + } + } + } + SASSERT(!found_lvl || new_lvl < scope_lvl()); + if (!found_lvl) { + TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); + new_lvl = scope_lvl() - 1; + } + return new_lvl; + } + + struct scoped_reset_marks { + imp& i; + scoped_reset_marks(imp& i):i(i) {} + ~scoped_reset_marks() { if (i.m_num_marks > 0) { i.m_num_marks = 0; for (char& m : i.m_marks) m = 0; } } + }; + + + /** + \brief Return true if the conflict was solved. + */ + bool resolve(clause & conflict) { + clause * conflict_clause = &conflict; + m_lemma_assumptions = nullptr; + start: + SASSERT(check_marks()); + TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); + TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); + m_stats.m_conflicts++; + TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; + tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; + tout << "scope_lvl: " << scope_lvl() << "\n"; + tout << "current assignment\n"; display_assignment(tout);); + + m_num_marks = 0; + m_lemma.reset(); + m_lemma_assumptions = nullptr; + scoped_reset_marks _sr(*this); + resolve_clause(null_bool_var, *conflict_clause); + + unsigned top = m_trail.size(); + bool found_decision; + while (true) { + found_decision = false; + while (m_num_marks > 0) { + checkpoint(); + SASSERT(top > 0); + trail & t = m_trail[top-1]; + SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage + if (t.m_kind == trail::BVAR_ASSIGNMENT) { + bool_var b = t.m_b; + if (is_marked(b)) { + TRACE("nlsat_resolve", tout << "found marked: b" << b << "\n"; display_atom(tout, b) << "\n";); + m_num_marks--; + reset_mark(b); + justification jst = m_justifications[b]; + switch (jst.get_kind()) { + case justification::CLAUSE: + resolve_clause(b, *(jst.get_clause())); + break; + case justification::LAZY: + resolve_lazy_justification(b, *(jst.get_lazy())); + break; + case justification::DECISION: + SASSERT(m_num_marks == 0); + found_decision = true; + TRACE("nlsat_resolve", tout << "found decision\n";); + m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); + break; + default: + UNREACHABLE(); + break; + } + } + } + top--; + } + + // m_lemma is an implicating clause after backtracking current scope level. + if (found_decision) + break; + + // If lemma only contains literals from previous stages, then we can stop. + // We make progress by returning to a previous stage with additional information (new lemma) + // that forces us to select a new partial interpretation + if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.data())) + break; + + // Conflict does not depend on the current decision, and it is still in the current stage. + // We should find + // - the maximal scope level in the lemma + // - remove literal assigned in the scope level from m_lemma + // - backtrack to this level + // - and continue conflict resolution from there + // - we must bump m_num_marks for literals removed from m_lemma + unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.data()); + TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); + SASSERT(max_lvl < scope_lvl()); + remove_literals_from_lvl(m_lemma, max_lvl); + undo_until_level(max_lvl); + top = m_trail.size(); + TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); + SASSERT(scope_lvl() == max_lvl); + } + + TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); + TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); + + if (m_lemma.empty()) { + TRACE("nlsat", tout << "empty clause generated\n";); + return false; // problem is unsat, empty clause was generated + } + + reset_marks(); // remove marks from the literals in m_lemmas. + TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n"; + tout << "found_decision: " << found_decision << "\n";); + + if (m_check_lemmas) { + check_lemma(m_lemma.size(), m_lemma.data(), false, m_lemma_assumptions.get()); + } + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lemma.size(), m_lemma.data(), false); + + // There are two possibilities: + // 1) m_lemma contains only literals from previous stages, and they + // are false in the current interpretation. We make progress + // by returning to a previous stage with additional information (new clause) + // that forces us to select a new partial interpretation + // >>> Return to some previous stage (we may also backjump many decisions and stages). + // + // 2) m_lemma contains at most one literal from the current level (the last literal). + // Moreover, this literal was a decision, but the new lemma forces it to + // be assigned to a different value. + // >>> In this case, we remain in the same stage but, we add a new asserted literal + // in a previous scope level. We may backjump many decisions. + // + unsigned sz = m_lemma.size(); + clause * new_cls = nullptr; + if (!found_decision) { + // Case 1) + // We just have to find the maximal variable in m_lemma, and return to that stage + // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; + var new_max_var = max_var(sz, m_lemma.data()); + TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); + undo_until_stage(new_max_var); + SASSERT(m_xk == new_max_var); + new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); + TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << "\n"; + if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); + } + else { + SASSERT(scope_lvl() >= 1); + // Case 2) + if (is_bool_lemma(m_lemma.size(), m_lemma.data())) { + // boolean lemma, we just backtrack until the last literal is unassigned. + bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); + undo_until_unassigned(max_bool_var); + } + else { + // We must find the maximal decision level in literals in the first sz-1 positions that + // are at the same stage. If all these literals are from previous stages, + // we just backtrack the current level. + unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.data()); + TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); + undo_until_level(new_lvl); + } + + if (lemma_is_clause(*conflict_clause)) { + TRACE("nlsat", tout << "found decision literal in conflict clause\n";); + VERIFY(process_clause(*conflict_clause, true)); + return true; + } + new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); + } + NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); + if (!process_clause(*new_cls, true)) { + TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n"; + display(tout, *new_cls) << "\n"; + ); + // we are still in conflict + conflict_clause = new_cls; + goto start; + } + TRACE("nlsat_resolve_done", display_assignment(tout);); + return true; + } + + bool lemma_is_clause(clause const& cls) const { + bool same = (m_lemma.size() == cls.size()); + for (unsigned i = 0; same && i < m_lemma.size(); ++i) { + same = m_lemma[i] == cls[i]; + } + return same; + } + + + // ----------------------- + // + // Debugging + // + // ----------------------- + + bool check_watches() const { +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + clause_vector const & cs = m_watches[x]; + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + SASSERT(max_var(*(cs[i])) == x); + } + } +#endif + return true; + } + + bool check_bwatches() const { +#ifdef Z3DEBUG + for (bool_var b = 0; b < m_bwatches.size(); b++) { + clause_vector const & cs = m_bwatches[b]; + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + clause const & c = *(cs[i]); + SASSERT(max_var(c) == null_var); + SASSERT(max_bvar(c) == b); + } + } +#endif + return true; + } + + bool check_invariant() const { + SASSERT(check_watches()); + SASSERT(check_bwatches()); + return true; + } + + bool check_satisfied(clause_vector const & cs) const { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + clause const & c = *(cs[i]); + if (!is_satisfied(c)) { + TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); + return false; + } + } + return true; + } + + bool check_satisfied() const { + TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); + unsigned num = m_atoms.size(); + if (m_bk != null_bool_var) + num = m_bk; + for (bool_var b = 0; b < num; b++) { + if (!check_satisfied(m_bwatches[b])) { + UNREACHABLE(); + return false; + } + } + if (m_xk != null_var) { + for (var x = 0; x < m_xk; x++) { + if (!check_satisfied(m_watches[x])) { + UNREACHABLE(); + return false; + } + } + } + return true; + } + + // ----------------------- + // + // Statistics + // + // ----------------------- + + void collect_statistics(statistics & st) { + st.update("nlsat conflicts", m_stats.m_conflicts); + st.update("nlsat propagations", m_stats.m_propagations); + st.update("nlsat decisions", m_stats.m_decisions); + st.update("nlsat restarts", m_stats.m_restarts); + st.update("nlsat stages", m_stats.m_stages); + st.update("nlsat simplifications", m_stats.m_simplifications); + st.update("nlsat irrational assignments", m_stats.m_irrational_assignments); + } + + void reset_statistics() { + m_stats.reset(); + } + + // ----------------------- + // + // Variable reordering + // + // ----------------------- + + struct var_info_collector { + pmanager & pm; + atom_vector const & m_atoms; + var_vector m_shuffle; + unsigned_vector m_max_degree; + unsigned_vector m_num_occs; + + var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): + pm(_pm), + m_atoms(atoms) { + m_max_degree.resize(num_vars, 0); + m_num_occs.resize(num_vars, 0); + } + + var_vector m_vars; + void collect(poly * p) { + m_vars.reset(); + pm.vars(p, m_vars); + unsigned sz = m_vars.size(); + for (unsigned i = 0; i < sz; i++) { + var x = m_vars[i]; + unsigned k = pm.degree(p, x); + m_num_occs[x]++; + if (k > m_max_degree[x]) + m_max_degree[x] = k; + } + } + + void collect(literal l) { + bool_var b = l.var(); + atom * a = m_atoms[b]; + if (a == nullptr) + return; + if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; i++) { + collect(to_ineq_atom(a)->p(i)); + } + } + else { + collect(to_root_atom(a)->p()); + } + } + + void collect(clause const & c) { + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) + collect(c[i]); + } + + void collect(clause_vector const & cs) { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) + collect(*(cs[i])); + } + + std::ostream& display(std::ostream & out, display_var_proc const & proc) { + unsigned sz = m_num_occs.size(); + for (unsigned i = 0; i < sz; i++) { + proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; + } + return out; + } + }; + + struct reorder_lt { + var_info_collector const & m_info; + reorder_lt(var_info_collector const & info):m_info(info) {} + bool operator()(var x, var y) const { + // high degree first + if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) + return false; + if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) + return true; + // more constrained first + if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) + return false; + if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) + return true; + return m_info.m_shuffle[x] < m_info.m_shuffle[y]; + } + }; + + // Order variables by degree and number of occurrences + void heuristic_reorder() { + unsigned num = num_vars(); + var_info_collector collector(m_pm, m_atoms, num); + collector.collect(m_clauses); + collector.collect(m_learned); + init_shuffle(collector.m_shuffle); + TRACE("nlsat_reorder", collector.display(tout, m_display_var);); + var_vector new_order; + for (var x = 0; x < num; x++) + new_order.push_back(x); + + std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); + TRACE("nlsat_reorder", + tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); + var_vector perm; + perm.resize(num, 0); + for (var x = 0; x < num; x++) { + perm[new_order[x]] = x; + } + reorder(perm.size(), perm.data()); + SASSERT(check_invariant()); + } + + void init_shuffle(var_vector& p) { + unsigned num = num_vars(); + for (var x = 0; x < num; x++) + p.push_back(x); + + random_gen r(++m_random_seed); + shuffle(p.size(), p.data(), r); + } + + void shuffle_vars() { + var_vector p; + init_shuffle(p); + reorder(p.size(), p.data()); + } + + bool can_reorder() const { + return all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) + && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); + } + + /** + \brief Reorder variables using the giving permutation. + p maps internal variables to their new positions + */ + + + void reorder(unsigned sz, var const * p) { + + remove_learned_roots(); + SASSERT(can_reorder()); + TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); + display_vars(tout); + tout << "\npermutation:\n"; + for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; + ); + // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; + reinit_cache(); + SASSERT(num_vars() == sz); + TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); + reset_watches(); + assignment new_assignment(m_am); + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) + new_assignment.set(p[x], m_assignment.value(x)); + } + var_vector new_inv_perm; + new_inv_perm.resize(sz); + // the undo_until_size(0) statement erases the Boolean assignment. + // undo_until_size(0) + undo_until_stage(null_var); + m_cache.reset(); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_watches[x].empty()); + } +#endif + // update m_perm mapping + for (unsigned ext_x = 0; ext_x < sz; ext_x++) { + // p: internal -> new pos + // m_perm: internal -> external + // m_inv_perm: external -> internal + new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; + m_perm.set(new_inv_perm[ext_x], ext_x); + } + bool_vector is_int; + is_int.swap(m_is_int); + for (var x = 0; x < sz; x++) { + m_is_int.setx(p[x], is_int[x], false); + SASSERT(m_infeasible[x] == 0); + } + m_inv_perm.swap(new_inv_perm); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(x == m_inv_perm[m_perm[x]]); + SASSERT(m_watches[x].empty()); + } +#endif + m_pm.rename(sz, p); + for (auto& b : m_bounds) + b.x = p[b.x]; + TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); + reinit_cache(); + m_assignment.swap(new_assignment); + reattach_arith_clauses(m_clauses); + reattach_arith_clauses(m_learned); + TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); + } + + + /** + \brief Restore variable order. + */ + void restore_order() { + // m_perm: internal -> external + // m_inv_perm: external -> internal + var_vector p; + p.append(m_perm); + reorder(p.size(), p.data()); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_perm[x] == x); + SASSERT(m_inv_perm[x] == x); + } +#endif + } + + /** + \brief After variable reordering some lemmas containing root atoms may be ill-formed. + */ + void remove_learned_roots() { + unsigned j = 0; + for (clause* c : m_learned) { + if (has_root_atom(*c)) { + del_clause(c); + } + else { + m_learned[j++] = c; + } + } + m_learned.shrink(j); + } + + /** + \brief Return true if the clause contains an ill formed root atom + */ + bool has_root_atom(clause const & c) const { + for (literal lit : c) { + bool_var b = lit.var(); + atom * a = m_atoms[b]; + if (a && a->is_root_atom()) + return true; + } + return false; + } + + /** + \brief reinsert all polynomials in the unique cache + */ + void reinit_cache() { + reinit_cache(m_clauses); + reinit_cache(m_learned); + for (atom* a : m_atoms) + reinit_cache(a); + } + void reinit_cache(clause_vector const & cs) { + for (clause* c : cs) + reinit_cache(*c); + } + void reinit_cache(clause const & c) { + for (literal l : c) + reinit_cache(l); + } + void reinit_cache(literal l) { + bool_var b = l.var(); + reinit_cache(m_atoms[b]); + } + void reinit_cache(atom* a) { + if (a == nullptr) { + + } + else if (a->is_ineq_atom()) { + var max = 0; + unsigned sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; i++) { + poly * p = to_ineq_atom(a)->p(i); + VERIFY(m_cache.mk_unique(p) == p); + var x = m_pm.max_var(p); + if (x > max) + max = x; + } + a->m_max_var = max; + } + else { + poly * p = to_root_atom(a)->p(); + VERIFY(m_cache.mk_unique(p) == p); + a->m_max_var = m_pm.max_var(p); + } + } + + void reset_watches() { + unsigned num = num_vars(); + for (var x = 0; x < num; x++) { + m_watches[x].reset(); + } + } + + void reattach_arith_clauses(clause_vector const & cs) { + for (clause* cp : cs) { + var x = max_var(*cp); + if (x != null_var) + m_watches[x].push_back(cp); + } + } + + // ----------------------- + // + // Solver initialization + // + // ----------------------- + + struct degree_lt { + unsigned_vector & m_degrees; + degree_lt(unsigned_vector & ds):m_degrees(ds) {} + bool operator()(unsigned i1, unsigned i2) const { + if (m_degrees[i1] < m_degrees[i2]) + return true; + if (m_degrees[i1] > m_degrees[i2]) + return false; + return i1 < i2; + } + }; + + unsigned_vector m_cs_degrees; + unsigned_vector m_cs_p; + void sort_clauses_by_degree(unsigned sz, clause ** cs) { + if (sz <= 1) + return; + TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + m_cs_degrees.reset(); + m_cs_p.reset(); + for (unsigned i = 0; i < sz; i++) { + m_cs_p.push_back(i); + m_cs_degrees.push_back(degree(*(cs[i]))); + } + std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); + TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); + apply_permutation(sz, cs, m_cs_p.data()); + TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + } + +//#linxi begin + + struct degree_lit_num_lt { + unsigned_vector & m_degrees; + unsigned_vector & m_lit_num; + degree_lit_num_lt(unsigned_vector & ds, unsigned_vector ln) : + m_degrees(ds), + m_lit_num(ln) { + } + bool operator()(unsigned i1, unsigned i2) const { + if (m_lit_num[i1] == 1 && m_lit_num[i2] > 1) + return true; + if (m_lit_num[i1] > 1 && m_lit_num[i2] == 1) + return false; + if (m_degrees[i1] != m_degrees[i2]) + return m_degrees[i1] < m_degrees[i2]; + if (m_lit_num[i1] != m_lit_num[i2]) + return m_lit_num[i1] < m_lit_num[i2]; + return i1 < i2; + } + }; + + unsigned_vector m_dl_degrees; + unsigned_vector m_dl_lit_num; + unsigned_vector m_dl_p; + void sort_clauses_by_degree_lit_num(unsigned sz, clause ** cs) { + if (sz <= 1) + return; + TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + m_dl_degrees.reset(); + m_dl_lit_num.reset(); + m_dl_p.reset(); + for (unsigned i = 0; i < sz; i++) { + m_dl_degrees.push_back(degree(*(cs[i]))); + m_dl_lit_num.push_back(cs[i]->size()); + m_dl_p.push_back(i); + } + std::sort(m_dl_p.begin(), m_dl_p.end(), degree_lit_num_lt(m_dl_degrees, m_dl_lit_num)); + TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_dl_p.begin(), m_dl_p.end()); tout << "\n";); + apply_permutation(sz, cs, m_dl_p.data()); + TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + } + +//#linxi end + void sort_watched_clauses() { + unsigned num = num_vars(); + for (unsigned i = 0; i < num; i++) { + clause_vector & ws = m_watches[i]; +//#linxi begin + // sort_clauses_by_degree(ws.size(), ws.data()); + if (m_linxi_simple_check) { + sort_clauses_by_degree_lit_num(ws.size(), ws.data()); + } + else { + sort_clauses_by_degree(ws.size(), ws.data()); + } +//#linxi end + } + } + + // ----------------------- + // + // Full dimensional + // + // A problem is in the full dimensional fragment if it does + // not contain equalities or non-strict inequalities. + // + // ----------------------- + + bool is_full_dimensional(literal l) const { + atom * a = m_atoms[l.var()]; + if (a == nullptr) + return true; + switch (a->get_kind()) { + case atom::EQ: return l.sign(); + case atom::LT: return !l.sign(); + case atom::GT: return !l.sign(); + case atom::ROOT_EQ: return l.sign(); + case atom::ROOT_LT: return !l.sign(); + case atom::ROOT_GT: return !l.sign(); + case atom::ROOT_LE: return l.sign(); + case atom::ROOT_GE: return l.sign(); + default: + UNREACHABLE(); + return false; + } + } + + bool is_full_dimensional(clause const & c) const { + for (literal l : c) { + if (!is_full_dimensional(l)) + return false; + } + return true; + } + + bool is_full_dimensional(clause_vector const & cs) const { + for (clause* c : cs) { + if (!is_full_dimensional(*c)) + return false; + } + return true; + } + + bool is_full_dimensional() const { + return is_full_dimensional(m_clauses); + } + + + // ----------------------- + // + // Simplification + // + // ----------------------- + + // solve simple equalities + // TBD WU-Reit decomposition? + + // - elim_unconstrained + // - solve_eqs + // - fm + + /** + \brief isolate variables in unit equalities. + Assume a clause is c == v*p + q + and the context implies p > 0 + + replace v by -q/p + remove clause c, + The for other occurrences of v, + replace v*r + v*v*r' > 0 by + by p*p*v*r + p*p*v*v*r' > 0 + by p*q*r + q*q*r' > 0 + + The method ignores lemmas and assumes constraints don't use roots. + */ + + + + // Eliminated variables are tracked in m_bounds. + // Each element in m_bounds tracks the eliminated variable and an upper or lower bound + // that has to be satisfied. Variables that are eliminated through equalities are tracked + // by non-strict bounds. A satisfiable solution is required to provide an evaluation that + // is consistent with the bounds. For equalities, the non-strict lower or upper bound can + // always be assigned as a value to the variable. + + void fix_patch() { + m_lo.reset(); m_hi.reset(); + for (auto& b : m_bounds) + m_assignment.reset(b.x); + for (unsigned i = m_bounds.size(); i-- > 0; ) + fix_patch(m_bounds[i]); + } + + // x is unassigned, lo < x -> x <- lo + 1 + // x is unassigned, x < hi -> x <- hi - 1 + // x is unassigned, lo <= x -> x <- lo + // x is unassigned, x <= hi -> x <- hi + // x is assigned above hi, lo is strict lo < x < hi -> set x <- (lo + hi)/2 + // x is assigned below hi, above lo -> no-op + // x is assigned below lo, hi is strict lo < x < hi -> set x <-> (lo + hi)/2 + // x is assigned above hi, x <= hi -> x <- hi + // x is assigned blow lo, lo <= x -> x <- lo + void fix_patch(bound_constraint& b) { + var x = b.x; + scoped_anum Av(m_am), Bv(m_am), val(m_am); + m_pm.eval(b.A, m_assignment, Av); + m_pm.eval(b.B, m_assignment, Bv); + m_am.neg(Bv); + val = Bv / Av; + // Ax >= B + // is-lower : A > 0 + // is-upper: A < 0 + // x <- B / A + bool is_lower = m_am.is_pos(Av); + TRACE("nlsat", + m_display_var(tout << "patch v" << x << " ", x) << "\n"; + if (m_assignment.is_assigned(x)) m_am.display(tout << "previous value: ", m_assignment.value(x)); tout << "\n"; + m_am.display(tout << "updated value: ", val); tout << "\n"; + ); + + if (!m_assignment.is_assigned(x)) { + if (!b.is_strict) + m_assignment.set_core(x, val); + else if (is_lower) + m_assignment.set_core(x, val + 1); + else + m_assignment.set_core(x, val - 1); + } + else { + auto& aval = m_assignment.value(x); + if (is_lower) { + // lo < value(x), lo < x -> x is unchanged + if (b.is_strict && m_am.lt(val, aval)) + ; + else if (!b.is_strict && m_am.le(val, aval)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + // aval < lo < x, hi is unassigned: x <- lo + 1 + else if (!m_hi.is_assigned(x)) + m_assignment.set_core(x, val + 1); + // aval < lo < x, hi is assigned: x <- (lo + hi) / 2 + else { + scoped_anum mid(m_am); + m_am.add(m_hi.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + else { + // dual to lower bounds + if (b.is_strict && m_am.lt(aval, val)) + ; + else if (!b.is_strict && m_am.le(aval, val)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + else if (!m_lo.is_assigned(x)) + m_assignment.set_core(x, val - 1); + else { + scoped_anum mid(m_am); + m_am.add(m_lo.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + } + + if (is_lower) { + if (!m_lo.is_assigned(x) || m_am.lt(m_lo.value(x), val)) + m_lo.set_core(x, val); + } + else { + if (!m_hi.is_assigned(x) || m_am.gt(m_hi.value(x), val)) + m_hi.set_core(x, val); + } + } + + bool is_unit_ineq(clause const& c) const { + return + c.size() == 1 && + m_atoms[c[0].var()] && + m_atoms[c[0].var()]->is_ineq_atom(); + } + + bool is_unit_eq(clause const& c) const { + return + is_unit_ineq(c) && + !c[0].sign() && + m_atoms[c[0].var()]->is_eq(); + } + + /** + \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. + */ + lbool is_cmp0(clause const& c, var& v) { + if (!is_unit_ineq(c)) + return l_undef; + literal lit = c[0]; + ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); + bool sign = lit.sign(); + poly * p0; + if (!is_single_poly(a, p0)) + return l_undef; + if (m_pm.is_var(p0, v)) { + if (!sign && a.get_kind() == atom::GT) { + return l_true; + } + if (!sign && a.get_kind() == atom::LT) { + return l_false; + } + return l_undef; + } + polynomial::scoped_numeral n(m_pm.m()); + if (m_pm.is_var_num(p0, v, n)) { + // x - k > 0 + if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { + return l_true; + } + // x + k < 0 + if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { + return l_false; + } + // !(x + k > 0) + if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { + return l_false; + } + // !(x - k < 0) + if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { + return l_true; + } + } + return l_undef; + } + + bool is_single_poly(ineq_atom const& a, poly*& p) { + unsigned sz = a.size(); + return sz == 1 && a.is_odd(0) && (p = a.p(0), true); + } + + bool is_unit(polynomial_ref const& p) { + if (!m_pm.is_const(p)) + return false; + auto const& c = m_pm.coeff(p, 0); + return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); + } + + // ----------------------- + // + // Pretty printing + // + // ----------------------- + + std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) { + proc(out, x); + out << " -> "; + m_am.display_decimal(out, m_assignment.value(x)); + out << "\n"; + } + } + return out; + } + + std::ostream& display_bool_assignment(std::ostream & out) const { + unsigned sz = m_atoms.size(); + for (bool_var b = 0; b < sz; b++) { + if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { + out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; + } + else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { + display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; + } + } + TRACE("nlsat_bool_assignment", + for (bool_var b = 0; b < sz; b++) { + out << "b" << b << " -> " << m_bvalues[b] << " "; + if (m_atoms[b]) display(out, *m_atoms[b]); + out << "\n"; + }); + return out; + } + + bool display_mathematica_assignment(std::ostream & out) const { + bool first = true; + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) { + if (first) + first = false; + else + out << " && "; + out << "x" << x << " == "; + m_am.display_mathematica(out, m_assignment.value(x)); + } + } + return !first; + } + + std::ostream& display_num_assignment(std::ostream & out) const { + return display_num_assignment(out, m_display_var); + } + + std::ostream& display_assignment(std::ostream& out) const { + display_bool_assignment(out); + display_num_assignment(out); + return out; + } + + std::ostream& display(std::ostream& out, justification j) const { + switch (j.get_kind()) { + case justification::CLAUSE: + display(out, *j.get_clause()) << "\n"; + break; + case justification::LAZY: { + lazy_justification const& lz = *j.get_lazy(); + display_not(out, lz.num_lits(), lz.lits()) << "\n"; + for (unsigned i = 0; i < lz.num_clauses(); ++i) { + display(out, lz.clause(i)) << "\n"; + } + break; + } + default: + out << j.get_kind() << "\n"; + break; + } + return out; + } + + bool m_display_eval = false; + std::ostream& display_eval(std::ostream& out, justification j) { + flet _display(m_display_eval, true); + return display(out, j); + } + + std::ostream& display_ineq(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { + unsigned sz = a.size(); + for (unsigned i = 0; i < sz; i++) { + if (use_star && i > 0) + out << "*"; + bool is_even = a.is_even(i); + if (is_even || sz > 1) + out << "("; + display_polynomial(out, a.p(i), proc, use_star); + if (is_even || sz > 1) + out << ")"; + if (is_even) + out << "^2"; + } + switch (a.get_kind()) { + case atom::LT: out << " < 0"; break; + case atom::GT: out << " > 0"; break; + case atom::EQ: out << " = 0"; break; + default: UNREACHABLE(); break; + } + return out; + } + + std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { + unsigned sz = a.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) + out << "*"; + bool is_even = a.is_even(i); + if (sz > 1) + out << "("; + if (is_even) + out << "("; + m_pm.display(out, a.p(i), display_var_proc(), true); + if (is_even) + out << "^2)"; + if (sz > 1) + out << ")"; + } + switch (a.get_kind()) { + case atom::LT: out << " < 0"; break; + case atom::GT: out << " > 0"; break; + case atom::EQ: out << " == 0"; break; + default: UNREACHABLE(); break; + } + return out; + } + + std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { + return m_pm.display_smt2(out, p, proc); + } + + std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { + switch (a.get_kind()) { + case atom::LT: out << "(< "; break; + case atom::GT: out << "(> "; break; + case atom::EQ: out << "(= "; break; + default: UNREACHABLE(); break; + } + unsigned sz = a.size(); + if (sz > 1) + out << "(* "; + for (unsigned i = 0; i < sz; i++) { + if (i > 0) out << " "; + if (a.is_even(i)) { + out << "(* "; + display_polynomial_smt2(out, a.p(i), proc); + out << " "; + display_polynomial_smt2(out, a.p(i), proc); + out << ")"; + } + else { + display_polynomial_smt2(out, a.p(i), proc); + } + } + if (sz > 1) + out << ")"; + out << " 0)"; + return out; + } + + std::ostream& display_poly_root(std::ostream& out, char const* y, root_atom const& a, display_var_proc const& proc) const { + out << "(exists (("; proc(out,a.x()); out << " Real))\n"; + out << "(and (= " << y << " "; + proc(out, a.x()); + out << ") (= 0 "; + display_polynomial_smt2(out, a.p(), proc); + out << ")))\n"; + return out; + } + + std::ostream& display_binary_smt2(std::ostream& out, poly const* p1, char const* rel, poly const* p2, display_var_proc const& proc) const { + out << "(" << rel << " "; + display_polynomial_smt2(out, p1, proc); + out << " "; + display_polynomial_smt2(out, p2, proc); + out << ")"; + return out; + } + + + std::ostream& display_linear_root_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + polynomial_ref A(m_pm), B(m_pm), Z(m_pm), Ax(m_pm); + polynomial::scoped_numeral zero(m_qm); + m_pm.m().set(zero, 0); + A = m_pm.derivative(a.p(), a.x()); + B = m_pm.neg(m_pm.substitute(a.p(), a.x(), zero)); + Z = m_pm.mk_zero(); + + Ax = m_pm.mul(m_pm.mk_polynomial(a.x()), A); + + // x < root[1](ax + b) == (a > 0 => ax + b < 0) & (a < 0 => ax + b > 0) + // x < root[1](ax + b) == (a > 0 => ax < -b) & (a < 0 => ax > -b) + + char const* rel1 = "<", *rel2 = ">"; + switch (a.get_kind()) { + case atom::ROOT_LT: rel1 = "<"; rel2 = ">"; break; + case atom::ROOT_GT: rel1 = ">"; rel2 = "<"; break; + case atom::ROOT_LE: rel1 = "<="; rel2 = ">="; break; + case atom::ROOT_GE: rel1 = ">="; rel2 = "<="; break; + case atom::ROOT_EQ: rel1 = rel2 = "="; break; + default: UNREACHABLE(); break; + } + + out << "(and "; + out << "(=> "; display_binary_smt2(out, A, ">", Z, proc); display_binary_smt2(out, Ax, rel1, B, proc); out << ") "; + out << "(=> "; display_binary_smt2(out, A, "<", Z, proc); display_binary_smt2(out, Ax, rel2, B, proc); out << ") "; + out << ")"; + + return out; + } + + + std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) + return display_linear_root_smt2(out, a, proc); +#if 1 + out << "(exists ("; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + out << "(" << y << " Real) "; + } + out << ")\n"; + out << "(and\n"; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + display_poly_root(out, y.c_str(), a, proc); + } + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + std::string y2 = std::string("y") + std::to_string(j+1); + out << "(< " << y1 << " " << y2 << ")\n"; + } + + std::string yn = "y" + std::to_string(a.i() - 1); + + // TODO we need (forall z : z < yn . p(z) => z = y1 or ... z = y_{n-1}) + // to say y1, .., yn are the first n distinct roots. + // + out << "(forall ((z Real)) (=> (and (< z " << yn << ") "; display_poly_root(out, "z", a, proc) << ") "; + if (a.i() == 1) { + out << "false))\n"; + } + else { + out << "(or "; + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + out << "(= z " << y1 << ") "; + } + out << ")))\n"; + } + switch (a.get_kind()) { + case atom::ROOT_LT: out << "(< "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GT: out << "(> "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; + default: UNREACHABLE(); break; + } + out << "))"; + return out; +#endif + + + return display_root(out, a, proc); + } + + std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + proc(out, a.x()); + switch (a.get_kind()) { + case atom::ROOT_LT: out << " < "; break; + case atom::ROOT_GT: out << " > "; break; + case atom::ROOT_LE: out << " <= "; break; + case atom::ROOT_GE: out << " >= "; break; + case atom::ROOT_EQ: out << " = "; break; + default: UNREACHABLE(); break; + } + out << "root[" << a.i() << "]("; + display_polynomial(out, a.p(), proc); + out << ")"; + return out; + } + + struct mathematica_var_proc : public display_var_proc { + var m_x; + public: + mathematica_var_proc(var x):m_x(x) {} + std::ostream& operator()(std::ostream & out, var x) const override { + if (m_x == x) + return out << "#1"; + else + return out << "x" << x; + } + }; + + std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { + out << "x" << a.x(); + switch (a.get_kind()) { + case atom::ROOT_LT: out << " < "; break; + case atom::ROOT_GT: out << " > "; break; + case atom::ROOT_LE: out << " <= "; break; + case atom::ROOT_GE: out << " >= "; break; + case atom::ROOT_EQ: out << " == "; break; + default: UNREACHABLE(); break; + } + out << "Root["; + display_polynomial(out, a.p(), mathematica_var_proc(a.x()), true); + out << " &, " << a.i() << "]"; + return out; + } + + std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { + if (a.is_ineq_atom()) + return display_ineq(out, static_cast(a), proc); + else + return display_root(out, static_cast(a), proc); + } + + std::ostream& display(std::ostream & out, atom const & a) const { + return display(out, a, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, atom const & a) const { + if (a.is_ineq_atom()) + return display_mathematica(out, static_cast(a)); + else + return display_mathematica(out, static_cast(a)); + } + + std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { + if (a.is_ineq_atom()) + return display_ineq_smt2(out, static_cast(a), proc); + else + return display_root_smt2(out, static_cast(a), proc); + } + + std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + if (b == 0) + out << "true"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display(out, *(m_atoms[b]), proc); + return out; + } + + std::ostream& display_atom(std::ostream & out, bool_var b) const { + return display_atom(out, b, m_display_var); + } + + std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { + if (b == 0) + out << "(0 < 1)"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display_mathematica(out, *(m_atoms[b])); + return out; + } + + std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + if (b == 0) + out << "true"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display_smt2(out, *(m_atoms[b]), proc); + return out; + } + + std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (m_atoms[b] != 0) + out << "("; + display_atom(out, b, proc); + if (m_atoms[b] != 0) + out << ")"; + } + else { + display_atom(out, l.var(), proc); + } + return out; + } + + std::ostream& display(std::ostream & out, literal l) const { + return display(out, l, m_display_var); + } + + std::ostream& display_smt2(std::ostream & out, literal l) const { + return display_smt2(out, l, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, literal l) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (m_atoms[b] != 0) + out << "("; + display_mathematica_atom(out, b); + if (m_atoms[b] != 0) + out << ")"; + } + else { + display_mathematica_atom(out, l.var()); + } + return out; + } + + std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { + if (l.sign()) { + bool_var b = l.var(); + out << "(not "; + display_smt2_atom(out, b, proc); + out << ")"; + } + else { + display_smt2_atom(out, l.var(), proc); + } + return out; + } + + std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { + if (!m_display_assumption) + return out; + vector deps; + m_asm.linearize(s, deps); + bool first = true; + for (auto dep : deps) { + if (first) first = false; else out << " "; + (*m_display_assumption)(out, dep); + } + return out; + } + + std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display(out, ls[i], proc); + } + return out; + } + + std::ostream& display(std::ostream & out, unsigned num, literal const * ls) const { + return display(out, num, ls, m_display_var); + } + + std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display(out, ~ls[i], proc); + } + return out; + } + + std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls) const { + return display_not(out, num, ls, m_display_var); + } + + std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { + return display(out, cs.size(), cs.data(), m_display_var); + } + + std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { + if (c.assumptions() != nullptr) { + display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); + out << " |- "; + } + return display(out, c.size(), c.data(), proc); + } + + std::ostream& display(std::ostream & out, clause const & c) const { + return display(out, c, m_display_var); + } + + + std::ostream& display_polynomial(std::ostream& out, poly* p, display_var_proc const & proc, bool use_star = false) const { + if (m_display_eval) { + polynomial_ref q(m_pm); + q = p; + for (var x = 0; x < num_vars(); x++) + if (m_assignment.is_assigned(x)) { + auto& a = m_assignment.value(x); + if (!m_am.is_rational(a)) + continue; + mpq r; + m_am.to_rational(a, r); + q = m_pm.substitute(q, 1, &x, &r); + } + m_pm.display(out, q, proc, use_star); + } + else + m_pm.display(out, p, proc, use_star); + return out; + } + + // -- + + std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const { + return display_smt2(out, n, ls, display_var_proc()); + } + + + std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + if (num == 0) { + out << "false"; + } + else if (num == 1) { + display_smt2(out, ls[0], proc); + } + else { + out << "(or"; + for (unsigned i = 0; i < num; i++) { + out << " "; + display_smt2(out, ls[i], proc); + } + out << ")"; + } + return out; + } + + std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { + return display_smt2(out, c.size(), c.data(), proc); + } + + std::ostream& display_abst(std::ostream & out, literal l) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (b == true_bool_var) + out << "true"; + else + out << "b" << b; + } + else { + out << "b" << l.var(); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display_abst(out, ls[i]); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { + return display_abst(out, cs.size(), cs.data()); + } + + std::ostream& display_abst(std::ostream & out, clause const & c) const { + return display_abst(out, c.size(), c.data()); + } + + std::ostream& display_mathematica(std::ostream & out, clause const & c) const { + out << "("; + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) + out << " || "; + display_mathematica(out, c[i]); + } + out << ")"; + return out; + } + + // Debugging support: + // Display generated lemma in Mathematica format. + // Mathematica must reduce lemma to True (modulo resource constraints). + std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { + out << "Resolve[ForAll[{"; + // var definition + for (unsigned i = 0; i < num_vars(); i++) { + if (i > 0) + out << ", "; + out << "x" << i; + } + out << "}, "; + if (include_assignment) { + out << "!("; + if (!display_mathematica_assignment(out)) + out << "0 < 1"; // nothing was printed + out << ") || "; + } + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " || "; + display_mathematica(out, ls[i]); + } + out << "], Reals]\n"; // end of exists + return out; + } + + std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { + for (clause* c : cs) { + display(out, *c, proc) << "\n"; + } + return out; + } + + std::ostream& display(std::ostream & out, clause_vector const & cs) const { + return display(out, cs, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) out << ",\n"; + display_mathematica(out << " ", *(cs[i])); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { + for (clause* c : cs) { + display_abst(out, *c) << "\n"; + } + return out; + } + + std::ostream& display(std::ostream & out, display_var_proc const & proc) const { + display(out, m_clauses, proc); + if (!m_learned.empty()) { + display(out << "Lemmas:\n", m_learned, proc); + } + return out; + } + + std::ostream& display_mathematica(std::ostream & out) const { + return display_mathematica(out << "{\n", m_clauses) << "}\n"; + } + + std::ostream& display_abst(std::ostream & out) const { + display_abst(out, m_clauses); + if (!m_learned.empty()) { + display_abst(out << "Lemmas:\n", m_learned); + } + return out; + } + + std::ostream& display(std::ostream & out) const { + display(out, m_display_var); + display_assignment(out << "assignment:\n"); + return out << "---\n"; + } + + std::ostream& display_vars(std::ostream & out) const { + for (unsigned i = 0; i < num_vars(); i++) { + out << i << " -> "; m_display_var(out, i); out << "\n"; + } + return out; + } + + std::ostream& display_smt2_arith_decls(std::ostream & out) const { + unsigned sz = m_is_int.size(); + for (unsigned i = 0; i < sz; i++) { + if (is_int(i)) { + out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; + } + else { + out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; + } + } + return out; + } + + std::ostream& display_smt2_bool_decls(std::ostream & out) const { + unsigned sz = m_atoms.size(); + for (unsigned i = 0; i < sz; i++) { + if (m_atoms[i] == nullptr) + out << "(declare-fun b" << i << " () Bool)\n"; + } + return out; + } + + std::ostream& display_smt2(std::ostream & out) const { + display_smt2_bool_decls(out); + display_smt2_arith_decls(out); + out << "(assert (and true\n"; + for (clause* c : m_clauses) { + display_smt2(out, *c, m_display_var) << "\n"; + } + out << "))\n" << std::endl; + return out; + } + }; + + solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { + m_ctx = alloc(ctx, rlim, p, incremental); + m_imp = alloc(imp, *this, *m_ctx); + } + + solver::solver(ctx& ctx) { + m_ctx = nullptr; + m_imp = alloc(imp, *this, ctx); + } + + solver::~solver() { + dealloc(m_imp); + dealloc(m_ctx); + } + + lbool solver::check() { + return m_imp->check(); + } + + lbool solver::check(literal_vector& assumptions) { + return m_imp->check(assumptions); + } + + void solver::get_core(vector& assumptions) { + return m_imp->get_core(assumptions); + } + + void solver::reset() { + m_imp->reset(); + } + + + void solver::updt_params(params_ref const & p) { + m_imp->updt_params(p); + } + + + void solver::collect_param_descrs(param_descrs & d) { + algebraic_numbers::manager::collect_param_descrs(d); + nlsat_params::collect_param_descrs(d); + } + + unsynch_mpq_manager & solver::qm() { + return m_imp->m_qm; + } + + anum_manager & solver::am() { + return m_imp->m_am; + } + + pmanager & solver::pm() { + return m_imp->m_pm; + } + + void solver::set_display_var(display_var_proc const & proc) { + m_imp->m_display_var.m_proc = &proc; + } + + void solver::set_display_assumption(display_assumption_proc const& proc) { + m_imp->m_display_assumption = &proc; + } + + + unsigned solver::num_vars() const { + return m_imp->num_vars(); + } + + bool solver::is_int(var x) const { + return m_imp->is_int(x); + } + + bool_var solver::mk_bool_var() { + return m_imp->mk_bool_var(); + } + + literal solver::mk_true() { + return literal(0, false); + } + + atom * solver::bool_var2atom(bool_var b) { + return m_imp->m_atoms[b]; + } + + void solver::vars(literal l, var_vector& vs) { + m_imp->vars(l, vs); + } + + atom_vector const& solver::get_atoms() { + return m_imp->m_atoms; + } + + atom_vector const& solver::get_var2eq() { + return m_imp->m_var2eq; + } + + evaluator& solver::get_evaluator() { + return m_imp->m_evaluator; + } + + explain& solver::get_explain() { + return m_imp->m_explain; + } + + void solver::reorder(unsigned sz, var const* p) { + m_imp->reorder(sz, p); + } + + void solver::restore_order() { + m_imp->restore_order(); + } + + void solver::set_rvalues(assignment const& as) { + m_imp->m_assignment.copy(as); + } + + void solver::get_rvalues(assignment& as) { + as.copy(m_imp->m_assignment); + } + + void solver::get_bvalues(svector const& bvars, svector& vs) { + vs.reset(); + for (bool_var b : bvars) { + vs.reserve(b + 1, l_undef); + if (!m_imp->m_atoms[b]) { + vs[b] = m_imp->m_bvalues[b]; + } + } + TRACE("nlsat", display(tout);); + } + + void solver::set_bvalues(svector const& vs) { + TRACE("nlsat", display(tout);); + for (bool_var b = 0; b < vs.size(); ++b) { + if (vs[b] != l_undef) { + m_imp->m_bvalues[b] = vs[b]; + SASSERT(!m_imp->m_atoms[b]); + } + } +#if 0 + m_imp->m_bvalues.reset(); + m_imp->m_bvalues.append(vs); + m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); + for (unsigned i = 0; i < m_imp->m_atoms.size(); ++i) { + atom* a = m_imp->m_atoms[i]; + SASSERT(!a); + if (a) { + m_imp->m_bvalues[i] = to_lbool(m_imp->m_evaluator.eval(a, false)); + } + } +#endif + TRACE("nlsat", display(tout);); + } + + void solver::del_clause(clause* c) { + m_imp->del_clause(c); + } + + var solver::mk_var(bool is_int) { + return m_imp->mk_var(is_int); + } + + bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + return m_imp->mk_ineq_atom(k, sz, ps, is_even); + } + + literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify) { + return m_imp->mk_ineq_literal(k, sz, ps, is_even, simplify); + } + + bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { + return m_imp->mk_root_atom(k, x, i, p); + } + + void solver::inc_ref(bool_var b) { + m_imp->inc_ref(b); + } + + void solver::dec_ref(bool_var b) { + m_imp->dec_ref(b); + } + + void solver::inc_ref(assumption a) { + m_imp->inc_ref(static_cast(a)); + } + + void solver::dec_ref(assumption a) { + m_imp->dec_ref(static_cast(a)); + } + + void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { + return m_imp->mk_external_clause(num_lits, lits, a); + } + + std::ostream& solver::display(std::ostream & out) const { + return m_imp->display(out); + } + + std::ostream& solver::display(std::ostream & out, literal l) const { + return m_imp->display(out, l); + } + + std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { + for (unsigned i = 0; i < n; ++i) { + display(out, ls[i]); + out << "; "; + } + return out; + } + + std::ostream& solver::display(std::ostream & out, literal_vector const& ls) const { + return display(out, ls.size(), ls.data()); + } + + std::ostream& solver::display_smt2(std::ostream & out, literal l) const { + return m_imp->display_smt2(out, l); + } + + std::ostream& solver::display_smt2(std::ostream & out, unsigned n, literal const* ls) const { + for (unsigned i = 0; i < n; ++i) { + display_smt2(out, ls[i]); + out << " "; + } + return out; + } + + std::ostream& solver::display(std::ostream& out, clause const& c) const { + return m_imp->display(out, c); + } + + std::ostream& solver::display_smt2(std::ostream & out) const { + return m_imp->display_smt2(out); + } + + std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { + return display_smt2(out, ls.size(), ls.data()); + } + + std::ostream& solver::display(std::ostream & out, var x) const { + return m_imp->m_display_var(out, x); + } + + std::ostream& solver::display(std::ostream & out, atom const& a) const { + return m_imp->display(out, a, m_imp->m_display_var); + } + + display_var_proc const & solver::display_proc() const { + return m_imp->m_display_var; + } + + anum const & solver::value(var x) const { + if (m_imp->m_assignment.is_assigned(x)) + return m_imp->m_assignment.value(x); + return m_imp->m_zero; + } + + lbool solver::bvalue(bool_var b) const { + return m_imp->m_bvalues[b]; + } + + lbool solver::value(literal l) const { + return m_imp->value(l); + } + + bool solver::is_interpreted(bool_var b) const { + return m_imp->m_atoms[b] != 0; + } + + void solver::reset_statistics() { + return m_imp->reset_statistics(); + } + + void solver::collect_statistics(statistics & st) { + return m_imp->collect_statistics(st); + } + + clause* solver::mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a) { + return m_imp->mk_clause(n, lits, learned, static_cast(a)); + } + + void solver::inc_simplify() { + m_imp->m_stats.m_simplifications++; + } + + bool solver::has_root_atom(clause const& c) const { + return m_imp->has_root_atom(c); + } + + void solver::add_bound(bound_constraint const& c) { + m_imp->m_bounds.push_back(c); + } + + assumption solver::join(assumption a, assumption b) { + return (m_imp->m_asm.mk_join(static_cast(a), static_cast(b))); + } + +}; From 518a8b2bdb662e141f40d13a6712522a439cee71 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 8 Aug 2024 12:42:30 -1000 Subject: [PATCH 142/414] fix the build Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_simple_checker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nlsat/nlsat_simple_checker.cpp b/src/nlsat/nlsat_simple_checker.cpp index 047b67e0e5d..f4bb5e64823 100644 --- a/src/nlsat/nlsat_simple_checker.cpp +++ b/src/nlsat/nlsat_simple_checker.cpp @@ -301,6 +301,7 @@ namespace nlsat { if (kd == atom::GT) return GT; UNREACHABLE(); + return EQ; } bool update_interval_intersection(Domain_Interval &ia, const Domain_Interval &ib) { TRACE("linxi_simple_checker", @@ -2059,4 +2060,4 @@ else { // ( == 0) + (c > 0) -> > 0 LINXI_DEBUG; return m_imp->operator()(); } -} \ No newline at end of file +} From f7905a5d6914e863e76e5a5f609935fcde1470d3 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 8 Aug 2024 14:50:17 -1000 Subject: [PATCH 143/414] remove printouts --- src/nlsat/nlsat_solver.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index d5075246064..486d88cc63d 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1838,7 +1838,6 @@ namespace nlsat { //#linxi begin symmetry check void symmetry_check() { - std::cout << "sym called\n"; unsigned arith_num = m_is_int.size(); if (arith_num > 10000) return ; @@ -1923,7 +1922,6 @@ namespace nlsat { } //#linxi begin Variable Ordering Strategy else if (m_linxi_variable_ordering_strategy > 0) { - std::cout << "m_linxi_variable_ordering_strategy=" << m_linxi_variable_ordering_strategy << "\n"; run_variable_ordering_strategy(); reordered = true; } From f81303f2f3c99d6afb6ceeb96e300f660bd95106 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 8 Aug 2024 15:06:33 -1000 Subject: [PATCH 144/414] delete unused nlsat_symmetry_checker Signed-off-by: Lev Nachmanson --- src/nlsat/CMakeLists.txt | 1 - src/nlsat/nlsat_params.pyg | 1 - src/nlsat/nlsat_solver.cpp | 46 +--- src/nlsat/nlsat_symmetry_checker.cpp | 356 --------------------------- src/nlsat/nlsat_symmetry_checker.h | 13 - 5 files changed, 1 insertion(+), 416 deletions(-) delete mode 100644 src/nlsat/nlsat_symmetry_checker.cpp delete mode 100644 src/nlsat/nlsat_symmetry_checker.h diff --git a/src/nlsat/CMakeLists.txt b/src/nlsat/CMakeLists.txt index 7aa1d83c5f0..077c420f9d9 100644 --- a/src/nlsat/CMakeLists.txt +++ b/src/nlsat/CMakeLists.txt @@ -9,7 +9,6 @@ z3_add_component(nlsat nlsat_types.cpp nlsat_simple_checker.cpp nlsat_variable_ordering_strategy.cpp - nlsat_symmetry_checker.cpp COMPONENT_DEPENDENCIES polynomial sat diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 73067d48bc6..00d17990133 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -6,7 +6,6 @@ def_module_params('nlsat', ('linxi_simple_check', BOOL, False, "linxi precheck about variables sign"), ('linxi_variable_ordering_strategy', UINT, 0, "linxi Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"), ('cell_sample', BOOL, True, "cell sample projection"), - ('linxi_symmetry_check', BOOL, False, "linxi symmetry check and learning"), ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 486d88cc63d..30ca55e31cf 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -36,7 +36,6 @@ Revision History: #include "nlsat/nlsat_simplify.h" #include "nlsat/nlsat_simple_checker.h" #include "nlsat/nlsat_variable_ordering_strategy.h" -#include "nlsat/nlsat_symmetry_checker.h" #define NLSAT_EXTRA_VERBOSE @@ -226,7 +225,6 @@ namespace nlsat { //#linxi begin bool m_linxi_simple_check; unsigned m_linxi_variable_ordering_strategy; - bool m_linxi_symmetry_check; bool m_linxi_set_0_more; bool m_cell_sample; //#linxi end @@ -302,7 +300,6 @@ namespace nlsat { //#linxi begin m_linxi_simple_check = p.linxi_simple_check(); m_linxi_variable_ordering_strategy = p.linxi_variable_ordering_strategy(); - m_linxi_symmetry_check = p.linxi_symmetry_check(); //#linxi end @@ -1836,41 +1833,7 @@ namespace nlsat { //#linxi end Variable Ordering Strategy -//#linxi begin symmetry check - void symmetry_check() { - unsigned arith_num = m_is_int.size(); - if (arith_num > 10000) - return ; - Symmetry_Checker checker(m_pm, m_qm, m_clauses, m_atoms, m_is_int, arith_num); - for (var x = 0; x < arith_num; ++x) { - for (var y = x + 1; y < arith_num; ++y) { - if (checker.check_symmetry(x, y)) { - TRACE("linxi_symmetry_checker", - tout << "symmetry: " << x << ", " << y << "\n"; - ); - - rational zero(0); - vector as; - vector xs; - as.push_back(rational(1)); - xs.push_back(x); - as.push_back(rational(-1)); - xs.push_back(y); - polynomial_ref pr(m_pm); - pr = m_pm.mk_linear(2, as.data(), xs.data(), zero); - poly* p = pr.get(); - bool is_even = false; - literal lit = ~mk_ineq_literal(atom::GT, 1, &p, &is_even); - clause *cla = mk_clause(1, &lit, true, nullptr); - } - } - } - TRACE("linxi_symmetry_checker", - display(tout); - ); - - } -//#linxi end symmetry check + void apply_reorder() { m_reordered = false; if (!can_reorder()) @@ -1887,13 +1850,6 @@ namespace nlsat { lbool check() { -//#linxi begin symmetry check - if (m_linxi_symmetry_check) { - symmetry_check(); - } - // exit(0); -//#linxi end symmetry check - //#linxi begin simple check if (m_linxi_simple_check) { if (!simple_check()) { diff --git a/src/nlsat/nlsat_symmetry_checker.cpp b/src/nlsat/nlsat_symmetry_checker.cpp deleted file mode 100644 index 8d0ee87663d..00000000000 --- a/src/nlsat/nlsat_symmetry_checker.cpp +++ /dev/null @@ -1,356 +0,0 @@ -#include "nlsat/nlsat_symmetry_checker.h" - -struct Debug_Tracer { - std::string tag_str; - Debug_Tracer(std::string _tag_str) { - tag_str = _tag_str; - TRACE("linxi_symmetry_checker", - tout << "Debug_Tracer begin\n"; - tout << tag_str << "\n"; - ); - } - ~Debug_Tracer() { - TRACE("linxi_symmetry_checker", - tout << "Debug_Tracer end\n"; - tout << tag_str << "\n"; - ); - } -}; - -// #define _LINXI_DEBUG - -#ifdef _LINXI_DEBUG -#define LINXI_DEBUG_CORE(x) std::stringstream DEBUG_ss_##x; DEBUG_ss_##x << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__; Debug_Tracer DEBUG_dt_##x(DEBUG_ss_##x.str()); -#define LINXI_DEBUG_TRANS(x) LINXI_DEBUG_CORE(x); -#define LINXI_DEBUG LINXI_DEBUG_TRANS(__LINE__); -#define LINXI_HERE TRACE("linxi_symmetry_checker", tout << "here\n";); -#else -#define LINXI_DEBUG { }((void) 0 ); -#define LINXI_HERE { }((void) 0 ); -#endif - - - -namespace nlsat { - struct Symmetry_Checker::imp { - // solver / - pmanager ± - unsynch_mpq_manager &qm; - const clause_vector &clauses; - const atom_vector &atoms; - const bool_vector &is_int; - const unsigned arith_var_num; - // vector vars_hash; - vector> vars_occur; - bool is_check; - var tx, ty; - struct Variable_Information { - unsigned long long hash_value; - unsigned deg; - var x; - bool is_int; - bool operator< (const Variable_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Variable_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (deg != rhs.deg) - return false; - if (x != rhs.x) - return false; - if (is_int != rhs.is_int) - return false; - return true; - } - bool operator!= (const Variable_Information &rhs) const { - return !(*this == rhs); - } - }; - unsigned long long VAR_BASE = 601; - void collect_var_info(Variable_Information &var_info, var x, unsigned deg) { - LINXI_DEBUG; - if (is_check) { - if (x == tx) { - x = ty; - } - else if (x == ty) { - x = tx; - } - else { - // do nothing - } - } - else { - vars_occur[x].push_back(deg); - } - var_info.deg = deg; - var_info.x = x; - var_info.is_int = is_int[x]; - var_info.hash_value = 0; - for (unsigned i = 0; i < deg; ++i) { - var_info.hash_value = var_info.hash_value*VAR_BASE + (unsigned long long)x; - } - var_info.hash_value = var_info.hash_value*VAR_BASE + (unsigned long long)var_info.is_int; - } - struct Monomial_Information { - unsigned long long hash_value; - unsigned long long coef; - vector vars_info; - bool operator< (const Monomial_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Monomial_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (coef != rhs.coef) - return false; - if (vars_info.size() != rhs.vars_info.size()) - return false; - for (unsigned i = 0, sz = vars_info.size(); i < sz; ++i) { - if (vars_info[i] != rhs.vars_info[i]) - return false; - } - return true; - } - bool operator!= (const Monomial_Information &rhs) const { - return !(*this == rhs); - } - }; - unsigned long long MONO_BASE = 99991; - - void collect_mono_info(Monomial_Information &mono_info, monomial *m, unsigned long long coef) { - LINXI_DEBUG; - unsigned sz = pm.size(m); - mono_info.vars_info.resize(sz); - for (unsigned i = 0; i < sz; ++i) { - collect_var_info(mono_info.vars_info[i], pm.get_var(m, i), pm.degree(m, i)); - } - mono_info.coef = coef; - mono_info.hash_value = coef; - std::sort(mono_info.vars_info.begin(), mono_info.vars_info.end()); - for (unsigned i = 0; i < sz; ++i) { - mono_info.hash_value = mono_info.hash_value*MONO_BASE + mono_info.vars_info[i].hash_value; - } - } - struct Polynomial_Information { - unsigned long long hash_value; - bool is_even; - vector monos_info; - bool operator< (const Polynomial_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Polynomial_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (is_even != rhs.is_even) - return false; - if (monos_info.size() != rhs.monos_info.size()) - return false; - for (unsigned i = 0, sz = monos_info.size(); i < sz; ++i) { - if (monos_info[i] != rhs.monos_info[i]) - return false; - } - return true; - } - bool operator!= (const Polynomial_Information &rhs) const { - return !(*this == rhs); - } - }; - unsigned long long POLY_BASE = 99991; - void collect_poly_info(Polynomial_Information &poly_info, poly *p, bool is_even) { - LINXI_DEBUG; - unsigned sz = pm.size(p); - poly_info.monos_info.resize(sz); - for (unsigned i = 0; i < sz; ++i) { - collect_mono_info(poly_info.monos_info[i], pm.get_monomial(p, i), qm.get_uint64(pm.coeff(p, i))); - } - poly_info.hash_value = 0; - std::sort(poly_info.monos_info.begin(), poly_info.monos_info.end()); - for (unsigned i = 0; i < sz; ++i) { - poly_info.hash_value = poly_info.hash_value*POLY_BASE + poly_info.monos_info[i].hash_value; - } - poly_info.is_even = is_even; - if (is_even) { - poly_info.hash_value = poly_info.hash_value*poly_info.hash_value; - } - } - struct Atom_Information { - unsigned long long hash_value; - atom::kind akd; - vector polys_info; - bool operator< (const Atom_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Atom_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (akd != rhs.akd) - return false; - if (polys_info.size() != rhs.polys_info.size()) - return false; - for (unsigned i = 0, sz = polys_info.size(); i < sz; ++i) { - if (polys_info[i] != rhs.polys_info[i]) - return false; - } - return true; - } - bool operator!= (const Atom_Information &rhs) const { - return !(*this == rhs); - } - }; - unsigned long long ATOM_BASE = 233; - void collect_atom_info(Atom_Information &atom_info, ineq_atom *iat) { - LINXI_DEBUG; - unsigned sz = iat->size(); - atom_info.polys_info.resize(sz); - for (unsigned i = 0; i < sz; ++i) { - collect_poly_info(atom_info.polys_info[i], iat->p(i), iat->is_even(i)); - } - atom_info.hash_value = 0; - std::sort(atom_info.polys_info.begin(), atom_info.polys_info.end()); - for (unsigned i = 0; i < sz; ++i) { - atom_info.hash_value = atom_info.hash_value*ATOM_BASE + atom_info.polys_info[i].hash_value; - } - atom_info.akd = iat->get_kind(); - atom_info.hash_value = atom_info.hash_value*ATOM_BASE + (unsigned long long)atom_info.akd; - } - struct Literal_Information { - unsigned long long hash_value; - vector atom_info; // not atoms - bool operator< (const Literal_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Literal_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (atom_info.size() != rhs.atom_info.size()) - return false; - for (unsigned i = 0, sz = atom_info.size(); i < sz; ++i) { - if (atom_info[i] != rhs.atom_info[i]) - return false; - } - return true; - } - bool operator!= (const Literal_Information &rhs) const { - return !(*this == rhs); - } - }; - void collect_lit_info(Literal_Information &lit_info, literal lit) { - LINXI_DEBUG; - atom *at = atoms[lit.var()]; - if (at == nullptr || !at->is_ineq_atom()) { - lit_info.hash_value = lit.to_uint(); - } - else { - lit_info.atom_info.resize(1); - collect_atom_info(lit_info.atom_info[0], to_ineq_atom(at)); - lit_info.hash_value = lit_info.atom_info[0].hash_value; - } - } - struct Clause_Information { - unsigned long long hash_value; - vector lits_info; - bool operator< (const Clause_Information &rhs) const { - return hash_value < rhs.hash_value; - } - bool operator== (const Clause_Information &rhs) const { - if (hash_value != rhs.hash_value) - return false; - if (lits_info.size() != rhs.lits_info.size()) - return false; - for (unsigned i = 0, sz = lits_info.size(); i < sz; ++i) { - if (lits_info[i] != rhs.lits_info[i]) - return false; - } - return true; - } - bool operator!= (const Clause_Information &rhs) const { - return !(*this == rhs); - } - }; - unsigned long long CLA_BASE = 9973; - void collect_cla_info(Clause_Information &cla_info, clause *cla) { - LINXI_DEBUG; - unsigned sz = cla->size(); - cla_info.lits_info.resize(sz); - for (unsigned i = 0; i < sz; ++i) { - literal lit = (*cla)[i]; - collect_lit_info(cla_info.lits_info[i], lit); - } - cla_info.hash_value = 0; - std::sort(cla_info.lits_info.begin(), cla_info.lits_info.end()); - for (unsigned i = 0; i < sz; ++i) { - cla_info.hash_value = cla_info.hash_value*CLA_BASE + cla_info.lits_info[i].hash_value; - } - } - - void collect_clas_info(vector &clas_info) { - LINXI_DEBUG; - unsigned sz = clauses.size(); - clas_info.resize(sz); - for (unsigned i = 0; i < sz; ++i) { - collect_cla_info(clas_info[i], clauses[i]); - } - std::sort(clas_info.begin(), clas_info.end()); - if (!is_check) { - for (unsigned i = 0; i < arith_var_num; ++i) { - std::sort(vars_occur[i].begin(), vars_occur[i].begin()); - } - } - } - vector ori_clas_info; - imp(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num) : - // sol(_sol), - pm(_pm), - qm(_qm), - clauses(_clauses), - atoms(_atoms), - is_int(_is_int), - arith_var_num(_arith_var_num), - is_check(false) { - vars_occur.resize(arith_var_num); - collect_clas_info(ori_clas_info); - // vars_hash.resize(arith_var_num, 0); - } - vector check_clas_info; - bool check_occur_same(var x, var y) { - if (vars_occur[x].size() != vars_occur[y].size()) - return false; - for (unsigned i = 0, sz = vars_occur[x].size(); i < sz; ++i) { - if (vars_occur[x][i] != vars_occur[y][i]) - return false; - } - return true; - } - bool check_symmetry(var x, var y) { - if (!check_occur_same(x, y)) { - return false; - } - is_check = true; - tx = x, ty = y; - collect_clas_info(check_clas_info); - for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { - if (ori_clas_info[i] != check_clas_info[i]) - return false; - } - return true; - } - }; - Symmetry_Checker::Symmetry_Checker(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num) { - LINXI_DEBUG; - // m_imp = alloc(imp, _sol, _pm, _am, _clauses, _learned, _atoms, _arith_var_num); - m_imp = alloc(imp, _pm, _qm, _clauses, _atoms, _is_int, _arith_var_num); - } - Symmetry_Checker::~Symmetry_Checker() { - LINXI_DEBUG; - dealloc(m_imp); - } - // bool Symmetry_Checker::operator()() { - // LINXI_DEBUG; - - // } - bool Symmetry_Checker::check_symmetry(var x, var y) { - return m_imp->check_symmetry(x, y); - } -} \ No newline at end of file diff --git a/src/nlsat/nlsat_symmetry_checker.h b/src/nlsat/nlsat_symmetry_checker.h deleted file mode 100644 index 1db5cb24be7..00000000000 --- a/src/nlsat/nlsat_symmetry_checker.h +++ /dev/null @@ -1,13 +0,0 @@ -#include "nlsat/nlsat_clause.h" - -namespace nlsat { - class Symmetry_Checker { - struct imp; - imp * m_imp; - public: - // Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num); - Symmetry_Checker(pmanager &_pm, unsynch_mpq_manager &_qm, const clause_vector &_clauses, const atom_vector &_atoms, const bool_vector &_is_int, const unsigned &_arith_var_num); - ~Symmetry_Checker(); - bool check_symmetry(var x, var y); - }; -} \ No newline at end of file From 752c999e0a3ef95936986d20f9f84ef64c8bcd3b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 9 Aug 2024 08:16:35 -1000 Subject: [PATCH 145/414] cleanup Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_params.pyg | 5 +- src/nlsat/nlsat_simple_checker.cpp | 564 ++---------------- src/nlsat/nlsat_solver.cpp | 59 +- .../nlsat_variable_ordering_strategy.cpp | 2 +- src/tactic/smtlogics/qfnra_tactic.cpp | 86 ++- 5 files changed, 95 insertions(+), 621 deletions(-) diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 00d17990133..01229a527bc 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -3,8 +3,8 @@ def_module_params('nlsat', description='nonlinear solver', export=True, params=(max_memory_param(), - ('linxi_simple_check', BOOL, False, "linxi precheck about variables sign"), - ('linxi_variable_ordering_strategy', UINT, 0, "linxi Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"), + ('simple_check', BOOL, False, "precheck polynomials using variables sign"), + ('variable_ordering_strategy', UINT, 0, "Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"), ('cell_sample', BOOL, True, "cell sample projection"), ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), @@ -19,4 +19,3 @@ def_module_params('nlsat', ('seed', UINT, 0, "random seed."), ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") )) - diff --git a/src/nlsat/nlsat_simple_checker.cpp b/src/nlsat/nlsat_simple_checker.cpp index f4bb5e64823..3e1e8dd1c36 100644 --- a/src/nlsat/nlsat_simple_checker.cpp +++ b/src/nlsat/nlsat_simple_checker.cpp @@ -4,33 +4,19 @@ struct Debug_Tracer { std::string tag_str; Debug_Tracer(std::string _tag_str) { tag_str = _tag_str; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "Debug_Tracer begin\n"; tout << tag_str << "\n"; ); } ~Debug_Tracer() { - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "Debug_Tracer end\n"; tout << tag_str << "\n"; ); } }; -// #define _LINXI_DEBUG - -#ifdef _LINXI_DEBUG -// #define LINXI_DEBUG std::stringstream DEBUG_ss; DEBUG_ss << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__; Debug_Tracer DEBUG_dt(DEBUG_ss.str()); -// #define LINXI_HERE TRACE("linxi_simple_checker", tout << "here\n";); -#define LINXI_DEBUG { }((void) 0 ); -#define LINXI_HERE { }((void) 0 ); - -#else -#define LINXI_DEBUG { }((void) 0 ); -#define LINXI_HERE { }((void) 0 ); -#endif - - namespace nlsat { struct Simple_Checker::imp { @@ -277,7 +263,6 @@ namespace nlsat { bool improved; enum special_ineq_kind {UNK = 0, AXBC, AXBSC, NK}; // None Kind vector> literal_special_kind; - // imp(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num) : imp(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) : // sol(_sol), pm(_pm), @@ -304,7 +289,7 @@ namespace nlsat { return EQ; } bool update_interval_intersection(Domain_Interval &ia, const Domain_Interval &ib) { - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "ia: "; display(tout, am, ia); tout << "\nib: "; @@ -326,70 +311,8 @@ namespace nlsat { return false; - // if (ia.m_lower_inf == 0 && ib.m_upper_inf == 0) { - // if (ia.m_lower > ib.m_upper) - // return false; - // if (ia.m_lower == ib.m_upper && (ia.m_lower_open || ib.m_upper_open)) - // return false; - // } - // if (ib.m_lower_inf == 0 && ia.m_upper_inf == 0) { - // if (ib.m_lower > ia.m_upper) - // return false; - // if (ib.m_lower == ia.m_upper && (ib.m_lower_open || ia.m_upper_open)) - // return false; - // } - // if (ia.m_lower_inf && ib.m_lower_inf) { - // // do nothing - // } - // else { - // if (ia.m_lower_inf) { - // ia.m_lower_inf = 0; - // ia.m_lower_open = ib.m_lower_open; - // am.set(ia.m_lower, ib.m_lower); - // } - // else if (ib.m_lower_inf) { - // // do nothing - // } - // else { - // if (ia.m_lower == ib.m_lower) { - // ia.m_lower_open = (ia.m_lower_open | ib.m_lower_open); - // } - // else if (ia.m_lower < ib.m_lower) { - // ia.m_lower_open = ib.m_lower_open; - // am.set(ia.m_lower, ib.m_lower); - // } - // else { - // // do nothing - // } - // } - // } - // if (ia.m_upper_inf && ib.m_upper_inf) { - // // do nothing - // } - // else { - // if (ia.m_upper_inf) { - // ia.m_upper_inf = 0; - // ia.m_upper_open = ib.m_upper_open; - // am.set(ia.m_upper, ib.m_upper); - // } - // else if (ib.m_upper_inf) { - // // do nothing - // } - // else { - // if (ia.m_upper == ib.m_upper) { - // ia.m_upper_open = (ia.m_upper_open | ib.m_upper_open); - // } - // else if (ia.m_upper < ib.m_upper) { - // // do nothing - // } - // else { - // ia.m_upper_open = ib.m_upper_open; - // am.set(ia.m_upper, ib.m_upper); - // } - // } - // } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "after update: "; display(tout, am, ia); tout << "\n"; @@ -401,7 +324,7 @@ namespace nlsat { return update_interval_intersection(vd.ori_val, di); } bool update_var_mag_domain_interval_by_ori(Var_Domain &vd, const Domain_Interval &di) { - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "vd mag val: "; display(tout, am, vd.mag_val); tout << "\n"; @@ -410,8 +333,7 @@ namespace nlsat { tout << "\n"; ); Domain_Interval mag_di(am, 0, 0, 1, 1); - // am.set(mag_di.m_lower.m_val, 0); - + if (di.m_lower.m_inf) { mag_di.m_upper.m_inf = 1; mag_di.m_upper.m_open = 1; @@ -477,7 +399,7 @@ namespace nlsat { } } } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "mag di: "; display(tout, am, mag_di); tout << "\n"; @@ -485,7 +407,6 @@ namespace nlsat { return update_interval_intersection(vd.mag_val, mag_di); } void calc_formula(scoped_anum &num, const scoped_anum &a, unsigned b, const scoped_anum &c) { - LINXI_DEBUG; scoped_anum frac(am); am.div(c, a, frac); am.neg(frac); @@ -495,11 +416,10 @@ namespace nlsat { am.set(num, frac); } bool process_odd_degree_formula(Var_Domain &vd, sign_kind nsk, const scoped_anum &a, unsigned b, const scoped_anum &c) { - LINXI_DEBUG; Domain_Interval now_di(am); scoped_anum num(am); calc_formula(num, a, b, c); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "nsk: "; display(tout, nsk); tout << '\n'; @@ -537,7 +457,7 @@ namespace nlsat { else { UNREACHABLE(); } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "now_di: "; display(tout, am, now_di); tout << "\n"; @@ -548,29 +468,8 @@ namespace nlsat { return false; return true; } -/* -if (nsk == EQ) { -return false; -} -else if (nsk == LT) { -return false; -} -else if (nsk == GT) { -return true; -} -else if (nsk == LE) { -return false; -} -else if (nsk == GE) { -return true; -} -else { -UNREACHABLE(); -} -*/ - bool process_even_degree_formula(Var_Domain &vd, sign_kind nsk, const scoped_anum &a, unsigned b, const scoped_anum &c) { - LINXI_DEBUG; + bool process_even_degree_formula(Var_Domain &vd, sign_kind nsk, const scoped_anum &a, unsigned b, const scoped_anum &c) { scoped_anum num(am), frac(am); am.div(c, a, frac); am.neg(frac); @@ -623,8 +522,6 @@ UNREACHABLE(); // di.m_upper_inf = 0; // am.set(di.m_lower, num); // am.set(di.m_upper, num); - // if (!update_interval_intersection(vd.ori_val, di)) - // return false; if (!update_interval_intersection(vd.mag_val, di)) return false; } @@ -692,7 +589,6 @@ UNREACHABLE(); } bool update_var_domain(sign_kind nsk, const scoped_anum &a, var x, unsigned b, const scoped_anum &c) { - LINXI_DEBUG; Var_Domain &vd = vars_domain[x]; if (am.is_neg(a)) { if (nsk == LT) @@ -722,22 +618,12 @@ UNREACHABLE(); bool check_is_axbc(const poly *p, scoped_anum &a, var &x, unsigned &b, scoped_anum& c) { // is a*(x^b) + c*1 form - // LINXI_DEBUG; - // LINXI_HERE; - // TRACE("linxi_simple_checker", - // tout << a << "x[" << x << "]^" << b << " "; - // tout << "+ " << c << " "; - // // display(tout, nsk); - // // tout << " 0\n"; - // ); if (pm.size(p) == 1 && pm.is_var(pm.get_monomial(p, 0), x)) { - LINXI_HERE; am.set(a, 1); b = 1; am.set(c, 0); return true; } - // LINXI_HERE; if (pm.size(p) != 2) return false; if (!pm.is_unit(pm.get_monomial(p, 1))) @@ -747,7 +633,7 @@ UNREACHABLE(); return false; x = pm.get_var(m, 0); b = pm.degree(m, 0); - // LINXI_HERE; + am.set(a, pm.coeff(p, 0)); am.set(c, pm.coeff(p, 1)); return true; @@ -755,7 +641,6 @@ UNREACHABLE(); bool collect_domain_axbc_form(unsigned cid, unsigned lid) { // is_var_num, a*(x^b) + c form - LINXI_DEBUG; literal lit = (*clauses[cid])[lid]; bool s = lit.sign(); ineq_atom *ia = to_ineq_atom(atoms[lit.var()]); @@ -786,14 +671,14 @@ UNREACHABLE(); else if (nsk == GT) nsk = LE; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << a << "x[" << x << "]^" << b << " + " << c << " "; display(tout, nsk); tout << " 0 \n"; ); if (!update_var_domain(nsk, a, x, b, c)) return false; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "original: "; display(tout, am, vars_domain[x].ori_val); tout << "\nmagnitude: "; @@ -804,7 +689,6 @@ UNREACHABLE(); } bool check_is_axbsc(const poly *p, vector &as, vector &xs, vector &bs, scoped_anum& c, unsigned &cnt) { // [a*(x^b)] + ... + [a*(x^b)] + c form - LINXI_DEBUG; unsigned psz = pm.size(p); am.set(c, 0); for (unsigned i = 0; i < psz; ++i) { @@ -812,23 +696,9 @@ UNREACHABLE(); if (pm.size(m) > 1) return false; } - LINXI_HERE; cnt = 0; for (unsigned i = 0; i < psz; ++i) { monomial *m = pm.get_monomial(p, i); - // TRACE("linxi_simple_checker", - // tout << "monomial: "; - // pm.display(tout, m); - // tout << '\n'; - // // tout << "coefficient: " << pm.coeff(p, i) << "\n"; - // tout << "m size: " << pm.size(m) << '\n'; - // tout << "# "; - // for (unsigned j = 0, sz = pm.size(m); j < sz; ++j) { - // var v = pm.get_var(m, j); - // tout << " (" << j << ", " << pm.degree_of(m, v) << ")"; - // } - // tout << "\n"; - // ); if (pm.size(m) == 0) { am.set(c, pm.coeff(p, i)); } @@ -837,9 +707,6 @@ UNREACHABLE(); am.set(as[cnt++], pm.coeff(p, i)); xs.push_back(pm.get_var(m, 0)); bs.push_back(pm.degree(m, 0)); - // TRACE("linxi_simple_checker", - // tout << as.back() << "x[" << xs.back() << "]^" << bs.back() << "\n"; - // ); } } return true; @@ -875,9 +742,6 @@ UNREACHABLE(); else if (am.is_pos(vd.ori_val.m_lower.m_val)) { ret = GT; } - // else { - // ret = NONE; - // } if (am.is_zero(vd.ori_val.m_upper.m_val)) { if (vd.ori_val.m_upper.m_open) ret = LT; @@ -886,10 +750,7 @@ UNREACHABLE(); } else if (am.is_neg(vd.ori_val.m_upper.m_val)) { ret = LT; - } - // else { - // ret = NONE; - // } + } } else if (!vd.ori_val.m_lower.m_inf) { if (am.is_pos(vd.ori_val.m_lower.m_val)) { @@ -934,7 +795,7 @@ UNREACHABLE(); unsigned sz = as.size(); for (unsigned i = 1; i < sz; ++i) { sign_kind now = get_axb_sign(as[i], xs[i], bs[i]); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "sta: "; display(tout, sta); tout << "\n"; @@ -959,43 +820,14 @@ UNREACHABLE(); if (sta != now) sta = GT; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "after merge\n"; tout << "sta: "; display(tout, sta); tout << "\n"; ); } - // if (am.is_zero(c)) { - // // sta = sta; - // } - // else if (am.is_neg(c)) { - // if (sta == EQ) - // sta = LT; - // // else if (sta == LT) - // // sta = LT; - // else if (sta == LE) - // sta = LT; - // else if (sta == GT) - // sta = NONE; - // else if (sta == GE) - // sta = NONE; - // } - // else { // a > 0 - // if (sta == EQ) - // sta = GT; - // else if (sta == LT) - // sta = NONE; - // else if (sta == LE) - // sta = NONE; - // // else if (sta == GT) - // // sta = GT; - // else if (sta == GE) - // sta = GT; - // } - // if (sta == NONE) - // return false; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "sta: "; display(tout, sta); tout << "\n"; @@ -1016,7 +848,6 @@ else if (am.is_zero(c)) { // ( == 0) + (c == 0) -> == 0 else { // ( == 0) + (c > 0) -> > 0 } - */ if (sta == EQ) { if (am.is_neg(c)) { // ( == 0) + (c < 0) -> < 0 @@ -1158,7 +989,6 @@ else { // ( == 0) + (c > 0) -> > 0 return false; } bool collect_domain_sign_ineq_consistent_form(sign_kind nsk, vector &as, vector &xs, vector &bs, scoped_anum& c) { - LINXI_DEBUG; for (unsigned i = 0, sz = as.size(); i < sz; ++i) { if (!update_var_domain(nsk, as[i], xs[i], bs[i], c)) return false; @@ -1166,9 +996,8 @@ else { // ( == 0) + (c > 0) -> > 0 return true; } bool process_axbsc_form(sign_kind nsk, unsigned cid, vector &as, vector &xs, vector &bs, scoped_anum& c) { - LINXI_DEBUG; bool is_conflict(false); - TRACE("linxi_simple_checker", + TRACE("simple_checker", for (unsigned i = 0, sz = as.size(); i < sz; ++i) { if (i > 0) tout << "+ "; @@ -1180,7 +1009,7 @@ else { // ( == 0) + (c > 0) -> > 0 ); if (!check_is_sign_ineq_consistent(nsk, as, xs, bs, c, is_conflict)) return true; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "is conflict: " << is_conflict << "\n" ); if (is_conflict) @@ -1191,7 +1020,6 @@ else { // ( == 0) + (c > 0) -> > 0 return true; } bool collect_domain_axbsc_form(unsigned cid, unsigned lid) { - LINXI_DEBUG; // [a*(x^k)] + ... + [a*(x^b)] + k form literal lit = (*clauses[cid])[lid]; bool s = lit.sign(); @@ -1216,7 +1044,7 @@ else { // ( == 0) + (c > 0) -> > 0 return true; } literal_special_kind[cid][lid] = AXBSC; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "as size: " << as.size() << '\n'; ); while (as.size() > cnt) @@ -1231,7 +1059,7 @@ else { // ( == 0) + (c > 0) -> > 0 else if (nsk == GT) nsk = LE; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "ineq atom: " << '\n'; for (unsigned i = 0, sz = iat->size(); i < sz; ++i) { pm.display(tout, iat->p(i)); @@ -1254,16 +1082,8 @@ else { // ( == 0) + (c > 0) -> > 0 return false; return true; } - // bool update_all_mag_domain_by_ori() { - // LINXI_HERE; - // for (unsigned i = 0; i < arith_var_num; ++i) { - // if (!update_var_mag_domain_interval_by_ori(vars_domain[i], vars_domain[i].ori_val)) - // return false; - // } - // return true; - // } + bool collect_var_domain() { - LINXI_DEBUG; // vector vec_id; for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { if (clauses_visited[i].visited) @@ -1286,9 +1106,7 @@ else { // ( == 0) + (c > 0) -> > 0 if (!collect_domain_axbc_form(i, 0)) return false; } - // if (!update_all_mag_domain_by_ori()) - // return false; - TRACE("linxi_simple_checker", + TRACE("simple_checker", for (unsigned i = 0; i < arith_var_num; ++i) { tout << "====== arith[" << i << "] ======\n"; tout << "original value: "; @@ -1299,16 +1117,11 @@ else { // ( == 0) + (c > 0) -> > 0 tout << "====== arith[" << i << "] ======\n"; } ); - // TRACE("linxi_simple_checker", - // tout << "vec_id.size(): " << vec_id.size() << "\n"; - // ); for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { // unsigned id = vec_id[i]; if (!collect_domain_axbsc_form(i, 0)) return false; } - // if (!update_all_mag_domain_by_ori()) - // return false; return true; } void endpoint_multiply(const Endpoint &a, const Endpoint &b, Endpoint &c) { @@ -1341,7 +1154,7 @@ else { // ( == 0) + (c > 0) -> > 0 } } void merge_mul_domain(Domain_Interval &pre, const Domain_Interval &now) { - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "dom: "; display(tout, am, pre); tout << "\n"; @@ -1365,241 +1178,10 @@ else { // ( == 0) + (c > 0) -> > 0 pre.m_lower.m_is_lower = 1; pre.m_upper.copy(*pmx); pre.m_upper.m_is_lower = 0; - - // if (pre.m_lower_inf && pre.m_upper_inf) { - // if ((!now.m_lower_inf && am.is_zero(now.m_lower)) && - // (!now.m_upper_inf && am.is_zero(now.m_upper))) { - - // } - // else { - // return ; - // } - // } - // if ((!pre.m_lower_inf && am.is_zero(pre.m_lower)) && - // (!pre.m_upper_inf && am.is_zero(pre.m_upper))) - // return ; - // if (now.m_lower_inf && now.m_upper_inf) { - // pre.m_lower_inf = 1; - // pre.m_upper_inf = 1; - // return ; - // } - // if (pre.m_lower_inf == 0 && !am.is_neg(pre.m_lower)) { - // // {+, +/inf} - // if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { - // // {+, +/inf} * {+, +/inf} - // // {a, b} * {c, d} -> {ac, bd/inf} - // pre.m_lower_open = (pre.m_lower_open | now.m_lower_open); - // am.set(pre.m_lower, now.m_lower * pre.m_lower); - - // pre.m_upper_inf = (pre.m_upper_inf | now.m_upper_inf); - // if (pre.m_upper_inf == 0) { - // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); - // am.set(pre.m_upper, now.m_upper * pre.m_upper); - // } - // } - // else if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { - // // {+, +/inf} * {-/inf, -} - // // {a, b} * {c, d} -> {bc, ad} - // Domain_Interval tmp_di(am, false); - // tmp_di.m_lower_inf = (pre.m_upper_inf | now.m_lower_inf); - // if (tmp_di.m_lower_inf == 0) { - // tmp_di.m_lower_open = (pre.m_upper_open | now.m_lower_open); - // am.set(tmp_di.m_lower, pre.m_upper * now.m_lower); - // } - - // tmp_di.m_upper_inf = 0; - // tmp_di.m_upper_open = (pre.m_lower_open | now.m_upper_open); - // am.set(tmp_di.m_upper, pre.m_lower * now.m_upper); - - // pre.copy(tmp_di); - // } - // else { - // // {+, +/inf} * {-/inf, +/inf} - // if (pre.m_upper_inf) { - // // {+, +inf) * {-/inf, +/inf} -> (-inf, +inf) - // pre.m_lower_inf = 1; - // } - // else { - // // {+, +} * {-/inf, +/inf} - // // {a, b} * {c, d} -> {bc, bd} - // // order matters! - // pre.m_lower_inf = now.m_lower_inf; - // if (pre.m_lower_inf == 0) { - // pre.m_lower_open = (pre.m_upper_open | now.m_lower_open); - // am.set(pre.m_lower, now.m_lower * pre.m_upper); - // } - // pre.m_upper_inf = now.m_upper_inf; - // if (pre.m_upper_inf == 0) { - // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); - // am.set(pre.m_upper, now.m_upper * pre.m_upper); - // } - // } - // } - // } - // else if (pre.m_upper_inf == 0 && !am.is_pos(pre.m_upper)) { - // LINXI_HERE; - // // {-/inf, -} - // if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { - // LINXI_HERE; - // // {-/inf, -} * {-/inf, -} - // if (pre.m_lower_inf || now.m_lower_inf) { - // // (-inf, b} * {c, d} -> {bd, +inf) - // // {a, b} * (-inf, d} -> {bd, +inf) - // pre.m_upper_inf = 1; - - // pre.m_lower_open = (pre.m_upper_open | now.m_upper_open); - // am.set(pre.m_lower, now.m_upper * pre.m_upper); - // } - // else { - // // {a, b} * {c, d} -> {bd, ac} - // Domain_Interval tmp_di(am, false); - // tmp_di.m_lower_inf = 0; - // tmp_di.m_upper_inf = 0; - // tmp_di.m_lower_open = (pre.m_upper_open | now.m_upper_open); - // tmp_di.m_upper_open = (pre.m_lower_open | now.m_lower_open); - // am.set(tmp_di.m_lower, pre.m_upper * now.m_upper); - // am.set(tmp_di.m_upper, pre.m_lower * now.m_lower); - // pre.copy(tmp_di); - // } - // } - // else if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { - // LINXI_HERE; - // // {-/inf, -} * {+, +/inf} - // // {a, b} * {c, d} -> {ad, bc} - // Domain_Interval tmp_di(am, false); - // tmp_di.m_lower_inf = (pre.m_lower_inf | now.m_upper_inf); - // if (tmp_di.m_lower_inf == 0) { - // tmp_di.m_lower_open = (pre.m_lower_open | now.m_upper_open); - // am.set(tmp_di.m_lower, pre.m_lower * now.m_upper); - // } - - // tmp_di.m_upper_inf = 0; - // tmp_di.m_upper_open = (pre.m_upper_open | now.m_lower_open); - // am.set(tmp_di.m_upper, pre.m_upper * now.m_lower); - // TRACE("linxi_simple_checker", - // tout << "tmp_di: "; - // display(tout, am, tmp_di); - // tout << "\n"; - // ); - // pre.copy(tmp_di); - // } - // else { - // LINXI_HERE; - // // {-/inf, -} * {-/inf, +/inf} - // if (pre.m_lower_inf) { - // // (-inf, -} * {-/inf, +/inf} -> (-inf, +inf) - // pre.m_upper_inf = 1; - // } - // else { - // // {-, -} * {-/inf, +/inf} - // // {pl, pu} * {nl, nu} -> {pl nu, pl nl} - // // order matters! - // pre.m_lower_inf = now.m_upper_inf; - // if (pre.m_lower_inf == 0) { - // pre.m_lower_open = (pre.m_lower_open | now.m_upper_open); - // am.set(pre.m_lower, now.m_upper * pre.m_lower); - // } - // pre.m_upper_inf = now.m_lower_inf; - // if (pre.m_upper_inf == 0) { - // pre.m_upper_open = (pre.m_lower_open | now.m_lower_open); - // am.set(pre.m_upper, now.m_lower * pre.m_lower); - // } - // } - // } - // } - // else { - // // {-/inf, +/inf} - // if (now.m_lower_inf == 0 && !am.is_neg(now.m_lower)) { - // // {-/inf, +/inf} * {+, +/inf} - // if (now.m_upper_inf) { - // // {-/inf, +/inf} * {+, +inf) -> (-inf, +inf) - // pre.m_lower_inf = 1; - // pre.m_upper_inf = 1; - // } - // else { - // // {a, b} * {c, d} -> {ad, bd} - // // {-/inf, +/inf} * {+, +} - // if (pre.m_lower_inf == 0) { - // pre.m_lower_open = (now.m_upper_open | pre.m_lower_open); - // am.set(pre.m_lower, now.m_upper * pre.m_lower); - // } - // if (pre.m_upper_inf == 0) { - // pre.m_upper_open = (now.m_upper_open | pre.m_upper_open); - // am.set(pre.m_upper, now.m_upper * pre.m_upper); - // } - // } - // } - // else if (now.m_upper_inf == 0 && !am.is_pos(now.m_upper)) { - // // {-/inf, +/inf} * {-/inf, -} - // if (now.m_lower_inf) { - // // {-/inf, +/inf} * (-inf, -} -> (-inf, +inf) - // pre.m_lower_inf = 1; - // pre.m_upper_inf = 1; - // } - // else { - // // {-/inf, +/inf} * {-, -} - // // {pl, pu} * {nl, nu} -> {pu nl, pl nl} - // // order matters! - // if (pre.m_lower_inf == 0) { - // pre.m_lower_open = (pre.m_upper_open | now.m_lower_open); - // am.set(pre.m_lower, now.m_lower * pre.m_upper); - // } - // if (pre.m_upper_inf == 0) { - // pre.m_upper_open = (pre.m_lower_open | now.m_lower_open); - // am.set(pre.m_upper, now.m_lower * pre.m_lower); - // } - // } - // } - // else { - // // {-/inf, +/inf} * {-/inf, +/inf} - // if (pre.m_lower_inf || pre.m_upper_inf || - // now.m_lower_inf || now.m_upper_inf) { - // pre.m_lower_inf = 1; - // pre.m_upper_inf = 1; - // } - // else { - // // {-, +} * {-, +} - // scoped_anum plnl(am), punu(am); - // unsigned plo, puo; - // am.set(plnl, pre.m_lower * now.m_lower); - // am.set(punu, pre.m_upper * now.m_upper); - // scoped_anum plnu(am), punl(am); - // am.set(plnu, pre.m_lower * now.m_upper); - // am.set(punl, pre.m_upper * now.m_lower); - // if (plnl > punu) { - // puo = (pre.m_lower_open | now.m_lower_open); - // am.set(pre.m_upper, plnl); - // } - // else if (plnl == punu) { - // puo = ((pre.m_lower_open | now.m_lower_open) & - // (pre.m_upper_open | now.m_upper_open)); - // am.set(pre.m_upper, plnl); - // } - // else { - // puo = (pre.m_upper_open | now.m_upper_open); - // am.set(pre.m_upper, punu); - // } - // if (plnu < punl) { - // plo = (pre.m_lower_open | now.m_upper_open); - // am.set(pre.m_lower, plnu); - // } - // else if (plnu == punl) { - // plo = ((pre.m_lower_open | now.m_upper_open) & - // (pre.m_upper_open | now.m_lower_open)); - // am.set(pre.m_lower, plnu); - // } - // else { - // plo = (pre.m_upper_open | now.m_lower_open); - // am.set(pre.m_lower, punl); - // } - // } - // } - // } } void get_monomial_domain(monomial *m, const scoped_anum &a, Domain_Interval &dom) { - LINXI_DEBUG; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "monomial: "; pm.display(tout, m); tout << '\n'; @@ -1618,7 +1200,7 @@ else { // ( == 0) + (c > 0) -> > 0 var v = pm.get_var(m, i); unsigned deg = pm.degree_of(m, v); const Domain_Interval &di = ((deg & 1) == 0 ? vars_domain[v].mag_val : vars_domain[v].ori_val); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "dom: "; display(tout, am, dom); tout << "\n"; @@ -1630,7 +1212,7 @@ else { // ( == 0) + (c > 0) -> > 0 for (unsigned j = 0; j < deg; ++j) { merge_mul_domain(dom, di); } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "after merge mul: "; display(tout, am, dom); tout << "\n"; @@ -1647,32 +1229,23 @@ else { // ( == 0) + (c > 0) -> > 0 a.m_open = (a.m_open | b.m_open); } } + void merge_add_domain(Domain_Interval &pre, const Domain_Interval &now) { endpoint_add(pre.m_lower, now.m_lower); endpoint_add(pre.m_upper, now.m_upper); - // pre.m_lower_inf = (pre.m_lower_inf | now.m_lower_inf); - // if (pre.m_lower_inf == 0) { - // am.set(pre.m_lower, pre.m_lower + now.m_lower); - // pre.m_lower_open = (pre.m_lower_open | now.m_lower_open); - // } - // pre.m_upper_inf = (pre.m_upper_inf | now.m_upper_inf); - // if (pre.m_upper_inf == 0) { - // am.set(pre.m_upper, pre.m_upper + now.m_upper); - // pre.m_upper_open = (pre.m_upper_open | now.m_upper_open); - // } } + sign_kind get_poly_sign(const poly *p) { - LINXI_DEBUG; scoped_anum a(am); am.set(a, pm.coeff(p, 0)); Domain_Interval pre(am); get_monomial_domain(pm.get_monomial(p, 0), a, pre); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "poly: "; pm.display(tout, p); tout << "\n"; ); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "pre: "; display(tout, am, pre); tout << "\n"; @@ -1681,7 +1254,7 @@ else { // ( == 0) + (c > 0) -> > 0 am.set(a, pm.coeff(p, i)); Domain_Interval now(am); get_monomial_domain(pm.get_monomial(p, i), a, now); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "pre: "; display(tout, am, pre); tout << "\n"; @@ -1692,7 +1265,7 @@ else { // ( == 0) + (c > 0) -> > 0 if (now.m_lower.m_inf && now.m_upper.m_inf) return NONE; merge_add_domain(pre, now); - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "after merge: "; display(tout, am, pre); tout << "\n"; @@ -1700,8 +1273,6 @@ else { // ( == 0) + (c > 0) -> > 0 if (pre.m_lower.m_inf && pre.m_upper.m_inf) return NONE; } - // if (pre.m_lower_inf && pre.m_upper_inf) - // return NONE; if (pre.m_lower.m_inf) { if (am.is_neg(pre.m_upper.m_val)) { // (-inf, -} @@ -1767,7 +1338,6 @@ else { // ( == 0) + (c > 0) -> > 0 } sign_kind get_poly_sign_degree(const poly *p, bool is_even) { - LINXI_DEBUG; sign_kind ret = get_poly_sign(p); if (is_even) { if (ret == GE || ret == LE || ret == NONE) @@ -1775,7 +1345,7 @@ else { // ( == 0) + (c > 0) -> > 0 else if (ret != EQ) ret = GT; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "ret sign: "; display(tout, ret); tout << "\n"; @@ -1841,8 +1411,7 @@ else { // ( == 0) + (c > 0) -> > 0 } } bool check_ineq_atom_satisfiable(const ineq_atom *iat, bool s) { - LINXI_DEBUG; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "s: " << s << "\n"; tout << "kd: " << iat->get_kind() << "\n"; ); @@ -1855,7 +1424,7 @@ else { // ( == 0) + (c > 0) -> > 0 else nsk = GE; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "ineq atom: " << '\n'; for (unsigned i = 0, sz = iat->size(); i < sz; ++i) { pm.display(tout, iat->p(i)); @@ -1870,25 +1439,14 @@ else { // ( == 0) + (c > 0) -> > 0 for (unsigned i = 1, sz = iat->size(); i < sz; ++i) { sign_kind now = get_poly_sign_degree(iat->p(i), iat->is_even(i)); - // TRACE("linxi_simple_checker", - // tout << "pre: "; - // display(tout, pre); - // tout << ", now: "; - // display(tout, now); - // tout << "\n"; - // ); merge_mul_sign(pre, now); - // TRACE("linxi_simple_checker", - // tout << "==> "; - // display(tout, pre); - // ); if (pre == NONE) return true; if ((nsk == EQ || nsk == GE || nsk == LE) && (pre == EQ || pre == GE || pre == LE)) return true; } - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "pre: "; display(tout, pre); tout << ", nsk: "; @@ -1910,7 +1468,6 @@ else { // ( == 0) + (c > 0) -> > 0 return true; } bool check_literal_satisfiable(unsigned cid, unsigned lit_id) { - LINXI_DEBUG; literal lit = (*clauses[cid])[lit_id]; const var &v = lit.var(); atom *at = atoms[v]; @@ -1922,16 +1479,15 @@ else { // ( == 0) + (c > 0) -> > 0 clauses_visited[cid].visited = true; return true; } - // TRACE("linxi_sign", + // TRACE("sign", // tout << "literal: " << lit << '\n'; // ); bool s = lit.sign(); return check_ineq_atom_satisfiable(to_ineq_atom(at), s); } bool check_clause_satisfiable(unsigned cid) { - LINXI_DEBUG; const clause *cla = clauses[cid]; - TRACE("linxi_simple_checker", + TRACE("simple_checker", tout << "clause size: " << cla->size() << '\n'; ); unsigned sz = cla->size(); @@ -1954,15 +1510,8 @@ else { // ( == 0) + (c > 0) -> > 0 clauses_visited[cid].literal_visited[i] = true; literal lit = (*clauses[cid])[i]; lit.neg(); - // if (atoms[lit.var()] != nullptr && atoms[lit.var()]->is_ineq_atom()) { - // ineq_atom *iat = to_ineq_atom(atoms[lit.var()]); - // if (to_sign_kind(iat->get_kind()) == EQ && lit.sign()) { - // continue; - // } - // } learned_unit.push_back(lit); - // sol.mk_clause(1, &lit); - TRACE("linxi_simple_checker_learned", + TRACE("simple_checker_learned", tout << "making new clauses!\n"; tout << "sign: " << lit.sign() << '\n'; if (atoms[lit.var()] != nullptr && atoms[lit.var()]->is_ineq_atom()) { @@ -1989,7 +1538,6 @@ else { // ( == 0) + (c > 0) -> > 0 return false; } bool check() { - LINXI_DEBUG; for (unsigned i = 0, sz = clauses.size(); i < sz; ++i) { if (clauses_visited[i].visited) continue; @@ -1999,14 +1547,6 @@ else { // ( == 0) + (c > 0) -> > 0 } return true; } - void test_anum() { - scoped_anum x(am), y(am); - am.set(x, 3); - am.set(y, 5); - TRACE("linxi_simple_checker", - tout << x << " " << y << std::endl; - ); - } bool operator()() { improved = true; @@ -2014,7 +1554,7 @@ else { // ( == 0) + (c > 0) -> > 0 improved = false; if (!check()) return false; - TRACE("linxi_simple_checker", + TRACE("simple_checker", for (unsigned i = 0; i < arith_var_num; ++i) { tout << "====== arith[" << i << "] ======\n"; tout << "original value: "; @@ -2026,38 +1566,16 @@ else { // ( == 0) + (c > 0) -> > 0 } ); } - // LINXI_DEBUG; - // // test_anum(); - // if (!collect_var_domain()) - // return false; - // TRACE("linxi_simple_checker", - // for (unsigned i = 0; i < arith_var_num; ++i) { - // tout << "====== arith[" << i << "] ======\n"; - // tout << "original value: "; - // display(tout, am, vars_domain[i].ori_val); - // tout << "\nmagitude value: "; - // display(tout, am, vars_domain[i].mag_val); - // tout << "\n"; - // tout << "====== arith[" << i << "] ======\n"; - // } - // ); - // if (!check()) - // return false; return true; } }; - // Simple_Checker::Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num) { Simple_Checker::Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) { - LINXI_DEBUG; - // m_imp = alloc(imp, _sol, _pm, _am, _clauses, _learned, _atoms, _arith_var_num); m_imp = alloc(imp, _pm, _am, _clauses, _learned_unit, _atoms, _arith_var_num); } Simple_Checker::~Simple_Checker() { - LINXI_DEBUG; dealloc(m_imp); } bool Simple_Checker::operator()() { - LINXI_DEBUG; return m_imp->operator()(); } } diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 30ca55e31cf..1c4aabe50ec 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -222,12 +222,10 @@ namespace nlsat { bool m_check_lemmas; unsigned m_max_conflicts; unsigned m_lemma_count; -//#linxi begin - bool m_linxi_simple_check; - unsigned m_linxi_variable_ordering_strategy; - bool m_linxi_set_0_more; + bool m_simple_check; + unsigned m_variable_ordering_strategy; + bool m_set_0_more; bool m_cell_sample; -//#linxi end struct stats { unsigned m_simplifications; @@ -297,10 +295,7 @@ namespace nlsat { m_inline_vars = p.inline_vars(); m_log_lemmas = p.log_lemmas(); m_check_lemmas = p.check_lemmas(); -//#linxi begin - m_linxi_simple_check = p.linxi_simple_check(); - m_linxi_variable_ordering_strategy = p.linxi_variable_ordering_strategy(); -//#linxi end + m_variable_ordering_strategy = p.variable_ordering_strategy(); m_cell_sample = p.cell_sample(); @@ -1784,24 +1779,11 @@ namespace nlsat { } bool m_reordered = false; -//#linxi begin Simple Check -// test - void test_anum() { - scoped_anum x(m_am), y(m_am); - m_am.set(x, 3); - m_am.set(y, 5); - TRACE("linxi_simple_checker", - tout << x << " " << y << std::endl; - ); - } bool simple_check() { // test_anum(); literal_vector learned_unit; // Simple_Checker checker(m_solver, m_pm, m_am, m_clauses, m_learned, m_atoms, m_is_int.size()); Simple_Checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); - // TRACE("linxi_simple_checker", - // tout << "here" << std::endl; - // ); if (!checker()) return false; for (unsigned i = 0, sz = learned_unit.size(); i < sz; ++i) { @@ -1809,31 +1791,24 @@ namespace nlsat { if (m_atoms[learned_unit[i].var()] == nullptr) { assign(learned_unit[i], mk_clause_jst(cla)); } - // decide(learned_unit[i]); } return true; } -//#linxi end Simple Check -//#linxi begin Variable Ordering Strategy void run_variable_ordering_strategy() { - TRACE("linxi_reorder", tout << "runing vos: " << m_linxi_variable_ordering_strategy << '\n';); + TRACE("reorder", tout << "runing vos: " << m_variable_ordering_strategy << '\n';); unsigned num = num_vars(); - VOS_Var_Info_Collector vos_collector(m_pm, m_atoms, num, m_linxi_variable_ordering_strategy); + VOS_Var_Info_Collector vos_collector(m_pm, m_atoms, num, m_variable_ordering_strategy); vos_collector.collect(m_clauses); vos_collector.collect(m_learned); - // TRACE("linxi_reorder", vos_collector.display(tout, m_display_var);); var_vector perm; vos_collector(perm); reorder(perm.size(), perm.data()); } -//#linxi end Variable Ordering Strategy - - void apply_reorder() { m_reordered = false; if (!can_reorder()) @@ -1850,21 +1825,15 @@ namespace nlsat { lbool check() { -//#linxi begin simple check - if (m_linxi_simple_check) { + if (m_simple_check) { if (!simple_check()) { - TRACE("linxi_simple_check", tout << "real unsat\n";); + TRACE("simple_check", tout << "real unsat\n";); return l_false; } - TRACE("linxi_simple_checker_learned", + TRACE("simple_checker_learned", tout << "simple check done\n"; ); - // exit(0); - // return l_undef; } - // exit(0); - // return l_false; -//#linxi end simple check TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); @@ -1876,12 +1845,10 @@ namespace nlsat { if (!can_reorder()) { } -//#linxi begin Variable Ordering Strategy - else if (m_linxi_variable_ordering_strategy > 0) { + else if (m_variable_ordering_strategy > 0) { run_variable_ordering_strategy(); reordered = true; } -//#linxi end Variable Ordering Strategy else if (m_random_order) { shuffle_vars(); reordered = true; @@ -2866,7 +2833,6 @@ namespace nlsat { TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); } -//#linxi begin struct degree_lit_num_lt { unsigned_vector & m_degrees; @@ -2909,20 +2875,17 @@ namespace nlsat { TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); } -//#linxi end void sort_watched_clauses() { unsigned num = num_vars(); for (unsigned i = 0; i < num; i++) { clause_vector & ws = m_watches[i]; -//#linxi begin // sort_clauses_by_degree(ws.size(), ws.data()); - if (m_linxi_simple_check) { + if (m_simple_check) { sort_clauses_by_degree_lit_num(ws.size(), ws.data()); } else { sort_clauses_by_degree(ws.size(), ws.data()); } -//#linxi end } } diff --git a/src/nlsat/nlsat_variable_ordering_strategy.cpp b/src/nlsat/nlsat_variable_ordering_strategy.cpp index 8c4723fd309..dfcc1420117 100644 --- a/src/nlsat/nlsat_variable_ordering_strategy.cpp +++ b/src/nlsat/nlsat_variable_ordering_strategy.cpp @@ -239,7 +239,7 @@ namespace nlsat { else { UNREACHABLE(); } - TRACE("linxi_reorder", + TRACE("reorder", tout << "new order: "; for (unsigned i = 0; i < num_vars; i++) tout << new_order[i] << " "; diff --git a/src/tactic/smtlogics/qfnra_tactic.cpp b/src/tactic/smtlogics/qfnra_tactic.cpp index 5ecfa242652..f95ae8a8c05 100644 --- a/src/tactic/smtlogics/qfnra_tactic.cpp +++ b/src/tactic/smtlogics/qfnra_tactic.cpp @@ -41,11 +41,11 @@ tactic * mk_multilinear_ls_tactic(ast_manager & m, params_ref const & p, unsigne return using_params(mk_smt_tactic(m), p_mls); } -tactic * linxi_mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) { ptr_vector ts; { params_ref p_sc = p; - p_sc.set_bool("linxi_simple_check", true); + p_sc.set_bool("simple_check", true); // p_sc.set_uint("seed", 997); ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 10 * 1000)); } @@ -55,24 +55,24 @@ tactic * linxi_mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) { ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_heuristic), 4 * 1000)); params_ref p_order_4 = p; - p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + p_order_4.set_uint("variable_ordering_strategy", 4); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 4 * 1000)); params_ref p_order_3 = p; - p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + p_order_3.set_uint("variable_ordering_strategy", 3); // p_order_3.set_uint("seed", 17); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 6 * 1000)); params_ref p_order_1 = p; - p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + p_order_1.set_uint("variable_ordering_strategy", 1); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 8 * 1000)); params_ref p_order_5 = p; - p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + p_order_5.set_uint("variable_ordering_strategy", 5); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 8 * 1000)); params_ref p_order_2 = p; - p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + p_order_2.set_uint("variable_ordering_strategy", 2); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 10 * 1000)); } { @@ -97,11 +97,11 @@ tactic * linxi_mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) { return or_else(ts.size(), ts.data()); } -tactic * linxi_mk_qfnra_small_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_small_solver(ast_manager& m, params_ref const& p) { ptr_vector ts; { params_ref p_sc = p; - p_sc.set_bool("linxi_simple_check", true); + p_sc.set_bool("simple_check", true); // p_sc.set_uint("seed", 997); ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 20 * 1000)); } @@ -111,26 +111,26 @@ tactic * linxi_mk_qfnra_small_solver(ast_manager& m, params_ref const& p) { ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_heuristic), 5 * 1000)); params_ref p_order_4 = p; - p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + p_order_4.set_uint("variable_ordering_strategy", 4); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 5 * 1000)); params_ref p_order_3 = p; - p_order_3.set_uint("linxi_variable_ordering_strategy", 3); + p_order_3.set_uint("variable_ordering_strategy", 3); // p_order_3.set_uint("seed", 17); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 10 * 1000)); params_ref p_order_1 = p; - p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + p_order_1.set_uint("variable_ordering_strategy", 1); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 15 * 1000)); params_ref p_order_5 = p; - p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + p_order_5.set_uint("variable_ordering_strategy", 5); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 15 * 1000)); params_ref p_order_2 = p; - p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + p_order_2.set_uint("variable_ordering_strategy", 2); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 20 * 1000)); } { @@ -155,12 +155,11 @@ tactic * linxi_mk_qfnra_small_solver(ast_manager& m, params_ref const& p) { return or_else(ts.size(), ts.data()); } -tactic * linxi_mk_qfnra_middle_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_middle_solver(ast_manager& m, params_ref const& p) { ptr_vector ts; { params_ref p_sc = p; - p_sc.set_bool("linxi_simple_check", true); - // p_sc.set_uint("seed", 997); + p_sc.set_bool("simple_check", true); ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 30 * 1000)); } { @@ -170,27 +169,26 @@ tactic * linxi_mk_qfnra_middle_solver(ast_manager& m, params_ref const& p) { params_ref p_order_4 = p; - p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + p_order_4.set_uint("variable_ordering_strategy", 4); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 15 * 1000)); params_ref p_order_3 = p; - p_order_3.set_uint("linxi_variable_ordering_strategy", 3); - // p_order_3.set_uint("seed", 17); + p_order_3.set_uint("variable_ordering_strategy", 3); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 15 * 1000)); params_ref p_order_1 = p; - p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + p_order_1.set_uint("variable_ordering_strategy", 1); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 20 * 1000)); params_ref p_order_5 = p; - p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + p_order_5.set_uint("variable_ordering_strategy", 5); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 20 * 1000)); params_ref p_order_2 = p; - p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + p_order_2.set_uint("variable_ordering_strategy", 2); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 25 * 1000)); } { @@ -215,38 +213,36 @@ tactic * linxi_mk_qfnra_middle_solver(ast_manager& m, params_ref const& p) { return or_else(ts.size(), ts.data()); } -tactic * linxi_mk_qfnra_large_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_large_solver(ast_manager& m, params_ref const& p) { ptr_vector ts; { params_ref p_sc = p; - p_sc.set_bool("linxi_simple_check", true); - // p_sc.set_uint("seed", 997); + p_sc.set_bool("simple_check", true); ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 50 * 1000)); } { params_ref p_order_4 = p; - p_order_4.set_uint("linxi_variable_ordering_strategy", 4); + p_order_4.set_uint("variable_ordering_strategy", 4); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_4), 15 * 1000)); params_ref p_order_3 = p; - p_order_3.set_uint("linxi_variable_ordering_strategy", 3); - // p_order_3.set_uint("seed", 17); + p_order_3.set_uint("variable_ordering_strategy", 3); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_3), 30 * 1000)); params_ref p_order_1 = p; - p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + p_order_1.set_uint("variable_ordering_strategy", 1); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 40 * 1000)); params_ref p_order_5 = p; - p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + p_order_5.set_uint("variable_ordering_strategy", 5); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 40 * 1000)); params_ref p_order_2 = p; - p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + p_order_2.set_uint("variable_ordering_strategy", 2); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 50 * 1000)); } { @@ -271,27 +267,26 @@ tactic * linxi_mk_qfnra_large_solver(ast_manager& m, params_ref const& p) { return or_else(ts.size(), ts.data()); } -tactic * linxi_mk_qfnra_very_large_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_very_large_solver(ast_manager& m, params_ref const& p) { ptr_vector ts; { params_ref p_sc = p; - p_sc.set_bool("linxi_simple_check", true); - // p_sc.set_uint("seed", 997); + p_sc.set_bool("simple_check", true); ts.push_back(try_for(and_then(mk_qfnra_nlsat_tactic(m, p_sc), mk_fail_if_undecided_tactic()), 100 * 1000)); } { params_ref p_order_1 = p; - p_order_1.set_uint("linxi_variable_ordering_strategy", 1); + p_order_1.set_uint("variable_ordering_strategy", 1); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_1), 80 * 1000)); params_ref p_order_5 = p; - p_order_5.set_uint("linxi_variable_ordering_strategy", 5); + p_order_5.set_uint("variable_ordering_strategy", 5); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_5), 80 * 1000)); params_ref p_order_2 = p; - p_order_2.set_uint("linxi_variable_ordering_strategy", 2); + p_order_2.set_uint("variable_ordering_strategy", 2); ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_order_2), 100 * 1000)); } { @@ -312,16 +307,16 @@ const double VERY_SMALL_THRESHOLD = 30.0; const double SMALL_THRESHOLD = 80.0; const double MIDDLE_THRESHOLD = 300.0; const double LARGE_THRESHOLD = 600.0; -tactic * linxi_mk_qfnra_mixed_solver(ast_manager& m, params_ref const& p) { +tactic * mk_qfnra_mixed_solver(ast_manager& m, params_ref const& p) { return cond(mk_lt(mk_memory_probe(), mk_const_probe(VERY_SMALL_THRESHOLD)), - linxi_mk_qfnra_very_small_solver(m, p), + mk_qfnra_very_small_solver(m, p), cond(mk_lt(mk_memory_probe(), mk_const_probe(SMALL_THRESHOLD)), - linxi_mk_qfnra_small_solver(m, p), + mk_qfnra_small_solver(m, p), cond(mk_lt(mk_memory_probe(), mk_const_probe(MIDDLE_THRESHOLD)), - linxi_mk_qfnra_middle_solver(m, p), + mk_qfnra_middle_solver(m, p), cond(mk_lt(mk_memory_probe(), mk_const_probe(LARGE_THRESHOLD)), - linxi_mk_qfnra_large_solver(m, p), - linxi_mk_qfnra_very_large_solver(m, p) + mk_qfnra_large_solver(m, p), + mk_qfnra_very_large_solver(m, p) ) ) ) @@ -332,7 +327,6 @@ tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), - // mk_multilinear_ls_tactic(m, p) - linxi_mk_qfnra_mixed_solver(m, p) + mk_qfnra_mixed_solver(m, p) ); } From 33f0256e206a8f7d02ed9d5159492ee69f6a45b3 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 10 Aug 2024 14:53:56 -1000 Subject: [PATCH 146/414] cleanup --- src/nlsat/nlsat_simple_checker.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nlsat/nlsat_simple_checker.h b/src/nlsat/nlsat_simple_checker.h index 969c7a14175..1a854e85544 100644 --- a/src/nlsat/nlsat_simple_checker.h +++ b/src/nlsat/nlsat_simple_checker.h @@ -7,7 +7,6 @@ namespace nlsat { struct imp; imp * m_imp; public: - // Simple_Checker(solver &_sol, pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, clause_vector &_learned, const atom_vector &_atoms, const unsigned &_arith_var_num); Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num); ~Simple_Checker(); bool operator()(); From 8999e1a340810dd181eddc7abbe14f3e2f828585 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 12 Aug 2024 08:29:06 -1000 Subject: [PATCH 147/414] use standard name conventions and add file headers Signed-off-by: Lev Nachmanson --- src/nlsat/nlsat_explain.h | 3 +- src/nlsat/nlsat_simple_checker.cpp | 26 +++------------- src/nlsat/nlsat_simple_checker.h | 25 ++++++++++++++-- src/nlsat/nlsat_solver.cpp | 4 +-- .../nlsat_variable_ordering_strategy.cpp | 30 +++++++++---------- src/nlsat/nlsat_variable_ordering_strategy.h | 27 +++++++++++++---- 6 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index 2fdb76b1150..f44a9ae9219 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -8,7 +8,8 @@ Module Name: Abstract: Functor that implements the "explain" procedure defined in Dejan and Leo's paper. - + Uses paper Haokun Li and Bican Xia, "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection",https://arxiv.org/abs/2003.00409, + and code from https://github.com/hybridSMT/hybridSMT.git Author: Leonardo de Moura (leonardo) 2012-01-13. diff --git a/src/nlsat/nlsat_simple_checker.cpp b/src/nlsat/nlsat_simple_checker.cpp index 3e1e8dd1c36..f61bc97ad29 100644 --- a/src/nlsat/nlsat_simple_checker.cpp +++ b/src/nlsat/nlsat_simple_checker.cpp @@ -1,25 +1,7 @@ #include "nlsat/nlsat_simple_checker.h" -struct Debug_Tracer { - std::string tag_str; - Debug_Tracer(std::string _tag_str) { - tag_str = _tag_str; - TRACE("simple_checker", - tout << "Debug_Tracer begin\n"; - tout << tag_str << "\n"; - ); - } - ~Debug_Tracer() { - TRACE("simple_checker", - tout << "Debug_Tracer end\n"; - tout << tag_str << "\n"; - ); - } -}; - - namespace nlsat { - struct Simple_Checker::imp { + struct simple_checker::imp { // solver / pmanager ± anum_manager &am; @@ -1569,13 +1551,13 @@ else { // ( == 0) + (c > 0) -> > 0 return true; } }; - Simple_Checker::Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) { + simple_checker::simple_checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num) { m_imp = alloc(imp, _pm, _am, _clauses, _learned_unit, _atoms, _arith_var_num); } - Simple_Checker::~Simple_Checker() { + simple_checker::~simple_checker() { dealloc(m_imp); } - bool Simple_Checker::operator()() { + bool simple_checker::operator()() { return m_imp->operator()(); } } diff --git a/src/nlsat/nlsat_simple_checker.h b/src/nlsat/nlsat_simple_checker.h index 1a854e85544..c13e2669c03 100644 --- a/src/nlsat/nlsat_simple_checker.h +++ b/src/nlsat/nlsat_simple_checker.h @@ -1,14 +1,33 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + nlsat_simple_checker.cpp + +Abstract: + + Attempts to find a conflict by using simple polynomial forms. +Author: + + Mengyu Zhao (Linxi) and Shaowei Cai + +Revision History: + +--*/ + +#pragma once #include "math/polynomial/algebraic_numbers.h" #include "nlsat/nlsat_clause.h" namespace nlsat { - class Simple_Checker { + class simple_checker { struct imp; imp * m_imp; public: - Simple_Checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num); - ~Simple_Checker(); + simple_checker(pmanager &_pm, anum_manager &_am, const clause_vector &_clauses, literal_vector &_learned_unit, const atom_vector &_atoms, const unsigned &_arith_var_num); + ~simple_checker(); bool operator()(); }; } \ No newline at end of file diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 1c4aabe50ec..7c2dd1defe9 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1783,7 +1783,7 @@ namespace nlsat { // test_anum(); literal_vector learned_unit; // Simple_Checker checker(m_solver, m_pm, m_am, m_clauses, m_learned, m_atoms, m_is_int.size()); - Simple_Checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); + simple_checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); if (!checker()) return false; for (unsigned i = 0, sz = learned_unit.size(); i < sz; ++i) { @@ -1800,7 +1800,7 @@ namespace nlsat { TRACE("reorder", tout << "runing vos: " << m_variable_ordering_strategy << '\n';); unsigned num = num_vars(); - VOS_Var_Info_Collector vos_collector(m_pm, m_atoms, num, m_variable_ordering_strategy); + vos_var_info_collector vos_collector(m_pm, m_atoms, num, m_variable_ordering_strategy); vos_collector.collect(m_clauses); vos_collector.collect(m_learned); diff --git a/src/nlsat/nlsat_variable_ordering_strategy.cpp b/src/nlsat/nlsat_variable_ordering_strategy.cpp index dfcc1420117..5b91501e364 100644 --- a/src/nlsat/nlsat_variable_ordering_strategy.cpp +++ b/src/nlsat/nlsat_variable_ordering_strategy.cpp @@ -1,7 +1,7 @@ #include "nlsat/nlsat_variable_ordering_strategy.h" namespace nlsat { - struct VOS_Var_Info_Collector::imp { + struct vos_var_info_collector::imp { pmanager & pm; atom_vector const & m_atoms; unsigned num_vars; @@ -138,8 +138,8 @@ namespace nlsat { struct univariate_reorder_lt { - VOS_Var_Info_Collector::imp const *m_info; - univariate_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + vos_var_info_collector::imp const *m_info; + univariate_reorder_lt(vos_var_info_collector::imp const *info):m_info(info) {} bool operator()(var x, var y) const { if (m_info->m_num_uni[x] != m_info->m_num_uni[y]) return m_info->m_num_uni[x] > m_info->m_num_uni[y]; @@ -148,8 +148,8 @@ namespace nlsat { }; struct feature_reorder_lt { - VOS_Var_Info_Collector::imp const *m_info; - feature_reorder_lt(VOS_Var_Info_Collector::imp const * info): m_info(info){} + vos_var_info_collector::imp const *m_info; + feature_reorder_lt(vos_var_info_collector::imp const * info): m_info(info){} bool operator()(var x, var y) const { if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) return m_info->m_max_degree[x] > m_info->m_max_degree[y]; @@ -162,8 +162,8 @@ namespace nlsat { } }; struct brown_reorder_lt { - VOS_Var_Info_Collector::imp const *m_info; - brown_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + vos_var_info_collector::imp const *m_info; + brown_reorder_lt(vos_var_info_collector::imp const *info):m_info(info) {} bool operator()(var x, var y) const { // if (a.max_degree != b.max_degree) // return a.max_degree > b.max_degree; @@ -180,8 +180,8 @@ namespace nlsat { } }; struct triangular_reorder_lt { - const VOS_Var_Info_Collector::imp *m_info; - triangular_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + const vos_var_info_collector::imp *m_info; + triangular_reorder_lt(vos_var_info_collector::imp const *info):m_info(info) {} bool operator()(var x, var y) const { // if (a.max_degree != b.max_degree) // return a.max_degree > b.max_degree; @@ -198,8 +198,8 @@ namespace nlsat { } }; struct onlypoly_reorder_lt { - const VOS_Var_Info_Collector::imp *m_info; - onlypoly_reorder_lt(VOS_Var_Info_Collector::imp const *info):m_info(info) {} + const vos_var_info_collector::imp *m_info; + onlypoly_reorder_lt(vos_var_info_collector::imp const *info):m_info(info) {} bool operator()(var x, var y) const { // high degree first if (m_info->m_max_degree[x] != m_info->m_max_degree[y]) @@ -267,16 +267,16 @@ namespace nlsat { // return out; // } }; - VOS_Var_Info_Collector::VOS_Var_Info_Collector(pmanager & _pm, atom_vector const & _atoms, unsigned _num_vars, unsigned _vos_type) { + vos_var_info_collector::vos_var_info_collector(pmanager & _pm, atom_vector const & _atoms, unsigned _num_vars, unsigned _vos_type) { m_imp = alloc(imp, _pm, _atoms, _num_vars, _vos_type); } - VOS_Var_Info_Collector::~VOS_Var_Info_Collector() { + vos_var_info_collector::~vos_var_info_collector() { dealloc(m_imp); } - void VOS_Var_Info_Collector::collect(clause_vector const & cs) { + void vos_var_info_collector::collect(clause_vector const & cs) { m_imp->collect(cs); } - void VOS_Var_Info_Collector::operator()(var_vector &perm) { + void vos_var_info_collector::operator()(var_vector &perm) { m_imp->operator()(perm); } } diff --git a/src/nlsat/nlsat_variable_ordering_strategy.h b/src/nlsat/nlsat_variable_ordering_strategy.h index 6e01825c391..b29bece279f 100644 --- a/src/nlsat/nlsat_variable_ordering_strategy.h +++ b/src/nlsat/nlsat_variable_ordering_strategy.h @@ -1,3 +1,22 @@ +/*++ +Copyright (c) 2024 Microsoft Corporation + +Module Name: + + nlsat_simple_checker.cpp + +Abstract: + + +Author: + + Mengyu Zhao (Linxi) and Shaowei Cai, ported from https://github.com/hybridSMT/hybridSMT.git + +Revision History: + +--*/ + +#pragma once #include "nlsat/nlsat_clause.h" @@ -11,16 +30,14 @@ namespace nlsat { typedef polynomial::manager::numeral_vector numeral_vector; - // enum Variable_Ordering_Strategy_Type {NONE = 0, BROWN, TRIANGULAR, ONLYPOLY}; - enum Variable_Ordering_Strategy_Type {NONE = 0, BROWN, TRIANGULAR, ONLYPOLY, UNIVARIATE, FEATURE, ROOT}; - class VOS_Var_Info_Collector { + class vos_var_info_collector { struct imp; imp * m_imp; public: - VOS_Var_Info_Collector(pmanager & _pm, atom_vector const & atoms, unsigned _num_vars, unsigned _vos_type); - ~VOS_Var_Info_Collector(); + vos_var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned _num_vars, unsigned _vos_type); + ~vos_var_info_collector(); void operator()(var_vector &perm); void collect(clause_vector const & cs); }; From f2d35ddc5eec3c1ad1c8ba292c5c7bef50993ca4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 12 Aug 2024 08:32:01 -1000 Subject: [PATCH 148/414] more cleanup --- src/nlsat/nlsat_solver.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 7c2dd1defe9..c3051547097 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1780,9 +1780,7 @@ namespace nlsat { bool m_reordered = false; bool simple_check() { - // test_anum(); literal_vector learned_unit; - // Simple_Checker checker(m_solver, m_pm, m_am, m_clauses, m_learned, m_atoms, m_is_int.size()); simple_checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); if (!checker()) return false; From bf34600f08d732680f7a9b3d85a257caf435418b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 12 Aug 2024 08:49:55 -1000 Subject: [PATCH 149/414] add release nodes and add the author reference in qfnra_tactic Signed-off-by: Lev Nachmanson --- RELEASE_NOTES.md | 7 +++++++ src/tactic/smtlogics/qfnra_tactic.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3830f566ffa..b69772dcd5f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,13 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.13.1 +============== +- single-sample cell projection in nlsat was designed by Haokun Li and Bican Xia. +- using simple-checker together with and variable ordering supported by qfnra_tactic was developed by Mengyu Zhao (Linxi) and Shaowei Cai. + + The projection is described in paper by Haokun Li and Bican Xia, [Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection](https://arxiv.org/abs/2003.00409). The code ported from https://github.com/hybridSMT/hybridSMT.git + Version 4.13.0 ============== - add ARM64 wheels for Python, thanks to Steven Moy, smoy diff --git a/src/tactic/smtlogics/qfnra_tactic.h b/src/tactic/smtlogics/qfnra_tactic.h index 09a5d0480c8..344ea3f366e 100644 --- a/src/tactic/smtlogics/qfnra_tactic.h +++ b/src/tactic/smtlogics/qfnra_tactic.h @@ -12,7 +12,7 @@ Module Name: Author: Leonardo (leonardo) 2012-02-28 - + Mengyu Zhao (Linxi) and Shaowei Cai, ported from https://github.com/hybridSMT/hybridSMT.git Notes: --*/ From 83f47bd84bbd2f22b1394cac03a024f1e49c5183 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 12 Aug 2024 10:28:27 -1000 Subject: [PATCH 150/414] wasm build problem Signed-off-by: Lev Nachmanson --- .github/workflows/wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 8e157f5a465..f0b8bd91b79 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -48,7 +48,7 @@ jobs: source $(dirname $(which emsdk))/emsdk_env.sh which node which clang++ - npm run build:wasm + npm run build:wasm -- -sINITIAL_MEMORY=128MB -sALLOW_MEMORY_GROWTH - name: Test run: npm test From ed17de56d2433dbdfd11cca03f78ea8a47adb98e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Aug 2024 08:51:20 -0700 Subject: [PATCH 151/414] fix #7343 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_parse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opt/opt_parse.cpp b/src/opt/opt_parse.cpp index 29e8a604c0b..78ce8bd8503 100644 --- a/src/opt/opt_parse.cpp +++ b/src/opt/opt_parse.cpp @@ -69,7 +69,7 @@ class opt_stream_buffer { bool opt_stream_buffer::parse_token(char const* token) { skip_whitespace(); char const* t = token; - while (ch() == *t) { + while (*t && ch() == *t) { next(); ++t; } From 656545564dfdf7d82e27c8dc1f0f14bbea122352 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Aug 2024 09:17:05 -0700 Subject: [PATCH 152/414] fix #7343 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_parse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opt/opt_parse.cpp b/src/opt/opt_parse.cpp index 78ce8bd8503..159246281e5 100644 --- a/src/opt/opt_parse.cpp +++ b/src/opt/opt_parse.cpp @@ -69,7 +69,7 @@ class opt_stream_buffer { bool opt_stream_buffer::parse_token(char const* token) { skip_whitespace(); char const* t = token; - while (*t && ch() == *t) { + while (*t && ch() && ch() == *t) { next(); ++t; } From 0612a0ba1703d0fc312de4b16af6ff996b3fb45d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 22:09:44 +0100 Subject: [PATCH 153/414] Bump docker/build-push-action from 6.5.0 to 6.6.1 (#7338) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.5.0 to 6.6.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.5.0...v6.6.1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 931ed071ee8..0b38274792b 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: context: . push: true From c1454dc31cf2de3c563fcdb83c60e030da0554a9 Mon Sep 17 00:00:00 2001 From: Zhang <5205699+Naville@users.noreply.github.com> Date: Fri, 16 Aug 2024 04:08:38 +0800 Subject: [PATCH 154/414] Fix building with Windows SDK and Clang-CL (#7337) * Fix building with Windows SDK and Clang-CL * Attempt to add Clang-CL to CI build configurations * Fix typo * Enable EHsc explicitly when using ClangCL due to it being default turned-off * Override CMAKE__FLAGS instead due to Z3 resets the _INIT variants --- .../workflows/msvc-static-build-clang-cl.yml | 23 +++++++++++++++++++ src/util/mpz.cpp | 22 +++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/msvc-static-build-clang-cl.yml diff --git a/.github/workflows/msvc-static-build-clang-cl.yml b/.github/workflows/msvc-static-build-clang-cl.yml new file mode 100644 index 00000000000..341f873c11a --- /dev/null +++ b/.github/workflows/msvc-static-build-clang-cl.yml @@ -0,0 +1,23 @@ +name: MSVC Clang-CL Static Build + +on: + push: + pull_request: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + build: + runs-on: windows-2019 + env: + BUILD_TYPE: Release + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Build + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DZ3_BUILD_LIBZ3_SHARED=OFF -DZ3_BUILD_LIBZ3_MSVC_STATIC=ON -T ClangCL -DCMAKE_C_FLAGS="/EHsc" -DCMAKE_CXX_FLAGS="/EHsc" + cmake --build build --config ${{ env.BUILD_TYPE }} --parallel + diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 296b4426e76..b1837662caf 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -46,10 +46,14 @@ Revision History: #define LEHMER_GCD #endif - -#if defined(__GNUC__) +#ifdef __has_builtin + #define HAS_BUILTIN(X) __has_builtin(X) +#else + #define HAS_BUILTIN(X) 0 +#endif +#if HAS_BUILTIN(__builtin_ctz) #define _trailing_zeros32(X) __builtin_ctz(X) -#elif defined(_WINDOWS) && (defined(_M_X86) || (defined(_M_X64) && !defined(_M_ARM64EC))) +#elif defined(_WINDOWS) && (defined(_M_X86) || (defined(_M_X64) && !defined(_M_ARM64EC))) && !defined(__clang__) // This is needed for _tzcnt_u32 and friends. #include #define _trailing_zeros32(X) _tzcnt_u32(X) @@ -62,11 +66,11 @@ static uint32_t _trailing_zeros32(uint32_t x) { #endif #if (defined(__LP64__) || defined(_WIN64)) && defined(_M_X64) && !defined(_M_ARM64EC) - #if defined(__GNUC__) - #define _trailing_zeros64(X) __builtin_ctzll(X) - #else - #define _trailing_zeros64(X) _tzcnt_u64(X) - #endif +#if HAS_BUILTIN(__builtin_ctzll) +#define _trailing_zeros64(X) __builtin_ctzll(X) +#elif !defined(__clang__) +#define _trailing_zeros64(X) _tzcnt_u64(X) +#endif #else static uint64_t _trailing_zeros64(uint64_t x) { uint64_t r = 0; @@ -75,6 +79,8 @@ static uint64_t _trailing_zeros64(uint64_t x) { } #endif +#undef HAS_BUILTIN + unsigned trailing_zeros(uint32_t x) { return static_cast(_trailing_zeros32(x)); } From 6a68cc55bbefcac1fdb417260850af9a84717260 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Aug 2024 09:59:50 -0700 Subject: [PATCH 155/414] #7353 - clear pointer when existing stack Signed-off-by: Nikolaj Bjorner --- src/sat/sat_ddfw.cpp | 2 +- src/tactic/arith/fix_dl_var_tactic.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index bd7b0d26cbf..ccf5a55db2c 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -613,7 +613,7 @@ namespace sat { out << ci.m_num_trues << " " << ci.m_weight << "\n"; } for (unsigned v = 0; v < num_vars(); ++v) { - out << v << ": " << reward(v) << "\n"; + out << v << ": rw " << reward(v) << "\n"; } out << "unsat vars: "; for (bool_var v : m_unsat_vars) { diff --git a/src/tactic/arith/fix_dl_var_tactic.cpp b/src/tactic/arith/fix_dl_var_tactic.cpp index 13479e1bffe..c96ca749b90 100644 --- a/src/tactic/arith/fix_dl_var_tactic.cpp +++ b/src/tactic/arith/fix_dl_var_tactic.cpp @@ -35,7 +35,7 @@ class fix_dl_var_tactic : public tactic { struct failed {}; ast_manager & m; arith_util & m_util; - expr_fast_mark1 * m_visited; + expr_fast_mark1 * m_visited = nullptr; ptr_vector m_todo; obj_map m_occs; obj_map m_non_nested_occs; @@ -215,7 +215,7 @@ class fix_dl_var_tactic : public tactic { app * operator()(goal const & g) { try { expr_fast_mark1 visited; - m_visited = &visited; + flet _visited(m_visited, &visited); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i)); From cff1e9233f752b69d9a14bd0270cdd33edb0d1ee Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinsky" Date: Mon, 26 Aug 2024 19:02:54 +0200 Subject: [PATCH 156/414] Avoid broken stack at few places (#7353) * Avoid broken stack by degree_lit_num_lt * Avoid broken stack by fix_dl_var_tactic --------- Co-authored-by: Nikolaj Bjorner --- src/nlsat/nlsat_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index c3051547097..e7e3071afe6 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -2835,7 +2835,7 @@ namespace nlsat { struct degree_lit_num_lt { unsigned_vector & m_degrees; unsigned_vector & m_lit_num; - degree_lit_num_lt(unsigned_vector & ds, unsigned_vector ln) : + degree_lit_num_lt(unsigned_vector & ds, unsigned_vector & ln) : m_degrees(ds), m_lit_num(ln) { } From 49ba3bc12fff6cd1e092a9671f3c4213819d749d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Aug 2024 11:32:37 -0700 Subject: [PATCH 157/414] address compiler warnings gcc-13 Signed-off-by: Nikolaj Bjorner --- src/ast/proofs/proof_utils.h | 2 +- src/ast/simplifiers/dominator_simplifier.cpp | 4 +- src/ast/sls/bv_sls_eval.cpp | 1 - src/muz/spacer/spacer_concretize.cpp | 10 +- src/nlsat/nlsat_explain.cpp | 4 +- src/nlsat/nlsat_simplify.cpp | 1 - src/nlsat/nlsat_solver.cpp | 8268 +++++++++--------- src/qe/mbp/mbp_arrays_tg.cpp | 6 +- src/sat/sat_aig_cuts.cpp | 2 +- 9 files changed, 4147 insertions(+), 4151 deletions(-) diff --git a/src/ast/proofs/proof_utils.h b/src/ast/proofs/proof_utils.h index cc1744d4e9f..dbb9dfac0bc 100644 --- a/src/ast/proofs/proof_utils.h +++ b/src/ast/proofs/proof_utils.h @@ -230,7 +230,7 @@ class elim_aux_assertions { << "New pf: " << mk_pp(newp, m) << "\n";); } - proof *r; + proof *r = nullptr; VERIFY(cache.find(pr, r)); DEBUG_CODE( diff --git a/src/ast/simplifiers/dominator_simplifier.cpp b/src/ast/simplifiers/dominator_simplifier.cpp index 2ef4528ab22..0f9d7078668 100644 --- a/src/ast/simplifiers/dominator_simplifier.cpp +++ b/src/ast/simplifiers/dominator_simplifier.cpp @@ -187,8 +187,8 @@ expr_ref dominator_simplifier::simplify_and_or(bool is_and, app * e) { } expr_ref dominator_simplifier::simplify_not(app * e) { - expr *ee; - ENSURE(m.is_not(e, ee)); + expr *ee = nullptr; + VERIFY(m.is_not(e, ee)); unsigned old_lvl = scope_level(); expr_ref t = simplify_rec(ee); local_pop(scope_level() - old_lvl); diff --git a/src/ast/sls/bv_sls_eval.cpp b/src/ast/sls/bv_sls_eval.cpp index f2dd165abe9..eecc42511ce 100644 --- a/src/ast/sls/bv_sls_eval.cpp +++ b/src/ast/sls/bv_sls_eval.cpp @@ -145,7 +145,6 @@ namespace bv { SASSERT(m.is_bool(e)); SASSERT(e->get_family_id() == basic_family_id); - auto id = e->get_id(); switch (e->get_decl_kind()) { case OP_TRUE: return true; diff --git a/src/muz/spacer/spacer_concretize.cpp b/src/muz/spacer/spacer_concretize.cpp index 809e9a97160..9700af1497e 100644 --- a/src/muz/spacer/spacer_concretize.cpp +++ b/src/muz/spacer/spacer_concretize.cpp @@ -27,7 +27,7 @@ struct proc { void operator()(var *n) const {} void operator()(quantifier *q) const {} void operator()(app const *n) const { - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; if (m_arith.is_mul(n, e1, e2)) { if (is_var(e1) && !is_var(e2)) m_marks.mark(e2); @@ -71,7 +71,7 @@ bool pob_concretizer::apply(const expr_ref_vector &cube, expr_ref_vector &out) { } bool pob_concretizer::is_split_var(expr *e, expr *&var, bool &pos) { - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; rational n; if (m_var_marks.is_marked(e)) { @@ -89,7 +89,7 @@ bool pob_concretizer::is_split_var(expr *e, expr *&var, bool &pos) { } void pob_concretizer::split_lit_le_lt(expr *_lit, expr_ref_vector &out) { - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; expr *lit = _lit; m.is_not(_lit, lit); @@ -133,7 +133,7 @@ void pob_concretizer::split_lit_le_lt(expr *_lit, expr_ref_vector &out) { } void pob_concretizer::split_lit_ge_gt(expr *_lit, expr_ref_vector &out) { - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; expr *lit = _lit; m.is_not(_lit, lit); @@ -182,7 +182,7 @@ bool pob_concretizer::apply_lit(expr *_lit, expr_ref_vector &out) { // split literals of the form a1*x1 + ... + an*xn ~ c, where c is a // constant, ~ is <, <=, >, or >=, and the top level operator of LHS is + - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; if ((m_arith.is_lt(lit, e1, e2) || m_arith.is_le(lit, e1, e2)) && m_arith.is_add(e1)) { SASSERT(m_arith.is_numeral(e2)); diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 49842100dba..12db1d3da70 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -126,7 +126,7 @@ namespace nlsat { scoped_literal_vector m_core2; // temporary fields for storing the result - scoped_literal_vector * m_result; + scoped_literal_vector * m_result = nullptr; svector m_already_added_literal; evaluator & m_evaluator; @@ -149,8 +149,6 @@ namespace nlsat { m_todo(u), m_core1(s), m_core2(s), - m_result(nullptr), - m_cell_sample(is_sample), m_evaluator(ev) { diff --git a/src/nlsat/nlsat_simplify.cpp b/src/nlsat/nlsat_simplify.cpp index 2901d22c213..d483a039124 100644 --- a/src/nlsat/nlsat_simplify.cpp +++ b/src/nlsat/nlsat_simplify.cpp @@ -93,7 +93,6 @@ namespace nlsat { } void update_clauses(u_map const& b2l) { - bool is_sat = true; literal_vector lits; unsigned n = m_clauses.size(); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index e7e3071afe6..f2c32cf6a2f 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1,4134 +1,4134 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - nlsat_solver.cpp - -Abstract: - - Nonlinear arithmetic satisfiability procedure. The procedure is - complete for nonlinear real arithmetic, but it also has limited - support for integers. - -Author: - - Leonardo de Moura (leonardo) 2012-01-02. - -Revision History: - ---*/ -#include "util/z3_exception.h" -#include "util/chashtable.h" -#include "util/id_gen.h" -#include "util/map.h" -#include "util/dependency.h" -#include "util/permutation.h" -#include "math/polynomial/algebraic_numbers.h" -#include "math/polynomial/polynomial_cache.h" -#include "nlsat/nlsat_solver.h" -#include "nlsat/nlsat_clause.h" -#include "nlsat/nlsat_assignment.h" -#include "nlsat/nlsat_justification.h" -#include "nlsat/nlsat_evaluator.h" -#include "nlsat/nlsat_explain.h" -#include "nlsat/nlsat_params.hpp" -#include "nlsat/nlsat_simplify.h" -#include "nlsat/nlsat_simple_checker.h" -#include "nlsat/nlsat_variable_ordering_strategy.h" - -#define NLSAT_EXTRA_VERBOSE - -#ifdef NLSAT_EXTRA_VERBOSE -#define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) -#else -#define NLSAT_VERBOSE(CODE) ((void)0) -#endif - -namespace nlsat { - - - typedef chashtable ineq_atom_table; - typedef chashtable root_atom_table; - - // for apply_permutation procedure - void swap(clause * & c1, clause * & c2) noexcept { - std::swap(c1, c2); - } - - struct solver::ctx { - params_ref m_params; - reslimit& m_rlimit; - small_object_allocator m_allocator; - unsynch_mpq_manager m_qm; - pmanager m_pm; - anum_manager m_am; - bool m_incremental; - ctx(reslimit& rlim, params_ref const & p, bool incremental): - m_params(p), - m_rlimit(rlim), - m_allocator("nlsat"), - m_pm(rlim, m_qm, &m_allocator), - m_am(rlim, m_qm, p, &m_allocator), - m_incremental(incremental) - {} - }; - - struct solver::imp { - - - struct dconfig { - typedef imp value_manager; - typedef small_object_allocator allocator; - typedef void* value; - static const bool ref_count = false; - }; - - typedef dependency_manager assumption_manager; - typedef assumption_manager::dependency* _assumption_set; - - typedef obj_ref assumption_set_ref; - - - typedef polynomial::cache cache; - typedef ptr_vector interval_set_vector; - - - - ctx& m_ctx; - solver& m_solver; - reslimit& m_rlimit; - small_object_allocator& m_allocator; - bool m_incremental; - unsynch_mpq_manager& m_qm; - pmanager& m_pm; - cache m_cache; - anum_manager& m_am; - mutable assumption_manager m_asm; - assignment m_assignment, m_lo, m_hi; // partial interpretation - evaluator m_evaluator; - interval_set_manager & m_ism; - ineq_atom_table m_ineq_atoms; - root_atom_table m_root_atoms; - - - vector m_bounds; - - id_gen m_cid_gen; - clause_vector m_clauses; // set of clauses - clause_vector m_learned; // set of learned clauses - clause_vector m_valids; - - unsigned m_num_bool_vars; - atom_vector m_atoms; // bool_var -> atom - svector m_bvalues; // boolean assignment - unsigned_vector m_levels; // bool_var -> level - svector m_justifications; - vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal - bool_vector m_dead; // mark dead boolean variables - id_gen m_bid_gen; - - simplify m_simplify; - - bool_vector m_is_int; // m_is_int[x] is true if variable is integer - vector m_watches; // var -> clauses where variable is maximal - interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. - atom_vector m_var2eq; // var -> to asserted equality - var_vector m_perm; // var -> var permutation of the variables - var_vector m_inv_perm; - // m_perm: internal -> external - // m_inv_perm: external -> internal - struct perm_display_var_proc : public display_var_proc { - var_vector & m_perm; - display_var_proc m_default_display_var; - display_var_proc const * m_proc; // display external var ids - perm_display_var_proc(var_vector & perm): - m_perm(perm), - m_proc(nullptr) { - } - std::ostream& operator()(std::ostream & out, var x) const override { - if (m_proc == nullptr) - m_default_display_var(out, x); - else - (*m_proc)(out, m_perm[x]); - return out; - } - }; - perm_display_var_proc m_display_var; - - display_assumption_proc const* m_display_assumption; - struct display_literal_assumption : public display_assumption_proc { - imp& i; - literal_vector const& lits; - display_literal_assumption(imp& i, literal_vector const& lits): i(i), lits(lits) {} - std::ostream& operator()(std::ostream& out, assumption a) const override { - if (lits.begin() <= a && a < lits.end()) { - out << *((literal const*)a); - } - else if (i.m_display_assumption) { - (*i.m_display_assumption)(out, a); - } - return out; - } - - }; - struct scoped_display_assumptions { - imp& i; - display_assumption_proc const* m_save; - scoped_display_assumptions(imp& i, display_assumption_proc const& p): i(i), m_save(i.m_display_assumption) { - i.m_display_assumption = &p; - } - ~scoped_display_assumptions() { - i.m_display_assumption = m_save; - } - }; - - explain m_explain; - - bool_var m_bk; // current Boolean variable we are processing - var m_xk; // current arith variable we are processing - - unsigned m_scope_lvl; - - struct bvar_assignment {}; - struct stage {}; - struct trail { - enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; - kind m_kind; - union { - bool_var m_b; - interval_set * m_old_set; - atom * m_old_eq; - }; - trail(bool_var b, bvar_assignment):m_kind(BVAR_ASSIGNMENT), m_b(b) {} - trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} - trail(bool s, stage):m_kind(s ? NEW_STAGE : NEW_LEVEL) {} - trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} - }; - svector m_trail; - - anum m_zero; - - // configuration - unsigned long long m_max_memory; - unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts - bool m_simplify_cores; - bool m_reorder; - bool m_randomize; - bool m_random_order; - unsigned m_random_seed; - bool m_inline_vars; - bool m_log_lemmas; - bool m_check_lemmas; - unsigned m_max_conflicts; - unsigned m_lemma_count; - bool m_simple_check; - unsigned m_variable_ordering_strategy; - bool m_set_0_more; - bool m_cell_sample; - - struct stats { - unsigned m_simplifications; - unsigned m_restarts; - unsigned m_conflicts; - unsigned m_propagations; - unsigned m_decisions; - unsigned m_stages; - unsigned m_irrational_assignments; // number of irrational witnesses - void reset() { memset(this, 0, sizeof(*this)); } - stats() { reset(); } - }; - // statistics - stats m_stats; - - imp(solver& s, ctx& c): - m_ctx(c), - m_solver(s), - m_rlimit(c.m_rlimit), - m_allocator(c.m_allocator), - m_incremental(c.m_incremental), - m_qm(c.m_qm), - m_pm(c.m_pm), - m_cache(m_pm), - m_am(c.m_am), - m_asm(*this, m_allocator), - m_assignment(m_am), m_lo(m_am), m_hi(m_am), - m_evaluator(s, m_assignment, m_pm, m_allocator), - m_ism(m_evaluator.ism()), - m_num_bool_vars(0), - m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), - m_display_var(m_perm), - m_display_assumption(nullptr), - m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()), - m_scope_lvl(0), - m_lemma(s), - m_lazy_clause(s), - m_lemma_assumptions(m_asm) { - updt_params(c.m_params); - reset_statistics(); - mk_true_bvar(); - m_lemma_count = 0; - } - - ~imp() { - clear(); - } - - void mk_true_bvar() { - bool_var b = mk_bool_var(); - SASSERT(b == true_bool_var); - literal true_lit(b, false); - mk_clause(1, &true_lit, false, nullptr); - } - - void updt_params(params_ref const & _p) { - nlsat_params p(_p); - m_max_memory = p.max_memory(); - m_lazy = p.lazy(); - m_simplify_cores = p.simplify_conflicts(); - bool min_cores = p.minimize_conflicts(); - m_reorder = p.reorder(); - m_randomize = p.randomize(); - m_max_conflicts = p.max_conflicts(); - m_random_order = p.shuffle_vars(); - m_random_seed = p.seed(); - m_inline_vars = p.inline_vars(); - m_log_lemmas = p.log_lemmas(); - m_check_lemmas = p.check_lemmas(); - m_variable_ordering_strategy = p.variable_ordering_strategy(); - - - m_cell_sample = p.cell_sample(); - - - m_ism.set_seed(m_random_seed); - m_explain.set_simplify_cores(m_simplify_cores); - m_explain.set_minimize_cores(min_cores); - m_explain.set_factor(p.factor()); - m_am.updt_params(p.p); - } - - void reset() { - m_explain.reset(); - m_lemma.reset(); - m_lazy_clause.reset(); - undo_until_size(0); - del_clauses(); - del_unref_atoms(); - m_cache.reset(); - m_assignment.reset(); - m_lo.reset(); - m_hi.reset(); - } - - void clear() { - m_explain.reset(); - m_lemma.reset(); - m_lazy_clause.reset(); - undo_until_size(0); - del_clauses(); - del_unref_atoms(); - } - - void checkpoint() { - if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); - if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); - } - - // ----------------------- - // - // Basic - // - // ----------------------- - - unsigned num_bool_vars() const { - return m_num_bool_vars; - } - - unsigned num_vars() const { - return m_is_int.size(); - } - - bool is_int(var x) const { - return m_is_int[x]; - } - - void inc_ref(assumption) {} - - void dec_ref(assumption) {} - - void inc_ref(_assumption_set a) { - if (a != nullptr) m_asm.inc_ref(a); - } - - void dec_ref(_assumption_set a) { - if (a != nullptr) m_asm.dec_ref(a); - } - - void inc_ref(bool_var b) { - if (b == null_bool_var) - return; - atom * a = m_atoms[b]; - if (a == nullptr) - return; - TRACE("ref", display(tout << "inc: " << b << " " << a->ref_count() << " ", *a) << "\n";); - a->inc_ref(); - } - - void inc_ref(literal l) { - inc_ref(l.var()); - } - - void dec_ref(bool_var b) { - if (b == null_bool_var) - return; - atom * a = m_atoms[b]; - if (a == nullptr) - return; - SASSERT(a->ref_count() > 0); - a->dec_ref(); - TRACE("ref", display(tout << "dec: " << b << " " << a->ref_count() << " ", *a) << "\n";); - if (a->ref_count() == 0) - del(a); - } - - void dec_ref(literal l) { - dec_ref(l.var()); - } - - bool is_arith_atom(bool_var b) const { return m_atoms[b] != nullptr; } - - bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } - - var max_var(poly const * p) const { - return m_pm.max_var(p); - } - - var max_var(bool_var b) const { - if (!is_arith_atom(b)) - return null_var; - else - return m_atoms[b]->max_var(); - } - - var max_var(literal l) const { - return max_var(l.var()); - } - - /** - \brief Return the maximum variable occurring in cls. - */ - var max_var(unsigned sz, literal const * cls) const { - var x = null_var; - for (unsigned i = 0; i < sz; i++) { - literal l = cls[i]; - if (is_arith_literal(l)) { - var y = max_var(l); - if (x == null_var || y > x) - x = y; - } - } - return x; - } - - var max_var(clause const & cls) const { - return max_var(cls.size(), cls.data()); - } - - /** - \brief Return the maximum Boolean variable occurring in cls. - */ - bool_var max_bvar(clause const & cls) const { - bool_var b = null_bool_var; - for (literal l : cls) { - if (b == null_bool_var || l.var() > b) - b = l.var(); - } - return b; - } - - /** - \brief Return the degree of the maximal variable of the given atom - */ - unsigned degree(atom const * a) const { - if (a->is_ineq_atom()) { - unsigned max = 0; - unsigned sz = to_ineq_atom(a)->size(); - var x = a->max_var(); - for (unsigned i = 0; i < sz; i++) { - unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); - if (d > max) - max = d; - } - return max; - } - else { - return m_pm.degree(to_root_atom(a)->p(), a->max_var()); - } - } - - /** - \brief Return the degree of the maximal variable in c - */ - unsigned degree(clause const & c) const { - var x = max_var(c); - if (x == null_var) - return 0; - unsigned max = 0; - for (literal l : c) { - atom const * a = m_atoms[l.var()]; - if (a == nullptr) - continue; - unsigned d = degree(a); - if (d > max) - max = d; - } - return max; - } - - // ----------------------- - // - // Variable, Atoms, Clauses & Assumption creation - // - // ----------------------- - - bool_var mk_bool_var_core() { - bool_var b = m_bid_gen.mk(); - m_num_bool_vars++; - m_atoms .setx(b, nullptr, nullptr); - m_bvalues .setx(b, l_undef, l_undef); - m_levels .setx(b, UINT_MAX, UINT_MAX); - m_justifications.setx(b, null_justification, null_justification); - m_bwatches .setx(b, clause_vector(), clause_vector()); - m_dead .setx(b, false, true); - return b; - } - - bool_var mk_bool_var() { - return mk_bool_var_core(); - } - - var mk_var(bool is_int) { - var x = m_pm.mk_var(); - register_var(x, is_int); - return x; - } - void register_var(var x, bool is_int) { - SASSERT(x == num_vars()); - m_is_int. push_back(is_int); - m_watches. push_back(clause_vector()); - m_infeasible.push_back(nullptr); - m_var2eq. push_back(nullptr); - m_perm. push_back(x); - m_inv_perm. push_back(x); - SASSERT(m_is_int.size() == m_watches.size()); - SASSERT(m_is_int.size() == m_infeasible.size()); - SASSERT(m_is_int.size() == m_var2eq.size()); - SASSERT(m_is_int.size() == m_perm.size()); - SASSERT(m_is_int.size() == m_inv_perm.size()); - } - - bool_vector m_found_vars; - void vars(literal l, var_vector& vs) { - vs.reset(); - atom * a = m_atoms[l.var()]; - if (a == nullptr) { - - } - else if (a->is_ineq_atom()) { - unsigned sz = to_ineq_atom(a)->size(); - var_vector new_vs; - for (unsigned j = 0; j < sz; j++) { - m_found_vars.reset(); - m_pm.vars(to_ineq_atom(a)->p(j), new_vs); - for (unsigned i = 0; i < new_vs.size(); ++i) { - if (!m_found_vars.get(new_vs[i], false)) { - m_found_vars.setx(new_vs[i], true, false); - vs.push_back(new_vs[i]); - } - } - } - } - else { - m_pm.vars(to_root_atom(a)->p(), vs); - //vs.erase(max_var(to_root_atom(a)->p())); - vs.push_back(to_root_atom(a)->x()); - } - } - - void deallocate(ineq_atom * a) { - unsigned obj_sz = ineq_atom::get_obj_size(a->size()); - a->~ineq_atom(); - m_allocator.deallocate(obj_sz, a); - } - - void deallocate(root_atom * a) { - a->~root_atom(); - m_allocator.deallocate(sizeof(root_atom), a); - } - - void del(bool_var b) { - SASSERT(m_bwatches[b].empty()); - //SASSERT(m_bvalues[b] == l_undef); - m_num_bool_vars--; - m_dead[b] = true; - m_atoms[b] = nullptr; - m_bvalues[b] = l_undef; - m_bid_gen.recycle(b); - } - - void del(ineq_atom * a) { - CTRACE("nlsat_solver", a->ref_count() > 0, display(tout, *a) << "\n";); - // this triggers in too many benign cases: - // SASSERT(a->ref_count() == 0); - m_ineq_atoms.erase(a); - del(a->bvar()); - unsigned sz = a->size(); - for (unsigned i = 0; i < sz; i++) - m_pm.dec_ref(a->p(i)); - deallocate(a); - } - - void del(root_atom * a) { - SASSERT(a->ref_count() == 0); - m_root_atoms.erase(a); - del(a->bvar()); - m_pm.dec_ref(a->p()); - deallocate(a); - } - - void del(atom * a) { - if (a == nullptr) - return; - TRACE("nlsat_verbose", display(tout << "del: b" << a->m_bool_var << " " << a->ref_count() << " ", *a) << "\n";); - if (a->is_ineq_atom()) - del(to_ineq_atom(a)); - else - del(to_root_atom(a)); - } - - // Delete atoms with ref_count == 0 - void del_unref_atoms() { - for (auto* a : m_atoms) { - del(a); - } - } - - - ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new, bool simplify) { - SASSERT(sz >= 1); - SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); - int sign = 1; - polynomial_ref p(m_pm); - ptr_buffer uniq_ps; - var max = null_var; - for (unsigned i = 0; i < sz; i++) { - p = m_pm.flip_sign_if_lm_neg(ps[i]); - if (p.get() != ps[i] && !is_even[i]) { - sign = -sign; - } - var curr_max = max_var(p.get()); - if (curr_max > max || max == null_var) - max = curr_max; - if (sz == 1 && simplify) { - if (sign < 0) - k = atom::flip(k); - sign = 1; - polynomial::manager::ineq_type t; - switch (k) { - case atom::EQ: t = polynomial::manager::ineq_type::EQ; break; - case atom::LT: t = polynomial::manager::ineq_type::LT; break; - case atom::GT: t = polynomial::manager::ineq_type::GT; break; - default: UNREACHABLE(); break; - } - polynomial::var_vector vars; - m_pm.vars(p, vars); - bool all_int = all_of(vars, [&](var x) { return is_int(x); }); - if (!all_int) - t = polynomial::manager::ineq_type::EQ; - m_pm.gcd_simplify(p, t); - } - uniq_ps.push_back(m_cache.mk_unique(p)); - TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); - //verbose_stream() << "p: " << p.get() << ", uniq: " << uniq_ps.back() << "\n"; - } - void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); - if (sign < 0) - k = atom::flip(k); - ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.data(), is_even, max); - ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); - CTRACE("nlsat_table_bug", tmp_atom != atom, ineq_atom::hash_proc h; - tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var) << "\n";); - CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout << "nonmax: ", *atom, m_display_var) << "\n";); - SASSERT(atom->max_var() == max); - is_new = (atom == tmp_atom); - if (is_new) { - for (unsigned i = 0; i < sz; i++) { - m_pm.inc_ref(atom->p(i)); - } - } - else { - deallocate(tmp_atom); - } - return atom; - } - - bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { - bool is_new = false; - ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new, simplify); - if (!is_new) { - return atom->bvar(); - } - else { - bool_var b = mk_bool_var_core(); - m_atoms[b] = atom; - atom->m_bool_var = b; - TRACE("nlsat_verbose", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); - return b; - } - } - - literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { - SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); - bool is_const = true; - polynomial::manager::scoped_numeral cnst(m_pm.m()); - m_pm.m().set(cnst, 1); - for (unsigned i = 0; i < sz; ++i) { - if (m_pm.is_const(ps[i])) { - if (m_pm.is_zero(ps[i])) { - m_pm.m().set(cnst, 0); - is_const = true; - break; - } - auto const& c = m_pm.coeff(ps[i], 0); - m_pm.m().mul(cnst, c, cnst); - if (is_even[i] && m_pm.m().is_neg(c)) { - m_pm.m().neg(cnst); - } - } - else { - is_const = false; - } - } - if (is_const) { - if (m_pm.m().is_pos(cnst) && k == atom::GT) return true_literal; - if (m_pm.m().is_neg(cnst) && k == atom::LT) return true_literal; - if (m_pm.m().is_zero(cnst) && k == atom::EQ) return true_literal; - return false_literal; - } - return literal(mk_ineq_atom(k, sz, ps, is_even, simplify), false); - } - - bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { - polynomial_ref p1(m_pm), uniq_p(m_pm); - p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. - uniq_p = m_cache.mk_unique(p1); - TRACE("nlsat_solver", tout << x << " " << p1 << " " << uniq_p << "\n";); - SASSERT(i > 0); - SASSERT(x >= max_var(p)); - SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); - - void * mem = m_allocator.allocate(sizeof(root_atom)); - root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); - root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); - SASSERT(old_atom->max_var() == x); - if (old_atom != new_atom) { - deallocate(new_atom); - return old_atom->bvar(); - } - bool_var b = mk_bool_var_core(); - m_atoms[b] = new_atom; - new_atom->m_bool_var = b; - m_pm.inc_ref(new_atom->p()); - return b; - } - - void attach_clause(clause & cls) { - var x = max_var(cls); - if (x != null_var) { - m_watches[x].push_back(&cls); - } - else { - bool_var b = max_bvar(cls); - m_bwatches[b].push_back(&cls); - } - } - - void deattach_clause(clause & cls) { - var x = max_var(cls); - if (x != null_var) { - m_watches[x].erase(&cls); - } - else { - bool_var b = max_bvar(cls); - m_bwatches[b].erase(&cls); - } - } - - void deallocate(clause * cls) { - size_t obj_sz = clause::get_obj_size(cls->size()); - cls->~clause(); - m_allocator.deallocate(obj_sz, cls); - } - - void del_clause(clause * cls) { - deattach_clause(*cls); - m_cid_gen.recycle(cls->id()); - unsigned sz = cls->size(); - for (unsigned i = 0; i < sz; i++) - dec_ref((*cls)[i]); - _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); - dec_ref(a); - deallocate(cls); - } - - void del_clause(clause * cls, clause_vector& clauses) { - clauses.erase(cls); - del_clause(cls); - } - - void del_clauses(ptr_vector & cs) { - for (clause* cp : cs) - del_clause(cp); - cs.reset(); - } - - void del_clauses() { - del_clauses(m_clauses); - del_clauses(m_learned); - del_clauses(m_valids); - } - - // We use a simple heuristic to sort literals - // - bool literals < arith literals - // - sort literals based on max_var - // - sort literal with the same max_var using degree - // break ties using the fact that ineqs are usually cheaper to process than eqs. - struct lit_lt { - imp & m; - lit_lt(imp & _m):m(_m) {} - - bool operator()(literal l1, literal l2) const { - atom * a1 = m.m_atoms[l1.var()]; - atom * a2 = m.m_atoms[l2.var()]; - if (a1 == nullptr && a2 == nullptr) - return l1.index() < l2.index(); - if (a1 == nullptr) - return true; - if (a2 == nullptr) - return false; - var x1 = a1->max_var(); - var x2 = a2->max_var(); - if (x1 < x2) - return true; - if (x1 > x2) - return false; - SASSERT(x1 == x2); - unsigned d1 = m.degree(a1); - unsigned d2 = m.degree(a2); - if (d1 < d2) - return true; - if (d1 > d2) - return false; - if (!a1->is_eq() && a2->is_eq()) - return true; - if (a1->is_eq() && !a2->is_eq()) - return false; - return l1.index() < l2.index(); - } - }; - - class scoped_bool_vars { - imp& s; - svector vec; - public: - scoped_bool_vars(imp& s):s(s) {} - ~scoped_bool_vars() { - for (bool_var v : vec) { - s.dec_ref(v); - } - } - void push_back(bool_var v) { - s.inc_ref(v); - vec.push_back(v); - } - bool_var const* begin() const { return vec.begin(); } - bool_var const* end() const { return vec.end(); } - bool_var operator[](bool_var v) const { return vec[v]; } - }; - - void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { - TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; - display(tout);); - IF_VERBOSE(2, display(verbose_stream() << "check lemma " << (is_valid?"valid: ":"consequence: "), n, cls) << "\n"); - for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); - scoped_suspend_rlimit _limit(m_rlimit); - ctx c(m_rlimit, m_ctx.m_params, m_ctx.m_incremental); - solver solver2(c); - imp& checker = *(solver2.m_imp); - checker.m_check_lemmas = false; - checker.m_log_lemmas = false; - checker.m_inline_vars = false; - - auto pconvert = [&](poly* p) { - return convert(m_pm, p, checker.m_pm); - }; - - // need to translate Boolean variables and literals - scoped_bool_vars tr(checker); - for (var x = 0; x < m_is_int.size(); ++x) { - checker.register_var(x, is_int(x)); - } - bool_var bv = 0; - tr.push_back(bv); - for (bool_var b = 1; b < m_atoms.size(); ++b) { - atom* a = m_atoms[b]; - if (a == nullptr) { - bv = checker.mk_bool_var(); - } - else if (a->is_ineq_atom()) { - ineq_atom& ia = *to_ineq_atom(a); - unsigned sz = ia.size(); - polynomial_ref_vector ps(checker.m_pm); - bool_vector is_even; - for (unsigned i = 0; i < sz; ++i) { - ps.push_back(pconvert(ia.p(i))); - is_even.push_back(ia.is_even(i)); - } - bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.data(), is_even.data()); - } - else if (a->is_root_atom()) { - root_atom& r = *to_root_atom(a); - if (r.x() >= max_var(r.p())) { - // permutation may be reverted after check completes, - // but then root atoms are not used in lemmas. - bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), pconvert(r.p())); - } - } - else { - UNREACHABLE(); - } - tr.push_back(bv); - } - if (!is_valid) { - for (clause* c : m_clauses) { - if (!a && c->assumptions()) { - continue; - } - literal_vector lits; - for (literal lit : *c) { - lits.push_back(literal(tr[lit.var()], lit.sign())); - } - checker.mk_external_clause(lits.size(), lits.data(), nullptr); - } - } - for (unsigned i = 0; i < n; ++i) { - literal lit = cls[i]; - literal nlit(tr[lit.var()], !lit.sign()); - checker.mk_external_clause(1, &nlit, nullptr); - } - lbool r = checker.check(); - if (r == l_true) { - for (bool_var b : tr) { - literal lit(b, false); - IF_VERBOSE(0, checker.display(verbose_stream(), lit) << " := " << checker.value(lit) << "\n"); - TRACE("nlsat", checker.display(tout, lit) << " := " << checker.value(lit) << "\n";); - } - for (clause* c : m_learned) { - bool found = false; - for (literal lit: *c) { - literal tlit(tr[lit.var()], lit.sign()); - found |= checker.value(tlit) == l_true; - } - if (!found) { - IF_VERBOSE(0, display(verbose_stream() << "violdated clause: ", *c) << "\n"); - TRACE("nlsat", display(tout << "violdated clause: ", *c) << "\n";); - } - } - for (clause* c : m_valids) { - bool found = false; - for (literal lit: *c) { - literal tlit(tr[lit.var()], lit.sign()); - found |= checker.value(tlit) == l_true; - } - if (!found) { - IF_VERBOSE(0, display(verbose_stream() << "violdated tautology clause: ", *c) << "\n"); - TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); - } - } - throw default_exception("lemma did not check"); - UNREACHABLE(); - } - } - - void log_lemma(std::ostream& out, clause const& cls) { - log_lemma(out, cls.size(), cls.data(), false); - } - - void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { - ++m_lemma_count; - out << "(set-logic NRA)\n"; - if (is_valid) { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); - } - else - display_smt2(out); - for (unsigned i = 0; i < n; ++i) - display_smt2(out << "(assert ", ~cls[i]) << ")\n"; - display(out << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"; - out << "(check-sat)\n(reset)\n"; - - TRACE("nlsat", display(tout << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"); - } - - clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { - SASSERT(num_lits > 0); - unsigned cid = m_cid_gen.mk(); - void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); - clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); - for (unsigned i = 0; i < num_lits; i++) - inc_ref(lits[i]); - inc_ref(a); - return cls; - } - - clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { - if (num_lits == 0) { - num_lits = 1; - lits = &false_literal; - } - SASSERT(num_lits > 0); - clause * cls = mk_clause_core(num_lits, lits, learned, a); - TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); - std::sort(cls->begin(), cls->end(), lit_lt(*this)); - TRACE("nlsat", display(tout << " after sort:\n", *cls) << "\n";); - if (learned && m_log_lemmas) { - log_lemma(verbose_stream(), *cls); - } - if (learned && m_check_lemmas && false) { - check_lemma(cls->size(), cls->data(), false, cls->assumptions()); - } - if (learned) - m_learned.push_back(cls); - else - m_clauses.push_back(cls); - attach_clause(*cls); - return cls; - } - - void mk_external_clause(unsigned num_lits, literal const * lits, assumption a) { - _assumption_set as = nullptr; - if (a != nullptr) - as = m_asm.mk_leaf(a); - if (num_lits == 0) { - num_lits = 1; - lits = &false_literal; - } - mk_clause(num_lits, lits, false, as); - } - - // ----------------------- - // - // Search - // - // ----------------------- - - void save_assign_trail(bool_var b) { - m_trail.push_back(trail(b, bvar_assignment())); - } - - void save_set_updt_trail(interval_set * old_set) { - m_trail.push_back(trail(old_set)); - } - - void save_updt_eq_trail(atom * old_eq) { - m_trail.push_back(trail(old_eq)); - } - - void save_new_stage_trail() { - m_trail.push_back(trail(true, stage())); - } - - void save_new_level_trail() { - m_trail.push_back(trail(false, stage())); - } - - void undo_bvar_assignment(bool_var b) { - m_bvalues[b] = l_undef; - m_levels[b] = UINT_MAX; - del_jst(m_allocator, m_justifications[b]); - m_justifications[b] = null_justification; - if (m_atoms[b] == nullptr && b < m_bk) - m_bk = b; - } - - void undo_set_updt(interval_set * old_set) { - if (m_xk == null_var) - return; - var x = m_xk; - if (x < m_infeasible.size()) { - m_ism.dec_ref(m_infeasible[x]); - m_infeasible[x] = old_set; - } - } - - void undo_new_stage() { - if (m_xk == 0) { - m_xk = null_var; - } - else if (m_xk != null_var) { - m_xk--; - m_assignment.reset(m_xk); - } - } - - void undo_new_level() { - SASSERT(m_scope_lvl > 0); - m_scope_lvl--; - m_evaluator.pop(1); - } - - void undo_updt_eq(atom * a) { - if (m_var2eq.size() > m_xk) - m_var2eq[m_xk] = a; - } - - template - void undo_until(Predicate const & pred) { - while (pred() && !m_trail.empty()) { - trail & t = m_trail.back(); - switch (t.m_kind) { - case trail::BVAR_ASSIGNMENT: - undo_bvar_assignment(t.m_b); - break; - case trail::INFEASIBLE_UPDT: - undo_set_updt(t.m_old_set); - break; - case trail::NEW_STAGE: - undo_new_stage(); - break; - case trail::NEW_LEVEL: - undo_new_level(); - break; - case trail::UPDT_EQ: - undo_updt_eq(t.m_old_eq); - break; - default: - break; - } - m_trail.pop_back(); - } - } - - struct size_pred { - svector & m_trail; - unsigned m_old_size; - size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} - bool operator()() const { return m_trail.size() > m_old_size; } - }; - - // Keep undoing until trail has the given size - void undo_until_size(unsigned old_size) { - SASSERT(m_trail.size() >= old_size); - undo_until(size_pred(m_trail, old_size)); - } - - struct stage_pred { - var const & m_xk; - var m_target; - stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} - bool operator()() const { return m_xk != m_target; } - }; - - // Keep undoing until stage is new_xk - void undo_until_stage(var new_xk) { - undo_until(stage_pred(m_xk, new_xk)); - } - - struct level_pred { - unsigned const & m_scope_lvl; - unsigned m_new_lvl; - level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} - bool operator()() const { return m_scope_lvl > m_new_lvl; } - }; - - // Keep undoing until level is new_lvl - void undo_until_level(unsigned new_lvl) { - undo_until(level_pred(m_scope_lvl, new_lvl)); - } - - struct unassigned_pred { - bool_var m_b; - svector const & m_bvalues; - unassigned_pred(svector const & bvalues, bool_var b): - m_b(b), - m_bvalues(bvalues) {} - bool operator()() const { return m_bvalues[m_b] != l_undef; } - }; - - // Keep undoing until b is unassigned - void undo_until_unassigned(bool_var b) { - undo_until(unassigned_pred(m_bvalues, b)); - SASSERT(m_bvalues[b] == l_undef); - } - - struct true_pred { - bool operator()() const { return true; } - }; - - void undo_until_empty() { - undo_until(true_pred()); - } - - /** - \brief Create a new scope level - */ - void new_level() { - m_evaluator.push(); - m_scope_lvl++; - save_new_level_trail(); - } - - /** - \brief Return the value of the given literal that was assigned by the search - engine. - */ - lbool assigned_value(literal l) const { - bool_var b = l.var(); - if (l.sign()) - return ~m_bvalues[b]; - else - return m_bvalues[b]; - } - - /** - \brief Assign literal using the given justification - */ - void assign(literal l, justification j) { - TRACE("nlsat_assign", - display(tout << "assigning literal: ", l); - display(tout << " <- ", j);); - - SASSERT(assigned_value(l) == l_undef); - SASSERT(j != null_justification); - SASSERT(!j.is_null()); - if (j.is_decision()) - m_stats.m_decisions++; - else - m_stats.m_propagations++; - bool_var b = l.var(); - m_bvalues[b] = to_lbool(!l.sign()); - m_levels[b] = m_scope_lvl; - m_justifications[b] = j; - save_assign_trail(b); - updt_eq(b, j); - TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << "\n";); - } - - /** - \brief Create a "case-split" - */ - void decide(literal l) { - new_level(); - assign(l, decided_justification); - } - - /** - \brief Return the value of a literal as defined in Dejan and Leo's paper. - */ - lbool value(literal l) { - lbool val = assigned_value(l); - if (val != l_undef) { - TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); - return val; - } - bool_var b = l.var(); - atom * a = m_atoms[b]; - if (a == nullptr) { - TRACE("nlsat_verbose", display(tout << " no atom for ", l) << "\n";); - return l_undef; - } - var max = a->max_var(); - if (!m_assignment.is_assigned(max)) { - TRACE("nlsat_verbose", display(tout << " maximal variable not assigned ", l) << "\n";); - return l_undef; - } - val = to_lbool(m_evaluator.eval(a, l.sign())); - TRACE("nlsat_verbose", display(tout << " evaluated value " << val << " for ", l) << "\n";); - TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; - tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; - display_assignment(tout);); - return val; - } - - /** - \brief Return true if the given clause is already satisfied in the current partial interpretation. - */ - bool is_satisfied(clause const & cls) const { - for (literal l : cls) { - if (const_cast(this)->value(l) == l_true) { - TRACE("value_bug:", tout << l << " := true\n";); - return true; - } - } - return false; - } - - /** - \brief Return true if the given clause is false in the current partial interpretation. - */ - bool is_inconsistent(unsigned sz, literal const * cls) { - for (unsigned i = 0; i < sz; i++) { - if (value(cls[i]) != l_false) { - TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); - return false; - } - } - return true; - } - - /** - \brief Process a clauses that contains only Boolean literals. - */ - bool process_boolean_clause(clause const & cls) { - SASSERT(m_xk == null_var); - unsigned num_undef = 0; - unsigned first_undef = UINT_MAX; - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = cls[i]; - SASSERT(m_atoms[l.var()] == nullptr); - SASSERT(value(l) != l_true); - if (value(l) == l_false) - continue; - SASSERT(value(l) == l_undef); - num_undef++; - if (first_undef == UINT_MAX) - first_undef = i; - } - if (num_undef == 0) - return false; - SASSERT(first_undef != UINT_MAX); - if (num_undef == 1) - assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause - else - decide(cls[first_undef]); - return true; - } - - /** - \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. - */ - literal_vector core; - ptr_vector clauses; - void R_propagate(literal l, interval_set const * s, bool include_l = true) { - m_ism.get_justifications(s, core, clauses); - if (include_l) - core.push_back(~l); - auto j = mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data()); - TRACE("nlsat_resolve", display(tout, j); display_eval(tout << "evaluated:", j)); - assign(l, j); - SASSERT(value(l) == l_true); - } - - /** - \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s - */ - void updt_infeasible(interval_set const * s) { - SASSERT(m_xk != null_var); - interval_set * xk_set = m_infeasible[m_xk]; - save_set_updt_trail(xk_set); - interval_set_ref new_set(m_ism); - TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set) << "\n"; m_ism.display(tout, s) << "\n";); - new_set = m_ism.mk_union(s, xk_set); - TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set) << "\n";); - SASSERT(!m_ism.is_full(new_set)); - m_ism.inc_ref(new_set); - m_infeasible[m_xk] = new_set; - } - - /** - \brief Update m_var2eq mapping. - */ - void updt_eq(bool_var b, justification j) { - if (!m_simplify_cores) - return; - if (m_bvalues[b] != l_true) - return; - atom * a = m_atoms[b]; - if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) - return; - switch (j.get_kind()) { - case justification::CLAUSE: - if (j.get_clause()->assumptions() != nullptr) return; - break; - case justification::LAZY: - if (j.get_lazy()->num_clauses() > 0) return; - if (j.get_lazy()->num_lits() > 0) return; - break; - default: - break; - } - var x = m_xk; - SASSERT(a->max_var() == x); - SASSERT(x != null_var); - if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) - return; // we only update m_var2eq if the new equality has smaller degree - TRACE("nlsat_simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; - tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)) << "\n"; - display(tout, j); - ); - save_updt_eq_trail(m_var2eq[x]); - m_var2eq[x] = a; - } - - /** - \brief Process a clause that contains nonlinear arithmetic literals - - If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 - */ - bool process_arith_clause(clause const & cls, bool satisfy_learned) { - if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { - TRACE("nlsat", tout << "skip learned\n";); - return true; // ignore lemmas in super lazy mode - } - SASSERT(m_xk == max_var(cls)); - unsigned num_undef = 0; // number of undefined literals - unsigned first_undef = UINT_MAX; // position of the first undefined literal - interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal - interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable - SASSERT(!m_ism.is_full(xk_set)); - for (unsigned idx = 0; idx < cls.size(); ++idx) { - literal l = cls[idx]; - checkpoint(); - if (value(l) == l_false) - continue; - if (value(l) == l_true) - return true; // could happen if clause is a tautology - CTRACE("nlsat", max_var(l) != m_xk || value(l) != l_undef, display(tout); - tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l) << "\n"; - display(tout, cls) << "\n";); - SASSERT(value(l) == l_undef); - SASSERT(max_var(l) == m_xk); - bool_var b = l.var(); - atom * a = m_atoms[b]; - SASSERT(a != nullptr); - interval_set_ref curr_set(m_ism); - curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); - TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; - display(tout, cls) << "\n";); - if (m_ism.is_empty(curr_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); - R_propagate(l, nullptr); - SASSERT(is_satisfied(cls)); - return true; - } - if (m_ism.is_full(curr_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); - R_propagate(~l, nullptr); - continue; - } - if (m_ism.subset(curr_set, xk_set)) { - TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); - R_propagate(l, xk_set); - return true; - } - interval_set_ref tmp(m_ism); - tmp = m_ism.mk_union(curr_set, xk_set); - if (m_ism.is_full(tmp)) { - TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; - display(tout, cls) << "\n"; - m_ism.display(tout, tmp); tout << "\n"; - ); - R_propagate(~l, tmp, false); - continue; - } - num_undef++; - if (first_undef == UINT_MAX) { - first_undef = idx; - first_undef_set = curr_set; - } - } - TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); - if (num_undef == 0) - return false; - SASSERT(first_undef != UINT_MAX); - if (num_undef == 1) { - // unit clause - assign(cls[first_undef], mk_clause_jst(&cls)); - updt_infeasible(first_undef_set); - } - else if ( satisfy_learned || - !cls.is_learned() /* must always satisfy input clauses */ || - m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { - decide(cls[first_undef]); - updt_infeasible(first_undef_set); - } - else { - TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() - << ", lazy: " << m_lazy << "\n";); - } - return true; - } - - /** - \brief Try to satisfy the given clause. Return true if succeeded. - - If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 - */ - bool process_clause(clause const & cls, bool satisfy_learned) { - if (is_satisfied(cls)) - return true; - if (m_xk == null_var) - return process_boolean_clause(cls); - else - return process_arith_clause(cls, satisfy_learned); - } - - /** - \brief Try to satisfy the given "set" of clauses. - Return 0, if the set was satisfied, or the violating clause otherwise - */ - clause * process_clauses(clause_vector const & cs) { - for (clause* c : cs) { - if (!process_clause(*c, false)) - return c; - } - return nullptr; // succeeded - } - - /** - \brief Make sure m_bk is the first unassigned pure Boolean variable. - Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. - */ - void peek_next_bool_var() { - while (m_bk < m_atoms.size()) { - if (!m_dead[m_bk] && m_atoms[m_bk] == nullptr && m_bvalues[m_bk] == l_undef) { - return; - } - m_bk++; - } - m_bk = null_bool_var; - } - - /** - \brief Create a new stage. See Dejan and Leo's paper. - */ - void new_stage() { - m_stats.m_stages++; - save_new_stage_trail(); - if (m_xk == null_var) - m_xk = 0; - else - m_xk++; - } - - /** - \brief Assign m_xk - */ - void select_witness() { - scoped_anum w(m_am); - SASSERT(!m_ism.is_full(m_infeasible[m_xk])); - m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); - TRACE("nlsat", - tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; - tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); - TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); - if (!m_am.is_rational(w)) - m_stats.m_irrational_assignments++; - m_assignment.set_core(m_xk, w); - } - - - - bool is_satisfied() { - if (m_bk == null_bool_var && m_xk >= num_vars()) { - TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); - fix_patch(); - SASSERT(check_satisfied(m_clauses)); - return true; // all variables were assigned, and all clauses were satisfied. - } - else { - return false; - } - } - - - /** - \brief main procedure - */ - lbool search() { - TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); - TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); - TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); - TRACE("nlsat_mathematica", display_mathematica(tout);); - TRACE("nlsat", display_smt2(tout);); - m_bk = 0; - m_xk = null_var; - - while (true) { - if (should_reorder()) - do_reorder(); - -#if 0 - if (should_gc()) - do_gc(); -#endif - - if (should_simplify()) - do_simplify(); - - CASSERT("nlsat", check_satisfied()); - if (m_xk == null_var) { - peek_next_bool_var(); - if (m_bk == null_bool_var) - new_stage(); // move to arith vars - } - else { - new_stage(); // peek next arith var - } - TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); - if (is_satisfied()) { - return l_true; - } - while (true) { - TRACE("nlsat_verbose", tout << "processing variable "; - if (m_xk != null_var) { - m_display_var(tout, m_xk); tout << " " << m_watches[m_xk].size(); - } - else { - tout << m_bwatches[m_bk].size() << " boolean b" << m_bk; - } - tout << "\n";); - checkpoint(); - clause * conflict_clause; - if (m_xk == null_var) - conflict_clause = process_clauses(m_bwatches[m_bk]); - else - conflict_clause = process_clauses(m_watches[m_xk]); - if (conflict_clause == nullptr) - break; - if (!resolve(*conflict_clause)) - return l_false; - if (m_stats.m_conflicts >= m_max_conflicts) - return l_undef; - log(); - } - - if (m_xk == null_var) { - if (m_bvalues[m_bk] == l_undef) { - decide(literal(m_bk, true)); - m_bk++; - } - } - else { - select_witness(); - } - } - } - - void gc() { - if (m_learned.size() <= 4*m_clauses.size()) - return; - reset_watches(); - reinit_cache(); - unsigned j = 0; - for (unsigned i = 0; i < m_learned.size(); ++i) { - auto cls = m_learned[i]; - if (i - j < m_clauses.size() && cls->size() > 1 && !cls->is_active()) - del_clause(cls); - else { - m_learned[j++] = cls; - cls->set_active(false); - } - } - m_learned.shrink(j); - reattach_arith_clauses(m_clauses); - reattach_arith_clauses(m_learned); - } - - - bool should_gc() { - return m_learned.size() > 10 * m_clauses.size(); - } - - void do_gc() { - undo_to_base(); - gc(); - } - - void undo_to_base() { - init_search(); - m_bk = 0; - m_xk = null_var; - } - - unsigned m_restart_threshold = 10000; - bool should_reorder() { - return m_stats.m_conflicts > 0 && m_stats.m_conflicts % m_restart_threshold == 0; - } - - void do_reorder() { - undo_to_base(); - m_stats.m_restarts++; - m_stats.m_conflicts++; - if (m_reordered) - restore_order(); - apply_reorder(); - } - - bool m_did_simplify = false; - bool should_simplify() { - return - !m_did_simplify && m_inline_vars && - !m_incremental && m_stats.m_conflicts > 100; - } - - void do_simplify() { - undo_to_base(); - m_did_simplify = true; - m_simplify(); - } - - unsigned m_next_conflict = 100; - void log() { - if (m_stats.m_conflicts != 1 && m_stats.m_conflicts < m_next_conflict) - return; - m_next_conflict += 100; - IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_stats.m_conflicts - << " :decisions " << m_stats.m_decisions - << " :propagations " << m_stats.m_propagations - << " :clauses " << m_clauses.size() - << " :learned " << m_learned.size() << ")\n"); - } - - - lbool search_check() { - lbool r = l_undef; - m_stats.m_conflicts = 0; - m_stats.m_restarts = 0; - m_next_conflict = 0; - while (true) { - r = search(); - if (r != l_true) - break; - ++m_stats.m_restarts; - vector> bounds; - - for (var x = 0; x < num_vars(); x++) { - if (is_int(x) && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { - scoped_anum v(m_am), vlo(m_am); - v = m_assignment.value(x); - rational lo; - m_am.int_lt(v, vlo); - if (!m_am.is_int(vlo)) - continue; - m_am.to_rational(vlo, lo); - // derive tight bounds. - while (true) { - lo++; - if (!m_am.gt(v, lo.to_mpq())) { - lo--; - break; - } - } - bounds.push_back(std::make_pair(x, lo)); - } - } - if (bounds.empty()) - break; - - gc(); - if (m_stats.m_restarts % 10 == 0) { - if (m_reordered) - restore_order(); - apply_reorder(); - } - - init_search(); - IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts - << " :decisions " << m_stats.m_decisions - << " :propagations " << m_stats.m_propagations - << " :clauses " << m_clauses.size() - << " :learned " << m_learned.size() << ")\n"); - for (auto const& b : bounds) { - var x = b.first; - rational lo = b.second; - rational hi = lo + 1; // rational::one(); - bool is_even = false; - polynomial_ref p(m_pm); - rational one(1); - m_lemma.reset(); - p = m_pm.mk_linear(1, &one, &x, -lo); - poly* p1 = p.get(); - m_lemma.push_back(~mk_ineq_literal(atom::GT, 1, &p1, &is_even)); - p = m_pm.mk_linear(1, &one, &x, -hi); - poly* p2 = p.get(); - m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); - - // perform branch and bound - clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); - IF_VERBOSE(4, display(verbose_stream(), *cls) << "\n"); - if (cls) { - TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); - } - } - } - return r; - } - - bool m_reordered = false; - bool simple_check() { - literal_vector learned_unit; - simple_checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); - if (!checker()) - return false; - for (unsigned i = 0, sz = learned_unit.size(); i < sz; ++i) { - clause *cla = mk_clause(1, &learned_unit[i], true, nullptr); - if (m_atoms[learned_unit[i].var()] == nullptr) { - assign(learned_unit[i], mk_clause_jst(cla)); - } - } - return true; - } - - - void run_variable_ordering_strategy() { - TRACE("reorder", tout << "runing vos: " << m_variable_ordering_strategy << '\n';); - - unsigned num = num_vars(); - vos_var_info_collector vos_collector(m_pm, m_atoms, num, m_variable_ordering_strategy); - vos_collector.collect(m_clauses); - vos_collector.collect(m_learned); - - var_vector perm; - vos_collector(perm); - reorder(perm.size(), perm.data()); - } - - void apply_reorder() { - m_reordered = false; - if (!can_reorder()) - ; - else if (m_random_order) { - shuffle_vars(); - m_reordered = true; - } - else if (m_reorder) { - heuristic_reorder(); - m_reordered = true; - } - } - - lbool check() { - - if (m_simple_check) { - if (!simple_check()) { - TRACE("simple_check", tout << "real unsat\n";); - return l_false; - } - TRACE("simple_checker_learned", - tout << "simple check done\n"; - ); - } - - TRACE("nlsat_smt2", display_smt2(tout);); - TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); - init_search(); - m_explain.set_full_dimensional(is_full_dimensional()); - bool reordered = false; - - - if (!can_reorder()) { - - } - else if (m_variable_ordering_strategy > 0) { - run_variable_ordering_strategy(); - reordered = true; - } - else if (m_random_order) { - shuffle_vars(); - reordered = true; - } - else if (m_reorder) { - heuristic_reorder(); - reordered = true; - } - sort_watched_clauses(); - lbool r = search_check(); - CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); - if (reordered) { - restore_order(); - } - CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); - CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); - SASSERT(r != l_true || check_satisfied(m_clauses)); - return r; - } - - void init_search() { - undo_until_empty(); - while (m_scope_lvl > 0) { - undo_new_level(); - } - m_xk = null_var; - for (unsigned i = 0; i < m_bvalues.size(); ++i) { - m_bvalues[i] = l_undef; - } - m_assignment.reset(); - } - - lbool check(literal_vector& assumptions) { - literal_vector result; - unsigned sz = assumptions.size(); - literal const* ptr = assumptions.data(); - for (unsigned i = 0; i < sz; ++i) { - mk_external_clause(1, ptr+i, (assumption)(ptr+i)); - } - display_literal_assumption dla(*this, assumptions); - scoped_display_assumptions _scoped_display(*this, dla); - lbool r = check(); - - if (r == l_false) { - // collect used literals from m_lemma_assumptions - vector deps; - get_core(deps); - for (unsigned i = 0; i < deps.size(); ++i) { - literal const* lp = (literal const*)(deps[i]); - if (ptr <= lp && lp < ptr + sz) { - result.push_back(*lp); - } - } - } - collect(assumptions, m_clauses); - collect(assumptions, m_learned); - del_clauses(m_valids); - if (m_check_lemmas) { - for (clause* c : m_learned) { - check_lemma(c->size(), c->data(), false, nullptr); - } - } - -#if 0 - for (clause* c : m_learned) { - IF_VERBOSE(0, display(verbose_stream() << "KEEP: ", c->size(), c->c_ptr()) << "\n"); - } -#endif - assumptions.reset(); - assumptions.append(result); - return r; - } - - void get_core(vector& deps) { - m_asm.linearize(m_lemma_assumptions.get(), deps); - } - - void collect(literal_vector const& assumptions, clause_vector& clauses) { - unsigned j = 0; - for (clause * c : clauses) { - if (collect(assumptions, *c)) { - del_clause(c); - } - else { - clauses[j++] = c; - } - } - clauses.shrink(j); - } - - bool collect(literal_vector const& assumptions, clause const& c) { - unsigned sz = assumptions.size(); - literal const* ptr = assumptions.data(); - _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); - if (asms == nullptr) { - return false; - } - vector deps; - m_asm.linearize(asms, deps); - for (auto dep : deps) { - if (ptr <= dep && dep < ptr + sz) { - return true; - } - } - return false; - } - - // ----------------------- - // - // Conflict Resolution - // - // ----------------------- - svector m_marks; // bool_var -> bool temp mark used during conflict resolution - unsigned m_num_marks; - scoped_literal_vector m_lemma; - scoped_literal_vector m_lazy_clause; - assumption_set_ref m_lemma_assumptions; // assumption tracking - - // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. - - bool check_marks() { - for (unsigned m : m_marks) { - (void)m; - SASSERT(m == 0); - } - return true; - } - - unsigned scope_lvl() const { return m_scope_lvl; } - - bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } - - void mark(bool_var b) { m_marks.setx(b, 1, 0); } - - void reset_mark(bool_var b) { m_marks[b] = 0; } - - void reset_marks() { - for (auto const& l : m_lemma) { - reset_mark(l.var()); - } - } - - void process_antecedent(literal antecedent) { - checkpoint(); - bool_var b = antecedent.var(); - TRACE("nlsat_resolve", display(tout << "resolving antecedent: ", antecedent) << "\n";); - if (assigned_value(antecedent) == l_undef) { - checkpoint(); - // antecedent must be false in the current arith interpretation - SASSERT(value(antecedent) == l_false || m_rlimit.is_canceled()); - if (!is_marked(b)) { - SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage - TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); - mark(b); - m_lemma.push_back(antecedent); - } - return; - } - - unsigned b_lvl = m_levels[b]; - TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); - if (!is_marked(b)) { - mark(b); - if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { - TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); - m_num_marks++; - } - else { - TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; - tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); - m_lemma.push_back(antecedent); - } - } - } - - void resolve_clause(bool_var b, unsigned sz, literal const * c) { - TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b) << "\n"; display(tout, sz, c); tout << "\n";); - TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); - - for (unsigned i = 0; i < sz; i++) { - if (c[i].var() != b) - process_antecedent(c[i]); - } - } - - void resolve_clause(bool_var b, clause & c) { - TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); - c.set_active(true); - resolve_clause(b, c.size(), c.data()); - m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); - } - - void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { - TRACE("nlsat_resolve", tout << "resolving lazy_justification for b" << b << "\n";); - unsigned sz = jst.num_lits(); - - // Dump lemma as Mathematica formula that must be true, - // if the current interpretation (really) makes the core in jst infeasible. - TRACE("nlsat_mathematica", - tout << "assignment lemma\n"; - literal_vector core; - for (unsigned i = 0; i < sz; i++) { - core.push_back(~jst.lit(i)); - } - display_mathematica_lemma(tout, core.size(), core.data(), true);); - - m_lazy_clause.reset(); - m_explain(jst.num_lits(), jst.lits(), m_lazy_clause); - for (unsigned i = 0; i < sz; i++) - m_lazy_clause.push_back(~jst.lit(i)); - - // lazy clause is a valid clause - TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); - TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.data()); tout << "\n";); - TRACE("nlsat_resolve", - tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; - tout << "new valid clause:\n"; - display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";); - - - if (m_log_lemmas) - log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true); - - if (m_check_lemmas) { - check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), true, nullptr); - m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr)); - } - -#ifdef Z3DEBUG - { - unsigned sz = m_lazy_clause.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = m_lazy_clause[i]; - if (l.var() != b) { - if (value(l) != l_false) - display(verbose_stream() << value(l) << " ", 1, &l); - SASSERT(value(l) == l_false || m_rlimit.is_canceled()); - } - else { - SASSERT(value(l) == l_true || m_rlimit.is_canceled()); - SASSERT(!l.sign() || m_bvalues[b] == l_false); - SASSERT(l.sign() || m_bvalues[b] == l_true); - } - } - } -#endif - checkpoint(); - resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.data()); - - for (unsigned i = 0; i < jst.num_clauses(); ++i) { - clause const& c = jst.clause(i); - TRACE("nlsat", display(tout << "adding clause assumptions ", c) << "\n";); - m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); - } - } - - /** - \brief Return true if all literals in ls are from previous stages. - */ - bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { - for (unsigned i = 0; i < num; i++) { - if (max_var(ls[i]) == m_xk) - return false; - } - return true; - } - - /** - \brief Return the maximum scope level in ls. - - \pre This method assumes value(ls[i]) is l_false for i in [0, num) - */ - unsigned max_scope_lvl(unsigned num, literal const * ls) { - unsigned max = 0; - for (unsigned i = 0; i < num; i++) { - literal l = ls[i]; - bool_var b = l.var(); - SASSERT(value(ls[i]) == l_false); - if (assigned_value(l) == l_false) { - unsigned lvl = m_levels[b]; - if (lvl > max) - max = lvl; - } - else { - // l must be a literal from a previous stage that is false in the current interpretation - SASSERT(assigned_value(l) == l_undef); - SASSERT(max_var(b) != null_var); - SASSERT(m_xk != null_var); - SASSERT(max_var(b) < m_xk); - } - } - return max; - } - - /** - \brief Remove literals of the given lvl (that are in the current stage) from lemma. - - \pre This method assumes value(ls[i]) is l_false for i in [0, num) - */ - void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { - TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); - unsigned sz = lemma.size(); - unsigned j = 0; - for (unsigned i = 0; i < sz; i++) { - literal l = lemma[i]; - bool_var b = l.var(); - SASSERT(is_marked(b)); - SASSERT(value(lemma[i]) == l_false); - if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { - m_num_marks++; - continue; - } - lemma.set(j, l); - j++; - } - lemma.shrink(j); - } - - /** - \brief Return true if it is a Boolean lemma. - */ - bool is_bool_lemma(unsigned sz, literal const * ls) const { - for (unsigned i = 0; i < sz; i++) { - if (m_atoms[ls[i].var()] != nullptr) - return false; - } - return true; - } - - - /** - Return the maximal decision level in lemma for literals in the first sz-1 positions that - are at the same stage. If all these literals are from previous stages, - we just backtrack the current level. - */ - unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { - SASSERT(!is_bool_lemma(sz, lemma)); - unsigned new_lvl = 0; - bool found_lvl = false; - for (unsigned i = 0; i < sz - 1; i++) { - literal l = lemma[i]; - if (max_var(l) == m_xk) { - bool_var b = l.var(); - if (!found_lvl) { - found_lvl = true; - new_lvl = m_levels[b]; - } - else { - if (m_levels[b] > new_lvl) - new_lvl = m_levels[b]; - } - } - } - SASSERT(!found_lvl || new_lvl < scope_lvl()); - if (!found_lvl) { - TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); - new_lvl = scope_lvl() - 1; - } - return new_lvl; - } - - struct scoped_reset_marks { - imp& i; - scoped_reset_marks(imp& i):i(i) {} - ~scoped_reset_marks() { if (i.m_num_marks > 0) { i.m_num_marks = 0; for (char& m : i.m_marks) m = 0; } } - }; - - - /** - \brief Return true if the conflict was solved. - */ - bool resolve(clause & conflict) { - clause * conflict_clause = &conflict; - m_lemma_assumptions = nullptr; - start: - SASSERT(check_marks()); - TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); - TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); - m_stats.m_conflicts++; - TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; - tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; - tout << "scope_lvl: " << scope_lvl() << "\n"; - tout << "current assignment\n"; display_assignment(tout);); - - m_num_marks = 0; - m_lemma.reset(); - m_lemma_assumptions = nullptr; - scoped_reset_marks _sr(*this); - resolve_clause(null_bool_var, *conflict_clause); - - unsigned top = m_trail.size(); - bool found_decision; - while (true) { - found_decision = false; - while (m_num_marks > 0) { - checkpoint(); - SASSERT(top > 0); - trail & t = m_trail[top-1]; - SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage - if (t.m_kind == trail::BVAR_ASSIGNMENT) { - bool_var b = t.m_b; - if (is_marked(b)) { - TRACE("nlsat_resolve", tout << "found marked: b" << b << "\n"; display_atom(tout, b) << "\n";); - m_num_marks--; - reset_mark(b); - justification jst = m_justifications[b]; - switch (jst.get_kind()) { - case justification::CLAUSE: - resolve_clause(b, *(jst.get_clause())); - break; - case justification::LAZY: - resolve_lazy_justification(b, *(jst.get_lazy())); - break; - case justification::DECISION: - SASSERT(m_num_marks == 0); - found_decision = true; - TRACE("nlsat_resolve", tout << "found decision\n";); - m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); - break; - default: - UNREACHABLE(); - break; - } - } - } - top--; - } - - // m_lemma is an implicating clause after backtracking current scope level. - if (found_decision) - break; - - // If lemma only contains literals from previous stages, then we can stop. - // We make progress by returning to a previous stage with additional information (new lemma) - // that forces us to select a new partial interpretation - if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.data())) - break; - - // Conflict does not depend on the current decision, and it is still in the current stage. - // We should find - // - the maximal scope level in the lemma - // - remove literal assigned in the scope level from m_lemma - // - backtrack to this level - // - and continue conflict resolution from there - // - we must bump m_num_marks for literals removed from m_lemma - unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.data()); - TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); - SASSERT(max_lvl < scope_lvl()); - remove_literals_from_lvl(m_lemma, max_lvl); - undo_until_level(max_lvl); - top = m_trail.size(); - TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); - SASSERT(scope_lvl() == max_lvl); - } - - TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); - TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); - - if (m_lemma.empty()) { - TRACE("nlsat", tout << "empty clause generated\n";); - return false; // problem is unsat, empty clause was generated - } - - reset_marks(); // remove marks from the literals in m_lemmas. - TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n"; - tout << "found_decision: " << found_decision << "\n";); - - if (m_check_lemmas) { - check_lemma(m_lemma.size(), m_lemma.data(), false, m_lemma_assumptions.get()); - } - - if (m_log_lemmas) - log_lemma(verbose_stream(), m_lemma.size(), m_lemma.data(), false); - - // There are two possibilities: - // 1) m_lemma contains only literals from previous stages, and they - // are false in the current interpretation. We make progress - // by returning to a previous stage with additional information (new clause) - // that forces us to select a new partial interpretation - // >>> Return to some previous stage (we may also backjump many decisions and stages). - // - // 2) m_lemma contains at most one literal from the current level (the last literal). - // Moreover, this literal was a decision, but the new lemma forces it to - // be assigned to a different value. - // >>> In this case, we remain in the same stage but, we add a new asserted literal - // in a previous scope level. We may backjump many decisions. - // - unsigned sz = m_lemma.size(); - clause * new_cls = nullptr; - if (!found_decision) { - // Case 1) - // We just have to find the maximal variable in m_lemma, and return to that stage - // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; - var new_max_var = max_var(sz, m_lemma.data()); - TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); - undo_until_stage(new_max_var); - SASSERT(m_xk == new_max_var); - new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); - TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << "\n"; - if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); - } - else { - SASSERT(scope_lvl() >= 1); - // Case 2) - if (is_bool_lemma(m_lemma.size(), m_lemma.data())) { - // boolean lemma, we just backtrack until the last literal is unassigned. - bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); - undo_until_unassigned(max_bool_var); - } - else { - // We must find the maximal decision level in literals in the first sz-1 positions that - // are at the same stage. If all these literals are from previous stages, - // we just backtrack the current level. - unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.data()); - TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); - undo_until_level(new_lvl); - } - - if (lemma_is_clause(*conflict_clause)) { - TRACE("nlsat", tout << "found decision literal in conflict clause\n";); - VERIFY(process_clause(*conflict_clause, true)); - return true; - } - new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); - } - NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); - if (!process_clause(*new_cls, true)) { - TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n"; - display(tout, *new_cls) << "\n"; - ); - // we are still in conflict - conflict_clause = new_cls; - goto start; - } - TRACE("nlsat_resolve_done", display_assignment(tout);); - return true; - } - - bool lemma_is_clause(clause const& cls) const { - bool same = (m_lemma.size() == cls.size()); - for (unsigned i = 0; same && i < m_lemma.size(); ++i) { - same = m_lemma[i] == cls[i]; - } - return same; - } - - - // ----------------------- - // - // Debugging - // - // ----------------------- - - bool check_watches() const { -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - clause_vector const & cs = m_watches[x]; - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - SASSERT(max_var(*(cs[i])) == x); - } - } -#endif - return true; - } - - bool check_bwatches() const { -#ifdef Z3DEBUG - for (bool_var b = 0; b < m_bwatches.size(); b++) { - clause_vector const & cs = m_bwatches[b]; - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause const & c = *(cs[i]); - SASSERT(max_var(c) == null_var); - SASSERT(max_bvar(c) == b); - } - } -#endif - return true; - } - - bool check_invariant() const { - SASSERT(check_watches()); - SASSERT(check_bwatches()); - return true; - } - - bool check_satisfied(clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause const & c = *(cs[i]); - if (!is_satisfied(c)) { - TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); - return false; - } - } - return true; - } - - bool check_satisfied() const { - TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); - unsigned num = m_atoms.size(); - if (m_bk != null_bool_var) - num = m_bk; - for (bool_var b = 0; b < num; b++) { - if (!check_satisfied(m_bwatches[b])) { - UNREACHABLE(); - return false; - } - } - if (m_xk != null_var) { - for (var x = 0; x < m_xk; x++) { - if (!check_satisfied(m_watches[x])) { - UNREACHABLE(); - return false; - } - } - } - return true; - } - - // ----------------------- - // - // Statistics - // - // ----------------------- - - void collect_statistics(statistics & st) { - st.update("nlsat conflicts", m_stats.m_conflicts); - st.update("nlsat propagations", m_stats.m_propagations); - st.update("nlsat decisions", m_stats.m_decisions); - st.update("nlsat restarts", m_stats.m_restarts); - st.update("nlsat stages", m_stats.m_stages); - st.update("nlsat simplifications", m_stats.m_simplifications); - st.update("nlsat irrational assignments", m_stats.m_irrational_assignments); - } - - void reset_statistics() { - m_stats.reset(); - } - - // ----------------------- - // - // Variable reordering - // - // ----------------------- - - struct var_info_collector { - pmanager & pm; - atom_vector const & m_atoms; - var_vector m_shuffle; - unsigned_vector m_max_degree; - unsigned_vector m_num_occs; - - var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): - pm(_pm), - m_atoms(atoms) { - m_max_degree.resize(num_vars, 0); - m_num_occs.resize(num_vars, 0); - } - - var_vector m_vars; - void collect(poly * p) { - m_vars.reset(); - pm.vars(p, m_vars); - unsigned sz = m_vars.size(); - for (unsigned i = 0; i < sz; i++) { - var x = m_vars[i]; - unsigned k = pm.degree(p, x); - m_num_occs[x]++; - if (k > m_max_degree[x]) - m_max_degree[x] = k; - } - } - - void collect(literal l) { - bool_var b = l.var(); - atom * a = m_atoms[b]; - if (a == nullptr) - return; - if (a->is_ineq_atom()) { - unsigned sz = to_ineq_atom(a)->size(); - for (unsigned i = 0; i < sz; i++) { - collect(to_ineq_atom(a)->p(i)); - } - } - else { - collect(to_root_atom(a)->p()); - } - } - - void collect(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - collect(c[i]); - } - - void collect(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) - collect(*(cs[i])); - } - - std::ostream& display(std::ostream & out, display_var_proc const & proc) { - unsigned sz = m_num_occs.size(); - for (unsigned i = 0; i < sz; i++) { - proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; - } - return out; - } - }; - - struct reorder_lt { - var_info_collector const & m_info; - reorder_lt(var_info_collector const & info):m_info(info) {} - bool operator()(var x, var y) const { - // high degree first - if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) - return false; - if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) - return true; - // more constrained first - if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) - return false; - if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) - return true; - return m_info.m_shuffle[x] < m_info.m_shuffle[y]; - } - }; - - // Order variables by degree and number of occurrences - void heuristic_reorder() { - unsigned num = num_vars(); - var_info_collector collector(m_pm, m_atoms, num); - collector.collect(m_clauses); - collector.collect(m_learned); - init_shuffle(collector.m_shuffle); - TRACE("nlsat_reorder", collector.display(tout, m_display_var);); - var_vector new_order; - for (var x = 0; x < num; x++) - new_order.push_back(x); - - std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); - TRACE("nlsat_reorder", - tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); - var_vector perm; - perm.resize(num, 0); - for (var x = 0; x < num; x++) { - perm[new_order[x]] = x; - } - reorder(perm.size(), perm.data()); - SASSERT(check_invariant()); - } - - void init_shuffle(var_vector& p) { - unsigned num = num_vars(); - for (var x = 0; x < num; x++) - p.push_back(x); - - random_gen r(++m_random_seed); - shuffle(p.size(), p.data(), r); - } - - void shuffle_vars() { - var_vector p; - init_shuffle(p); - reorder(p.size(), p.data()); - } - - bool can_reorder() const { - return all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) - && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); - } - - /** - \brief Reorder variables using the giving permutation. - p maps internal variables to their new positions - */ - - - void reorder(unsigned sz, var const * p) { - - remove_learned_roots(); - SASSERT(can_reorder()); - TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); - display_vars(tout); - tout << "\npermutation:\n"; - for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; - ); - // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; - reinit_cache(); - SASSERT(num_vars() == sz); - TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); - reset_watches(); - assignment new_assignment(m_am); - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) - new_assignment.set(p[x], m_assignment.value(x)); - } - var_vector new_inv_perm; - new_inv_perm.resize(sz); - // the undo_until_size(0) statement erases the Boolean assignment. - // undo_until_size(0) - undo_until_stage(null_var); - m_cache.reset(); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_watches[x].empty()); - } -#endif - // update m_perm mapping - for (unsigned ext_x = 0; ext_x < sz; ext_x++) { - // p: internal -> new pos - // m_perm: internal -> external - // m_inv_perm: external -> internal - new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; - m_perm.set(new_inv_perm[ext_x], ext_x); - } - bool_vector is_int; - is_int.swap(m_is_int); - for (var x = 0; x < sz; x++) { - m_is_int.setx(p[x], is_int[x], false); - SASSERT(m_infeasible[x] == 0); - } - m_inv_perm.swap(new_inv_perm); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(x == m_inv_perm[m_perm[x]]); - SASSERT(m_watches[x].empty()); - } -#endif - m_pm.rename(sz, p); - for (auto& b : m_bounds) - b.x = p[b.x]; - TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); - reinit_cache(); - m_assignment.swap(new_assignment); - reattach_arith_clauses(m_clauses); - reattach_arith_clauses(m_learned); - TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); - } - - - /** - \brief Restore variable order. - */ - void restore_order() { - // m_perm: internal -> external - // m_inv_perm: external -> internal - var_vector p; - p.append(m_perm); - reorder(p.size(), p.data()); -#ifdef Z3DEBUG - for (var x = 0; x < num_vars(); x++) { - SASSERT(m_perm[x] == x); - SASSERT(m_inv_perm[x] == x); - } -#endif - } - - /** - \brief After variable reordering some lemmas containing root atoms may be ill-formed. - */ - void remove_learned_roots() { - unsigned j = 0; - for (clause* c : m_learned) { - if (has_root_atom(*c)) { - del_clause(c); - } - else { - m_learned[j++] = c; - } - } - m_learned.shrink(j); - } - - /** - \brief Return true if the clause contains an ill formed root atom - */ - bool has_root_atom(clause const & c) const { - for (literal lit : c) { - bool_var b = lit.var(); - atom * a = m_atoms[b]; - if (a && a->is_root_atom()) - return true; - } - return false; - } - - /** - \brief reinsert all polynomials in the unique cache - */ - void reinit_cache() { - reinit_cache(m_clauses); - reinit_cache(m_learned); - for (atom* a : m_atoms) - reinit_cache(a); - } - void reinit_cache(clause_vector const & cs) { - for (clause* c : cs) - reinit_cache(*c); - } - void reinit_cache(clause const & c) { - for (literal l : c) - reinit_cache(l); - } - void reinit_cache(literal l) { - bool_var b = l.var(); - reinit_cache(m_atoms[b]); - } - void reinit_cache(atom* a) { - if (a == nullptr) { - - } - else if (a->is_ineq_atom()) { - var max = 0; - unsigned sz = to_ineq_atom(a)->size(); - for (unsigned i = 0; i < sz; i++) { - poly * p = to_ineq_atom(a)->p(i); - VERIFY(m_cache.mk_unique(p) == p); - var x = m_pm.max_var(p); - if (x > max) - max = x; - } - a->m_max_var = max; - } - else { - poly * p = to_root_atom(a)->p(); - VERIFY(m_cache.mk_unique(p) == p); - a->m_max_var = m_pm.max_var(p); - } - } - - void reset_watches() { - unsigned num = num_vars(); - for (var x = 0; x < num; x++) { - m_watches[x].reset(); - } - } - - void reattach_arith_clauses(clause_vector const & cs) { - for (clause* cp : cs) { - var x = max_var(*cp); - if (x != null_var) - m_watches[x].push_back(cp); - } - } - - // ----------------------- - // - // Solver initialization - // - // ----------------------- - - struct degree_lt { - unsigned_vector & m_degrees; - degree_lt(unsigned_vector & ds):m_degrees(ds) {} - bool operator()(unsigned i1, unsigned i2) const { - if (m_degrees[i1] < m_degrees[i2]) - return true; - if (m_degrees[i1] > m_degrees[i2]) - return false; - return i1 < i2; - } - }; - - unsigned_vector m_cs_degrees; - unsigned_vector m_cs_p; - void sort_clauses_by_degree(unsigned sz, clause ** cs) { - if (sz <= 1) - return; - TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - m_cs_degrees.reset(); - m_cs_p.reset(); - for (unsigned i = 0; i < sz; i++) { - m_cs_p.push_back(i); - m_cs_degrees.push_back(degree(*(cs[i]))); - } - std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); - TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); - apply_permutation(sz, cs, m_cs_p.data()); - TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - } - - - struct degree_lit_num_lt { - unsigned_vector & m_degrees; - unsigned_vector & m_lit_num; - degree_lit_num_lt(unsigned_vector & ds, unsigned_vector & ln) : - m_degrees(ds), - m_lit_num(ln) { - } - bool operator()(unsigned i1, unsigned i2) const { - if (m_lit_num[i1] == 1 && m_lit_num[i2] > 1) - return true; - if (m_lit_num[i1] > 1 && m_lit_num[i2] == 1) - return false; - if (m_degrees[i1] != m_degrees[i2]) - return m_degrees[i1] < m_degrees[i2]; - if (m_lit_num[i1] != m_lit_num[i2]) - return m_lit_num[i1] < m_lit_num[i2]; - return i1 < i2; - } - }; - - unsigned_vector m_dl_degrees; - unsigned_vector m_dl_lit_num; - unsigned_vector m_dl_p; - void sort_clauses_by_degree_lit_num(unsigned sz, clause ** cs) { - if (sz <= 1) - return; - TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - m_dl_degrees.reset(); - m_dl_lit_num.reset(); - m_dl_p.reset(); - for (unsigned i = 0; i < sz; i++) { - m_dl_degrees.push_back(degree(*(cs[i]))); - m_dl_lit_num.push_back(cs[i]->size()); - m_dl_p.push_back(i); - } - std::sort(m_dl_p.begin(), m_dl_p.end(), degree_lit_num_lt(m_dl_degrees, m_dl_lit_num)); - TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_dl_p.begin(), m_dl_p.end()); tout << "\n";); - apply_permutation(sz, cs, m_dl_p.data()); - TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); - } - - void sort_watched_clauses() { - unsigned num = num_vars(); - for (unsigned i = 0; i < num; i++) { - clause_vector & ws = m_watches[i]; - // sort_clauses_by_degree(ws.size(), ws.data()); - if (m_simple_check) { - sort_clauses_by_degree_lit_num(ws.size(), ws.data()); - } - else { - sort_clauses_by_degree(ws.size(), ws.data()); - } - } - } - - // ----------------------- - // - // Full dimensional - // - // A problem is in the full dimensional fragment if it does - // not contain equalities or non-strict inequalities. - // - // ----------------------- - - bool is_full_dimensional(literal l) const { - atom * a = m_atoms[l.var()]; - if (a == nullptr) - return true; - switch (a->get_kind()) { - case atom::EQ: return l.sign(); - case atom::LT: return !l.sign(); - case atom::GT: return !l.sign(); - case atom::ROOT_EQ: return l.sign(); - case atom::ROOT_LT: return !l.sign(); - case atom::ROOT_GT: return !l.sign(); - case atom::ROOT_LE: return l.sign(); - case atom::ROOT_GE: return l.sign(); - default: - UNREACHABLE(); - return false; - } - } - - bool is_full_dimensional(clause const & c) const { - for (literal l : c) { - if (!is_full_dimensional(l)) - return false; - } - return true; - } - - bool is_full_dimensional(clause_vector const & cs) const { - for (clause* c : cs) { - if (!is_full_dimensional(*c)) - return false; - } - return true; - } - - bool is_full_dimensional() const { - return is_full_dimensional(m_clauses); - } - - - // ----------------------- - // - // Simplification - // - // ----------------------- - - // solve simple equalities - // TBD WU-Reit decomposition? - - // - elim_unconstrained - // - solve_eqs - // - fm - - /** - \brief isolate variables in unit equalities. - Assume a clause is c == v*p + q - and the context implies p > 0 - - replace v by -q/p - remove clause c, - The for other occurrences of v, - replace v*r + v*v*r' > 0 by - by p*p*v*r + p*p*v*v*r' > 0 - by p*q*r + q*q*r' > 0 - - The method ignores lemmas and assumes constraints don't use roots. - */ - - - - // Eliminated variables are tracked in m_bounds. - // Each element in m_bounds tracks the eliminated variable and an upper or lower bound - // that has to be satisfied. Variables that are eliminated through equalities are tracked - // by non-strict bounds. A satisfiable solution is required to provide an evaluation that - // is consistent with the bounds. For equalities, the non-strict lower or upper bound can - // always be assigned as a value to the variable. - - void fix_patch() { - m_lo.reset(); m_hi.reset(); - for (auto& b : m_bounds) - m_assignment.reset(b.x); - for (unsigned i = m_bounds.size(); i-- > 0; ) - fix_patch(m_bounds[i]); - } - - // x is unassigned, lo < x -> x <- lo + 1 - // x is unassigned, x < hi -> x <- hi - 1 - // x is unassigned, lo <= x -> x <- lo - // x is unassigned, x <= hi -> x <- hi - // x is assigned above hi, lo is strict lo < x < hi -> set x <- (lo + hi)/2 - // x is assigned below hi, above lo -> no-op - // x is assigned below lo, hi is strict lo < x < hi -> set x <-> (lo + hi)/2 - // x is assigned above hi, x <= hi -> x <- hi - // x is assigned blow lo, lo <= x -> x <- lo - void fix_patch(bound_constraint& b) { - var x = b.x; - scoped_anum Av(m_am), Bv(m_am), val(m_am); - m_pm.eval(b.A, m_assignment, Av); - m_pm.eval(b.B, m_assignment, Bv); - m_am.neg(Bv); - val = Bv / Av; - // Ax >= B - // is-lower : A > 0 - // is-upper: A < 0 - // x <- B / A - bool is_lower = m_am.is_pos(Av); - TRACE("nlsat", - m_display_var(tout << "patch v" << x << " ", x) << "\n"; - if (m_assignment.is_assigned(x)) m_am.display(tout << "previous value: ", m_assignment.value(x)); tout << "\n"; - m_am.display(tout << "updated value: ", val); tout << "\n"; - ); - - if (!m_assignment.is_assigned(x)) { - if (!b.is_strict) - m_assignment.set_core(x, val); - else if (is_lower) - m_assignment.set_core(x, val + 1); - else - m_assignment.set_core(x, val - 1); - } - else { - auto& aval = m_assignment.value(x); - if (is_lower) { - // lo < value(x), lo < x -> x is unchanged - if (b.is_strict && m_am.lt(val, aval)) - ; - else if (!b.is_strict && m_am.le(val, aval)) - ; - else if (!b.is_strict) - m_assignment.set_core(x, val); - // aval < lo < x, hi is unassigned: x <- lo + 1 - else if (!m_hi.is_assigned(x)) - m_assignment.set_core(x, val + 1); - // aval < lo < x, hi is assigned: x <- (lo + hi) / 2 - else { - scoped_anum mid(m_am); - m_am.add(m_hi.value(x), val, mid); - mid = mid / 2; - m_assignment.set_core(x, mid); - } - } - else { - // dual to lower bounds - if (b.is_strict && m_am.lt(aval, val)) - ; - else if (!b.is_strict && m_am.le(aval, val)) - ; - else if (!b.is_strict) - m_assignment.set_core(x, val); - else if (!m_lo.is_assigned(x)) - m_assignment.set_core(x, val - 1); - else { - scoped_anum mid(m_am); - m_am.add(m_lo.value(x), val, mid); - mid = mid / 2; - m_assignment.set_core(x, mid); - } - } - } - - if (is_lower) { - if (!m_lo.is_assigned(x) || m_am.lt(m_lo.value(x), val)) - m_lo.set_core(x, val); - } - else { - if (!m_hi.is_assigned(x) || m_am.gt(m_hi.value(x), val)) - m_hi.set_core(x, val); - } - } - - bool is_unit_ineq(clause const& c) const { - return - c.size() == 1 && - m_atoms[c[0].var()] && - m_atoms[c[0].var()]->is_ineq_atom(); - } - - bool is_unit_eq(clause const& c) const { - return - is_unit_ineq(c) && - !c[0].sign() && - m_atoms[c[0].var()]->is_eq(); - } - - /** - \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. - */ - lbool is_cmp0(clause const& c, var& v) { - if (!is_unit_ineq(c)) - return l_undef; - literal lit = c[0]; - ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); - bool sign = lit.sign(); - poly * p0; - if (!is_single_poly(a, p0)) - return l_undef; - if (m_pm.is_var(p0, v)) { - if (!sign && a.get_kind() == atom::GT) { - return l_true; - } - if (!sign && a.get_kind() == atom::LT) { - return l_false; - } - return l_undef; - } - polynomial::scoped_numeral n(m_pm.m()); - if (m_pm.is_var_num(p0, v, n)) { - // x - k > 0 - if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { - return l_true; - } - // x + k < 0 - if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { - return l_false; - } - // !(x + k > 0) - if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { - return l_false; - } - // !(x - k < 0) - if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { - return l_true; - } - } - return l_undef; - } - - bool is_single_poly(ineq_atom const& a, poly*& p) { - unsigned sz = a.size(); - return sz == 1 && a.is_odd(0) && (p = a.p(0), true); - } - - bool is_unit(polynomial_ref const& p) { - if (!m_pm.is_const(p)) - return false; - auto const& c = m_pm.coeff(p, 0); - return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); - } - - // ----------------------- - // - // Pretty printing - // - // ----------------------- - - std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) { - proc(out, x); - out << " -> "; - m_am.display_decimal(out, m_assignment.value(x)); - out << "\n"; - } - } - return out; - } - - std::ostream& display_bool_assignment(std::ostream & out) const { - unsigned sz = m_atoms.size(); - for (bool_var b = 0; b < sz; b++) { - if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { - out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; - } - else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { - display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; - } - } - TRACE("nlsat_bool_assignment", - for (bool_var b = 0; b < sz; b++) { - out << "b" << b << " -> " << m_bvalues[b] << " "; - if (m_atoms[b]) display(out, *m_atoms[b]); - out << "\n"; - }); - return out; - } - - bool display_mathematica_assignment(std::ostream & out) const { - bool first = true; - for (var x = 0; x < num_vars(); x++) { - if (m_assignment.is_assigned(x)) { - if (first) - first = false; - else - out << " && "; - out << "x" << x << " == "; - m_am.display_mathematica(out, m_assignment.value(x)); - } - } - return !first; - } - - std::ostream& display_num_assignment(std::ostream & out) const { - return display_num_assignment(out, m_display_var); - } - - std::ostream& display_assignment(std::ostream& out) const { - display_bool_assignment(out); - display_num_assignment(out); - return out; - } - - std::ostream& display(std::ostream& out, justification j) const { - switch (j.get_kind()) { - case justification::CLAUSE: - display(out, *j.get_clause()) << "\n"; - break; - case justification::LAZY: { - lazy_justification const& lz = *j.get_lazy(); - display_not(out, lz.num_lits(), lz.lits()) << "\n"; - for (unsigned i = 0; i < lz.num_clauses(); ++i) { - display(out, lz.clause(i)) << "\n"; - } - break; - } - default: - out << j.get_kind() << "\n"; - break; - } - return out; - } - - bool m_display_eval = false; - std::ostream& display_eval(std::ostream& out, justification j) { - flet _display(m_display_eval, true); - return display(out, j); - } - - std::ostream& display_ineq(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { - unsigned sz = a.size(); - for (unsigned i = 0; i < sz; i++) { - if (use_star && i > 0) - out << "*"; - bool is_even = a.is_even(i); - if (is_even || sz > 1) - out << "("; - display_polynomial(out, a.p(i), proc, use_star); - if (is_even || sz > 1) - out << ")"; - if (is_even) - out << "^2"; - } - switch (a.get_kind()) { - case atom::LT: out << " < 0"; break; - case atom::GT: out << " > 0"; break; - case atom::EQ: out << " = 0"; break; - default: UNREACHABLE(); break; - } - return out; - } - - std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { - unsigned sz = a.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) - out << "*"; - bool is_even = a.is_even(i); - if (sz > 1) - out << "("; - if (is_even) - out << "("; - m_pm.display(out, a.p(i), display_var_proc(), true); - if (is_even) - out << "^2)"; - if (sz > 1) - out << ")"; - } - switch (a.get_kind()) { - case atom::LT: out << " < 0"; break; - case atom::GT: out << " > 0"; break; - case atom::EQ: out << " == 0"; break; - default: UNREACHABLE(); break; - } - return out; - } - - std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { - return m_pm.display_smt2(out, p, proc); - } - - std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { - switch (a.get_kind()) { - case atom::LT: out << "(< "; break; - case atom::GT: out << "(> "; break; - case atom::EQ: out << "(= "; break; - default: UNREACHABLE(); break; - } - unsigned sz = a.size(); - if (sz > 1) - out << "(* "; - for (unsigned i = 0; i < sz; i++) { - if (i > 0) out << " "; - if (a.is_even(i)) { - out << "(* "; - display_polynomial_smt2(out, a.p(i), proc); - out << " "; - display_polynomial_smt2(out, a.p(i), proc); - out << ")"; - } - else { - display_polynomial_smt2(out, a.p(i), proc); - } - } - if (sz > 1) - out << ")"; - out << " 0)"; - return out; - } - - std::ostream& display_poly_root(std::ostream& out, char const* y, root_atom const& a, display_var_proc const& proc) const { - out << "(exists (("; proc(out,a.x()); out << " Real))\n"; - out << "(and (= " << y << " "; - proc(out, a.x()); - out << ") (= 0 "; - display_polynomial_smt2(out, a.p(), proc); - out << ")))\n"; - return out; - } - - std::ostream& display_binary_smt2(std::ostream& out, poly const* p1, char const* rel, poly const* p2, display_var_proc const& proc) const { - out << "(" << rel << " "; - display_polynomial_smt2(out, p1, proc); - out << " "; - display_polynomial_smt2(out, p2, proc); - out << ")"; - return out; - } - - - std::ostream& display_linear_root_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { - polynomial_ref A(m_pm), B(m_pm), Z(m_pm), Ax(m_pm); - polynomial::scoped_numeral zero(m_qm); - m_pm.m().set(zero, 0); - A = m_pm.derivative(a.p(), a.x()); - B = m_pm.neg(m_pm.substitute(a.p(), a.x(), zero)); - Z = m_pm.mk_zero(); - - Ax = m_pm.mul(m_pm.mk_polynomial(a.x()), A); - - // x < root[1](ax + b) == (a > 0 => ax + b < 0) & (a < 0 => ax + b > 0) - // x < root[1](ax + b) == (a > 0 => ax < -b) & (a < 0 => ax > -b) - - char const* rel1 = "<", *rel2 = ">"; - switch (a.get_kind()) { - case atom::ROOT_LT: rel1 = "<"; rel2 = ">"; break; - case atom::ROOT_GT: rel1 = ">"; rel2 = "<"; break; - case atom::ROOT_LE: rel1 = "<="; rel2 = ">="; break; - case atom::ROOT_GE: rel1 = ">="; rel2 = "<="; break; - case atom::ROOT_EQ: rel1 = rel2 = "="; break; - default: UNREACHABLE(); break; - } - - out << "(and "; - out << "(=> "; display_binary_smt2(out, A, ">", Z, proc); display_binary_smt2(out, Ax, rel1, B, proc); out << ") "; - out << "(=> "; display_binary_smt2(out, A, "<", Z, proc); display_binary_smt2(out, Ax, rel2, B, proc); out << ") "; - out << ")"; - - return out; - } - - - std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { - if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) - return display_linear_root_smt2(out, a, proc); -#if 1 - out << "(exists ("; - for (unsigned j = 0; j < a.i(); ++j) { - std::string y = std::string("y") + std::to_string(j); - out << "(" << y << " Real) "; - } - out << ")\n"; - out << "(and\n"; - for (unsigned j = 0; j < a.i(); ++j) { - std::string y = std::string("y") + std::to_string(j); - display_poly_root(out, y.c_str(), a, proc); - } - for (unsigned j = 0; j + 1 < a.i(); ++j) { - std::string y1 = std::string("y") + std::to_string(j); - std::string y2 = std::string("y") + std::to_string(j+1); - out << "(< " << y1 << " " << y2 << ")\n"; - } - - std::string yn = "y" + std::to_string(a.i() - 1); - - // TODO we need (forall z : z < yn . p(z) => z = y1 or ... z = y_{n-1}) - // to say y1, .., yn are the first n distinct roots. - // - out << "(forall ((z Real)) (=> (and (< z " << yn << ") "; display_poly_root(out, "z", a, proc) << ") "; - if (a.i() == 1) { - out << "false))\n"; - } - else { - out << "(or "; - for (unsigned j = 0; j + 1 < a.i(); ++j) { - std::string y1 = std::string("y") + std::to_string(j); - out << "(= z " << y1 << ") "; - } - out << ")))\n"; - } - switch (a.get_kind()) { - case atom::ROOT_LT: out << "(< "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_GT: out << "(> "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; - case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; - default: UNREACHABLE(); break; - } - out << "))"; - return out; -#endif - - - return display_root(out, a, proc); - } - - std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { - proc(out, a.x()); - switch (a.get_kind()) { - case atom::ROOT_LT: out << " < "; break; - case atom::ROOT_GT: out << " > "; break; - case atom::ROOT_LE: out << " <= "; break; - case atom::ROOT_GE: out << " >= "; break; - case atom::ROOT_EQ: out << " = "; break; - default: UNREACHABLE(); break; - } - out << "root[" << a.i() << "]("; - display_polynomial(out, a.p(), proc); - out << ")"; - return out; - } - - struct mathematica_var_proc : public display_var_proc { - var m_x; - public: - mathematica_var_proc(var x):m_x(x) {} - std::ostream& operator()(std::ostream & out, var x) const override { - if (m_x == x) - return out << "#1"; - else - return out << "x" << x; - } - }; - - std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { - out << "x" << a.x(); - switch (a.get_kind()) { - case atom::ROOT_LT: out << " < "; break; - case atom::ROOT_GT: out << " > "; break; - case atom::ROOT_LE: out << " <= "; break; - case atom::ROOT_GE: out << " >= "; break; - case atom::ROOT_EQ: out << " == "; break; - default: UNREACHABLE(); break; - } - out << "Root["; - display_polynomial(out, a.p(), mathematica_var_proc(a.x()), true); - out << " &, " << a.i() << "]"; - return out; - } - - std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { - if (a.is_ineq_atom()) - return display_ineq(out, static_cast(a), proc); - else - return display_root(out, static_cast(a), proc); - } - - std::ostream& display(std::ostream & out, atom const & a) const { - return display(out, a, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, atom const & a) const { - if (a.is_ineq_atom()) - return display_mathematica(out, static_cast(a)); - else - return display_mathematica(out, static_cast(a)); - } - - std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { - if (a.is_ineq_atom()) - return display_ineq_smt2(out, static_cast(a), proc); - else - return display_root_smt2(out, static_cast(a), proc); - } - - std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { - if (b == 0) - out << "true"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display(out, *(m_atoms[b]), proc); - return out; - } - - std::ostream& display_atom(std::ostream & out, bool_var b) const { - return display_atom(out, b, m_display_var); - } - - std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { - if (b == 0) - out << "(0 < 1)"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display_mathematica(out, *(m_atoms[b])); - return out; - } - - std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { - if (b == 0) - out << "true"; - else if (m_atoms[b] == 0) - out << "b" << b; - else - display_smt2(out, *(m_atoms[b]), proc); - return out; - } - - std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (m_atoms[b] != 0) - out << "("; - display_atom(out, b, proc); - if (m_atoms[b] != 0) - out << ")"; - } - else { - display_atom(out, l.var(), proc); - } - return out; - } - - std::ostream& display(std::ostream & out, literal l) const { - return display(out, l, m_display_var); - } - - std::ostream& display_smt2(std::ostream & out, literal l) const { - return display_smt2(out, l, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, literal l) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (m_atoms[b] != 0) - out << "("; - display_mathematica_atom(out, b); - if (m_atoms[b] != 0) - out << ")"; - } - else { - display_mathematica_atom(out, l.var()); - } - return out; - } - - std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { - if (l.sign()) { - bool_var b = l.var(); - out << "(not "; - display_smt2_atom(out, b, proc); - out << ")"; - } - else { - display_smt2_atom(out, l.var(), proc); - } - return out; - } - - std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { - if (!m_display_assumption) - return out; - vector deps; - m_asm.linearize(s, deps); - bool first = true; - for (auto dep : deps) { - if (first) first = false; else out << " "; - (*m_display_assumption)(out, dep); - } - return out; - } - - std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display(out, ls[i], proc); - } - return out; - } - - std::ostream& display(std::ostream & out, unsigned num, literal const * ls) const { - return display(out, num, ls, m_display_var); - } - - std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display(out, ~ls[i], proc); - } - return out; - } - - std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls) const { - return display_not(out, num, ls, m_display_var); - } - - std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { - return display(out, cs.size(), cs.data(), m_display_var); - } - - std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { - if (c.assumptions() != nullptr) { - display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); - out << " |- "; - } - return display(out, c.size(), c.data(), proc); - } - - std::ostream& display(std::ostream & out, clause const & c) const { - return display(out, c, m_display_var); - } - - - std::ostream& display_polynomial(std::ostream& out, poly* p, display_var_proc const & proc, bool use_star = false) const { - if (m_display_eval) { - polynomial_ref q(m_pm); - q = p; - for (var x = 0; x < num_vars(); x++) - if (m_assignment.is_assigned(x)) { - auto& a = m_assignment.value(x); - if (!m_am.is_rational(a)) - continue; - mpq r; - m_am.to_rational(a, r); - q = m_pm.substitute(q, 1, &x, &r); - } - m_pm.display(out, q, proc, use_star); - } - else - m_pm.display(out, p, proc, use_star); - return out; - } - - // -- - - std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const { - return display_smt2(out, n, ls, display_var_proc()); - } - - - std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { - if (num == 0) { - out << "false"; - } - else if (num == 1) { - display_smt2(out, ls[0], proc); - } - else { - out << "(or"; - for (unsigned i = 0; i < num; i++) { - out << " "; - display_smt2(out, ls[i], proc); - } - out << ")"; - } - return out; - } - - std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { - return display_smt2(out, c.size(), c.data(), proc); - } - - std::ostream& display_abst(std::ostream & out, literal l) const { - if (l.sign()) { - bool_var b = l.var(); - out << "!"; - if (b == true_bool_var) - out << "true"; - else - out << "b" << b; - } - else { - out << "b" << l.var(); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " or "; - display_abst(out, ls[i]); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { - return display_abst(out, cs.size(), cs.data()); - } - - std::ostream& display_abst(std::ostream & out, clause const & c) const { - return display_abst(out, c.size(), c.data()); - } - - std::ostream& display_mathematica(std::ostream & out, clause const & c) const { - out << "("; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) - out << " || "; - display_mathematica(out, c[i]); - } - out << ")"; - return out; - } - - // Debugging support: - // Display generated lemma in Mathematica format. - // Mathematica must reduce lemma to True (modulo resource constraints). - std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { - out << "Resolve[ForAll[{"; - // var definition - for (unsigned i = 0; i < num_vars(); i++) { - if (i > 0) - out << ", "; - out << "x" << i; - } - out << "}, "; - if (include_assignment) { - out << "!("; - if (!display_mathematica_assignment(out)) - out << "0 < 1"; // nothing was printed - out << ") || "; - } - for (unsigned i = 0; i < num; i++) { - if (i > 0) - out << " || "; - display_mathematica(out, ls[i]); - } - out << "], Reals]\n"; // end of exists - return out; - } - - std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { - for (clause* c : cs) { - display(out, *c, proc) << "\n"; - } - return out; - } - - std::ostream& display(std::ostream & out, clause_vector const & cs) const { - return display(out, cs, m_display_var); - } - - std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) out << ",\n"; - display_mathematica(out << " ", *(cs[i])); - } - return out; - } - - std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { - for (clause* c : cs) { - display_abst(out, *c) << "\n"; - } - return out; - } - - std::ostream& display(std::ostream & out, display_var_proc const & proc) const { - display(out, m_clauses, proc); - if (!m_learned.empty()) { - display(out << "Lemmas:\n", m_learned, proc); - } - return out; - } - - std::ostream& display_mathematica(std::ostream & out) const { - return display_mathematica(out << "{\n", m_clauses) << "}\n"; - } - - std::ostream& display_abst(std::ostream & out) const { - display_abst(out, m_clauses); - if (!m_learned.empty()) { - display_abst(out << "Lemmas:\n", m_learned); - } - return out; - } - - std::ostream& display(std::ostream & out) const { - display(out, m_display_var); - display_assignment(out << "assignment:\n"); - return out << "---\n"; - } - - std::ostream& display_vars(std::ostream & out) const { - for (unsigned i = 0; i < num_vars(); i++) { - out << i << " -> "; m_display_var(out, i); out << "\n"; - } - return out; - } - - std::ostream& display_smt2_arith_decls(std::ostream & out) const { - unsigned sz = m_is_int.size(); - for (unsigned i = 0; i < sz; i++) { - if (is_int(i)) { - out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; - } - else { - out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; - } - } - return out; - } - - std::ostream& display_smt2_bool_decls(std::ostream & out) const { - unsigned sz = m_atoms.size(); - for (unsigned i = 0; i < sz; i++) { - if (m_atoms[i] == nullptr) - out << "(declare-fun b" << i << " () Bool)\n"; - } - return out; - } - - std::ostream& display_smt2(std::ostream & out) const { - display_smt2_bool_decls(out); - display_smt2_arith_decls(out); - out << "(assert (and true\n"; - for (clause* c : m_clauses) { - display_smt2(out, *c, m_display_var) << "\n"; - } - out << "))\n" << std::endl; - return out; - } - }; - - solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { - m_ctx = alloc(ctx, rlim, p, incremental); - m_imp = alloc(imp, *this, *m_ctx); - } - - solver::solver(ctx& ctx) { - m_ctx = nullptr; - m_imp = alloc(imp, *this, ctx); - } - - solver::~solver() { - dealloc(m_imp); - dealloc(m_ctx); - } - - lbool solver::check() { - return m_imp->check(); - } - - lbool solver::check(literal_vector& assumptions) { - return m_imp->check(assumptions); - } - - void solver::get_core(vector& assumptions) { - return m_imp->get_core(assumptions); - } - - void solver::reset() { - m_imp->reset(); - } - - - void solver::updt_params(params_ref const & p) { - m_imp->updt_params(p); - } - - - void solver::collect_param_descrs(param_descrs & d) { - algebraic_numbers::manager::collect_param_descrs(d); - nlsat_params::collect_param_descrs(d); - } - - unsynch_mpq_manager & solver::qm() { - return m_imp->m_qm; - } - - anum_manager & solver::am() { - return m_imp->m_am; - } - - pmanager & solver::pm() { - return m_imp->m_pm; - } - - void solver::set_display_var(display_var_proc const & proc) { - m_imp->m_display_var.m_proc = &proc; - } - - void solver::set_display_assumption(display_assumption_proc const& proc) { - m_imp->m_display_assumption = &proc; - } - - - unsigned solver::num_vars() const { - return m_imp->num_vars(); - } - - bool solver::is_int(var x) const { - return m_imp->is_int(x); - } - - bool_var solver::mk_bool_var() { - return m_imp->mk_bool_var(); - } - - literal solver::mk_true() { - return literal(0, false); - } - - atom * solver::bool_var2atom(bool_var b) { - return m_imp->m_atoms[b]; - } - - void solver::vars(literal l, var_vector& vs) { - m_imp->vars(l, vs); - } - - atom_vector const& solver::get_atoms() { - return m_imp->m_atoms; - } - - atom_vector const& solver::get_var2eq() { - return m_imp->m_var2eq; - } - - evaluator& solver::get_evaluator() { - return m_imp->m_evaluator; - } - - explain& solver::get_explain() { - return m_imp->m_explain; - } - - void solver::reorder(unsigned sz, var const* p) { - m_imp->reorder(sz, p); - } - - void solver::restore_order() { - m_imp->restore_order(); - } - - void solver::set_rvalues(assignment const& as) { - m_imp->m_assignment.copy(as); - } - - void solver::get_rvalues(assignment& as) { - as.copy(m_imp->m_assignment); - } - - void solver::get_bvalues(svector const& bvars, svector& vs) { - vs.reset(); - for (bool_var b : bvars) { - vs.reserve(b + 1, l_undef); - if (!m_imp->m_atoms[b]) { - vs[b] = m_imp->m_bvalues[b]; - } - } - TRACE("nlsat", display(tout);); - } - - void solver::set_bvalues(svector const& vs) { - TRACE("nlsat", display(tout);); - for (bool_var b = 0; b < vs.size(); ++b) { - if (vs[b] != l_undef) { - m_imp->m_bvalues[b] = vs[b]; - SASSERT(!m_imp->m_atoms[b]); - } - } -#if 0 - m_imp->m_bvalues.reset(); - m_imp->m_bvalues.append(vs); - m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); - for (unsigned i = 0; i < m_imp->m_atoms.size(); ++i) { - atom* a = m_imp->m_atoms[i]; - SASSERT(!a); - if (a) { - m_imp->m_bvalues[i] = to_lbool(m_imp->m_evaluator.eval(a, false)); - } - } -#endif - TRACE("nlsat", display(tout);); - } - - void solver::del_clause(clause* c) { - m_imp->del_clause(c); - } - - var solver::mk_var(bool is_int) { - return m_imp->mk_var(is_int); - } - - bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { - return m_imp->mk_ineq_atom(k, sz, ps, is_even); - } - - literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify) { - return m_imp->mk_ineq_literal(k, sz, ps, is_even, simplify); - } - - bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { - return m_imp->mk_root_atom(k, x, i, p); - } - - void solver::inc_ref(bool_var b) { - m_imp->inc_ref(b); - } - - void solver::dec_ref(bool_var b) { - m_imp->dec_ref(b); - } - - void solver::inc_ref(assumption a) { - m_imp->inc_ref(static_cast(a)); - } - - void solver::dec_ref(assumption a) { - m_imp->dec_ref(static_cast(a)); - } - - void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { - return m_imp->mk_external_clause(num_lits, lits, a); - } - - std::ostream& solver::display(std::ostream & out) const { - return m_imp->display(out); - } - - std::ostream& solver::display(std::ostream & out, literal l) const { - return m_imp->display(out, l); - } - - std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { - for (unsigned i = 0; i < n; ++i) { - display(out, ls[i]); - out << "; "; - } - return out; - } - - std::ostream& solver::display(std::ostream & out, literal_vector const& ls) const { - return display(out, ls.size(), ls.data()); - } - - std::ostream& solver::display_smt2(std::ostream & out, literal l) const { - return m_imp->display_smt2(out, l); - } - - std::ostream& solver::display_smt2(std::ostream & out, unsigned n, literal const* ls) const { - for (unsigned i = 0; i < n; ++i) { - display_smt2(out, ls[i]); - out << " "; - } - return out; - } - - std::ostream& solver::display(std::ostream& out, clause const& c) const { - return m_imp->display(out, c); - } - - std::ostream& solver::display_smt2(std::ostream & out) const { - return m_imp->display_smt2(out); - } - - std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { - return display_smt2(out, ls.size(), ls.data()); - } - - std::ostream& solver::display(std::ostream & out, var x) const { - return m_imp->m_display_var(out, x); - } - - std::ostream& solver::display(std::ostream & out, atom const& a) const { - return m_imp->display(out, a, m_imp->m_display_var); - } - - display_var_proc const & solver::display_proc() const { - return m_imp->m_display_var; - } - - anum const & solver::value(var x) const { - if (m_imp->m_assignment.is_assigned(x)) - return m_imp->m_assignment.value(x); - return m_imp->m_zero; - } - - lbool solver::bvalue(bool_var b) const { - return m_imp->m_bvalues[b]; - } - - lbool solver::value(literal l) const { - return m_imp->value(l); - } - - bool solver::is_interpreted(bool_var b) const { - return m_imp->m_atoms[b] != 0; - } - - void solver::reset_statistics() { - return m_imp->reset_statistics(); - } - - void solver::collect_statistics(statistics & st) { - return m_imp->collect_statistics(st); - } - - clause* solver::mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a) { - return m_imp->mk_clause(n, lits, learned, static_cast(a)); - } - - void solver::inc_simplify() { - m_imp->m_stats.m_simplifications++; - } - - bool solver::has_root_atom(clause const& c) const { - return m_imp->has_root_atom(c); - } - - void solver::add_bound(bound_constraint const& c) { - m_imp->m_bounds.push_back(c); - } - - assumption solver::join(assumption a, assumption b) { - return (m_imp->m_asm.mk_join(static_cast(a), static_cast(b))); - } - -}; +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + nlsat_solver.cpp + +Abstract: + + Nonlinear arithmetic satisfiability procedure. The procedure is + complete for nonlinear real arithmetic, but it also has limited + support for integers. + +Author: + + Leonardo de Moura (leonardo) 2012-01-02. + +Revision History: + +--*/ +#include "util/z3_exception.h" +#include "util/chashtable.h" +#include "util/id_gen.h" +#include "util/map.h" +#include "util/dependency.h" +#include "util/permutation.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial_cache.h" +#include "nlsat/nlsat_solver.h" +#include "nlsat/nlsat_clause.h" +#include "nlsat/nlsat_assignment.h" +#include "nlsat/nlsat_justification.h" +#include "nlsat/nlsat_evaluator.h" +#include "nlsat/nlsat_explain.h" +#include "nlsat/nlsat_params.hpp" +#include "nlsat/nlsat_simplify.h" +#include "nlsat/nlsat_simple_checker.h" +#include "nlsat/nlsat_variable_ordering_strategy.h" + +#define NLSAT_EXTRA_VERBOSE + +#ifdef NLSAT_EXTRA_VERBOSE +#define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) +#else +#define NLSAT_VERBOSE(CODE) ((void)0) +#endif + +namespace nlsat { + + + typedef chashtable ineq_atom_table; + typedef chashtable root_atom_table; + + // for apply_permutation procedure + void swap(clause * & c1, clause * & c2) noexcept { + std::swap(c1, c2); + } + + struct solver::ctx { + params_ref m_params; + reslimit& m_rlimit; + small_object_allocator m_allocator; + unsynch_mpq_manager m_qm; + pmanager m_pm; + anum_manager m_am; + bool m_incremental; + ctx(reslimit& rlim, params_ref const & p, bool incremental): + m_params(p), + m_rlimit(rlim), + m_allocator("nlsat"), + m_pm(rlim, m_qm, &m_allocator), + m_am(rlim, m_qm, p, &m_allocator), + m_incremental(incremental) + {} + }; + + struct solver::imp { + + + struct dconfig { + typedef imp value_manager; + typedef small_object_allocator allocator; + typedef void* value; + static const bool ref_count = false; + }; + + typedef dependency_manager assumption_manager; + typedef assumption_manager::dependency* _assumption_set; + + typedef obj_ref assumption_set_ref; + + + typedef polynomial::cache cache; + typedef ptr_vector interval_set_vector; + + + + ctx& m_ctx; + solver& m_solver; + reslimit& m_rlimit; + small_object_allocator& m_allocator; + bool m_incremental; + unsynch_mpq_manager& m_qm; + pmanager& m_pm; + cache m_cache; + anum_manager& m_am; + mutable assumption_manager m_asm; + assignment m_assignment, m_lo, m_hi; // partial interpretation + evaluator m_evaluator; + interval_set_manager & m_ism; + ineq_atom_table m_ineq_atoms; + root_atom_table m_root_atoms; + + + vector m_bounds; + + id_gen m_cid_gen; + clause_vector m_clauses; // set of clauses + clause_vector m_learned; // set of learned clauses + clause_vector m_valids; + + unsigned m_num_bool_vars; + atom_vector m_atoms; // bool_var -> atom + svector m_bvalues; // boolean assignment + unsigned_vector m_levels; // bool_var -> level + svector m_justifications; + vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal + bool_vector m_dead; // mark dead boolean variables + id_gen m_bid_gen; + + simplify m_simplify; + + bool_vector m_is_int; // m_is_int[x] is true if variable is integer + vector m_watches; // var -> clauses where variable is maximal + interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. + atom_vector m_var2eq; // var -> to asserted equality + var_vector m_perm; // var -> var permutation of the variables + var_vector m_inv_perm; + // m_perm: internal -> external + // m_inv_perm: external -> internal + struct perm_display_var_proc : public display_var_proc { + var_vector & m_perm; + display_var_proc m_default_display_var; + display_var_proc const * m_proc; // display external var ids + perm_display_var_proc(var_vector & perm): + m_perm(perm), + m_proc(nullptr) { + } + std::ostream& operator()(std::ostream & out, var x) const override { + if (m_proc == nullptr) + m_default_display_var(out, x); + else + (*m_proc)(out, m_perm[x]); + return out; + } + }; + perm_display_var_proc m_display_var; + + display_assumption_proc const* m_display_assumption; + struct display_literal_assumption : public display_assumption_proc { + imp& i; + literal_vector const& lits; + display_literal_assumption(imp& i, literal_vector const& lits): i(i), lits(lits) {} + std::ostream& operator()(std::ostream& out, assumption a) const override { + if (lits.begin() <= a && a < lits.end()) { + out << *((literal const*)a); + } + else if (i.m_display_assumption) { + (*i.m_display_assumption)(out, a); + } + return out; + } + + }; + struct scoped_display_assumptions { + imp& i; + display_assumption_proc const* m_save; + scoped_display_assumptions(imp& i, display_assumption_proc const& p): i(i), m_save(i.m_display_assumption) { + i.m_display_assumption = &p; + } + ~scoped_display_assumptions() { + i.m_display_assumption = m_save; + } + }; + + explain m_explain; + + bool_var m_bk; // current Boolean variable we are processing + var m_xk; // current arith variable we are processing + + unsigned m_scope_lvl; + + struct bvar_assignment {}; + struct stage {}; + struct trail { + enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; + kind m_kind; + union { + bool_var m_b; + interval_set * m_old_set; + atom * m_old_eq; + }; + trail(bool_var b, bvar_assignment):m_kind(BVAR_ASSIGNMENT), m_b(b) {} + trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} + trail(bool s, stage):m_kind(s ? NEW_STAGE : NEW_LEVEL) {} + trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} + }; + svector m_trail; + + anum m_zero; + + // configuration + unsigned long long m_max_memory; + unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts + bool m_simplify_cores; + bool m_reorder; + bool m_randomize; + bool m_random_order; + unsigned m_random_seed; + bool m_inline_vars; + bool m_log_lemmas; + bool m_check_lemmas; + unsigned m_max_conflicts; + unsigned m_lemma_count; + bool m_simple_check; + unsigned m_variable_ordering_strategy; + bool m_set_0_more; + bool m_cell_sample; + + struct stats { + unsigned m_simplifications; + unsigned m_restarts; + unsigned m_conflicts; + unsigned m_propagations; + unsigned m_decisions; + unsigned m_stages; + unsigned m_irrational_assignments; // number of irrational witnesses + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; + // statistics + stats m_stats; + + imp(solver& s, ctx& c): + m_ctx(c), + m_solver(s), + m_rlimit(c.m_rlimit), + m_allocator(c.m_allocator), + m_incremental(c.m_incremental), + m_qm(c.m_qm), + m_pm(c.m_pm), + m_cache(m_pm), + m_am(c.m_am), + m_asm(*this, m_allocator), + m_assignment(m_am), m_lo(m_am), m_hi(m_am), + m_evaluator(s, m_assignment, m_pm, m_allocator), + m_ism(m_evaluator.ism()), + m_num_bool_vars(0), + m_simplify(s, m_atoms, m_clauses, m_learned, m_pm), + m_display_var(m_perm), + m_display_assumption(nullptr), + m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()), + m_scope_lvl(0), + m_lemma(s), + m_lazy_clause(s), + m_lemma_assumptions(m_asm) { + updt_params(c.m_params); + reset_statistics(); + mk_true_bvar(); + m_lemma_count = 0; + } + + ~imp() { + clear(); + } + + void mk_true_bvar() { + bool_var b = mk_bool_var(); + SASSERT(b == true_bool_var); + literal true_lit(b, false); + mk_clause(1, &true_lit, false, nullptr); + } + + void updt_params(params_ref const & _p) { + nlsat_params p(_p); + m_max_memory = p.max_memory(); + m_lazy = p.lazy(); + m_simplify_cores = p.simplify_conflicts(); + bool min_cores = p.minimize_conflicts(); + m_reorder = p.reorder(); + m_randomize = p.randomize(); + m_max_conflicts = p.max_conflicts(); + m_random_order = p.shuffle_vars(); + m_random_seed = p.seed(); + m_inline_vars = p.inline_vars(); + m_log_lemmas = p.log_lemmas(); + m_check_lemmas = p.check_lemmas(); + m_variable_ordering_strategy = p.variable_ordering_strategy(); + + + m_cell_sample = p.cell_sample(); + + + m_ism.set_seed(m_random_seed); + m_explain.set_simplify_cores(m_simplify_cores); + m_explain.set_minimize_cores(min_cores); + m_explain.set_factor(p.factor()); + m_am.updt_params(p.p); + } + + void reset() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + m_cache.reset(); + m_assignment.reset(); + m_lo.reset(); + m_hi.reset(); + } + + void clear() { + m_explain.reset(); + m_lemma.reset(); + m_lazy_clause.reset(); + undo_until_size(0); + del_clauses(); + del_unref_atoms(); + } + + void checkpoint() { + if (!m_rlimit.inc()) throw solver_exception(m_rlimit.get_cancel_msg()); + if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); + } + + // ----------------------- + // + // Basic + // + // ----------------------- + + unsigned num_bool_vars() const { + return m_num_bool_vars; + } + + unsigned num_vars() const { + return m_is_int.size(); + } + + bool is_int(var x) const { + return m_is_int[x]; + } + + void inc_ref(assumption) {} + + void dec_ref(assumption) {} + + void inc_ref(_assumption_set a) { + if (a != nullptr) m_asm.inc_ref(a); + } + + void dec_ref(_assumption_set a) { + if (a != nullptr) m_asm.dec_ref(a); + } + + void inc_ref(bool_var b) { + if (b == null_bool_var) + return; + atom * a = m_atoms[b]; + if (a == nullptr) + return; + TRACE("ref", display(tout << "inc: " << b << " " << a->ref_count() << " ", *a) << "\n";); + a->inc_ref(); + } + + void inc_ref(literal l) { + inc_ref(l.var()); + } + + void dec_ref(bool_var b) { + if (b == null_bool_var) + return; + atom * a = m_atoms[b]; + if (a == nullptr) + return; + SASSERT(a->ref_count() > 0); + a->dec_ref(); + TRACE("ref", display(tout << "dec: " << b << " " << a->ref_count() << " ", *a) << "\n";); + if (a->ref_count() == 0) + del(a); + } + + void dec_ref(literal l) { + dec_ref(l.var()); + } + + bool is_arith_atom(bool_var b) const { return m_atoms[b] != nullptr; } + + bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } + + var max_var(poly const * p) const { + return m_pm.max_var(p); + } + + var max_var(bool_var b) const { + if (!is_arith_atom(b)) + return null_var; + else + return m_atoms[b]->max_var(); + } + + var max_var(literal l) const { + return max_var(l.var()); + } + + /** + \brief Return the maximum variable occurring in cls. + */ + var max_var(unsigned sz, literal const * cls) const { + var x = null_var; + for (unsigned i = 0; i < sz; i++) { + literal l = cls[i]; + if (is_arith_literal(l)) { + var y = max_var(l); + if (x == null_var || y > x) + x = y; + } + } + return x; + } + + var max_var(clause const & cls) const { + return max_var(cls.size(), cls.data()); + } + + /** + \brief Return the maximum Boolean variable occurring in cls. + */ + bool_var max_bvar(clause const & cls) const { + bool_var b = null_bool_var; + for (literal l : cls) { + if (b == null_bool_var || l.var() > b) + b = l.var(); + } + return b; + } + + /** + \brief Return the degree of the maximal variable of the given atom + */ + unsigned degree(atom const * a) const { + if (a->is_ineq_atom()) { + unsigned max = 0; + unsigned sz = to_ineq_atom(a)->size(); + var x = a->max_var(); + for (unsigned i = 0; i < sz; i++) { + unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); + if (d > max) + max = d; + } + return max; + } + else { + return m_pm.degree(to_root_atom(a)->p(), a->max_var()); + } + } + + /** + \brief Return the degree of the maximal variable in c + */ + unsigned degree(clause const & c) const { + var x = max_var(c); + if (x == null_var) + return 0; + unsigned max = 0; + for (literal l : c) { + atom const * a = m_atoms[l.var()]; + if (a == nullptr) + continue; + unsigned d = degree(a); + if (d > max) + max = d; + } + return max; + } + + // ----------------------- + // + // Variable, Atoms, Clauses & Assumption creation + // + // ----------------------- + + bool_var mk_bool_var_core() { + bool_var b = m_bid_gen.mk(); + m_num_bool_vars++; + m_atoms .setx(b, nullptr, nullptr); + m_bvalues .setx(b, l_undef, l_undef); + m_levels .setx(b, UINT_MAX, UINT_MAX); + m_justifications.setx(b, null_justification, null_justification); + m_bwatches .setx(b, clause_vector(), clause_vector()); + m_dead .setx(b, false, true); + return b; + } + + bool_var mk_bool_var() { + return mk_bool_var_core(); + } + + var mk_var(bool is_int) { + var x = m_pm.mk_var(); + register_var(x, is_int); + return x; + } + void register_var(var x, bool is_int) { + SASSERT(x == num_vars()); + m_is_int. push_back(is_int); + m_watches. push_back(clause_vector()); + m_infeasible.push_back(nullptr); + m_var2eq. push_back(nullptr); + m_perm. push_back(x); + m_inv_perm. push_back(x); + SASSERT(m_is_int.size() == m_watches.size()); + SASSERT(m_is_int.size() == m_infeasible.size()); + SASSERT(m_is_int.size() == m_var2eq.size()); + SASSERT(m_is_int.size() == m_perm.size()); + SASSERT(m_is_int.size() == m_inv_perm.size()); + } + + bool_vector m_found_vars; + void vars(literal l, var_vector& vs) { + vs.reset(); + atom * a = m_atoms[l.var()]; + if (a == nullptr) { + + } + else if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + var_vector new_vs; + for (unsigned j = 0; j < sz; j++) { + m_found_vars.reset(); + m_pm.vars(to_ineq_atom(a)->p(j), new_vs); + for (unsigned i = 0; i < new_vs.size(); ++i) { + if (!m_found_vars.get(new_vs[i], false)) { + m_found_vars.setx(new_vs[i], true, false); + vs.push_back(new_vs[i]); + } + } + } + } + else { + m_pm.vars(to_root_atom(a)->p(), vs); + //vs.erase(max_var(to_root_atom(a)->p())); + vs.push_back(to_root_atom(a)->x()); + } + } + + void deallocate(ineq_atom * a) { + unsigned obj_sz = ineq_atom::get_obj_size(a->size()); + a->~ineq_atom(); + m_allocator.deallocate(obj_sz, a); + } + + void deallocate(root_atom * a) { + a->~root_atom(); + m_allocator.deallocate(sizeof(root_atom), a); + } + + void del(bool_var b) { + SASSERT(m_bwatches[b].empty()); + //SASSERT(m_bvalues[b] == l_undef); + m_num_bool_vars--; + m_dead[b] = true; + m_atoms[b] = nullptr; + m_bvalues[b] = l_undef; + m_bid_gen.recycle(b); + } + + void del(ineq_atom * a) { + CTRACE("nlsat_solver", a->ref_count() > 0, display(tout, *a) << "\n";); + // this triggers in too many benign cases: + // SASSERT(a->ref_count() == 0); + m_ineq_atoms.erase(a); + del(a->bvar()); + unsigned sz = a->size(); + for (unsigned i = 0; i < sz; i++) + m_pm.dec_ref(a->p(i)); + deallocate(a); + } + + void del(root_atom * a) { + SASSERT(a->ref_count() == 0); + m_root_atoms.erase(a); + del(a->bvar()); + m_pm.dec_ref(a->p()); + deallocate(a); + } + + void del(atom * a) { + if (a == nullptr) + return; + TRACE("nlsat_verbose", display(tout << "del: b" << a->m_bool_var << " " << a->ref_count() << " ", *a) << "\n";); + if (a->is_ineq_atom()) + del(to_ineq_atom(a)); + else + del(to_root_atom(a)); + } + + // Delete atoms with ref_count == 0 + void del_unref_atoms() { + for (auto* a : m_atoms) { + del(a); + } + } + + + ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new, bool simplify) { + SASSERT(sz >= 1); + SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); + int sign = 1; + polynomial_ref p(m_pm); + ptr_buffer uniq_ps; + var max = null_var; + for (unsigned i = 0; i < sz; i++) { + p = m_pm.flip_sign_if_lm_neg(ps[i]); + if (p.get() != ps[i] && !is_even[i]) { + sign = -sign; + } + var curr_max = max_var(p.get()); + if (curr_max > max || max == null_var) + max = curr_max; + if (sz == 1 && simplify) { + if (sign < 0) + k = atom::flip(k); + sign = 1; + polynomial::manager::ineq_type t = polynomial::manager::ineq_type::EQ; + switch (k) { + case atom::EQ: t = polynomial::manager::ineq_type::EQ; break; + case atom::LT: t = polynomial::manager::ineq_type::LT; break; + case atom::GT: t = polynomial::manager::ineq_type::GT; break; + default: UNREACHABLE(); break; + } + polynomial::var_vector vars; + m_pm.vars(p, vars); + bool all_int = all_of(vars, [&](var x) { return is_int(x); }); + if (!all_int) + t = polynomial::manager::ineq_type::EQ; + m_pm.gcd_simplify(p, t); + } + uniq_ps.push_back(m_cache.mk_unique(p)); + TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); + //verbose_stream() << "p: " << p.get() << ", uniq: " << uniq_ps.back() << "\n"; + } + void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); + if (sign < 0) + k = atom::flip(k); + ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.data(), is_even, max); + ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); + CTRACE("nlsat_table_bug", tmp_atom != atom, ineq_atom::hash_proc h; + tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var) << "\n";); + CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout << "nonmax: ", *atom, m_display_var) << "\n";); + SASSERT(atom->max_var() == max); + is_new = (atom == tmp_atom); + if (is_new) { + for (unsigned i = 0; i < sz; i++) { + m_pm.inc_ref(atom->p(i)); + } + } + else { + deallocate(tmp_atom); + } + return atom; + } + + bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { + bool is_new = false; + ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new, simplify); + if (!is_new) { + return atom->bvar(); + } + else { + bool_var b = mk_bool_var_core(); + m_atoms[b] = atom; + atom->m_bool_var = b; + TRACE("nlsat_verbose", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); + return b; + } + } + + literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify = false) { + SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); + bool is_const = true; + polynomial::manager::scoped_numeral cnst(m_pm.m()); + m_pm.m().set(cnst, 1); + for (unsigned i = 0; i < sz; ++i) { + if (m_pm.is_const(ps[i])) { + if (m_pm.is_zero(ps[i])) { + m_pm.m().set(cnst, 0); + is_const = true; + break; + } + auto const& c = m_pm.coeff(ps[i], 0); + m_pm.m().mul(cnst, c, cnst); + if (is_even[i] && m_pm.m().is_neg(c)) { + m_pm.m().neg(cnst); + } + } + else { + is_const = false; + } + } + if (is_const) { + if (m_pm.m().is_pos(cnst) && k == atom::GT) return true_literal; + if (m_pm.m().is_neg(cnst) && k == atom::LT) return true_literal; + if (m_pm.m().is_zero(cnst) && k == atom::EQ) return true_literal; + return false_literal; + } + return literal(mk_ineq_atom(k, sz, ps, is_even, simplify), false); + } + + bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { + polynomial_ref p1(m_pm), uniq_p(m_pm); + p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. + uniq_p = m_cache.mk_unique(p1); + TRACE("nlsat_solver", tout << x << " " << p1 << " " << uniq_p << "\n";); + SASSERT(i > 0); + SASSERT(x >= max_var(p)); + SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); + + void * mem = m_allocator.allocate(sizeof(root_atom)); + root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); + root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); + SASSERT(old_atom->max_var() == x); + if (old_atom != new_atom) { + deallocate(new_atom); + return old_atom->bvar(); + } + bool_var b = mk_bool_var_core(); + m_atoms[b] = new_atom; + new_atom->m_bool_var = b; + m_pm.inc_ref(new_atom->p()); + return b; + } + + void attach_clause(clause & cls) { + var x = max_var(cls); + if (x != null_var) { + m_watches[x].push_back(&cls); + } + else { + bool_var b = max_bvar(cls); + m_bwatches[b].push_back(&cls); + } + } + + void deattach_clause(clause & cls) { + var x = max_var(cls); + if (x != null_var) { + m_watches[x].erase(&cls); + } + else { + bool_var b = max_bvar(cls); + m_bwatches[b].erase(&cls); + } + } + + void deallocate(clause * cls) { + size_t obj_sz = clause::get_obj_size(cls->size()); + cls->~clause(); + m_allocator.deallocate(obj_sz, cls); + } + + void del_clause(clause * cls) { + deattach_clause(*cls); + m_cid_gen.recycle(cls->id()); + unsigned sz = cls->size(); + for (unsigned i = 0; i < sz; i++) + dec_ref((*cls)[i]); + _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); + dec_ref(a); + deallocate(cls); + } + + void del_clause(clause * cls, clause_vector& clauses) { + clauses.erase(cls); + del_clause(cls); + } + + void del_clauses(ptr_vector & cs) { + for (clause* cp : cs) + del_clause(cp); + cs.reset(); + } + + void del_clauses() { + del_clauses(m_clauses); + del_clauses(m_learned); + del_clauses(m_valids); + } + + // We use a simple heuristic to sort literals + // - bool literals < arith literals + // - sort literals based on max_var + // - sort literal with the same max_var using degree + // break ties using the fact that ineqs are usually cheaper to process than eqs. + struct lit_lt { + imp & m; + lit_lt(imp & _m):m(_m) {} + + bool operator()(literal l1, literal l2) const { + atom * a1 = m.m_atoms[l1.var()]; + atom * a2 = m.m_atoms[l2.var()]; + if (a1 == nullptr && a2 == nullptr) + return l1.index() < l2.index(); + if (a1 == nullptr) + return true; + if (a2 == nullptr) + return false; + var x1 = a1->max_var(); + var x2 = a2->max_var(); + if (x1 < x2) + return true; + if (x1 > x2) + return false; + SASSERT(x1 == x2); + unsigned d1 = m.degree(a1); + unsigned d2 = m.degree(a2); + if (d1 < d2) + return true; + if (d1 > d2) + return false; + if (!a1->is_eq() && a2->is_eq()) + return true; + if (a1->is_eq() && !a2->is_eq()) + return false; + return l1.index() < l2.index(); + } + }; + + class scoped_bool_vars { + imp& s; + svector vec; + public: + scoped_bool_vars(imp& s):s(s) {} + ~scoped_bool_vars() { + for (bool_var v : vec) { + s.dec_ref(v); + } + } + void push_back(bool_var v) { + s.inc_ref(v); + vec.push_back(v); + } + bool_var const* begin() const { return vec.begin(); } + bool_var const* end() const { return vec.end(); } + bool_var operator[](bool_var v) const { return vec[v]; } + }; + + void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { + TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; + display(tout);); + IF_VERBOSE(2, display(verbose_stream() << "check lemma " << (is_valid?"valid: ":"consequence: "), n, cls) << "\n"); + for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); + scoped_suspend_rlimit _limit(m_rlimit); + ctx c(m_rlimit, m_ctx.m_params, m_ctx.m_incremental); + solver solver2(c); + imp& checker = *(solver2.m_imp); + checker.m_check_lemmas = false; + checker.m_log_lemmas = false; + checker.m_inline_vars = false; + + auto pconvert = [&](poly* p) { + return convert(m_pm, p, checker.m_pm); + }; + + // need to translate Boolean variables and literals + scoped_bool_vars tr(checker); + for (var x = 0; x < m_is_int.size(); ++x) { + checker.register_var(x, is_int(x)); + } + bool_var bv = 0; + tr.push_back(bv); + for (bool_var b = 1; b < m_atoms.size(); ++b) { + atom* a = m_atoms[b]; + if (a == nullptr) { + bv = checker.mk_bool_var(); + } + else if (a->is_ineq_atom()) { + ineq_atom& ia = *to_ineq_atom(a); + unsigned sz = ia.size(); + polynomial_ref_vector ps(checker.m_pm); + bool_vector is_even; + for (unsigned i = 0; i < sz; ++i) { + ps.push_back(pconvert(ia.p(i))); + is_even.push_back(ia.is_even(i)); + } + bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.data(), is_even.data()); + } + else if (a->is_root_atom()) { + root_atom& r = *to_root_atom(a); + if (r.x() >= max_var(r.p())) { + // permutation may be reverted after check completes, + // but then root atoms are not used in lemmas. + bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), pconvert(r.p())); + } + } + else { + UNREACHABLE(); + } + tr.push_back(bv); + } + if (!is_valid) { + for (clause* c : m_clauses) { + if (!a && c->assumptions()) { + continue; + } + literal_vector lits; + for (literal lit : *c) { + lits.push_back(literal(tr[lit.var()], lit.sign())); + } + checker.mk_external_clause(lits.size(), lits.data(), nullptr); + } + } + for (unsigned i = 0; i < n; ++i) { + literal lit = cls[i]; + literal nlit(tr[lit.var()], !lit.sign()); + checker.mk_external_clause(1, &nlit, nullptr); + } + lbool r = checker.check(); + if (r == l_true) { + for (bool_var b : tr) { + literal lit(b, false); + IF_VERBOSE(0, checker.display(verbose_stream(), lit) << " := " << checker.value(lit) << "\n"); + TRACE("nlsat", checker.display(tout, lit) << " := " << checker.value(lit) << "\n";); + } + for (clause* c : m_learned) { + bool found = false; + for (literal lit: *c) { + literal tlit(tr[lit.var()], lit.sign()); + found |= checker.value(tlit) == l_true; + } + if (!found) { + IF_VERBOSE(0, display(verbose_stream() << "violdated clause: ", *c) << "\n"); + TRACE("nlsat", display(tout << "violdated clause: ", *c) << "\n";); + } + } + for (clause* c : m_valids) { + bool found = false; + for (literal lit: *c) { + literal tlit(tr[lit.var()], lit.sign()); + found |= checker.value(tlit) == l_true; + } + if (!found) { + IF_VERBOSE(0, display(verbose_stream() << "violdated tautology clause: ", *c) << "\n"); + TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); + } + } + throw default_exception("lemma did not check"); + UNREACHABLE(); + } + } + + void log_lemma(std::ostream& out, clause const& cls) { + log_lemma(out, cls.size(), cls.data(), false); + } + + void log_lemma(std::ostream& out, unsigned n, literal const* cls, bool is_valid) { + ++m_lemma_count; + out << "(set-logic NRA)\n"; + if (is_valid) { + display_smt2_bool_decls(out); + display_smt2_arith_decls(out); + } + else + display_smt2(out); + for (unsigned i = 0; i < n; ++i) + display_smt2(out << "(assert ", ~cls[i]) << ")\n"; + display(out << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"; + out << "(check-sat)\n(reset)\n"; + + TRACE("nlsat", display(tout << "(echo \"#" << m_lemma_count << " ", n, cls) << "\")\n"); + } + + clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { + SASSERT(num_lits > 0); + unsigned cid = m_cid_gen.mk(); + void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); + clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); + for (unsigned i = 0; i < num_lits; i++) + inc_ref(lits[i]); + inc_ref(a); + return cls; + } + + clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { + if (num_lits == 0) { + num_lits = 1; + lits = &false_literal; + } + SASSERT(num_lits > 0); + clause * cls = mk_clause_core(num_lits, lits, learned, a); + TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); + std::sort(cls->begin(), cls->end(), lit_lt(*this)); + TRACE("nlsat", display(tout << " after sort:\n", *cls) << "\n";); + if (learned && m_log_lemmas) { + log_lemma(verbose_stream(), *cls); + } + if (learned && m_check_lemmas && false) { + check_lemma(cls->size(), cls->data(), false, cls->assumptions()); + } + if (learned) + m_learned.push_back(cls); + else + m_clauses.push_back(cls); + attach_clause(*cls); + return cls; + } + + void mk_external_clause(unsigned num_lits, literal const * lits, assumption a) { + _assumption_set as = nullptr; + if (a != nullptr) + as = m_asm.mk_leaf(a); + if (num_lits == 0) { + num_lits = 1; + lits = &false_literal; + } + mk_clause(num_lits, lits, false, as); + } + + // ----------------------- + // + // Search + // + // ----------------------- + + void save_assign_trail(bool_var b) { + m_trail.push_back(trail(b, bvar_assignment())); + } + + void save_set_updt_trail(interval_set * old_set) { + m_trail.push_back(trail(old_set)); + } + + void save_updt_eq_trail(atom * old_eq) { + m_trail.push_back(trail(old_eq)); + } + + void save_new_stage_trail() { + m_trail.push_back(trail(true, stage())); + } + + void save_new_level_trail() { + m_trail.push_back(trail(false, stage())); + } + + void undo_bvar_assignment(bool_var b) { + m_bvalues[b] = l_undef; + m_levels[b] = UINT_MAX; + del_jst(m_allocator, m_justifications[b]); + m_justifications[b] = null_justification; + if (m_atoms[b] == nullptr && b < m_bk) + m_bk = b; + } + + void undo_set_updt(interval_set * old_set) { + if (m_xk == null_var) + return; + var x = m_xk; + if (x < m_infeasible.size()) { + m_ism.dec_ref(m_infeasible[x]); + m_infeasible[x] = old_set; + } + } + + void undo_new_stage() { + if (m_xk == 0) { + m_xk = null_var; + } + else if (m_xk != null_var) { + m_xk--; + m_assignment.reset(m_xk); + } + } + + void undo_new_level() { + SASSERT(m_scope_lvl > 0); + m_scope_lvl--; + m_evaluator.pop(1); + } + + void undo_updt_eq(atom * a) { + if (m_var2eq.size() > m_xk) + m_var2eq[m_xk] = a; + } + + template + void undo_until(Predicate const & pred) { + while (pred() && !m_trail.empty()) { + trail & t = m_trail.back(); + switch (t.m_kind) { + case trail::BVAR_ASSIGNMENT: + undo_bvar_assignment(t.m_b); + break; + case trail::INFEASIBLE_UPDT: + undo_set_updt(t.m_old_set); + break; + case trail::NEW_STAGE: + undo_new_stage(); + break; + case trail::NEW_LEVEL: + undo_new_level(); + break; + case trail::UPDT_EQ: + undo_updt_eq(t.m_old_eq); + break; + default: + break; + } + m_trail.pop_back(); + } + } + + struct size_pred { + svector & m_trail; + unsigned m_old_size; + size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} + bool operator()() const { return m_trail.size() > m_old_size; } + }; + + // Keep undoing until trail has the given size + void undo_until_size(unsigned old_size) { + SASSERT(m_trail.size() >= old_size); + undo_until(size_pred(m_trail, old_size)); + } + + struct stage_pred { + var const & m_xk; + var m_target; + stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} + bool operator()() const { return m_xk != m_target; } + }; + + // Keep undoing until stage is new_xk + void undo_until_stage(var new_xk) { + undo_until(stage_pred(m_xk, new_xk)); + } + + struct level_pred { + unsigned const & m_scope_lvl; + unsigned m_new_lvl; + level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} + bool operator()() const { return m_scope_lvl > m_new_lvl; } + }; + + // Keep undoing until level is new_lvl + void undo_until_level(unsigned new_lvl) { + undo_until(level_pred(m_scope_lvl, new_lvl)); + } + + struct unassigned_pred { + bool_var m_b; + svector const & m_bvalues; + unassigned_pred(svector const & bvalues, bool_var b): + m_b(b), + m_bvalues(bvalues) {} + bool operator()() const { return m_bvalues[m_b] != l_undef; } + }; + + // Keep undoing until b is unassigned + void undo_until_unassigned(bool_var b) { + undo_until(unassigned_pred(m_bvalues, b)); + SASSERT(m_bvalues[b] == l_undef); + } + + struct true_pred { + bool operator()() const { return true; } + }; + + void undo_until_empty() { + undo_until(true_pred()); + } + + /** + \brief Create a new scope level + */ + void new_level() { + m_evaluator.push(); + m_scope_lvl++; + save_new_level_trail(); + } + + /** + \brief Return the value of the given literal that was assigned by the search + engine. + */ + lbool assigned_value(literal l) const { + bool_var b = l.var(); + if (l.sign()) + return ~m_bvalues[b]; + else + return m_bvalues[b]; + } + + /** + \brief Assign literal using the given justification + */ + void assign(literal l, justification j) { + TRACE("nlsat_assign", + display(tout << "assigning literal: ", l); + display(tout << " <- ", j);); + + SASSERT(assigned_value(l) == l_undef); + SASSERT(j != null_justification); + SASSERT(!j.is_null()); + if (j.is_decision()) + m_stats.m_decisions++; + else + m_stats.m_propagations++; + bool_var b = l.var(); + m_bvalues[b] = to_lbool(!l.sign()); + m_levels[b] = m_scope_lvl; + m_justifications[b] = j; + save_assign_trail(b); + updt_eq(b, j); + TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << "\n";); + } + + /** + \brief Create a "case-split" + */ + void decide(literal l) { + new_level(); + assign(l, decided_justification); + } + + /** + \brief Return the value of a literal as defined in Dejan and Leo's paper. + */ + lbool value(literal l) { + lbool val = assigned_value(l); + if (val != l_undef) { + TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); + return val; + } + bool_var b = l.var(); + atom * a = m_atoms[b]; + if (a == nullptr) { + TRACE("nlsat_verbose", display(tout << " no atom for ", l) << "\n";); + return l_undef; + } + var max = a->max_var(); + if (!m_assignment.is_assigned(max)) { + TRACE("nlsat_verbose", display(tout << " maximal variable not assigned ", l) << "\n";); + return l_undef; + } + val = to_lbool(m_evaluator.eval(a, l.sign())); + TRACE("nlsat_verbose", display(tout << " evaluated value " << val << " for ", l) << "\n";); + TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; + tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; + display_assignment(tout);); + return val; + } + + /** + \brief Return true if the given clause is already satisfied in the current partial interpretation. + */ + bool is_satisfied(clause const & cls) const { + for (literal l : cls) { + if (const_cast(this)->value(l) == l_true) { + TRACE("value_bug:", tout << l << " := true\n";); + return true; + } + } + return false; + } + + /** + \brief Return true if the given clause is false in the current partial interpretation. + */ + bool is_inconsistent(unsigned sz, literal const * cls) { + for (unsigned i = 0; i < sz; i++) { + if (value(cls[i]) != l_false) { + TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); + return false; + } + } + return true; + } + + /** + \brief Process a clauses that contains only Boolean literals. + */ + bool process_boolean_clause(clause const & cls) { + SASSERT(m_xk == null_var); + unsigned num_undef = 0; + unsigned first_undef = UINT_MAX; + unsigned sz = cls.size(); + for (unsigned i = 0; i < sz; i++) { + literal l = cls[i]; + SASSERT(m_atoms[l.var()] == nullptr); + SASSERT(value(l) != l_true); + if (value(l) == l_false) + continue; + SASSERT(value(l) == l_undef); + num_undef++; + if (first_undef == UINT_MAX) + first_undef = i; + } + if (num_undef == 0) + return false; + SASSERT(first_undef != UINT_MAX); + if (num_undef == 1) + assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause + else + decide(cls[first_undef]); + return true; + } + + /** + \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. + */ + literal_vector core; + ptr_vector clauses; + void R_propagate(literal l, interval_set const * s, bool include_l = true) { + m_ism.get_justifications(s, core, clauses); + if (include_l) + core.push_back(~l); + auto j = mk_lazy_jst(m_allocator, core.size(), core.data(), clauses.size(), clauses.data()); + TRACE("nlsat_resolve", display(tout, j); display_eval(tout << "evaluated:", j)); + assign(l, j); + SASSERT(value(l) == l_true); + } + + /** + \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s + */ + void updt_infeasible(interval_set const * s) { + SASSERT(m_xk != null_var); + interval_set * xk_set = m_infeasible[m_xk]; + save_set_updt_trail(xk_set); + interval_set_ref new_set(m_ism); + TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set) << "\n"; m_ism.display(tout, s) << "\n";); + new_set = m_ism.mk_union(s, xk_set); + TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set) << "\n";); + SASSERT(!m_ism.is_full(new_set)); + m_ism.inc_ref(new_set); + m_infeasible[m_xk] = new_set; + } + + /** + \brief Update m_var2eq mapping. + */ + void updt_eq(bool_var b, justification j) { + if (!m_simplify_cores) + return; + if (m_bvalues[b] != l_true) + return; + atom * a = m_atoms[b]; + if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) + return; + switch (j.get_kind()) { + case justification::CLAUSE: + if (j.get_clause()->assumptions() != nullptr) return; + break; + case justification::LAZY: + if (j.get_lazy()->num_clauses() > 0) return; + if (j.get_lazy()->num_lits() > 0) return; + break; + default: + break; + } + var x = m_xk; + SASSERT(a->max_var() == x); + SASSERT(x != null_var); + if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) + return; // we only update m_var2eq if the new equality has smaller degree + TRACE("nlsat_simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; + tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)) << "\n"; + display(tout, j); + ); + save_updt_eq_trail(m_var2eq[x]); + m_var2eq[x] = a; + } + + /** + \brief Process a clause that contains nonlinear arithmetic literals + + If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 + */ + bool process_arith_clause(clause const & cls, bool satisfy_learned) { + if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { + TRACE("nlsat", tout << "skip learned\n";); + return true; // ignore lemmas in super lazy mode + } + SASSERT(m_xk == max_var(cls)); + unsigned num_undef = 0; // number of undefined literals + unsigned first_undef = UINT_MAX; // position of the first undefined literal + interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal + interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable + SASSERT(!m_ism.is_full(xk_set)); + for (unsigned idx = 0; idx < cls.size(); ++idx) { + literal l = cls[idx]; + checkpoint(); + if (value(l) == l_false) + continue; + if (value(l) == l_true) + return true; // could happen if clause is a tautology + CTRACE("nlsat", max_var(l) != m_xk || value(l) != l_undef, display(tout); + tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l) << "\n"; + display(tout, cls) << "\n";); + SASSERT(value(l) == l_undef); + SASSERT(max_var(l) == m_xk); + bool_var b = l.var(); + atom * a = m_atoms[b]; + SASSERT(a != nullptr); + interval_set_ref curr_set(m_ism); + curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); + TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; + display(tout, cls) << "\n";); + if (m_ism.is_empty(curr_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); + R_propagate(l, nullptr); + SASSERT(is_satisfied(cls)); + return true; + } + if (m_ism.is_full(curr_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); + R_propagate(~l, nullptr); + continue; + } + if (m_ism.subset(curr_set, xk_set)) { + TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); + R_propagate(l, xk_set); + return true; + } + interval_set_ref tmp(m_ism); + tmp = m_ism.mk_union(curr_set, xk_set); + if (m_ism.is_full(tmp)) { + TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; + display(tout, cls) << "\n"; + m_ism.display(tout, tmp); tout << "\n"; + ); + R_propagate(~l, tmp, false); + continue; + } + num_undef++; + if (first_undef == UINT_MAX) { + first_undef = idx; + first_undef_set = curr_set; + } + } + TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); + if (num_undef == 0) + return false; + SASSERT(first_undef != UINT_MAX); + if (num_undef == 1) { + // unit clause + assign(cls[first_undef], mk_clause_jst(&cls)); + updt_infeasible(first_undef_set); + } + else if ( satisfy_learned || + !cls.is_learned() /* must always satisfy input clauses */ || + m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { + decide(cls[first_undef]); + updt_infeasible(first_undef_set); + } + else { + TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() + << ", lazy: " << m_lazy << "\n";); + } + return true; + } + + /** + \brief Try to satisfy the given clause. Return true if succeeded. + + If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 + */ + bool process_clause(clause const & cls, bool satisfy_learned) { + if (is_satisfied(cls)) + return true; + if (m_xk == null_var) + return process_boolean_clause(cls); + else + return process_arith_clause(cls, satisfy_learned); + } + + /** + \brief Try to satisfy the given "set" of clauses. + Return 0, if the set was satisfied, or the violating clause otherwise + */ + clause * process_clauses(clause_vector const & cs) { + for (clause* c : cs) { + if (!process_clause(*c, false)) + return c; + } + return nullptr; // succeeded + } + + /** + \brief Make sure m_bk is the first unassigned pure Boolean variable. + Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. + */ + void peek_next_bool_var() { + while (m_bk < m_atoms.size()) { + if (!m_dead[m_bk] && m_atoms[m_bk] == nullptr && m_bvalues[m_bk] == l_undef) { + return; + } + m_bk++; + } + m_bk = null_bool_var; + } + + /** + \brief Create a new stage. See Dejan and Leo's paper. + */ + void new_stage() { + m_stats.m_stages++; + save_new_stage_trail(); + if (m_xk == null_var) + m_xk = 0; + else + m_xk++; + } + + /** + \brief Assign m_xk + */ + void select_witness() { + scoped_anum w(m_am); + SASSERT(!m_ism.is_full(m_infeasible[m_xk])); + m_ism.pick_in_complement(m_infeasible[m_xk], is_int(m_xk), w, m_randomize); + TRACE("nlsat", + tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; + tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); + TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); + if (!m_am.is_rational(w)) + m_stats.m_irrational_assignments++; + m_assignment.set_core(m_xk, w); + } + + + + bool is_satisfied() { + if (m_bk == null_bool_var && m_xk >= num_vars()) { + TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); + fix_patch(); + SASSERT(check_satisfied(m_clauses)); + return true; // all variables were assigned, and all clauses were satisfied. + } + else { + return false; + } + } + + + /** + \brief main procedure + */ + lbool search() { + TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); + TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); + TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); + TRACE("nlsat_mathematica", display_mathematica(tout);); + TRACE("nlsat", display_smt2(tout);); + m_bk = 0; + m_xk = null_var; + + while (true) { + if (should_reorder()) + do_reorder(); + +#if 0 + if (should_gc()) + do_gc(); +#endif + + if (should_simplify()) + do_simplify(); + + CASSERT("nlsat", check_satisfied()); + if (m_xk == null_var) { + peek_next_bool_var(); + if (m_bk == null_bool_var) + new_stage(); // move to arith vars + } + else { + new_stage(); // peek next arith var + } + TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); + if (is_satisfied()) { + return l_true; + } + while (true) { + TRACE("nlsat_verbose", tout << "processing variable "; + if (m_xk != null_var) { + m_display_var(tout, m_xk); tout << " " << m_watches[m_xk].size(); + } + else { + tout << m_bwatches[m_bk].size() << " boolean b" << m_bk; + } + tout << "\n";); + checkpoint(); + clause * conflict_clause; + if (m_xk == null_var) + conflict_clause = process_clauses(m_bwatches[m_bk]); + else + conflict_clause = process_clauses(m_watches[m_xk]); + if (conflict_clause == nullptr) + break; + if (!resolve(*conflict_clause)) + return l_false; + if (m_stats.m_conflicts >= m_max_conflicts) + return l_undef; + log(); + } + + if (m_xk == null_var) { + if (m_bvalues[m_bk] == l_undef) { + decide(literal(m_bk, true)); + m_bk++; + } + } + else { + select_witness(); + } + } + } + + void gc() { + if (m_learned.size() <= 4*m_clauses.size()) + return; + reset_watches(); + reinit_cache(); + unsigned j = 0; + for (unsigned i = 0; i < m_learned.size(); ++i) { + auto cls = m_learned[i]; + if (i - j < m_clauses.size() && cls->size() > 1 && !cls->is_active()) + del_clause(cls); + else { + m_learned[j++] = cls; + cls->set_active(false); + } + } + m_learned.shrink(j); + reattach_arith_clauses(m_clauses); + reattach_arith_clauses(m_learned); + } + + + bool should_gc() { + return m_learned.size() > 10 * m_clauses.size(); + } + + void do_gc() { + undo_to_base(); + gc(); + } + + void undo_to_base() { + init_search(); + m_bk = 0; + m_xk = null_var; + } + + unsigned m_restart_threshold = 10000; + bool should_reorder() { + return m_stats.m_conflicts > 0 && m_stats.m_conflicts % m_restart_threshold == 0; + } + + void do_reorder() { + undo_to_base(); + m_stats.m_restarts++; + m_stats.m_conflicts++; + if (m_reordered) + restore_order(); + apply_reorder(); + } + + bool m_did_simplify = false; + bool should_simplify() { + return + !m_did_simplify && m_inline_vars && + !m_incremental && m_stats.m_conflicts > 100; + } + + void do_simplify() { + undo_to_base(); + m_did_simplify = true; + m_simplify(); + } + + unsigned m_next_conflict = 100; + void log() { + if (m_stats.m_conflicts != 1 && m_stats.m_conflicts < m_next_conflict) + return; + m_next_conflict += 100; + IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); + } + + + lbool search_check() { + lbool r = l_undef; + m_stats.m_conflicts = 0; + m_stats.m_restarts = 0; + m_next_conflict = 0; + while (true) { + r = search(); + if (r != l_true) + break; + ++m_stats.m_restarts; + vector> bounds; + + for (var x = 0; x < num_vars(); x++) { + if (is_int(x) && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { + scoped_anum v(m_am), vlo(m_am); + v = m_assignment.value(x); + rational lo; + m_am.int_lt(v, vlo); + if (!m_am.is_int(vlo)) + continue; + m_am.to_rational(vlo, lo); + // derive tight bounds. + while (true) { + lo++; + if (!m_am.gt(v, lo.to_mpq())) { + lo--; + break; + } + } + bounds.push_back(std::make_pair(x, lo)); + } + } + if (bounds.empty()) + break; + + gc(); + if (m_stats.m_restarts % 10 == 0) { + if (m_reordered) + restore_order(); + apply_reorder(); + } + + init_search(); + IF_VERBOSE(2, verbose_stream() << "(nlsat-b&b :conflicts " << m_stats.m_conflicts + << " :decisions " << m_stats.m_decisions + << " :propagations " << m_stats.m_propagations + << " :clauses " << m_clauses.size() + << " :learned " << m_learned.size() << ")\n"); + for (auto const& b : bounds) { + var x = b.first; + rational lo = b.second; + rational hi = lo + 1; // rational::one(); + bool is_even = false; + polynomial_ref p(m_pm); + rational one(1); + m_lemma.reset(); + p = m_pm.mk_linear(1, &one, &x, -lo); + poly* p1 = p.get(); + m_lemma.push_back(~mk_ineq_literal(atom::GT, 1, &p1, &is_even)); + p = m_pm.mk_linear(1, &one, &x, -hi); + poly* p2 = p.get(); + m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); + + // perform branch and bound + clause * cls = mk_clause(m_lemma.size(), m_lemma.data(), true, nullptr); + IF_VERBOSE(4, display(verbose_stream(), *cls) << "\n"); + if (cls) { + TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); + } + } + } + return r; + } + + bool m_reordered = false; + bool simple_check() { + literal_vector learned_unit; + simple_checker checker(m_pm, m_am, m_clauses, learned_unit, m_atoms, m_is_int.size()); + if (!checker()) + return false; + for (unsigned i = 0, sz = learned_unit.size(); i < sz; ++i) { + clause *cla = mk_clause(1, &learned_unit[i], true, nullptr); + if (m_atoms[learned_unit[i].var()] == nullptr) { + assign(learned_unit[i], mk_clause_jst(cla)); + } + } + return true; + } + + + void run_variable_ordering_strategy() { + TRACE("reorder", tout << "runing vos: " << m_variable_ordering_strategy << '\n';); + + unsigned num = num_vars(); + vos_var_info_collector vos_collector(m_pm, m_atoms, num, m_variable_ordering_strategy); + vos_collector.collect(m_clauses); + vos_collector.collect(m_learned); + + var_vector perm; + vos_collector(perm); + reorder(perm.size(), perm.data()); + } + + void apply_reorder() { + m_reordered = false; + if (!can_reorder()) + ; + else if (m_random_order) { + shuffle_vars(); + m_reordered = true; + } + else if (m_reorder) { + heuristic_reorder(); + m_reordered = true; + } + } + + lbool check() { + + if (m_simple_check) { + if (!simple_check()) { + TRACE("simple_check", tout << "real unsat\n";); + return l_false; + } + TRACE("simple_checker_learned", + tout << "simple check done\n"; + ); + } + + TRACE("nlsat_smt2", display_smt2(tout);); + TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); + init_search(); + m_explain.set_full_dimensional(is_full_dimensional()); + bool reordered = false; + + + if (!can_reorder()) { + + } + else if (m_variable_ordering_strategy > 0) { + run_variable_ordering_strategy(); + reordered = true; + } + else if (m_random_order) { + shuffle_vars(); + reordered = true; + } + else if (m_reorder) { + heuristic_reorder(); + reordered = true; + } + sort_watched_clauses(); + lbool r = search_check(); + CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); + if (reordered) { + restore_order(); + } + CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); + CTRACE("nlsat", r == l_false, display(tout << "unsat\n");); + SASSERT(r != l_true || check_satisfied(m_clauses)); + return r; + } + + void init_search() { + undo_until_empty(); + while (m_scope_lvl > 0) { + undo_new_level(); + } + m_xk = null_var; + for (unsigned i = 0; i < m_bvalues.size(); ++i) { + m_bvalues[i] = l_undef; + } + m_assignment.reset(); + } + + lbool check(literal_vector& assumptions) { + literal_vector result; + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.data(); + for (unsigned i = 0; i < sz; ++i) { + mk_external_clause(1, ptr+i, (assumption)(ptr+i)); + } + display_literal_assumption dla(*this, assumptions); + scoped_display_assumptions _scoped_display(*this, dla); + lbool r = check(); + + if (r == l_false) { + // collect used literals from m_lemma_assumptions + vector deps; + get_core(deps); + for (unsigned i = 0; i < deps.size(); ++i) { + literal const* lp = (literal const*)(deps[i]); + if (ptr <= lp && lp < ptr + sz) { + result.push_back(*lp); + } + } + } + collect(assumptions, m_clauses); + collect(assumptions, m_learned); + del_clauses(m_valids); + if (m_check_lemmas) { + for (clause* c : m_learned) { + check_lemma(c->size(), c->data(), false, nullptr); + } + } + +#if 0 + for (clause* c : m_learned) { + IF_VERBOSE(0, display(verbose_stream() << "KEEP: ", c->size(), c->c_ptr()) << "\n"); + } +#endif + assumptions.reset(); + assumptions.append(result); + return r; + } + + void get_core(vector& deps) { + m_asm.linearize(m_lemma_assumptions.get(), deps); + } + + void collect(literal_vector const& assumptions, clause_vector& clauses) { + unsigned j = 0; + for (clause * c : clauses) { + if (collect(assumptions, *c)) { + del_clause(c); + } + else { + clauses[j++] = c; + } + } + clauses.shrink(j); + } + + bool collect(literal_vector const& assumptions, clause const& c) { + unsigned sz = assumptions.size(); + literal const* ptr = assumptions.data(); + _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); + if (asms == nullptr) { + return false; + } + vector deps; + m_asm.linearize(asms, deps); + for (auto dep : deps) { + if (ptr <= dep && dep < ptr + sz) { + return true; + } + } + return false; + } + + // ----------------------- + // + // Conflict Resolution + // + // ----------------------- + svector m_marks; // bool_var -> bool temp mark used during conflict resolution + unsigned m_num_marks; + scoped_literal_vector m_lemma; + scoped_literal_vector m_lazy_clause; + assumption_set_ref m_lemma_assumptions; // assumption tracking + + // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. + + bool check_marks() { + for (unsigned m : m_marks) { + (void)m; + SASSERT(m == 0); + } + return true; + } + + unsigned scope_lvl() const { return m_scope_lvl; } + + bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } + + void mark(bool_var b) { m_marks.setx(b, 1, 0); } + + void reset_mark(bool_var b) { m_marks[b] = 0; } + + void reset_marks() { + for (auto const& l : m_lemma) { + reset_mark(l.var()); + } + } + + void process_antecedent(literal antecedent) { + checkpoint(); + bool_var b = antecedent.var(); + TRACE("nlsat_resolve", display(tout << "resolving antecedent: ", antecedent) << "\n";); + if (assigned_value(antecedent) == l_undef) { + checkpoint(); + // antecedent must be false in the current arith interpretation + SASSERT(value(antecedent) == l_false || m_rlimit.is_canceled()); + if (!is_marked(b)) { + SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage + TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); + mark(b); + m_lemma.push_back(antecedent); + } + return; + } + + unsigned b_lvl = m_levels[b]; + TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); + if (!is_marked(b)) { + mark(b); + if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { + TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); + m_num_marks++; + } + else { + TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; + tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); + m_lemma.push_back(antecedent); + } + } + } + + void resolve_clause(bool_var b, unsigned sz, literal const * c) { + TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b) << "\n"; display(tout, sz, c); tout << "\n";); + TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); + + for (unsigned i = 0; i < sz; i++) { + if (c[i].var() != b) + process_antecedent(c[i]); + } + } + + void resolve_clause(bool_var b, clause & c) { + TRACE("nlsat_resolve", tout << "resolving clause "; if (b != null_bool_var) tout << "for b: " << b << "\n"; display(tout, c) << "\n";); + c.set_active(true); + resolve_clause(b, c.size(), c.data()); + m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); + } + + void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { + TRACE("nlsat_resolve", tout << "resolving lazy_justification for b" << b << "\n";); + unsigned sz = jst.num_lits(); + + // Dump lemma as Mathematica formula that must be true, + // if the current interpretation (really) makes the core in jst infeasible. + TRACE("nlsat_mathematica", + tout << "assignment lemma\n"; + literal_vector core; + for (unsigned i = 0; i < sz; i++) { + core.push_back(~jst.lit(i)); + } + display_mathematica_lemma(tout, core.size(), core.data(), true);); + + m_lazy_clause.reset(); + m_explain(jst.num_lits(), jst.lits(), m_lazy_clause); + for (unsigned i = 0; i < sz; i++) + m_lazy_clause.push_back(~jst.lit(i)); + + // lazy clause is a valid clause + TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.data());); + TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.data()); tout << "\n";); + TRACE("nlsat_resolve", + tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; + tout << "new valid clause:\n"; + display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";); + + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lazy_clause.size(), m_lazy_clause.data(), true); + + if (m_check_lemmas) { + check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), true, nullptr); + m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr)); + } + +#ifdef Z3DEBUG + { + unsigned sz = m_lazy_clause.size(); + for (unsigned i = 0; i < sz; i++) { + literal l = m_lazy_clause[i]; + if (l.var() != b) { + if (value(l) != l_false) + display(verbose_stream() << value(l) << " ", 1, &l); + SASSERT(value(l) == l_false || m_rlimit.is_canceled()); + } + else { + SASSERT(value(l) == l_true || m_rlimit.is_canceled()); + SASSERT(!l.sign() || m_bvalues[b] == l_false); + SASSERT(l.sign() || m_bvalues[b] == l_true); + } + } + } +#endif + checkpoint(); + resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.data()); + + for (unsigned i = 0; i < jst.num_clauses(); ++i) { + clause const& c = jst.clause(i); + TRACE("nlsat", display(tout << "adding clause assumptions ", c) << "\n";); + m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); + } + } + + /** + \brief Return true if all literals in ls are from previous stages. + */ + bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { + for (unsigned i = 0; i < num; i++) { + if (max_var(ls[i]) == m_xk) + return false; + } + return true; + } + + /** + \brief Return the maximum scope level in ls. + + \pre This method assumes value(ls[i]) is l_false for i in [0, num) + */ + unsigned max_scope_lvl(unsigned num, literal const * ls) { + unsigned max = 0; + for (unsigned i = 0; i < num; i++) { + literal l = ls[i]; + bool_var b = l.var(); + SASSERT(value(ls[i]) == l_false); + if (assigned_value(l) == l_false) { + unsigned lvl = m_levels[b]; + if (lvl > max) + max = lvl; + } + else { + // l must be a literal from a previous stage that is false in the current interpretation + SASSERT(assigned_value(l) == l_undef); + SASSERT(max_var(b) != null_var); + SASSERT(m_xk != null_var); + SASSERT(max_var(b) < m_xk); + } + } + return max; + } + + /** + \brief Remove literals of the given lvl (that are in the current stage) from lemma. + + \pre This method assumes value(ls[i]) is l_false for i in [0, num) + */ + void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { + TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); + unsigned sz = lemma.size(); + unsigned j = 0; + for (unsigned i = 0; i < sz; i++) { + literal l = lemma[i]; + bool_var b = l.var(); + SASSERT(is_marked(b)); + SASSERT(value(lemma[i]) == l_false); + if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { + m_num_marks++; + continue; + } + lemma.set(j, l); + j++; + } + lemma.shrink(j); + } + + /** + \brief Return true if it is a Boolean lemma. + */ + bool is_bool_lemma(unsigned sz, literal const * ls) const { + for (unsigned i = 0; i < sz; i++) { + if (m_atoms[ls[i].var()] != nullptr) + return false; + } + return true; + } + + + /** + Return the maximal decision level in lemma for literals in the first sz-1 positions that + are at the same stage. If all these literals are from previous stages, + we just backtrack the current level. + */ + unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { + SASSERT(!is_bool_lemma(sz, lemma)); + unsigned new_lvl = 0; + bool found_lvl = false; + for (unsigned i = 0; i < sz - 1; i++) { + literal l = lemma[i]; + if (max_var(l) == m_xk) { + bool_var b = l.var(); + if (!found_lvl) { + found_lvl = true; + new_lvl = m_levels[b]; + } + else { + if (m_levels[b] > new_lvl) + new_lvl = m_levels[b]; + } + } + } + SASSERT(!found_lvl || new_lvl < scope_lvl()); + if (!found_lvl) { + TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); + new_lvl = scope_lvl() - 1; + } + return new_lvl; + } + + struct scoped_reset_marks { + imp& i; + scoped_reset_marks(imp& i):i(i) {} + ~scoped_reset_marks() { if (i.m_num_marks > 0) { i.m_num_marks = 0; for (char& m : i.m_marks) m = 0; } } + }; + + + /** + \brief Return true if the conflict was solved. + */ + bool resolve(clause & conflict) { + clause * conflict_clause = &conflict; + m_lemma_assumptions = nullptr; + start: + SASSERT(check_marks()); + TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); + TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); + m_stats.m_conflicts++; + TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; + tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; + tout << "scope_lvl: " << scope_lvl() << "\n"; + tout << "current assignment\n"; display_assignment(tout);); + + m_num_marks = 0; + m_lemma.reset(); + m_lemma_assumptions = nullptr; + scoped_reset_marks _sr(*this); + resolve_clause(null_bool_var, *conflict_clause); + + unsigned top = m_trail.size(); + bool found_decision; + while (true) { + found_decision = false; + while (m_num_marks > 0) { + checkpoint(); + SASSERT(top > 0); + trail & t = m_trail[top-1]; + SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage + if (t.m_kind == trail::BVAR_ASSIGNMENT) { + bool_var b = t.m_b; + if (is_marked(b)) { + TRACE("nlsat_resolve", tout << "found marked: b" << b << "\n"; display_atom(tout, b) << "\n";); + m_num_marks--; + reset_mark(b); + justification jst = m_justifications[b]; + switch (jst.get_kind()) { + case justification::CLAUSE: + resolve_clause(b, *(jst.get_clause())); + break; + case justification::LAZY: + resolve_lazy_justification(b, *(jst.get_lazy())); + break; + case justification::DECISION: + SASSERT(m_num_marks == 0); + found_decision = true; + TRACE("nlsat_resolve", tout << "found decision\n";); + m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); + break; + default: + UNREACHABLE(); + break; + } + } + } + top--; + } + + // m_lemma is an implicating clause after backtracking current scope level. + if (found_decision) + break; + + // If lemma only contains literals from previous stages, then we can stop. + // We make progress by returning to a previous stage with additional information (new lemma) + // that forces us to select a new partial interpretation + if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.data())) + break; + + // Conflict does not depend on the current decision, and it is still in the current stage. + // We should find + // - the maximal scope level in the lemma + // - remove literal assigned in the scope level from m_lemma + // - backtrack to this level + // - and continue conflict resolution from there + // - we must bump m_num_marks for literals removed from m_lemma + unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.data()); + TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); + SASSERT(max_lvl < scope_lvl()); + remove_literals_from_lvl(m_lemma, max_lvl); + undo_until_level(max_lvl); + top = m_trail.size(); + TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); + SASSERT(scope_lvl() == max_lvl); + } + + TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); + TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); + + if (m_lemma.empty()) { + TRACE("nlsat", tout << "empty clause generated\n";); + return false; // problem is unsat, empty clause was generated + } + + reset_marks(); // remove marks from the literals in m_lemmas. + TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n"; + tout << "found_decision: " << found_decision << "\n";); + + if (m_check_lemmas) { + check_lemma(m_lemma.size(), m_lemma.data(), false, m_lemma_assumptions.get()); + } + + if (m_log_lemmas) + log_lemma(verbose_stream(), m_lemma.size(), m_lemma.data(), false); + + // There are two possibilities: + // 1) m_lemma contains only literals from previous stages, and they + // are false in the current interpretation. We make progress + // by returning to a previous stage with additional information (new clause) + // that forces us to select a new partial interpretation + // >>> Return to some previous stage (we may also backjump many decisions and stages). + // + // 2) m_lemma contains at most one literal from the current level (the last literal). + // Moreover, this literal was a decision, but the new lemma forces it to + // be assigned to a different value. + // >>> In this case, we remain in the same stage but, we add a new asserted literal + // in a previous scope level. We may backjump many decisions. + // + unsigned sz = m_lemma.size(); + clause * new_cls = nullptr; + if (!found_decision) { + // Case 1) + // We just have to find the maximal variable in m_lemma, and return to that stage + // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; + var new_max_var = max_var(sz, m_lemma.data()); + TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); + undo_until_stage(new_max_var); + SASSERT(m_xk == new_max_var); + new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); + TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << "\n"; + if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); + } + else { + SASSERT(scope_lvl() >= 1); + // Case 2) + if (is_bool_lemma(m_lemma.size(), m_lemma.data())) { + // boolean lemma, we just backtrack until the last literal is unassigned. + bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); + undo_until_unassigned(max_bool_var); + } + else { + // We must find the maximal decision level in literals in the first sz-1 positions that + // are at the same stage. If all these literals are from previous stages, + // we just backtrack the current level. + unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.data()); + TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); + undo_until_level(new_lvl); + } + + if (lemma_is_clause(*conflict_clause)) { + TRACE("nlsat", tout << "found decision literal in conflict clause\n";); + VERIFY(process_clause(*conflict_clause, true)); + return true; + } + new_cls = mk_clause(sz, m_lemma.data(), true, m_lemma_assumptions.get()); + } + NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); + if (!process_clause(*new_cls, true)) { + TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n"; + display(tout, *new_cls) << "\n"; + ); + // we are still in conflict + conflict_clause = new_cls; + goto start; + } + TRACE("nlsat_resolve_done", display_assignment(tout);); + return true; + } + + bool lemma_is_clause(clause const& cls) const { + bool same = (m_lemma.size() == cls.size()); + for (unsigned i = 0; same && i < m_lemma.size(); ++i) { + same = m_lemma[i] == cls[i]; + } + return same; + } + + + // ----------------------- + // + // Debugging + // + // ----------------------- + + bool check_watches() const { +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + clause_vector const & cs = m_watches[x]; + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + SASSERT(max_var(*(cs[i])) == x); + } + } +#endif + return true; + } + + bool check_bwatches() const { +#ifdef Z3DEBUG + for (bool_var b = 0; b < m_bwatches.size(); b++) { + clause_vector const & cs = m_bwatches[b]; + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + clause const & c = *(cs[i]); + SASSERT(max_var(c) == null_var); + SASSERT(max_bvar(c) == b); + } + } +#endif + return true; + } + + bool check_invariant() const { + SASSERT(check_watches()); + SASSERT(check_bwatches()); + return true; + } + + bool check_satisfied(clause_vector const & cs) const { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + clause const & c = *(cs[i]); + if (!is_satisfied(c)) { + TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); + return false; + } + } + return true; + } + + bool check_satisfied() const { + TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); + unsigned num = m_atoms.size(); + if (m_bk != null_bool_var) + num = m_bk; + for (bool_var b = 0; b < num; b++) { + if (!check_satisfied(m_bwatches[b])) { + UNREACHABLE(); + return false; + } + } + if (m_xk != null_var) { + for (var x = 0; x < m_xk; x++) { + if (!check_satisfied(m_watches[x])) { + UNREACHABLE(); + return false; + } + } + } + return true; + } + + // ----------------------- + // + // Statistics + // + // ----------------------- + + void collect_statistics(statistics & st) { + st.update("nlsat conflicts", m_stats.m_conflicts); + st.update("nlsat propagations", m_stats.m_propagations); + st.update("nlsat decisions", m_stats.m_decisions); + st.update("nlsat restarts", m_stats.m_restarts); + st.update("nlsat stages", m_stats.m_stages); + st.update("nlsat simplifications", m_stats.m_simplifications); + st.update("nlsat irrational assignments", m_stats.m_irrational_assignments); + } + + void reset_statistics() { + m_stats.reset(); + } + + // ----------------------- + // + // Variable reordering + // + // ----------------------- + + struct var_info_collector { + pmanager & pm; + atom_vector const & m_atoms; + var_vector m_shuffle; + unsigned_vector m_max_degree; + unsigned_vector m_num_occs; + + var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): + pm(_pm), + m_atoms(atoms) { + m_max_degree.resize(num_vars, 0); + m_num_occs.resize(num_vars, 0); + } + + var_vector m_vars; + void collect(poly * p) { + m_vars.reset(); + pm.vars(p, m_vars); + unsigned sz = m_vars.size(); + for (unsigned i = 0; i < sz; i++) { + var x = m_vars[i]; + unsigned k = pm.degree(p, x); + m_num_occs[x]++; + if (k > m_max_degree[x]) + m_max_degree[x] = k; + } + } + + void collect(literal l) { + bool_var b = l.var(); + atom * a = m_atoms[b]; + if (a == nullptr) + return; + if (a->is_ineq_atom()) { + unsigned sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; i++) { + collect(to_ineq_atom(a)->p(i)); + } + } + else { + collect(to_root_atom(a)->p()); + } + } + + void collect(clause const & c) { + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) + collect(c[i]); + } + + void collect(clause_vector const & cs) { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) + collect(*(cs[i])); + } + + std::ostream& display(std::ostream & out, display_var_proc const & proc) { + unsigned sz = m_num_occs.size(); + for (unsigned i = 0; i < sz; i++) { + proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; + } + return out; + } + }; + + struct reorder_lt { + var_info_collector const & m_info; + reorder_lt(var_info_collector const & info):m_info(info) {} + bool operator()(var x, var y) const { + // high degree first + if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) + return false; + if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) + return true; + // more constrained first + if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) + return false; + if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) + return true; + return m_info.m_shuffle[x] < m_info.m_shuffle[y]; + } + }; + + // Order variables by degree and number of occurrences + void heuristic_reorder() { + unsigned num = num_vars(); + var_info_collector collector(m_pm, m_atoms, num); + collector.collect(m_clauses); + collector.collect(m_learned); + init_shuffle(collector.m_shuffle); + TRACE("nlsat_reorder", collector.display(tout, m_display_var);); + var_vector new_order; + for (var x = 0; x < num; x++) + new_order.push_back(x); + + std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); + TRACE("nlsat_reorder", + tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); + var_vector perm; + perm.resize(num, 0); + for (var x = 0; x < num; x++) { + perm[new_order[x]] = x; + } + reorder(perm.size(), perm.data()); + SASSERT(check_invariant()); + } + + void init_shuffle(var_vector& p) { + unsigned num = num_vars(); + for (var x = 0; x < num; x++) + p.push_back(x); + + random_gen r(++m_random_seed); + shuffle(p.size(), p.data(), r); + } + + void shuffle_vars() { + var_vector p; + init_shuffle(p); + reorder(p.size(), p.data()); + } + + bool can_reorder() const { + return all_of(m_learned, [&](clause* c) { return !has_root_atom(*c); }) + && all_of(m_clauses, [&](clause* c) { return !has_root_atom(*c); }); + } + + /** + \brief Reorder variables using the giving permutation. + p maps internal variables to their new positions + */ + + + void reorder(unsigned sz, var const * p) { + + remove_learned_roots(); + SASSERT(can_reorder()); + TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); + display_vars(tout); + tout << "\npermutation:\n"; + for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; + ); + // verbose_stream() << "\npermutation: " << p[0] << " count " << count << " " << m_rlimit.is_canceled() << "\n"; + reinit_cache(); + SASSERT(num_vars() == sz); + TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); + reset_watches(); + assignment new_assignment(m_am); + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) + new_assignment.set(p[x], m_assignment.value(x)); + } + var_vector new_inv_perm; + new_inv_perm.resize(sz); + // the undo_until_size(0) statement erases the Boolean assignment. + // undo_until_size(0) + undo_until_stage(null_var); + m_cache.reset(); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_watches[x].empty()); + } +#endif + // update m_perm mapping + for (unsigned ext_x = 0; ext_x < sz; ext_x++) { + // p: internal -> new pos + // m_perm: internal -> external + // m_inv_perm: external -> internal + new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; + m_perm.set(new_inv_perm[ext_x], ext_x); + } + bool_vector is_int; + is_int.swap(m_is_int); + for (var x = 0; x < sz; x++) { + m_is_int.setx(p[x], is_int[x], false); + SASSERT(m_infeasible[x] == 0); + } + m_inv_perm.swap(new_inv_perm); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(x == m_inv_perm[m_perm[x]]); + SASSERT(m_watches[x].empty()); + } +#endif + m_pm.rename(sz, p); + for (auto& b : m_bounds) + b.x = p[b.x]; + TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); + reinit_cache(); + m_assignment.swap(new_assignment); + reattach_arith_clauses(m_clauses); + reattach_arith_clauses(m_learned); + TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); + } + + + /** + \brief Restore variable order. + */ + void restore_order() { + // m_perm: internal -> external + // m_inv_perm: external -> internal + var_vector p; + p.append(m_perm); + reorder(p.size(), p.data()); +#ifdef Z3DEBUG + for (var x = 0; x < num_vars(); x++) { + SASSERT(m_perm[x] == x); + SASSERT(m_inv_perm[x] == x); + } +#endif + } + + /** + \brief After variable reordering some lemmas containing root atoms may be ill-formed. + */ + void remove_learned_roots() { + unsigned j = 0; + for (clause* c : m_learned) { + if (has_root_atom(*c)) { + del_clause(c); + } + else { + m_learned[j++] = c; + } + } + m_learned.shrink(j); + } + + /** + \brief Return true if the clause contains an ill formed root atom + */ + bool has_root_atom(clause const & c) const { + for (literal lit : c) { + bool_var b = lit.var(); + atom * a = m_atoms[b]; + if (a && a->is_root_atom()) + return true; + } + return false; + } + + /** + \brief reinsert all polynomials in the unique cache + */ + void reinit_cache() { + reinit_cache(m_clauses); + reinit_cache(m_learned); + for (atom* a : m_atoms) + reinit_cache(a); + } + void reinit_cache(clause_vector const & cs) { + for (clause* c : cs) + reinit_cache(*c); + } + void reinit_cache(clause const & c) { + for (literal l : c) + reinit_cache(l); + } + void reinit_cache(literal l) { + bool_var b = l.var(); + reinit_cache(m_atoms[b]); + } + void reinit_cache(atom* a) { + if (a == nullptr) { + + } + else if (a->is_ineq_atom()) { + var max = 0; + unsigned sz = to_ineq_atom(a)->size(); + for (unsigned i = 0; i < sz; i++) { + poly * p = to_ineq_atom(a)->p(i); + VERIFY(m_cache.mk_unique(p) == p); + var x = m_pm.max_var(p); + if (x > max) + max = x; + } + a->m_max_var = max; + } + else { + poly * p = to_root_atom(a)->p(); + VERIFY(m_cache.mk_unique(p) == p); + a->m_max_var = m_pm.max_var(p); + } + } + + void reset_watches() { + unsigned num = num_vars(); + for (var x = 0; x < num; x++) { + m_watches[x].reset(); + } + } + + void reattach_arith_clauses(clause_vector const & cs) { + for (clause* cp : cs) { + var x = max_var(*cp); + if (x != null_var) + m_watches[x].push_back(cp); + } + } + + // ----------------------- + // + // Solver initialization + // + // ----------------------- + + struct degree_lt { + unsigned_vector & m_degrees; + degree_lt(unsigned_vector & ds):m_degrees(ds) {} + bool operator()(unsigned i1, unsigned i2) const { + if (m_degrees[i1] < m_degrees[i2]) + return true; + if (m_degrees[i1] > m_degrees[i2]) + return false; + return i1 < i2; + } + }; + + unsigned_vector m_cs_degrees; + unsigned_vector m_cs_p; + void sort_clauses_by_degree(unsigned sz, clause ** cs) { + if (sz <= 1) + return; + TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + m_cs_degrees.reset(); + m_cs_p.reset(); + for (unsigned i = 0; i < sz; i++) { + m_cs_p.push_back(i); + m_cs_degrees.push_back(degree(*(cs[i]))); + } + std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); + TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); + apply_permutation(sz, cs, m_cs_p.data()); + TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + } + + + struct degree_lit_num_lt { + unsigned_vector & m_degrees; + unsigned_vector & m_lit_num; + degree_lit_num_lt(unsigned_vector & ds, unsigned_vector & ln) : + m_degrees(ds), + m_lit_num(ln) { + } + bool operator()(unsigned i1, unsigned i2) const { + if (m_lit_num[i1] == 1 && m_lit_num[i2] > 1) + return true; + if (m_lit_num[i1] > 1 && m_lit_num[i2] == 1) + return false; + if (m_degrees[i1] != m_degrees[i2]) + return m_degrees[i1] < m_degrees[i2]; + if (m_lit_num[i1] != m_lit_num[i2]) + return m_lit_num[i1] < m_lit_num[i2]; + return i1 < i2; + } + }; + + unsigned_vector m_dl_degrees; + unsigned_vector m_dl_lit_num; + unsigned_vector m_dl_p; + void sort_clauses_by_degree_lit_num(unsigned sz, clause ** cs) { + if (sz <= 1) + return; + TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + m_dl_degrees.reset(); + m_dl_lit_num.reset(); + m_dl_p.reset(); + for (unsigned i = 0; i < sz; i++) { + m_dl_degrees.push_back(degree(*(cs[i]))); + m_dl_lit_num.push_back(cs[i]->size()); + m_dl_p.push_back(i); + } + std::sort(m_dl_p.begin(), m_dl_p.end(), degree_lit_num_lt(m_dl_degrees, m_dl_lit_num)); + TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_dl_p.begin(), m_dl_p.end()); tout << "\n";); + apply_permutation(sz, cs, m_dl_p.data()); + TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); + } + + void sort_watched_clauses() { + unsigned num = num_vars(); + for (unsigned i = 0; i < num; i++) { + clause_vector & ws = m_watches[i]; + // sort_clauses_by_degree(ws.size(), ws.data()); + if (m_simple_check) { + sort_clauses_by_degree_lit_num(ws.size(), ws.data()); + } + else { + sort_clauses_by_degree(ws.size(), ws.data()); + } + } + } + + // ----------------------- + // + // Full dimensional + // + // A problem is in the full dimensional fragment if it does + // not contain equalities or non-strict inequalities. + // + // ----------------------- + + bool is_full_dimensional(literal l) const { + atom * a = m_atoms[l.var()]; + if (a == nullptr) + return true; + switch (a->get_kind()) { + case atom::EQ: return l.sign(); + case atom::LT: return !l.sign(); + case atom::GT: return !l.sign(); + case atom::ROOT_EQ: return l.sign(); + case atom::ROOT_LT: return !l.sign(); + case atom::ROOT_GT: return !l.sign(); + case atom::ROOT_LE: return l.sign(); + case atom::ROOT_GE: return l.sign(); + default: + UNREACHABLE(); + return false; + } + } + + bool is_full_dimensional(clause const & c) const { + for (literal l : c) { + if (!is_full_dimensional(l)) + return false; + } + return true; + } + + bool is_full_dimensional(clause_vector const & cs) const { + for (clause* c : cs) { + if (!is_full_dimensional(*c)) + return false; + } + return true; + } + + bool is_full_dimensional() const { + return is_full_dimensional(m_clauses); + } + + + // ----------------------- + // + // Simplification + // + // ----------------------- + + // solve simple equalities + // TBD WU-Reit decomposition? + + // - elim_unconstrained + // - solve_eqs + // - fm + + /** + \brief isolate variables in unit equalities. + Assume a clause is c == v*p + q + and the context implies p > 0 + + replace v by -q/p + remove clause c, + The for other occurrences of v, + replace v*r + v*v*r' > 0 by + by p*p*v*r + p*p*v*v*r' > 0 + by p*q*r + q*q*r' > 0 + + The method ignores lemmas and assumes constraints don't use roots. + */ + + + + // Eliminated variables are tracked in m_bounds. + // Each element in m_bounds tracks the eliminated variable and an upper or lower bound + // that has to be satisfied. Variables that are eliminated through equalities are tracked + // by non-strict bounds. A satisfiable solution is required to provide an evaluation that + // is consistent with the bounds. For equalities, the non-strict lower or upper bound can + // always be assigned as a value to the variable. + + void fix_patch() { + m_lo.reset(); m_hi.reset(); + for (auto& b : m_bounds) + m_assignment.reset(b.x); + for (unsigned i = m_bounds.size(); i-- > 0; ) + fix_patch(m_bounds[i]); + } + + // x is unassigned, lo < x -> x <- lo + 1 + // x is unassigned, x < hi -> x <- hi - 1 + // x is unassigned, lo <= x -> x <- lo + // x is unassigned, x <= hi -> x <- hi + // x is assigned above hi, lo is strict lo < x < hi -> set x <- (lo + hi)/2 + // x is assigned below hi, above lo -> no-op + // x is assigned below lo, hi is strict lo < x < hi -> set x <-> (lo + hi)/2 + // x is assigned above hi, x <= hi -> x <- hi + // x is assigned blow lo, lo <= x -> x <- lo + void fix_patch(bound_constraint& b) { + var x = b.x; + scoped_anum Av(m_am), Bv(m_am), val(m_am); + m_pm.eval(b.A, m_assignment, Av); + m_pm.eval(b.B, m_assignment, Bv); + m_am.neg(Bv); + val = Bv / Av; + // Ax >= B + // is-lower : A > 0 + // is-upper: A < 0 + // x <- B / A + bool is_lower = m_am.is_pos(Av); + TRACE("nlsat", + m_display_var(tout << "patch v" << x << " ", x) << "\n"; + if (m_assignment.is_assigned(x)) m_am.display(tout << "previous value: ", m_assignment.value(x)); tout << "\n"; + m_am.display(tout << "updated value: ", val); tout << "\n"; + ); + + if (!m_assignment.is_assigned(x)) { + if (!b.is_strict) + m_assignment.set_core(x, val); + else if (is_lower) + m_assignment.set_core(x, val + 1); + else + m_assignment.set_core(x, val - 1); + } + else { + auto& aval = m_assignment.value(x); + if (is_lower) { + // lo < value(x), lo < x -> x is unchanged + if (b.is_strict && m_am.lt(val, aval)) + ; + else if (!b.is_strict && m_am.le(val, aval)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + // aval < lo < x, hi is unassigned: x <- lo + 1 + else if (!m_hi.is_assigned(x)) + m_assignment.set_core(x, val + 1); + // aval < lo < x, hi is assigned: x <- (lo + hi) / 2 + else { + scoped_anum mid(m_am); + m_am.add(m_hi.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + else { + // dual to lower bounds + if (b.is_strict && m_am.lt(aval, val)) + ; + else if (!b.is_strict && m_am.le(aval, val)) + ; + else if (!b.is_strict) + m_assignment.set_core(x, val); + else if (!m_lo.is_assigned(x)) + m_assignment.set_core(x, val - 1); + else { + scoped_anum mid(m_am); + m_am.add(m_lo.value(x), val, mid); + mid = mid / 2; + m_assignment.set_core(x, mid); + } + } + } + + if (is_lower) { + if (!m_lo.is_assigned(x) || m_am.lt(m_lo.value(x), val)) + m_lo.set_core(x, val); + } + else { + if (!m_hi.is_assigned(x) || m_am.gt(m_hi.value(x), val)) + m_hi.set_core(x, val); + } + } + + bool is_unit_ineq(clause const& c) const { + return + c.size() == 1 && + m_atoms[c[0].var()] && + m_atoms[c[0].var()]->is_ineq_atom(); + } + + bool is_unit_eq(clause const& c) const { + return + is_unit_ineq(c) && + !c[0].sign() && + m_atoms[c[0].var()]->is_eq(); + } + + /** + \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. + */ + lbool is_cmp0(clause const& c, var& v) { + if (!is_unit_ineq(c)) + return l_undef; + literal lit = c[0]; + ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); + bool sign = lit.sign(); + poly * p0; + if (!is_single_poly(a, p0)) + return l_undef; + if (m_pm.is_var(p0, v)) { + if (!sign && a.get_kind() == atom::GT) { + return l_true; + } + if (!sign && a.get_kind() == atom::LT) { + return l_false; + } + return l_undef; + } + polynomial::scoped_numeral n(m_pm.m()); + if (m_pm.is_var_num(p0, v, n)) { + // x - k > 0 + if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { + return l_true; + } + // x + k < 0 + if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { + return l_false; + } + // !(x + k > 0) + if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { + return l_false; + } + // !(x - k < 0) + if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { + return l_true; + } + } + return l_undef; + } + + bool is_single_poly(ineq_atom const& a, poly*& p) { + unsigned sz = a.size(); + return sz == 1 && a.is_odd(0) && (p = a.p(0), true); + } + + bool is_unit(polynomial_ref const& p) { + if (!m_pm.is_const(p)) + return false; + auto const& c = m_pm.coeff(p, 0); + return m_pm.m().is_one(c) || m_pm.m().is_minus_one(c); + } + + // ----------------------- + // + // Pretty printing + // + // ----------------------- + + std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) { + proc(out, x); + out << " -> "; + m_am.display_decimal(out, m_assignment.value(x)); + out << "\n"; + } + } + return out; + } + + std::ostream& display_bool_assignment(std::ostream & out) const { + unsigned sz = m_atoms.size(); + for (bool_var b = 0; b < sz; b++) { + if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { + out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; + } + else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { + display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << " @" << m_levels[b] << "\n"; + } + } + TRACE("nlsat_bool_assignment", + for (bool_var b = 0; b < sz; b++) { + out << "b" << b << " -> " << m_bvalues[b] << " "; + if (m_atoms[b]) display(out, *m_atoms[b]); + out << "\n"; + }); + return out; + } + + bool display_mathematica_assignment(std::ostream & out) const { + bool first = true; + for (var x = 0; x < num_vars(); x++) { + if (m_assignment.is_assigned(x)) { + if (first) + first = false; + else + out << " && "; + out << "x" << x << " == "; + m_am.display_mathematica(out, m_assignment.value(x)); + } + } + return !first; + } + + std::ostream& display_num_assignment(std::ostream & out) const { + return display_num_assignment(out, m_display_var); + } + + std::ostream& display_assignment(std::ostream& out) const { + display_bool_assignment(out); + display_num_assignment(out); + return out; + } + + std::ostream& display(std::ostream& out, justification j) const { + switch (j.get_kind()) { + case justification::CLAUSE: + display(out, *j.get_clause()) << "\n"; + break; + case justification::LAZY: { + lazy_justification const& lz = *j.get_lazy(); + display_not(out, lz.num_lits(), lz.lits()) << "\n"; + for (unsigned i = 0; i < lz.num_clauses(); ++i) { + display(out, lz.clause(i)) << "\n"; + } + break; + } + default: + out << j.get_kind() << "\n"; + break; + } + return out; + } + + bool m_display_eval = false; + std::ostream& display_eval(std::ostream& out, justification j) { + flet _display(m_display_eval, true); + return display(out, j); + } + + std::ostream& display_ineq(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { + unsigned sz = a.size(); + for (unsigned i = 0; i < sz; i++) { + if (use_star && i > 0) + out << "*"; + bool is_even = a.is_even(i); + if (is_even || sz > 1) + out << "("; + display_polynomial(out, a.p(i), proc, use_star); + if (is_even || sz > 1) + out << ")"; + if (is_even) + out << "^2"; + } + switch (a.get_kind()) { + case atom::LT: out << " < 0"; break; + case atom::GT: out << " > 0"; break; + case atom::EQ: out << " = 0"; break; + default: UNREACHABLE(); break; + } + return out; + } + + std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { + unsigned sz = a.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) + out << "*"; + bool is_even = a.is_even(i); + if (sz > 1) + out << "("; + if (is_even) + out << "("; + m_pm.display(out, a.p(i), display_var_proc(), true); + if (is_even) + out << "^2)"; + if (sz > 1) + out << ")"; + } + switch (a.get_kind()) { + case atom::LT: out << " < 0"; break; + case atom::GT: out << " > 0"; break; + case atom::EQ: out << " == 0"; break; + default: UNREACHABLE(); break; + } + return out; + } + + std::ostream& display_polynomial_smt2(std::ostream & out, poly const* p, display_var_proc const & proc) const { + return m_pm.display_smt2(out, p, proc); + } + + std::ostream& display_ineq_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { + switch (a.get_kind()) { + case atom::LT: out << "(< "; break; + case atom::GT: out << "(> "; break; + case atom::EQ: out << "(= "; break; + default: UNREACHABLE(); break; + } + unsigned sz = a.size(); + if (sz > 1) + out << "(* "; + for (unsigned i = 0; i < sz; i++) { + if (i > 0) out << " "; + if (a.is_even(i)) { + out << "(* "; + display_polynomial_smt2(out, a.p(i), proc); + out << " "; + display_polynomial_smt2(out, a.p(i), proc); + out << ")"; + } + else { + display_polynomial_smt2(out, a.p(i), proc); + } + } + if (sz > 1) + out << ")"; + out << " 0)"; + return out; + } + + std::ostream& display_poly_root(std::ostream& out, char const* y, root_atom const& a, display_var_proc const& proc) const { + out << "(exists (("; proc(out,a.x()); out << " Real))\n"; + out << "(and (= " << y << " "; + proc(out, a.x()); + out << ") (= 0 "; + display_polynomial_smt2(out, a.p(), proc); + out << ")))\n"; + return out; + } + + std::ostream& display_binary_smt2(std::ostream& out, poly const* p1, char const* rel, poly const* p2, display_var_proc const& proc) const { + out << "(" << rel << " "; + display_polynomial_smt2(out, p1, proc); + out << " "; + display_polynomial_smt2(out, p2, proc); + out << ")"; + return out; + } + + + std::ostream& display_linear_root_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + polynomial_ref A(m_pm), B(m_pm), Z(m_pm), Ax(m_pm); + polynomial::scoped_numeral zero(m_qm); + m_pm.m().set(zero, 0); + A = m_pm.derivative(a.p(), a.x()); + B = m_pm.neg(m_pm.substitute(a.p(), a.x(), zero)); + Z = m_pm.mk_zero(); + + Ax = m_pm.mul(m_pm.mk_polynomial(a.x()), A); + + // x < root[1](ax + b) == (a > 0 => ax + b < 0) & (a < 0 => ax + b > 0) + // x < root[1](ax + b) == (a > 0 => ax < -b) & (a < 0 => ax > -b) + + char const* rel1 = "<", *rel2 = ">"; + switch (a.get_kind()) { + case atom::ROOT_LT: rel1 = "<"; rel2 = ">"; break; + case atom::ROOT_GT: rel1 = ">"; rel2 = "<"; break; + case atom::ROOT_LE: rel1 = "<="; rel2 = ">="; break; + case atom::ROOT_GE: rel1 = ">="; rel2 = "<="; break; + case atom::ROOT_EQ: rel1 = rel2 = "="; break; + default: UNREACHABLE(); break; + } + + out << "(and "; + out << "(=> "; display_binary_smt2(out, A, ">", Z, proc); display_binary_smt2(out, Ax, rel1, B, proc); out << ") "; + out << "(=> "; display_binary_smt2(out, A, "<", Z, proc); display_binary_smt2(out, Ax, rel2, B, proc); out << ") "; + out << ")"; + + return out; + } + + + std::ostream& display_root_smt2(std::ostream& out, root_atom const& a, display_var_proc const& proc) const { + if (a.i() == 1 && m_pm.degree(a.p(), a.x()) == 1) + return display_linear_root_smt2(out, a, proc); +#if 1 + out << "(exists ("; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + out << "(" << y << " Real) "; + } + out << ")\n"; + out << "(and\n"; + for (unsigned j = 0; j < a.i(); ++j) { + std::string y = std::string("y") + std::to_string(j); + display_poly_root(out, y.c_str(), a, proc); + } + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + std::string y2 = std::string("y") + std::to_string(j+1); + out << "(< " << y1 << " " << y2 << ")\n"; + } + + std::string yn = "y" + std::to_string(a.i() - 1); + + // TODO we need (forall z : z < yn . p(z) => z = y1 or ... z = y_{n-1}) + // to say y1, .., yn are the first n distinct roots. + // + out << "(forall ((z Real)) (=> (and (< z " << yn << ") "; display_poly_root(out, "z", a, proc) << ") "; + if (a.i() == 1) { + out << "false))\n"; + } + else { + out << "(or "; + for (unsigned j = 0; j + 1 < a.i(); ++j) { + std::string y1 = std::string("y") + std::to_string(j); + out << "(= z " << y1 << ") "; + } + out << ")))\n"; + } + switch (a.get_kind()) { + case atom::ROOT_LT: out << "(< "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GT: out << "(> "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_LE: out << "(<= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_GE: out << "(>= "; proc(out, a.x()); out << " " << yn << ")"; break; + case atom::ROOT_EQ: out << "(= "; proc(out, a.x()); out << " " << yn << ")"; NOT_IMPLEMENTED_YET(); break; + default: UNREACHABLE(); break; + } + out << "))"; + return out; +#endif + + + return display_root(out, a, proc); + } + + std::ostream& display_root(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + proc(out, a.x()); + switch (a.get_kind()) { + case atom::ROOT_LT: out << " < "; break; + case atom::ROOT_GT: out << " > "; break; + case atom::ROOT_LE: out << " <= "; break; + case atom::ROOT_GE: out << " >= "; break; + case atom::ROOT_EQ: out << " = "; break; + default: UNREACHABLE(); break; + } + out << "root[" << a.i() << "]("; + display_polynomial(out, a.p(), proc); + out << ")"; + return out; + } + + struct mathematica_var_proc : public display_var_proc { + var m_x; + public: + mathematica_var_proc(var x):m_x(x) {} + std::ostream& operator()(std::ostream & out, var x) const override { + if (m_x == x) + return out << "#1"; + else + return out << "x" << x; + } + }; + + std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { + out << "x" << a.x(); + switch (a.get_kind()) { + case atom::ROOT_LT: out << " < "; break; + case atom::ROOT_GT: out << " > "; break; + case atom::ROOT_LE: out << " <= "; break; + case atom::ROOT_GE: out << " >= "; break; + case atom::ROOT_EQ: out << " == "; break; + default: UNREACHABLE(); break; + } + out << "Root["; + display_polynomial(out, a.p(), mathematica_var_proc(a.x()), true); + out << " &, " << a.i() << "]"; + return out; + } + + std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { + if (a.is_ineq_atom()) + return display_ineq(out, static_cast(a), proc); + else + return display_root(out, static_cast(a), proc); + } + + std::ostream& display(std::ostream & out, atom const & a) const { + return display(out, a, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, atom const & a) const { + if (a.is_ineq_atom()) + return display_mathematica(out, static_cast(a)); + else + return display_mathematica(out, static_cast(a)); + } + + std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { + if (a.is_ineq_atom()) + return display_ineq_smt2(out, static_cast(a), proc); + else + return display_root_smt2(out, static_cast(a), proc); + } + + std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + if (b == 0) + out << "true"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display(out, *(m_atoms[b]), proc); + return out; + } + + std::ostream& display_atom(std::ostream & out, bool_var b) const { + return display_atom(out, b, m_display_var); + } + + std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { + if (b == 0) + out << "(0 < 1)"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display_mathematica(out, *(m_atoms[b])); + return out; + } + + std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + if (b == 0) + out << "true"; + else if (m_atoms[b] == 0) + out << "b" << b; + else + display_smt2(out, *(m_atoms[b]), proc); + return out; + } + + std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (m_atoms[b] != 0) + out << "("; + display_atom(out, b, proc); + if (m_atoms[b] != 0) + out << ")"; + } + else { + display_atom(out, l.var(), proc); + } + return out; + } + + std::ostream& display(std::ostream & out, literal l) const { + return display(out, l, m_display_var); + } + + std::ostream& display_smt2(std::ostream & out, literal l) const { + return display_smt2(out, l, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, literal l) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (m_atoms[b] != 0) + out << "("; + display_mathematica_atom(out, b); + if (m_atoms[b] != 0) + out << ")"; + } + else { + display_mathematica_atom(out, l.var()); + } + return out; + } + + std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { + if (l.sign()) { + bool_var b = l.var(); + out << "(not "; + display_smt2_atom(out, b, proc); + out << ")"; + } + else { + display_smt2_atom(out, l.var(), proc); + } + return out; + } + + std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { + if (!m_display_assumption) + return out; + vector deps; + m_asm.linearize(s, deps); + bool first = true; + for (auto dep : deps) { + if (first) first = false; else out << " "; + (*m_display_assumption)(out, dep); + } + return out; + } + + std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display(out, ls[i], proc); + } + return out; + } + + std::ostream& display(std::ostream & out, unsigned num, literal const * ls) const { + return display(out, num, ls, m_display_var); + } + + std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display(out, ~ls[i], proc); + } + return out; + } + + std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls) const { + return display_not(out, num, ls, m_display_var); + } + + std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { + return display(out, cs.size(), cs.data(), m_display_var); + } + + std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { + if (c.assumptions() != nullptr) { + display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); + out << " |- "; + } + return display(out, c.size(), c.data(), proc); + } + + std::ostream& display(std::ostream & out, clause const & c) const { + return display(out, c, m_display_var); + } + + + std::ostream& display_polynomial(std::ostream& out, poly* p, display_var_proc const & proc, bool use_star = false) const { + if (m_display_eval) { + polynomial_ref q(m_pm); + q = p; + for (var x = 0; x < num_vars(); x++) + if (m_assignment.is_assigned(x)) { + auto& a = m_assignment.value(x); + if (!m_am.is_rational(a)) + continue; + mpq r; + m_am.to_rational(a, r); + q = m_pm.substitute(q, 1, &x, &r); + } + m_pm.display(out, q, proc, use_star); + } + else + m_pm.display(out, p, proc, use_star); + return out; + } + + // -- + + std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const { + return display_smt2(out, n, ls, display_var_proc()); + } + + + std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + if (num == 0) { + out << "false"; + } + else if (num == 1) { + display_smt2(out, ls[0], proc); + } + else { + out << "(or"; + for (unsigned i = 0; i < num; i++) { + out << " "; + display_smt2(out, ls[i], proc); + } + out << ")"; + } + return out; + } + + std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { + return display_smt2(out, c.size(), c.data(), proc); + } + + std::ostream& display_abst(std::ostream & out, literal l) const { + if (l.sign()) { + bool_var b = l.var(); + out << "!"; + if (b == true_bool_var) + out << "true"; + else + out << "b" << b; + } + else { + out << "b" << l.var(); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " or "; + display_abst(out, ls[i]); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { + return display_abst(out, cs.size(), cs.data()); + } + + std::ostream& display_abst(std::ostream & out, clause const & c) const { + return display_abst(out, c.size(), c.data()); + } + + std::ostream& display_mathematica(std::ostream & out, clause const & c) const { + out << "("; + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) + out << " || "; + display_mathematica(out, c[i]); + } + out << ")"; + return out; + } + + // Debugging support: + // Display generated lemma in Mathematica format. + // Mathematica must reduce lemma to True (modulo resource constraints). + std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { + out << "Resolve[ForAll[{"; + // var definition + for (unsigned i = 0; i < num_vars(); i++) { + if (i > 0) + out << ", "; + out << "x" << i; + } + out << "}, "; + if (include_assignment) { + out << "!("; + if (!display_mathematica_assignment(out)) + out << "0 < 1"; // nothing was printed + out << ") || "; + } + for (unsigned i = 0; i < num; i++) { + if (i > 0) + out << " || "; + display_mathematica(out, ls[i]); + } + out << "], Reals]\n"; // end of exists + return out; + } + + std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { + for (clause* c : cs) { + display(out, *c, proc) << "\n"; + } + return out; + } + + std::ostream& display(std::ostream & out, clause_vector const & cs) const { + return display(out, cs, m_display_var); + } + + std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { + unsigned sz = cs.size(); + for (unsigned i = 0; i < sz; i++) { + if (i > 0) out << ",\n"; + display_mathematica(out << " ", *(cs[i])); + } + return out; + } + + std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { + for (clause* c : cs) { + display_abst(out, *c) << "\n"; + } + return out; + } + + std::ostream& display(std::ostream & out, display_var_proc const & proc) const { + display(out, m_clauses, proc); + if (!m_learned.empty()) { + display(out << "Lemmas:\n", m_learned, proc); + } + return out; + } + + std::ostream& display_mathematica(std::ostream & out) const { + return display_mathematica(out << "{\n", m_clauses) << "}\n"; + } + + std::ostream& display_abst(std::ostream & out) const { + display_abst(out, m_clauses); + if (!m_learned.empty()) { + display_abst(out << "Lemmas:\n", m_learned); + } + return out; + } + + std::ostream& display(std::ostream & out) const { + display(out, m_display_var); + display_assignment(out << "assignment:\n"); + return out << "---\n"; + } + + std::ostream& display_vars(std::ostream & out) const { + for (unsigned i = 0; i < num_vars(); i++) { + out << i << " -> "; m_display_var(out, i); out << "\n"; + } + return out; + } + + std::ostream& display_smt2_arith_decls(std::ostream & out) const { + unsigned sz = m_is_int.size(); + for (unsigned i = 0; i < sz; i++) { + if (is_int(i)) { + out << "(declare-fun "; m_display_var(out, i) << " () Int)\n"; + } + else { + out << "(declare-fun "; m_display_var(out, i) << " () Real)\n"; + } + } + return out; + } + + std::ostream& display_smt2_bool_decls(std::ostream & out) const { + unsigned sz = m_atoms.size(); + for (unsigned i = 0; i < sz; i++) { + if (m_atoms[i] == nullptr) + out << "(declare-fun b" << i << " () Bool)\n"; + } + return out; + } + + std::ostream& display_smt2(std::ostream & out) const { + display_smt2_bool_decls(out); + display_smt2_arith_decls(out); + out << "(assert (and true\n"; + for (clause* c : m_clauses) { + display_smt2(out, *c, m_display_var) << "\n"; + } + out << "))\n" << std::endl; + return out; + } + }; + + solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { + m_ctx = alloc(ctx, rlim, p, incremental); + m_imp = alloc(imp, *this, *m_ctx); + } + + solver::solver(ctx& ctx) { + m_ctx = nullptr; + m_imp = alloc(imp, *this, ctx); + } + + solver::~solver() { + dealloc(m_imp); + dealloc(m_ctx); + } + + lbool solver::check() { + return m_imp->check(); + } + + lbool solver::check(literal_vector& assumptions) { + return m_imp->check(assumptions); + } + + void solver::get_core(vector& assumptions) { + return m_imp->get_core(assumptions); + } + + void solver::reset() { + m_imp->reset(); + } + + + void solver::updt_params(params_ref const & p) { + m_imp->updt_params(p); + } + + + void solver::collect_param_descrs(param_descrs & d) { + algebraic_numbers::manager::collect_param_descrs(d); + nlsat_params::collect_param_descrs(d); + } + + unsynch_mpq_manager & solver::qm() { + return m_imp->m_qm; + } + + anum_manager & solver::am() { + return m_imp->m_am; + } + + pmanager & solver::pm() { + return m_imp->m_pm; + } + + void solver::set_display_var(display_var_proc const & proc) { + m_imp->m_display_var.m_proc = &proc; + } + + void solver::set_display_assumption(display_assumption_proc const& proc) { + m_imp->m_display_assumption = &proc; + } + + + unsigned solver::num_vars() const { + return m_imp->num_vars(); + } + + bool solver::is_int(var x) const { + return m_imp->is_int(x); + } + + bool_var solver::mk_bool_var() { + return m_imp->mk_bool_var(); + } + + literal solver::mk_true() { + return literal(0, false); + } + + atom * solver::bool_var2atom(bool_var b) { + return m_imp->m_atoms[b]; + } + + void solver::vars(literal l, var_vector& vs) { + m_imp->vars(l, vs); + } + + atom_vector const& solver::get_atoms() { + return m_imp->m_atoms; + } + + atom_vector const& solver::get_var2eq() { + return m_imp->m_var2eq; + } + + evaluator& solver::get_evaluator() { + return m_imp->m_evaluator; + } + + explain& solver::get_explain() { + return m_imp->m_explain; + } + + void solver::reorder(unsigned sz, var const* p) { + m_imp->reorder(sz, p); + } + + void solver::restore_order() { + m_imp->restore_order(); + } + + void solver::set_rvalues(assignment const& as) { + m_imp->m_assignment.copy(as); + } + + void solver::get_rvalues(assignment& as) { + as.copy(m_imp->m_assignment); + } + + void solver::get_bvalues(svector const& bvars, svector& vs) { + vs.reset(); + for (bool_var b : bvars) { + vs.reserve(b + 1, l_undef); + if (!m_imp->m_atoms[b]) { + vs[b] = m_imp->m_bvalues[b]; + } + } + TRACE("nlsat", display(tout);); + } + + void solver::set_bvalues(svector const& vs) { + TRACE("nlsat", display(tout);); + for (bool_var b = 0; b < vs.size(); ++b) { + if (vs[b] != l_undef) { + m_imp->m_bvalues[b] = vs[b]; + SASSERT(!m_imp->m_atoms[b]); + } + } +#if 0 + m_imp->m_bvalues.reset(); + m_imp->m_bvalues.append(vs); + m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); + for (unsigned i = 0; i < m_imp->m_atoms.size(); ++i) { + atom* a = m_imp->m_atoms[i]; + SASSERT(!a); + if (a) { + m_imp->m_bvalues[i] = to_lbool(m_imp->m_evaluator.eval(a, false)); + } + } +#endif + TRACE("nlsat", display(tout);); + } + + void solver::del_clause(clause* c) { + m_imp->del_clause(c); + } + + var solver::mk_var(bool is_int) { + return m_imp->mk_var(is_int); + } + + bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + return m_imp->mk_ineq_atom(k, sz, ps, is_even); + } + + literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool simplify) { + return m_imp->mk_ineq_literal(k, sz, ps, is_even, simplify); + } + + bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { + return m_imp->mk_root_atom(k, x, i, p); + } + + void solver::inc_ref(bool_var b) { + m_imp->inc_ref(b); + } + + void solver::dec_ref(bool_var b) { + m_imp->dec_ref(b); + } + + void solver::inc_ref(assumption a) { + m_imp->inc_ref(static_cast(a)); + } + + void solver::dec_ref(assumption a) { + m_imp->dec_ref(static_cast(a)); + } + + void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { + return m_imp->mk_external_clause(num_lits, lits, a); + } + + std::ostream& solver::display(std::ostream & out) const { + return m_imp->display(out); + } + + std::ostream& solver::display(std::ostream & out, literal l) const { + return m_imp->display(out, l); + } + + std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { + for (unsigned i = 0; i < n; ++i) { + display(out, ls[i]); + out << "; "; + } + return out; + } + + std::ostream& solver::display(std::ostream & out, literal_vector const& ls) const { + return display(out, ls.size(), ls.data()); + } + + std::ostream& solver::display_smt2(std::ostream & out, literal l) const { + return m_imp->display_smt2(out, l); + } + + std::ostream& solver::display_smt2(std::ostream & out, unsigned n, literal const* ls) const { + for (unsigned i = 0; i < n; ++i) { + display_smt2(out, ls[i]); + out << " "; + } + return out; + } + + std::ostream& solver::display(std::ostream& out, clause const& c) const { + return m_imp->display(out, c); + } + + std::ostream& solver::display_smt2(std::ostream & out) const { + return m_imp->display_smt2(out); + } + + std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { + return display_smt2(out, ls.size(), ls.data()); + } + + std::ostream& solver::display(std::ostream & out, var x) const { + return m_imp->m_display_var(out, x); + } + + std::ostream& solver::display(std::ostream & out, atom const& a) const { + return m_imp->display(out, a, m_imp->m_display_var); + } + + display_var_proc const & solver::display_proc() const { + return m_imp->m_display_var; + } + + anum const & solver::value(var x) const { + if (m_imp->m_assignment.is_assigned(x)) + return m_imp->m_assignment.value(x); + return m_imp->m_zero; + } + + lbool solver::bvalue(bool_var b) const { + return m_imp->m_bvalues[b]; + } + + lbool solver::value(literal l) const { + return m_imp->value(l); + } + + bool solver::is_interpreted(bool_var b) const { + return m_imp->m_atoms[b] != 0; + } + + void solver::reset_statistics() { + return m_imp->reset_statistics(); + } + + void solver::collect_statistics(statistics & st) { + return m_imp->collect_statistics(st); + } + + clause* solver::mk_clause(unsigned n, literal const* lits, bool learned, internal_assumption a) { + return m_imp->mk_clause(n, lits, learned, static_cast(a)); + } + + void solver::inc_simplify() { + m_imp->m_stats.m_simplifications++; + } + + bool solver::has_root_atom(clause const& c) const { + return m_imp->has_root_atom(c); + } + + void solver::add_bound(bound_constraint const& c) { + m_imp->m_bounds.push_back(c); + } + + assumption solver::join(assumption a, assumption b) { + return (m_imp->m_asm.mk_join(static_cast(a), static_cast(b))); + } + +}; diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index c3d91ae6455..da1bb183760 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -116,7 +116,7 @@ struct mbp_array_tg::impl { bool is_rd_wr(expr* t, expr*& wr_ind, expr*& rd_ind, expr*& b, expr*& v) { if (!is_rd_wr(t)) return false; - expr* a; + expr* a = nullptr; VERIFY(m_array_util.is_select1(t, a, rd_ind)); VERIFY(m_array_util.is_store1(a, b, wr_ind, v)); return true; @@ -185,7 +185,7 @@ struct mbp_array_tg::impl { // or &&_{i \in indices} j \neq i && // !(select(y, j) = elem) void elimwreq(peq p, bool is_neg) { - expr* a, *j, *elem; + expr* a = nullptr, *j = nullptr, *elem = nullptr; VERIFY(is_arr_write(p.lhs(), a, j, elem)); TRACE("mbp_tg", tout << "applying elimwreq on " << expr_ref(p.mk_peq(), m) << " is neg: " << is_neg;); @@ -279,7 +279,7 @@ struct mbp_array_tg::impl { // rewrite select(store(a, i, k), j) into either select(a, j) or k void elimrdwr(expr *term) { TRACE("mbp_tg", tout << "applying elimrdwr on " << expr_ref(term, m);); - expr* wr_ind, *rd_ind, *b, *v; + expr* wr_ind = nullptr, *rd_ind = nullptr, *b = nullptr, *v = nullptr; VERIFY(is_rd_wr(term, wr_ind, rd_ind, b, v)); if (m_mdl.are_equal(wr_ind, rd_ind)) m_tg.add_eq(wr_ind, rd_ind); diff --git a/src/sat/sat_aig_cuts.cpp b/src/sat/sat_aig_cuts.cpp index 20f62cbdd02..df736e4ba0d 100644 --- a/src/sat/sat_aig_cuts.cpp +++ b/src/sat/sat_aig_cuts.cpp @@ -534,7 +534,7 @@ namespace sat { } cut_val aig_cuts::eval(node const& n, cut_eval const& env) const { - uint64_t result; + uint64_t result = 0; switch (n.op()) { case var_op: UNREACHABLE(); From b84b4e7f9ace073a6d2a3c65e699b35a8e20f400 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Aug 2024 11:38:27 -0700 Subject: [PATCH 158/414] fix attribute order Signed-off-by: Nikolaj Bjorner --- src/nlsat/nlsat_explain.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 12db1d3da70..a188206b430 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -146,11 +146,10 @@ namespace nlsat { m_factors(m_pm), m_factors_save(m_pm), m_roots_tmp(m_am), + m_cell_sample(is_sample), m_todo(u), m_core1(s), m_core2(s), - m_cell_sample(is_sample), - m_evaluator(ev) { m_simplify_cores = false; m_full_dimensional = false; From 84da614de3cd2cb67118ebe7ab0764d74af4521c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Aug 2024 11:40:01 -0700 Subject: [PATCH 159/414] make gcc linting happy Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_legacy_frames.cpp | 4 ++-- src/muz/spacer/spacer_proof_utils.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/muz/spacer/spacer_legacy_frames.cpp b/src/muz/spacer/spacer_legacy_frames.cpp index a21df5038c4..15b37b1275a 100644 --- a/src/muz/spacer/spacer_legacy_frames.cpp +++ b/src/muz/spacer/spacer_legacy_frames.cpp @@ -90,10 +90,10 @@ bool pred_transformer::legacy_frames::propagate_to_next_level(unsigned src_level for (unsigned i = 0; i < m_levels[src_level].size();) { expr_ref_vector &src = m_levels[src_level]; expr * curr = src[i].get(); - unsigned stored_lvl; + unsigned stored_lvl = 0; VERIFY(m_prop2level.find(curr, stored_lvl)); SASSERT(stored_lvl >= src_level); - unsigned solver_level; + unsigned solver_level = 0; if (stored_lvl > src_level) { TRACE("spacer", tout << "at level: " << stored_lvl << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 9fbd4e01c2e..114dfc885d0 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -432,8 +432,8 @@ namespace spacer { ptr_buffer args; for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - proof *pp, *tmp; - pp = m.get_parent(p, i); + proof *tmp = nullptr; + proof* pp = m.get_parent(p, i); VERIFY(m_cache.find(pp, tmp)); args.push_back(tmp); dirty |= (pp != tmp); @@ -455,8 +455,8 @@ namespace spacer { } } - proof* res; - VERIFY(m_cache.find(pr,res)); + proof* res = nullptr; + VERIFY(m_cache.find(pr, res)); DEBUG_CODE( proof_checker pc(m); expr_ref_vector side(m); From 349ebd0a5b2486779c1375873a0794a8c2c0825f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Aug 2024 14:22:28 -0700 Subject: [PATCH 160/414] #7344 --- src/smt/smt_context.cpp | 7 ++++++- src/smt/smt_context.h | 1 + src/smt/smt_model_generator.cpp | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 428460801ce..4ff8c12e72a 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1858,8 +1858,10 @@ namespace smt { lbool phase = l_undef; m_case_split_queue->next_case_split(var, phase); used_queue = true; - if (var == null_bool_var) + if (var == null_bool_var) { + push_trail(value_trail(m_has_case_split, false)); return false; + } TRACE_CODE({ static unsigned counter = 0; @@ -4642,6 +4644,9 @@ namespace smt { } bool context::has_case_splits() { + if (!m_has_case_split) + return false; + for (unsigned i = get_num_b_internalized(); i-- > 0; ) { if (is_relevant(i) && get_assignment(i) == l_undef) return true; diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 4feeae1b50c..da7cde7e76c 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1155,6 +1155,7 @@ namespace smt { bool guess(bool_var var, lbool phase); protected: + bool m_has_case_split = true; bool decide(); void update_phase_cache_counter(); diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 6537e638d55..8f408d5cf24 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -99,7 +99,6 @@ namespace smt { if (m.is_bool(s)) { CTRACE("model", m_context->get_assignment(r) == l_undef, tout << mk_pp(r->get_expr(), m) << "\n";); - SASSERT(m_context->get_assignment(r) != l_undef); if (m_context->get_assignment(r) == l_true) proc = alloc(expr_wrapper_proc, m.mk_true()); else From e1f1d677ff53f9dd559a75950c4590fb738c53c7 Mon Sep 17 00:00:00 2001 From: Audrey Dutcher Date: Tue, 27 Aug 2024 17:12:31 -0700 Subject: [PATCH 161/414] New python packaging and tests (#7356) * Simplify/modernize python packaging * Modify azure CI to utilize new python packaging --- azure-pipelines.yml | 29 +++++++---- src/api/python/pyproject.toml | 2 +- src/api/python/setup.py | 96 ++--------------------------------- 3 files changed, 25 insertions(+), 102 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 48dcf094a2e..ac3b9105c76 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -43,8 +43,20 @@ jobs: - ${{if eq(variables['runRegressions'], 'True')}}: - template: scripts/test-regressions.yml -- job: LinuxBuildsArm64 - displayName: "ManyLinux ARM64 build" +- job: "ManylinuxPythonBuildAmd64" + displayName: "Python bindings (manylinux Centos AMD64) build" + pool: + vmImage: "ubuntu-latest" + container: "quay.io/pypa/manylinux2014_x86_64:latest" + steps: + - script: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env" + - script: 'echo "##vso[task.prependpath]$PWD/env/bin"' + - script: "pip install build git+https://github.com/rhelmot/auditwheel" # @TODO remove when patches make it upstream + - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." + - script: "pip install ./src/api/python/wheelhouse/*.whl && python - =46.4.0", "wheel", "cmake"] +requires = ["setuptools>=59", "wheel", "cmake"] build-backend = "setuptools.build_meta" diff --git a/src/api/python/setup.py b/src/api/python/setup.py index c7af0e646e1..f17527a0140 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -7,14 +7,12 @@ import re import glob from setuptools import setup -from distutils.util import get_platform -from distutils.errors import LibError -from distutils.command.build import build as _build -from distutils.command.sdist import sdist as _sdist -from distutils.command.clean import clean as _clean +from setuptools.command.build import build as _build +from setuptools.command.sdist import sdist as _sdist from setuptools.command.develop import develop as _develop -from setuptools.command.bdist_egg import bdist_egg as _bdist_egg +class LibError(Exception): + pass build_env = dict(os.environ) build_env['PYTHON'] = sys.executable @@ -238,27 +236,12 @@ def run(self): self.execute(_copy_bins, (), msg="Copying binaries") _develop.run(self) -class bdist_egg(_bdist_egg): - def run(self): - self.run_command('build') - _bdist_egg.run(self) - class sdist(_sdist): def run(self): self.execute(_clean_bins, (), msg="Cleaning binary files and headers") self.execute(_copy_sources, (), msg="Copying source files") _sdist.run(self) -class clean(_clean): - def run(self): - self.execute(_clean_bins, (), msg="Cleaning binary files and headers") - self.execute(_clean_native_build, (), msg="Cleaning native build") - _clean.run(self) - -# the build directory needs to exist -#try: os.makedirs(os.path.join(ROOT_DIR, 'build')) -#except OSError: pass - # platform.freedesktop_os_release was added in 3.10 os_id = '' if hasattr(platform, 'freedesktop_os_release'): @@ -269,75 +252,6 @@ def run(self): except OSError: pass -if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: - if RELEASE_DIR is None: - name = get_platform() - if 'linux' in name: - # linux_* platform tags are disallowed because the python ecosystem is fubar - # linux builds should be built in the centos 5 vm for maximum compatibility - # see https://github.com/pypa/manylinux - # see also https://github.com/angr/angr-dev/blob/master/admin/bdist.py - plat_name = 'manylinux_2_28_' + platform.machine() - elif 'mingw' in name: - if platform.architecture()[0] == '64bit': - plat_name = 'win_amd64' - else: - plat_name ='win32' - else: - # https://www.python.org/dev/peps/pep-0425/ - plat_name = name.replace('.', '_').replace('-', '_') - else: - # extract the architecture of the release from the directory name - arch = RELEASE_METADATA[1] - distos = RELEASE_METADATA[2] - if distos in ('debian', 'ubuntu'): - raise Exception( - "Linux binary distributions must be built on centos to conform to PEP 513 or alpine if targeting musl" - ) - elif distos == 'glibc': - if arch == 'x64': - plat_name = 'manylinux_2_28_x86_64' - elif arch == 'arm64' or arch == 'aarch64': - # context on why are we match on arm64 - # but use aarch64 on the plat_name is - # due to a workaround current python - # legacy build doesn't support aarch64 - # so using the currently supported arm64 - # build and simply rename it to aarch64 - # see full context on #7148 - plat_name = 'manylinux_2_28_aarch64' - else: - plat_name = 'manylinux_2_28_i686' - elif distos == 'linux' and os_id == 'alpine': - if arch == 'x64': - plat_name = 'musllinux_1_1_x86_64' - else: - plat_name = 'musllinux_1_1_i686' - elif distos == 'win': - if arch == 'x64': - plat_name = 'win_amd64' - else: - plat_name = 'win32' - elif distos == 'osx': - osver = RELEASE_METADATA[3] - if osver.count('.') > 1: - osver = '.'.join(osver.split('.')[:2]) - if osver.startswith("11"): - osver = "11_0" - if arch == 'x64': - plat_name ='macosx_%s_x86_64' % osver.replace('.', '_') - elif arch == 'arm64': - plat_name ='macosx_%s_arm64' % osver.replace('.', '_') - else: - raise Exception(f"idk how os {distos} {osver} works. what goes here?") - else: - raise Exception(f"idk how to translate between this z3 release os {distos} and the python naming scheme") - - idx = sys.argv.index('bdist_wheel') + 1 - sys.argv.insert(idx, '--plat-name') - sys.argv.insert(idx + 1, plat_name) - sys.argv.insert(idx + 2, '--universal') # supports py2+py3. if --plat-name is not specified this will also mean that the package can be installed on any machine regardless of architecture, so watch out! - setup( name='z3-solver', version=_z3_version(), @@ -356,5 +270,5 @@ def run(self): 'z3': [os.path.join('lib', '*'), os.path.join('include', '*.h'), os.path.join('include', 'c++', '*.h')] }, data_files=[('bin',[os.path.join('bin',EXECUTABLE_FILE)])], - cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_egg': bdist_egg, 'clean': clean}, + cmdclass={'build': build, 'develop': develop, 'sdist': sdist}, ) From f6dbaee6ce32bbe83c285939731e3e36c1d6bcfc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Aug 2024 17:17:53 -0700 Subject: [PATCH 162/414] adding to nightly Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 1 - scripts/nightly.yaml | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ac3b9105c76..4be3f33af0b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -73,7 +73,6 @@ jobs: - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin' - script: echo $PATH - script: "stat `which aarch64-none-linux-gnu-gcc`" - - script: "pip install build git+https://github.com/rhelmot/auditwheel" # @TODO remove when patches make it upstream - script: "cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - job: "Ubuntu20OCaml" diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 62976cce2af..fae95d2d200 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -167,7 +167,41 @@ stages: inputs: artifactName: 'UbuntuDoc' targetPath: $(Build.ArtifactStagingDirectory) - + # TODO copy artifacts + + - job: "ManylinuxPythonBuildAmd64" + displayName: "Python bindings (manylinux Centos AMD64) build" + pool: + vmImage: "ubuntu-latest" + container: "quay.io/pypa/manylinux2014_x86_64:latest" + steps: + - script: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env" + - script: 'echo "##vso[task.prependpath]$PWD/env/bin"' + - script: "pip install build git+https://github.com/rhelmot/auditwheel" # @TODO remove when patches make it upstream + - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." + - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Wed, 28 Aug 2024 09:32:00 -0700 Subject: [PATCH 163/414] Bump docker/build-push-action from 6.6.1 to 6.7.0 (#7350) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.6.1...v6.7.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 0b38274792b..7e8ca0b1226 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: . push: true From 5360656440cbe39d9d646ec9780b69b513d0dfc1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2024 09:40:51 -0700 Subject: [PATCH 164/414] fix expected Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index c3f40a52bc7..bd38d5baa68 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6798,7 +6798,7 @@ def __len__(self): sat >>> st = s.statistics() >>> len(st) - 6 + 7 """ return int(Z3_stats_size(self.ctx.ref(), self.stats)) @@ -6812,11 +6812,11 @@ def __getitem__(self, idx): sat >>> st = s.statistics() >>> len(st) - 6 + 7 >>> st[0] ('nlsat propagations', 2) >>> st[1] - ('nlsat stages', 2) + ('nlsat restarts', 1) """ if idx >= len(self): raise IndexError @@ -10220,7 +10220,7 @@ def FPs(names, fpsort, ctx=None): >>> x.ebits() 8 >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) - x + y * z + (x + y) * z """ ctx = _get_ctx(ctx) if isinstance(names, str): From 954dddbfb3e045566c4a19680c0ad43ca16cd7d7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2024 09:44:28 -0700 Subject: [PATCH 165/414] retain pip install build, remove audit Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4be3f33af0b..ca74633cafa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,7 +51,7 @@ jobs: steps: - script: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env" - script: 'echo "##vso[task.prependpath]$PWD/env/bin"' - - script: "pip install build git+https://github.com/rhelmot/auditwheel" # @TODO remove when patches make it upstream + - script: "pip install build" - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Wed, 28 Aug 2024 10:32:07 -0700 Subject: [PATCH 166/414] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e6db550c010..0254160c70f 100644 --- a/README.md +++ b/README.md @@ -236,4 +236,7 @@ to Z3's C API. For more information, see [MachineArithmetic/README.md](https://g * [Julia](https://github.com/ahumenberger/Z3.jl) * [Smalltalk](https://github.com/shingarov/MachineArithmetic/blob/pure-z3/MachineArithmetic/README.md) (supports Pharo and Smalltalk/X) +## Power Tools +* The [Axiom Profiler](https://github.com/viperproject/axiom-profiler-2) currently developed by ETH Zurich + From cd898673202f0bf242b1d03d10578b98a7570a99 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2024 14:10:16 -0700 Subject: [PATCH 167/414] add back auditwheel Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ca74633cafa..ee852a6e970 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,7 +51,7 @@ jobs: steps: - script: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env" - script: 'echo "##vso[task.prependpath]$PWD/env/bin"' - - script: "pip install build" + - script: "pip install build git+https://github.com/rhelmot/auditwheel"" - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Wed, 28 Aug 2024 15:41:18 -0700 Subject: [PATCH 168/414] Update azure-pipelines.yml --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ee852a6e970..b7ad2ab1ed0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,7 +51,7 @@ jobs: steps: - script: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env" - script: 'echo "##vso[task.prependpath]$PWD/env/bin"' - - script: "pip install build git+https://github.com/rhelmot/auditwheel"" + - script: "pip install build git+https://github.com/rhelmot/auditwheel" - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Wed, 28 Aug 2024 17:41:49 -0700 Subject: [PATCH 169/414] update nightly Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index fae95d2d200..0de2d3ed6c2 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -199,6 +199,7 @@ stages: - script: echo '##vso[task.prependpath]/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin' - script: echo $PATH - script: "stat `which aarch64-none-linux-gnu-gcc`" + - script: "pip install build git+https://github.com/rhelmot/auditwheel" - script: "cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." # TODO copy artifacts From 59853d070bb3f1ccbe9b7b169b13258f2106a7f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 29 Aug 2024 10:41:03 -0700 Subject: [PATCH 170/414] Update nightly.yaml create build artifacts --- scripts/nightly.yaml | 78 ++++++++------------------------------------ 1 file changed, 14 insertions(+), 64 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 0de2d3ed6c2..8198d343710 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -180,6 +180,15 @@ stages: - script: "pip install build git+https://github.com/rhelmot/auditwheel" # @TODO remove when patches make it upstream - script: "cd src/api/python && python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Thu, 29 Aug 2024 10:43:31 -0700 Subject: [PATCH 171/414] Update nightly.yaml disable signing options --- scripts/nightly.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 8198d343710..74ceb2a54d5 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -410,6 +410,7 @@ stages: patchVersion: $(Patch) arguments: 'pack $(Agent.TempDirectory)\package\out\Microsoft.Z3.x86.sym.nuspec -Version $(NightlyVersion) -OutputDirectory $(Build.ArtifactStagingDirectory) -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath $(Agent.TempDirectory)\package\out' - task: EsrpCodeSigning@2 + condition: eq(1,0) continueOnError: true displayName: 'Sign Package' inputs: @@ -438,6 +439,7 @@ stages: MaxConcurrency: '50' MaxRetryAttempts: '5' - task: EsrpCodeSigning@2 + condition: eq(1,0) continueOnError: true displayName: 'Sign Symbol Package' inputs: From dcdb7c4506c931fc332f44787ef40191e6dd6e08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 29 Aug 2024 11:34:47 -0700 Subject: [PATCH 172/414] wheelhouse Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 74ceb2a54d5..adbdd04b905 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -182,7 +182,7 @@ stages: - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Thu, 29 Aug 2024 17:10:36 -0700 Subject: [PATCH 173/414] Update nightly.yaml for Azure Pipelines --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index adbdd04b905..60aca338b44 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -182,7 +182,7 @@ stages: - script: "pip install ./src/api/python/wheelhouse/*.whl && python - Date: Fri, 30 Aug 2024 11:52:01 -0700 Subject: [PATCH 174/414] update gitignore to prepare for genaiscript Signed-off-by: Nikolaj Bjorner --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 710ce4b7ee4..cb3d0190c24 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,11 @@ callgrind.out.* # .hpp files are automatically generated *.hpp .z3-trace +.env +.genaiscript +package-lock.json +package.json +node_modules # OCaml generated files *.a *.o From 9a87bb109723cbddecaad2ce7d6884316ec59162 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Aug 2024 14:30:14 -0700 Subject: [PATCH 175/414] #7362 Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 2446609c658..6db52a98e61 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -457,7 +457,7 @@ struct evaluator_cfg : public default_rewriter_cfg { if (interp) { var_subst vs(m, false); result = vs(fi->get_interp(), num, args); - result = m.mk_ite(m.mk_eq(m_au.mk_real(rational(0)), args[1]), result, m.mk_app(f, num, args)); + result = m.mk_ite(m.mk_eq(m_au.mk_numeral(rational(0), args[1]->get_sort()), args[1]), result, m.mk_app(f, num, args)); return BR_DONE; } } From 01a419546fed8c501c772363a603e6491ad42478 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Aug 2024 14:35:37 -0700 Subject: [PATCH 176/414] #7362 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 4ff8c12e72a..84cdc6e6924 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -99,13 +99,16 @@ namespace smt { m_model_generator->set_context(this); } - + :: /** \brief retrieve flag for when cancelation is possible. */ bool context::get_cancel_flag() { - return !m.limit().inc(); + if (m.limit().inc()) + return false; + m_last_search_failure = CANCELED; + return true; } void context::updt_params(params_ref const& p) { @@ -3745,8 +3748,9 @@ namespace smt { VERIFY(!resolve_conflict()); return l_false; } - if (get_cancel_flag()) + if (get_cancel_flag()) return l_undef; + timeit tt(get_verbosity_level() >= 100, "smt.stats"); reset_model(); SASSERT(at_search_level()); @@ -3959,10 +3963,8 @@ namespace smt { if (m_last_search_failure != OK) return true; - if (get_cancel_flag()) { - m_last_search_failure = CANCELED; - return true; - } + if (get_cancel_flag()) + return true; if (m_progress_callback) { m_progress_callback->fast_progress_sample(); @@ -3973,10 +3975,8 @@ namespace smt { } } - if (get_cancel_flag()) { - m_last_search_failure = CANCELED; - return true; - } + if (get_cancel_flag()) + return true; if (memory::above_high_watermark()) { m_last_search_failure = MEMOUT; From a1bcf136a6c3a5cec5522cebd34b99ebbec90a8c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Aug 2024 17:34:54 -0700 Subject: [PATCH 177/414] fix build Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 1 - src/util/vector.h | 576 +++++++++++++++++++++++++++++++++++----- 2 files changed, 506 insertions(+), 71 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 84cdc6e6924..c11084e5669 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -99,7 +99,6 @@ namespace smt { m_model_generator->set_context(this); } - :: /** \brief retrieve flag for when cancelation is possible. */ diff --git a/src/util/vector.h b/src/util/vector.h index d684f43ebe8..e8ccf5ae834 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -45,120 +45,556 @@ Revision History: template using std_vector = std::vector>; -#if 0 - -// portability guide to std::vector. -// memory allocator should be based on memory_allocator -// -// template -// struct memory_allocator { -// typedef T value_type; -// etc (interface seems to change between C++17, 20 versions) -// }; -// - -// Note: -// polynomial.h contains declaration -// typedef svector numeral_vector; -// it is crucial that it uses svector and not vector. The destructors on elements of the numeral vector are handled outside. -// Numeral gets instantiated by mpz and mpz does not support copy constructors. -// porting svector to vector is therefore blocked on the semantics of svector being -// copy-constructor free. -// +#if 1 -#include +template +class vector { + SZ m_capacity = 0; + SZ m_size = 0; + T* m_data = nullptr; + + void destroy_elements() { + std::destroy_n(m_data, size()); + } + + void free_memory() { + memory::deallocate(m_data); + m_data = nullptr; + } + + void expand_vector() { + // ensure that the data is sufficiently aligned + // better fail to compile than produce code that may crash + + if (m_data == nullptr) { + m_capacity = 2; + m_size = 0; + m_data = reinterpret_cast(memory::allocate(sizeof(T) * m_capacity)); + } + else { + static_assert(std::is_nothrow_move_constructible::value); + SASSERT(capacity() > 0); + SZ old_capacity = m_capacity; + SZ new_capacity = (3 * old_capacity + 1) >> 1; + if (new_capacity <= old_capacity) { + throw default_exception("Overflow encountered when expanding vector"); + } + if (std::is_trivially_copyable::value) { + m_data = (T*)memory::reallocate(m_data, new_capacity); + } + else { + T* new_data = (T*)memory::allocate(new_capacity); + auto old_size = size(); + std::uninitialized_move_n(m_data, old_size, new_data); + destroy(); + m_data = new_data; + } + m_capacity = new_capacity; + } + } + + void copy_core(vector const& source) { + SASSERT(!m_data); + SZ size = source.size(); + SZ capacity = source.capacity(); + m_data = reinterpret_cast(memory::allocate(sizeof(T) * capacity)); + m_capacity = capacity; + m_size = size; + std::uninitialized_copy(source.begin(), source.end(), begin()); + } + + void destroy() { + if (m_data) { + if (CallDestructors) + destroy_elements(); + free_memory(); + } + } -template -class vector : public std::vector { public: typedef T data_t; - typedef typename std::vector::iterator iterator; + typedef T* iterator; + typedef const T* const_iterator; + + vector() = default; - vector() {} vector(SZ s) { - // TODO resize(s, T()); + init(s); + } + + void init(SZ s) { + SASSERT(m_data == nullptr); + if (s == 0) { + return; + } + m_data = reinterpret_cast(memory::allocate(sizeof(T) * s)); + m_capacity = s; + m_size = s; + // initialize elements + iterator it = begin(); + iterator e = end(); + for (; it != e; ++it) { + new (it) T(); + } } - vector(SZ s, T const& e) { - // TODO resize(s, e); + + vector(SZ s, T const& elem) { + resize(s, elem); } - vector(SZ s, T const* e) { - // TODO + vector(vector const& source) { + if (source.m_data) { + copy_core(source); + } + SASSERT(size() == source.size()); } - void reset() { clear(); } - void finalize() { clear(); } - void reserve(SZ s, T const & d) { - if (s > size()) - resize(s, d); + vector(vector&& other) noexcept { + std::swap(m_data, other.m_data); } - void reserve(SZ s) { - + + vector(SZ s, T const* data) { + for (SZ i = 0; i < s; i++) { + push_back(data[i]); + } } - void setx(SZ idx, T const & elem, T const & d) { - if (idx >= size()) - resize(idx+1, d); - (*this)[idx] = elem; + + ~vector() { + destroy(); } - T const & get(SZ idx, T const & d) const { - if (idx >= size()) { - return d; + void finalize() { + destroy(); + m_data = nullptr; + } + + bool operator==(vector const& other) const { + if (this == &other) { + return true; + } + if (size() != other.size()) + return false; + for (unsigned i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) + return false; } - return (*this)[idx]; + return true; } - void insert(T const & elem) { + bool operator!=(vector const& other) const { + return !(*this == other); + } + + vector& operator=(vector const& source) { + if (this == &source) { + return *this; + } + destroy(); + if (source.m_data) + copy_core(source); + return *this; + } + + vector& operator=(vector&& source) noexcept { + if (this == &source) { + return *this; + } + destroy(); + std::swap(m_data, source.m_data); + return *this; + } + + bool containsp(std::function& predicate) const { + for (auto const& t : *this) + if (predicate(t)) + return true; + return false; + } + + /** + * retain elements that satisfy predicate. aka 'where'. + */ + vector filter_pure(std::function& predicate) const { + vector result; + for (auto& t : *this) + if (predicate(t)) + result.push_back(t); + return result; + } + + vector& filter_update(std::function& predicate) { + unsigned j = 0; + for (auto& t : *this) + if (predicate(t)) + set(j++, t); + shrink(j); + return *this; + } + + /** + * update elements using f, aka 'select' + */ + template + vector map_pure(std::function& f) const { + vector result; + for (auto& t : *this) + result.push_back(f(t)); + return result; + } + + vector& map_update(std::function& f) { + unsigned j = 0; + for (auto& t : *this) + set(j++, f(t)); + return *this; + } + + void reset() { + if (m_data) { + if (CallDestructors) { + destroy_elements(); + } + m_size = 0; + } + } + + void clear() { reset(); } + + bool empty() const { + return m_data == nullptr || m_size == 0; + } + + SZ size() const { + if (m_data == nullptr) { + return 0; + } + return m_size; + } + + SZ capacity() const { + if (m_data == nullptr) { + return 0; + } + return m_capacity; + } + + iterator begin() { + return m_data; + } + + iterator end() { + return m_data + size(); + } + + const_iterator begin() const { + return m_data; + } + + const_iterator end() const { + return m_data + size(); + } + + class reverse_iterator { + T* v; + public: + reverse_iterator(T* v) :v(v) {} + + T operator*() { return *v; } + reverse_iterator operator++(int) { + reverse_iterator tmp = *this; + --v; + return tmp; + } + reverse_iterator& operator++() { + --v; + return *this; + } + + bool operator==(reverse_iterator const& other) const { + return other.v == v; + } + bool operator!=(reverse_iterator const& other) const { + return other.v != v; + } + }; + + reverse_iterator rbegin() { return reverse_iterator(end() - 1); } + reverse_iterator rend() { return reverse_iterator(begin() - 1); } + + void set_end(iterator it) { + if (m_data) { + SZ new_sz = static_cast(it - m_data); + if (CallDestructors) { + iterator e = end(); + for (; it != e; ++it) { + it->~T(); + } + } + m_size = new_sz; + } + else { + SASSERT(it == 0); + } + } + + T& operator[](SZ idx) { + SASSERT(idx < size()); + return m_data[idx]; + } + + T const& operator[](SZ idx) const { + SASSERT(idx < size()); + return m_data[idx]; + } + + T& get(SZ idx) { + SASSERT(idx < size()); + return m_data[idx]; + } + + T const& get(SZ idx) const { + SASSERT(idx < size()); + return m_data[idx]; + } + + void set(SZ idx, T const& val) { + SASSERT(idx < size()); + m_data[idx] = val; + } + + void set(SZ idx, T&& val) { + SASSERT(idx < size()); + m_data[idx] = std::move(val); + } + + T& back() { + SASSERT(!empty()); + return operator[](size() - 1); + } + + T const& back() const { + SASSERT(!empty()); + return operator[](size() - 1); + } + + void pop_back() { + SASSERT(!empty()); + if (CallDestructors) { + back().~T(); + } + m_size--; + } + + vector& push_back(T const& elem) { + if (m_data == nullptr || m_size == m_capacity) { + expand_vector(); + } + new (m_data + m_size) T(elem); + m_size++; + return *this; + } + + template + vector& push_back(T const& elem, T elem2, Args ... elems) { push_back(elem); + push_back(elem2, elems ...); + return *this; } - - void erase(iterator pos) { - // TODO + + vector& push_back(T&& elem) { + if (m_data == nullptr || m_size == m_capacity) { + expand_vector(); + } + new (m_data + m_size) T(std::move(elem)); + ++m_size; + return *this; } - void erase(T const& e) { - // TODO + void insert(T const& elem) { + push_back(elem); } - void fill(T const & elem) { - for (auto& e : *this) - e = elem; + + void erase(iterator pos) { + SASSERT(pos >= begin() && pos < end()); + iterator prev = pos; + ++pos; + iterator e = end(); + for (; pos != e; ++pos, ++prev) { + *prev = std::move(*pos); + } + pop_back(); } - void fill(unsigned sz, T const & elem) { - resize(sz); - fill(elem); + void erase(T const& elem) { + iterator it = std::find(begin(), end(), elem); + if (it != end()) { + erase(it); + } + } + + /** Erase all elements that satisfy the given predicate. Returns the number of erased elements. */ + template + SZ erase_if(UnaryPredicate should_erase) { + iterator i = begin(); + iterator const e = end(); + for (iterator j = begin(); j != e; ++j) + if (!should_erase(std::as_const(*j))) + *(i++) = std::move(*j); + SZ const count = e - i; + SASSERT_EQ(i - begin(), size() - count); + shrink(size() - count); + return count; } void shrink(SZ s) { - resize(s); + if (m_data) { + SASSERT(s <= m_size); + if (CallDestructors) { + iterator it = m_data + s; + iterator e = end(); + for (; it != e; ++it) { + it->~T(); + } + } + m_size = s; + } + else { + SASSERT(s == 0); + } } - void reverse() { + + template + void resize(SZ s, Args args...) { SZ sz = size(); - for (SZ i = 0; i < sz/2; ++i) { - std::swap((*this)[i], (*this)[sz-i-1]); + if (s <= sz) { shrink(s); return; } + while (s > capacity()) { + expand_vector(); + } + SASSERT(m_data != 0); + m_size = s; + iterator it = m_data + sz; + iterator end = m_data + s; + for (; it != end; ++it) { + new (it) T(std::forward(args)); } } - void append(vector const & other) { - for(SZ i = 0; i < other.size(); ++i) { + void resize(SZ s) { + SZ sz = size(); + if (s <= sz) { shrink(s); return; } + while (s > capacity()) { + expand_vector(); + } + SASSERT(m_data != 0); + m_size = s; + iterator it = m_data + sz; + iterator end = m_data + s; + for (; it != end; ++it) { + new (it) T(); + } + } + + void append(vector const& other) { + for (SZ i = 0; i < other.size(); ++i) { push_back(other[i]); } } - void append(unsigned n, T const* elems) { - // TODO + void append(SZ sz, T const* data) { + for (SZ i = 0; i < sz; ++i) { + push_back(data[i]); + } } - bool contains(T const & elem) const { - for (auto const& e : *this) - if (e == elem) + void init(vector const& other) { + if (this == &other) + return; + reset(); + append(other); + } + + void init(SZ sz, T const* data) { + reset(); + append(sz, data); + } + + T* data() const { + return m_data; + } + + void swap(vector& other) noexcept { + std::swap(m_data, other.m_data); + } + + void reverse() { + SZ sz = size(); + for (SZ i = 0; i < sz / 2; ++i) { + std::swap(m_data[i], m_data[sz - i - 1]); + } + } + + void fill(T const& elem) { + iterator i = begin(); + iterator e = end(); + for (; i != e; ++i) { + *i = elem; + } + } + + void fill(unsigned sz, T const& elem) { + resize(sz); + fill(elem); + } + + bool contains(T const& elem) const { + const_iterator it = begin(); + const_iterator e = end(); + for (; it != e; ++it) { + if (*it == elem) { return true; + } + } return false; } - -}; + // set pos idx with elem. If idx >= size, then expand using default. + void setx(SZ idx, T const& elem, T const& d) { + if (idx >= size()) { + resize(idx + 1, d); + } + m_data[idx] = elem; + } + + // return element at position idx, if idx >= size, then return default + T const& get(SZ idx, T const& d) const { + if (idx >= size()) { + return d; + } + return m_data[idx]; + } + + void reserve(SZ s, T const& d) { + if (s > size()) + resize(s, d); + } + + void reserve(SZ s) { + if (s > size()) + resize(s); + } + + struct scoped_stack { + vector& s; + unsigned sz; + scoped_stack(vector& s) :s(s), sz(s.size()) {} + ~scoped_stack() { s.shrink(sz); } + }; + +}; + + + #else From a3eb2ff58d9da628c98a6e06faf3e449d04b6a54 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 Aug 2024 17:43:15 -0700 Subject: [PATCH 178/414] revert update to vector for testing #6902 Signed-off-by: Nikolaj Bjorner --- src/util/vector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/vector.h b/src/util/vector.h index e8ccf5ae834..31ced7f4bab 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -45,7 +45,7 @@ Revision History: template using std_vector = std::vector>; -#if 1 +#if 0 template class vector { From ef58376c1463780af3ea1545ddb3e305f2bf2a2b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 2 Sep 2024 16:13:46 +0100 Subject: [PATCH 179/414] replace a few old-school constructors for a 0.5% reduction in code size don't waste those 128 KB! --- src/ast/pattern/pattern_inference.h | 6 +++--- src/util/chashtable.h | 3 +-- src/util/memory_manager.h | 9 +++------ src/util/obj_hashtable.h | 5 ++--- src/util/symbol.h | 6 ++---- src/util/symbol_table.h | 7 ++++--- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index 8d179ba33e5..09c4e333b88 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -114,9 +114,9 @@ class pattern_inference_cfg : public default_rewriter_cfg { // class collect { struct entry { - expr * m_node; - unsigned m_delta; - entry():m_node(nullptr), m_delta(0) {} + expr * m_node = nullptr; + unsigned m_delta = 0; + entry() = default; entry(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->get_id(), m_delta); diff --git a/src/util/chashtable.h b/src/util/chashtable.h index b15d6017f77..394e5998c4f 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -52,9 +52,8 @@ class chashtable : private HashProc, private EqProc { protected: struct cell { - cell * m_next; + cell * m_next = (cell*)1; T m_data; - cell():m_next(reinterpret_cast(1)) {} bool is_free() const { return GET_TAG(m_next) == 1; } void mark_free() { m_next = TAG(cell*, m_next, 1); } void unmark_free() { m_next = UNTAG(cell*, m_next); } diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index af56c4507f4..a52e3e3bd41 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -19,6 +19,7 @@ Revision History: #pragma once #include +#include #include #include #include "util/z3_exception.h" @@ -105,18 +106,14 @@ ALLOC_ATTR T * alloc_vect(unsigned sz); template T * alloc_vect(unsigned sz) { T * r = static_cast(memory::allocate(sizeof(T) * sz)); - T * curr = r; - for (unsigned i = 0; i < sz; i++, curr++) - new (curr) T(); + std::uninitialized_default_construct_n(r, sz); return r; } template void dealloc_vect(T * ptr, unsigned sz) { if (ptr == nullptr) return; - T * curr = ptr; - for (unsigned i = 0; i < sz; i++, curr++) - curr->~T(); + std::destroy_n(ptr, sz); memory::deallocate(ptr); } diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 49b37ca6189..dbb2b776ebb 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -56,10 +56,9 @@ template class obj_map { public: struct key_data { - Key * m_key; + Key * m_key = nullptr; Value m_value; - key_data():m_key(nullptr), m_value() { - } + key_data() = default; key_data(Key * k): m_key(k), m_value() { } diff --git a/src/util/symbol.h b/src/util/symbol.h index ab7d41c989b..721351b7ea4 100644 --- a/src/util/symbol.h +++ b/src/util/symbol.h @@ -29,7 +29,7 @@ template class symbol_table; class symbol { - char const * m_data; + char const * m_data = nullptr; template friend class symbol_table; @@ -50,9 +50,7 @@ class symbol { } static symbol m_dummy; public: - symbol(): - m_data(nullptr) { - } + symbol() = default; explicit symbol(char const * d); explicit symbol(const std::string & str) : symbol(str.c_str()) {} explicit symbol(unsigned idx): diff --git a/src/util/symbol_table.h b/src/util/symbol_table.h index 6a683f20107..1a805ff352f 100644 --- a/src/util/symbol_table.h +++ b/src/util/symbol_table.h @@ -31,8 +31,7 @@ class symbol_table { symbol m_key; T m_data; - key_data() { - } + key_data() = default; explicit key_data(symbol k): m_key(k) { @@ -59,10 +58,12 @@ class symbol_table { struct hash_entry { typedef key_data data; key_data m_data; - + +#if 0 hash_entry() { SASSERT(m_data.m_key == symbol::null); } +#endif unsigned get_hash() const { return m_data.m_key.hash(); From db4176adf41e7386e5b1f6f0fd3968486659f122 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 2 Sep 2024 17:01:25 -0700 Subject: [PATCH 180/414] #6902 See discussion under #6902. Add genaiscript for commit messages for future use. --- genaisrc/gcm.genai.mts | 81 +++++++++++++++++++++++++++++++++++++ src/util/memory_manager.cpp | 24 ++++++----- 2 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 genaisrc/gcm.genai.mts diff --git a/genaisrc/gcm.genai.mts b/genaisrc/gcm.genai.mts new file mode 100644 index 00000000000..b39e9a8e05a --- /dev/null +++ b/genaisrc/gcm.genai.mts @@ -0,0 +1,81 @@ +import { select, input, confirm } from "@inquirer/prompts" + +// Check for staged changes and stage all changes if none are staged +let diff = await host.exec("git", ["diff", "--cached"]) +if (!diff.stdout) { + const stage = await confirm({ + message: "No staged changes. Stage all changes?", + default: true, + }) + if (stage) { + await host.exec("git", ["add", "."]) + diff = await host.exec("git", [ + "diff", + "--cached", + "--", + ".", + ":!**/genaiscript.d.ts", + ]) + } + if (!diff.stdout) cancel("no staged changes") +} + +console.log(diff.stdout) + +let choice +let message +do { + // Generate commit message + message = ( + await runPrompt( + (_) => { + _.def("GIT_DIFF", diff, { maxTokens: 20000 }) + _.$`GIT_DIFF is a diff of all staged changes, coming from the command: +\`\`\` +git diff --cached +\`\`\` +Please generate a concise, one-line commit message for these changes. +- do NOT add quotes` + }, + { cache: false, temperature: 0.8 } + ) + ).text + + // Prompt user for commit message + choice = await select({ + message, + choices: [ + { + name: "commit", + value: "commit", + description: "accept message and commit", + }, + { + name: "edit", + value: "edit", + description: "edit message and commit", + }, + { + name: "regenerate", + value: "regenerate", + description: "regenerate message", + }, + ], + }) + + // Handle user choice + if (choice === "edit") { + message = await input({ + message: "Edit commit message", + required: true, + }) + choice = "commit" + } + // Regenerate message + if (choice === "commit" && message) { + console.log((await host.exec("git", ["commit", "-m", message])).stdout) + if (await confirm({ message: "Push changes?", default: true })) + console.log((await host.exec("git", ["push"])).stdout) + break + } +} while (choice !== "commit") diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 290881ba54f..4ef1cac66fc 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -29,6 +29,8 @@ Copyright (c) 2015 Microsoft Corporation # define malloc_usable_size _msize #endif +#define SIZE_T_ALIGN 2 + // The following two function are automatically generated by the mk_make.py script. // The script collects ADD_INITIALIZER and ADD_FINALIZER commands in the .h files. // For example, rational.h contains @@ -278,7 +280,7 @@ void memory::deallocate(void * p) { size_t sz = malloc_usable_size(p); void * real_p = p; #else - size_t * sz_p = reinterpret_cast(p) - 1; + size_t * sz_p = reinterpret_cast(p) - SIZE_T_ALIGN; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); #endif @@ -291,7 +293,7 @@ void memory::deallocate(void * p) { void * memory::allocate(size_t s) { #ifndef HAS_MALLOC_USABLE_SIZE - s = s + sizeof(size_t); // we allocate an extra field! + s = s + SIZE_T_ALIGN * sizeof(size_t); // we allocate an extra field! #endif g_memory_thread_alloc_size += s; g_memory_thread_alloc_count += 1; @@ -308,7 +310,7 @@ void * memory::allocate(size_t s) { return r; #else *(static_cast(r)) = s; - return static_cast(r) + 1; // we return a pointer to the location after the extra field + return static_cast(r) + SIZE_T_ALIGN; // we return a pointer to the location after the extra field #endif } @@ -323,7 +325,7 @@ void* memory::reallocate(void *p, size_t s) { size_t *sz_p = reinterpret_cast(p)-1; size_t sz = *sz_p; void *real_p = reinterpret_cast(sz_p); - s = s + sizeof(size_t); // we allocate an extra field! + s = s + SIZE_T_ALIGN * sizeof(size_t); // we allocate an extra field! #endif g_memory_thread_alloc_size += s - sz; g_memory_thread_alloc_count += 1; @@ -341,7 +343,7 @@ void* memory::reallocate(void *p, size_t s) { return r; #else *(static_cast(r)) = s; - return static_cast(r) + 1; // we return a pointer to the location after the extra field + return static_cast(r) + SIZE_T_ALIGN; // we return a pointer to the location after the extra field #endif } @@ -358,7 +360,7 @@ void memory::deallocate(void * p) { size_t sz = malloc_usable_size(p); void * real_p = p; #else - size_t * sz_p = reinterpret_cast(p) - 1; + size_t * sz_p = reinterpret_cast(p) - SIZE_T_ALIGN; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); #endif @@ -368,7 +370,7 @@ void memory::deallocate(void * p) { void * memory::allocate(size_t s) { #ifndef HAS_MALLOC_USABLE_SIZE - s = s + sizeof(size_t); // we allocate an extra field! + s = s + SIZE_T_ALIGN * sizeof(size_t); // we allocate an extra field! #endif g_memory_alloc_size += s; g_memory_alloc_count += 1; @@ -389,7 +391,7 @@ void * memory::allocate(size_t s) { return r; #else *(static_cast(r)) = s; - return static_cast(r) + 1; // we return a pointer to the location after the extra field + return static_cast(r) + SIZE_T_ALIGN; // we return a pointer to the location after the extra field #endif } @@ -401,10 +403,10 @@ void* memory::reallocate(void *p, size_t s) { if (sz >= s) return p; #else - size_t * sz_p = reinterpret_cast(p) - 1; + size_t * sz_p = reinterpret_cast(p) - SIZE_T_ALIGN; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); - s = s + sizeof(size_t); // we allocate an extra field! + s = s + SIZE_T_ALIGN * sizeof(size_t); // we allocate an extra field! #endif g_memory_alloc_size += s - sz; g_memory_alloc_count += 1; @@ -425,7 +427,7 @@ void* memory::reallocate(void *p, size_t s) { return r; #else *(static_cast(r)) = s; - return static_cast(r) + 1; // we return a pointer to the location after the extra field + return static_cast(r) + SIZE_T_ALIGN; // we return a pointer to the location after the extra field #endif } From 6086a30c071b7a3905c722810f3b53369edb32c0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 2 Sep 2024 17:15:49 -0700 Subject: [PATCH 181/414] Add reference URL to GenAI script file for auto Git commit guide --- genaisrc/gcm.genai.mts | 1 + 1 file changed, 1 insertion(+) diff --git a/genaisrc/gcm.genai.mts b/genaisrc/gcm.genai.mts index b39e9a8e05a..54f6762a8e9 100644 --- a/genaisrc/gcm.genai.mts +++ b/genaisrc/gcm.genai.mts @@ -1,3 +1,4 @@ +// https://microsoft.github.io/genaiscript/guides/auto-git-commit-message/ import { select, input, confirm } from "@inquirer/prompts" // Check for staged changes and stage all changes if none are staged From 5237e7def20fdbdace592ea60bd68ab51f9389b5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 3 Sep 2024 11:17:47 -0700 Subject: [PATCH 182/414] Adjust memory reallocation to consider SIZE_T_ALIGN in memory_manager --- src/util/memory_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 4ef1cac66fc..8c6bfc7e7e8 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -322,7 +322,7 @@ void* memory::reallocate(void *p, size_t s) { if (sz >= s) return p; #else - size_t *sz_p = reinterpret_cast(p)-1; + size_t *sz_p = reinterpret_cast(p) - SIZE_T_ALIGN; size_t sz = *sz_p; void *real_p = reinterpret_cast(sz_p); s = s + SIZE_T_ALIGN * sizeof(size_t); // we allocate an extra field! From 0837e3b8e8736a6bd00f3f00976b1da1e2f762fc Mon Sep 17 00:00:00 2001 From: Audrey Dutcher Date: Tue, 3 Sep 2024 16:11:42 -0700 Subject: [PATCH 183/414] Fix nightly (#7365) - add some logic to setup.py to handle cross platform tagging correctly this adds a dependency on setuptools>=70 - rearrange the nightly CI to use these new builds correctly --- scripts/nightly.yaml | 13 +++++------- src/api/python/pyproject.toml | 2 +- src/api/python/setup.py | 38 +++++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 60aca338b44..ccbed95f862 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -183,7 +183,7 @@ stages: - task: CopyFiles@2 inputs: sourceFolder: src/api/python/wheelhouse - contents: '*.zip' + contents: '*.whl' targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: @@ -209,11 +209,11 @@ stages: - script: echo $PATH - script: "stat `which aarch64-none-linux-gnu-gcc`" - script: "pip install build git+https://github.com/rhelmot/auditwheel" - - script: "cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." + - script: "cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld Z3_CROSS_COMPILING=aarch64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." - task: CopyFiles@2 inputs: sourceFolder: src/api/python/wheelhouse - contents: '*.zip' + contents: '*.whl' targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@0 inputs: @@ -504,21 +504,18 @@ stages: targetPath: $(Agent.TempDirectory) - script: cd $(Agent.TempDirectory); mkdir osx-x64-bin; cd osx-x64-bin; unzip ../*x64-osx*.zip - script: cd $(Agent.TempDirectory); mkdir osx-arm64-bin; cd osx-arm64-bin; unzip ../*arm64-osx*.zip - - script: cd $(Agent.TempDirectory); mkdir libc-x64-bin; cd libc-x64-bin; unzip ../*x64-glibc*.zip - - script: cd $(Agent.TempDirectory); mkdir libc-arm64-bin; cd libc-arm64-bin; unzip ../*arm64-glibc*.zip # - script: cd $(Agent.TempDirectory); mkdir musl-bin; cd musl-bin; unzip ../*-linux.zip - script: cd $(Agent.TempDirectory); mkdir win32-bin; cd win32-bin; unzip ../*x86-win*.zip - script: cd $(Agent.TempDirectory); mkdir win64-bin; cd win64-bin; unzip ../*x64-win*.zip - - script: python3 -m pip install --user -U setuptools wheel + - script: python3 -m pip install --user -U setuptools - script: cd src/api/python; python3 setup.py sdist # take a look at this PREMIUM HACK I came up with to get around the fact that the azure variable syntax overloads the bash syntax for subshells - - script: cd src/api/python; echo $(Agent.TempDirectory)/libc-x64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - - script: cd src/api/python; echo $(Agent.TempDirectory)/libc-arm64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel # - script: cd src/api/python; echo $(Agent.TempDirectory)/musl-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win32-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/osx-x64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/osx-arm64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python3 setup.py bdist_wheel + - script: cp $(Agent.TempDirectory)/*.whl src/api/python/dist - task: PublishPipelineArtifact@0 inputs: artifactName: 'Python packages' diff --git a/src/api/python/pyproject.toml b/src/api/python/pyproject.toml index 62f43b591d9..ead5d1b2d12 100644 --- a/src/api/python/pyproject.toml +++ b/src/api/python/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=59", "wheel", "cmake"] +requires = ["setuptools>=70", "cmake"] build-backend = "setuptools.build_meta" diff --git a/src/api/python/setup.py b/src/api/python/setup.py index f17527a0140..f788aaf6956 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -9,6 +9,7 @@ from setuptools import setup from setuptools.command.build import build as _build from setuptools.command.sdist import sdist as _sdist +from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel from setuptools.command.develop import develop as _develop class LibError(Exception): @@ -31,6 +32,8 @@ class LibError(Exception): HEADER_DIRS = [os.path.join(SRC_DIR, 'src', 'api'), os.path.join(SRC_DIR, 'src', 'api', 'c++')] RELEASE_METADATA = None BUILD_PLATFORM = sys.platform + BUILD_ARCH = os.environ.get("Z3_CROSS_COMPILING", platform.machine()) + BUILD_OS_VERSION = platform.mac_ver()[0].split(".") else: if not os.path.isdir(RELEASE_DIR): raise Exception("RELEASE_DIR (%s) is not a directory!" % RELEASE_DIR) @@ -41,6 +44,11 @@ class LibError(Exception): raise Exception("RELEASE_DIR (%s) must be in the format z3-version-arch-os[-osversion] so we can extract metadata from it. Sorry!" % RELEASE_DIR) RELEASE_METADATA.pop(0) BUILD_PLATFORM = RELEASE_METADATA[2] + BUILD_ARCH = RELEASE_METADATA[1] + if len(RELEASE_METADATA) == 4: + BUILD_OS_VERSION = RELEASE_METADATA[3].split(".") + else: + BUILD_OS_VERSION = None # determine where destinations are LIBS_DIR = os.path.join(ROOT_DIR, 'z3', 'lib') @@ -242,21 +250,29 @@ def run(self): self.execute(_copy_sources, (), msg="Copying source files") _sdist.run(self) -# platform.freedesktop_os_release was added in 3.10 -os_id = '' -if hasattr(platform, 'freedesktop_os_release'): - try: - osr = platform.freedesktop_os_release() - print(osr) - os_id = osr['ID'] - except OSError: - pass +class bdist_wheel(_bdist_wheel): + def finalize_options(self): + if BUILD_ARCH is not None and BUILD_PLATFORM is not None: + os_version_tag = '_'.join(BUILD_OS_VERSION[:2]) if BUILD_OS_VERSION is not None else 'xxxxxx' + TAGS = { + # linux tags cannot be deployed - they must be auditwheel'd to pick the right compatibility tag based on imported libc symbol versions + ("linux", "x86_64"): "linux_x86_64", + ("linux", "aarch64"): "linux_aarch64", + # windows arm64 is not supported by pypi yet + ("win", "x64"): "win_amd64", + ("win", "x86"): "win32", + ("osx", "x64"): f"macosx_{os_version_tag}_x86_64", + ("osx", "arm64"): f"macosx_{os_version_tag}_arm64", + } # type: dict[tuple[str, str], str] + self.plat_name = TAGS[(BUILD_PLATFORM, BUILD_ARCH)] + return super().finalize_options() + setup( name='z3-solver', version=_z3_version(), description='an efficient SMT solver library', - long_description='Z3 is a theorem prover from Microsoft Research with support for bitvectors, booleans, arrays, floating point numbers, strings, and other data types.\n\nFor documentation, please read http://z3prover.github.io/api/html/z3.html\n\nIn the event of technical difficulties related to configuration, compilation, or installation, please submit issues to https://github.com/z3prover/z3.git', + long_description='Z3 is a theorem prover from Microsoft Research with support for bitvectors, booleans, arrays, floating point numbers, strings, and other data types.\n\nFor documentation, please read http://z3prover.github.io/api/html/z3.html', author="The Z3 Theorem Prover Project", maintainer="Audrey Dutcher and Nikolaj Bjorner", maintainer_email="audrey@rhelmot.io", @@ -270,5 +286,5 @@ def run(self): 'z3': [os.path.join('lib', '*'), os.path.join('include', '*.h'), os.path.join('include', 'c++', '*.h')] }, data_files=[('bin',[os.path.join('bin',EXECUTABLE_FILE)])], - cmdclass={'build': build, 'develop': develop, 'sdist': sdist}, + cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_wheel': bdist_wheel}, ) From 8061765574aa9224e5f1d572b08c0881efe51285 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 4 Sep 2024 22:30:23 +0100 Subject: [PATCH 184/414] remove default destructors & some default constructors Another ~700 KB reduction in binary size --- src/ackermannization/lackr_model_constructor.cpp | 2 -- src/ast/ast_pp_dot.cpp | 2 -- src/ast/euf/euf_ac_plugin.h | 3 --- src/ast/euf/euf_arith_plugin.h | 2 -- src/ast/euf/euf_bv_plugin.h | 2 -- src/ast/euf/euf_specrel_plugin.h | 2 -- src/ast/rewriter/label_rewriter.cpp | 2 -- src/ast/rewriter/label_rewriter.h | 1 - src/ast/rewriter/rewriter.h | 2 -- src/ast/simplifiers/reduce_args_simplifier.cpp | 2 -- src/muz/base/rule_properties.cpp | 2 -- src/muz/base/rule_properties.h | 1 - src/muz/bmc/dl_bmc_engine.cpp | 2 -- src/muz/bmc/dl_bmc_engine.h | 2 -- src/muz/clp/clp_context.cpp | 2 -- src/muz/ddnf/ddnf.cpp | 6 ------ src/muz/spacer/spacer_expand_bnd_generalizer.h | 1 - src/muz/spacer/spacer_global_generalizer.h | 1 - src/muz/tab/tab_context.cpp | 2 -- src/muz/transforms/dl_mk_array_eq_rewrite.h | 1 - src/muz/transforms/dl_mk_array_instantiation.h | 1 - src/muz/transforms/dl_mk_backwards.cpp | 2 -- src/muz/transforms/dl_mk_backwards.h | 1 - src/muz/transforms/dl_mk_bit_blast.cpp | 2 -- src/muz/transforms/dl_mk_elim_term_ite.cpp | 2 -- src/muz/transforms/dl_mk_elim_term_ite.h | 1 - src/muz/transforms/dl_mk_karr_invariants.cpp | 2 -- src/muz/transforms/dl_mk_karr_invariants.h | 2 -- src/muz/transforms/dl_mk_loop_counter.cpp | 2 -- src/muz/transforms/dl_mk_loop_counter.h | 1 - src/muz/transforms/dl_mk_magic_symbolic.cpp | 2 -- src/muz/transforms/dl_mk_magic_symbolic.h | 1 - src/qe/mbp/mbp_arith.cpp | 2 -- src/qe/mbp/mbp_arrays.cpp | 1 - src/qe/mbp/mbp_term_graph.cpp | 2 -- src/qe/nlqsat.cpp | 1 - src/sat/sat_model_converter.h | 1 - src/sat/sat_mus.cpp | 2 -- src/sat/sat_mus.h | 1 - src/sat/smt/arith_sls.h | 1 - src/sat/smt/array_solver.cpp | 2 -- src/sat/smt/array_solver.h | 1 - src/sat/smt/intblast_solver.h | 2 -- src/smt/arith_eq_adapter.cpp | 2 -- src/smt/smt_model_finder.cpp | 2 -- src/smt/smt_relevancy.h | 2 -- src/smt/theory_seq.h | 1 - src/solver/simplifier_solver.cpp | 1 - src/solver/solver_na2as.cpp | 2 -- src/solver/solver_na2as.h | 1 - src/tactic/arith/nla2bv_tactic.cpp | 2 -- src/tactic/bv/bv_bound_chk_tactic.cpp | 6 +----- src/tactic/bv/bv_bounds_tactic.cpp | 2 -- src/tactic/core/ctx_simplify_tactic.cpp | 5 ++--- src/tactic/core/symmetry_reduce_tactic.cpp | 10 +++------- src/tactic/probe.h | 3 +-- src/tactic/tactic.cpp | 2 -- src/tactic/tactic.h | 4 +--- src/util/chashtable.h | 2 +- src/util/optional.h | 6 +++--- src/util/params.cpp | 3 +-- src/util/queue.h | 7 ++----- src/util/rational.h | 4 ++-- src/util/s_integer.h | 4 ++-- src/util/small_object_allocator.h | 3 +-- src/util/vector.h | 4 ++-- 66 files changed, 22 insertions(+), 131 deletions(-) diff --git a/src/ackermannization/lackr_model_constructor.cpp b/src/ackermannization/lackr_model_constructor.cpp index 76c1803f211..ec472715242 100644 --- a/src/ackermannization/lackr_model_constructor.cpp +++ b/src/ackermannization/lackr_model_constructor.cpp @@ -41,8 +41,6 @@ struct lackr_model_constructor::imp { , m_ackr_helper(m) {} - ~imp() { } - // // Returns true iff model was successfully constructed. // Conflicts are saved as a side effect. diff --git a/src/ast/ast_pp_dot.cpp b/src/ast/ast_pp_dot.cpp index 47da4d4f4e3..1298239a23c 100644 --- a/src/ast/ast_pp_dot.cpp +++ b/src/ast/ast_pp_dot.cpp @@ -45,8 +45,6 @@ struct ast_pp_dot_st { m_printed(), m_to_print(), m_first(true) {} - - ~ast_pp_dot_st() {}; void push_term(const expr * a) { m_to_print.push_back(a); } diff --git a/src/ast/euf/euf_ac_plugin.h b/src/ast/euf/euf_ac_plugin.h index 7da346dbafa..2963c1f8b0e 100644 --- a/src/ast/euf/euf_ac_plugin.h +++ b/src/ast/euf/euf_ac_plugin.h @@ -47,7 +47,6 @@ namespace euf { unsigned_vector eqs; // equality occurrences unsigned root_id() const { return root->n->get_id(); } - ~node() {} static node* mk(region& r, enode* n); }; @@ -270,8 +269,6 @@ namespace euf { ac_plugin(egraph& g, unsigned fid, unsigned op); ac_plugin(egraph& g, func_decl* f); - - ~ac_plugin() override {} theory_id get_id() const override { return m_fid; } diff --git a/src/ast/euf/euf_arith_plugin.h b/src/ast/euf/euf_arith_plugin.h index 4c2a88d3603..0cc122d99fb 100644 --- a/src/ast/euf/euf_arith_plugin.h +++ b/src/ast/euf/euf_arith_plugin.h @@ -33,8 +33,6 @@ namespace euf { public: arith_plugin(egraph& g); - ~arith_plugin() override {} - theory_id get_id() const override { return a.get_family_id(); } void register_node(enode* n) override; diff --git a/src/ast/euf/euf_bv_plugin.h b/src/ast/euf/euf_bv_plugin.h index ec2c0b448f4..6bf48df2a2f 100644 --- a/src/ast/euf/euf_bv_plugin.h +++ b/src/ast/euf/euf_bv_plugin.h @@ -95,8 +95,6 @@ namespace euf { public: bv_plugin(egraph& g); - ~bv_plugin() override {} - theory_id get_id() const override { return bv.get_family_id(); } void register_node(enode* n) override; diff --git a/src/ast/euf/euf_specrel_plugin.h b/src/ast/euf/euf_specrel_plugin.h index ae93bd2a5eb..5e3578ccfdc 100644 --- a/src/ast/euf/euf_specrel_plugin.h +++ b/src/ast/euf/euf_specrel_plugin.h @@ -34,8 +34,6 @@ namespace euf { public: specrel_plugin(egraph& g); - - ~specrel_plugin() override {} theory_id get_id() const override { return sp.get_family_id(); } diff --git a/src/ast/rewriter/label_rewriter.cpp b/src/ast/rewriter/label_rewriter.cpp index 8adefb8961b..548d4fa8271 100644 --- a/src/ast/rewriter/label_rewriter.cpp +++ b/src/ast/rewriter/label_rewriter.cpp @@ -26,8 +26,6 @@ label_rewriter::label_rewriter(ast_manager & m) : m_label_fid(m.get_label_family_id()), m_rwr(m, false, *this) {} -label_rewriter::~label_rewriter() {} - br_status label_rewriter::reduce_app( func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { diff --git a/src/ast/rewriter/label_rewriter.h b/src/ast/rewriter/label_rewriter.h index 8bec62ed6f7..a4720853cc5 100644 --- a/src/ast/rewriter/label_rewriter.h +++ b/src/ast/rewriter/label_rewriter.h @@ -27,7 +27,6 @@ class label_rewriter : public default_rewriter_cfg { rewriter_tpl m_rwr; public: label_rewriter(ast_manager & m); - ~label_rewriter(); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index cd89bb2f9b3..1afa19f8445 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -346,8 +346,6 @@ class rewriter_tpl : public rewriter_core { ast_manager & m() const { return this->m_manager; } Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } - - ~rewriter_tpl() override {}; void reset(); void cleanup(); diff --git a/src/ast/simplifiers/reduce_args_simplifier.cpp b/src/ast/simplifiers/reduce_args_simplifier.cpp index f9c7887920a..96607734bf1 100644 --- a/src/ast/simplifiers/reduce_args_simplifier.cpp +++ b/src/ast/simplifiers/reduce_args_simplifier.cpp @@ -375,8 +375,6 @@ class reduce_args_simplifier : public dependent_expr_simplifier { m_bv(m) {} - ~reduce_args_simplifier() override {} - char const* name() const override { return "reduce-args"; } void collect_statistics(statistics& st) const override { diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 239fa73b68d..abd79c51567 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -31,8 +31,6 @@ rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx m_dt(m), m_dl(m), m_a(m), m_bv(m), m_ar(m), m_rec(m), m_generate_proof(false), m_collected(false), m_is_monotone(true) {} -rule_properties::~rule_properties() {} - void rule_properties::collect(rule_set const& rules) { reset(); m_collected = true; diff --git a/src/muz/base/rule_properties.h b/src/muz/base/rule_properties.h index a7ef9a0dec3..3159c28a613 100644 --- a/src/muz/base/rule_properties.h +++ b/src/muz/base/rule_properties.h @@ -58,7 +58,6 @@ namespace datalog { bool check_accessor(app* n); public: rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& is_predicate); - ~rule_properties(); void set_generate_proof(bool generate_proof) { m_generate_proof = generate_proof; } void collect(rule_set const& r); void check_quantifier_free(); diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 913e35f369d..e83569e0ecd 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -1443,8 +1443,6 @@ namespace datalog { m_rule_trace(ctx.get_rule_manager()) { } - bmc::~bmc() {} - lbool bmc::query(expr* query) { m_solver = nullptr; m_answer = nullptr; diff --git a/src/muz/bmc/dl_bmc_engine.h b/src/muz/bmc/dl_bmc_engine.h index 90c4ab86264..05d5707a66e 100644 --- a/src/muz/bmc/dl_bmc_engine.h +++ b/src/muz/bmc/dl_bmc_engine.h @@ -51,8 +51,6 @@ namespace datalog { public: bmc(context& ctx); - ~bmc() override; - lbool query(expr* query) override; void display_certificate(std::ostream& out) const override; diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index 89ae71e36b8..9d4baa4aef3 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -59,8 +59,6 @@ namespace datalog { m_fparams.m_mbqi = false; } - ~imp() {} - lbool query(expr* query) { m_ctx.ensure_opened(); m_solver.reset(); diff --git a/src/muz/ddnf/ddnf.cpp b/src/muz/ddnf/ddnf.cpp index cf759c76e93..f4f946d09b8 100644 --- a/src/muz/ddnf/ddnf.cpp +++ b/src/muz/ddnf/ddnf.cpp @@ -78,8 +78,6 @@ namespace datalog { m_descendants(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { } - ~ddnf_node() {} - unsigned inc_ref() { return ++m_refs; } @@ -429,8 +427,6 @@ namespace datalog { class ddnfs { u_map m_mgrs; public: - ddnfs() {} - ~ddnfs() { u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); for (; it != end; ++it) { @@ -503,8 +499,6 @@ namespace datalog { m_inner_ctx.updt_params(params); } - ~imp() {} - lbool query(expr* query) { m_ctx.ensure_opened(); rule_set& old_rules = m_ctx.get_rules(); diff --git a/src/muz/spacer/spacer_expand_bnd_generalizer.h b/src/muz/spacer/spacer_expand_bnd_generalizer.h index 91ba3b41700..6d4e68d17b4 100644 --- a/src/muz/spacer/spacer_expand_bnd_generalizer.h +++ b/src/muz/spacer/spacer_expand_bnd_generalizer.h @@ -43,7 +43,6 @@ class lemma_expand_bnd_generalizer : public lemma_generalizer { public: lemma_expand_bnd_generalizer(context &ctx); - ~lemma_expand_bnd_generalizer() override {} void operator()(lemma_ref &lemma) override; diff --git a/src/muz/spacer/spacer_global_generalizer.h b/src/muz/spacer/spacer_global_generalizer.h index 00b85ecd5af..b7ee0575702 100644 --- a/src/muz/spacer/spacer_global_generalizer.h +++ b/src/muz/spacer/spacer_global_generalizer.h @@ -161,7 +161,6 @@ class lemma_global_generalizer : public lemma_generalizer { public: lemma_global_generalizer(context &ctx); - ~lemma_global_generalizer() override {} void operator()(lemma_ref &lemma) override; diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index f65661e9eaf..175c8aeb885 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -1354,8 +1354,6 @@ namespace datalog { m_fparams.m_mbqi = false; } - ~imp() {} - lbool query(expr* query) { m_ctx.ensure_opened(); m_index.reset(); diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.h b/src/muz/transforms/dl_mk_array_eq_rewrite.h index 7f6a29bf3a4..eabef4792f5 100644 --- a/src/muz/transforms/dl_mk_array_eq_rewrite.h +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.h @@ -40,7 +40,6 @@ namespace datalog { public: mk_array_eq_rewrite(context & ctx, unsigned priority); rule_set * operator()(rule_set const & source) override; - ~mk_array_eq_rewrite() override{} }; diff --git a/src/muz/transforms/dl_mk_array_instantiation.h b/src/muz/transforms/dl_mk_array_instantiation.h index 0002f4ecdc5..71924288f9d 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.h +++ b/src/muz/transforms/dl_mk_array_instantiation.h @@ -112,7 +112,6 @@ namespace datalog { public: mk_array_instantiation(context & ctx, unsigned priority); rule_set * operator()(rule_set const & source) override; - ~mk_array_instantiation() override{} }; diff --git a/src/muz/transforms/dl_mk_backwards.cpp b/src/muz/transforms/dl_mk_backwards.cpp index 5d124336b31..5024d4631e3 100644 --- a/src/muz/transforms/dl_mk_backwards.cpp +++ b/src/muz/transforms/dl_mk_backwards.cpp @@ -27,8 +27,6 @@ namespace datalog { m(ctx.get_manager()), m_ctx(ctx) { } - - mk_backwards::~mk_backwards() { } rule_set * mk_backwards::operator()(rule_set const & source) { context& ctx = source.get_context(); diff --git a/src/muz/transforms/dl_mk_backwards.h b/src/muz/transforms/dl_mk_backwards.h index 78a9d2fd095..4d1522e021f 100644 --- a/src/muz/transforms/dl_mk_backwards.h +++ b/src/muz/transforms/dl_mk_backwards.h @@ -27,7 +27,6 @@ namespace datalog { context& m_ctx; public: mk_backwards(context & ctx, unsigned priority = 33000); - ~mk_backwards() override; rule_set * operator()(rule_set const & source) override; }; diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 439cb4540af..0072f00781b 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -146,8 +146,6 @@ namespace datalog { m_dst(nullptr) {} - ~expand_mkbv_cfg() {} - void set_src(rule_set const* src) { m_src = src; } void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } diff --git a/src/muz/transforms/dl_mk_elim_term_ite.cpp b/src/muz/transforms/dl_mk_elim_term_ite.cpp index 753b82cea55..98bd3c41a97 100644 --- a/src/muz/transforms/dl_mk_elim_term_ite.cpp +++ b/src/muz/transforms/dl_mk_elim_term_ite.cpp @@ -80,8 +80,6 @@ namespace datalog { rm(ctx.get_rule_manager()), m_ground(m) {} - mk_elim_term_ite::~mk_elim_term_ite() {} - /** \brief map free variables in e to ground, fresh, constants m_ground is reset on every new rule so it is safe to assume diff --git a/src/muz/transforms/dl_mk_elim_term_ite.h b/src/muz/transforms/dl_mk_elim_term_ite.h index 98acd12f15f..d42a7d77370 100644 --- a/src/muz/transforms/dl_mk_elim_term_ite.h +++ b/src/muz/transforms/dl_mk_elim_term_ite.h @@ -35,7 +35,6 @@ namespace datalog { expr_ref ground(expr* e); public: mk_elim_term_ite(context &ctx, unsigned priority); - ~mk_elim_term_ite() override; rule_set * operator()(const rule_set &source) override; }; } diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index 18d67f549c5..955c3ca921b 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -58,8 +58,6 @@ namespace datalog { m_inner_ctx.updt_params(params); } - mk_karr_invariants::~mk_karr_invariants() { } - void matrix::display_row( std::ostream& out, vector const& row, rational const& b, bool is_eq) { for (unsigned j = 0; j < row.size(); ++j) { diff --git a/src/muz/transforms/dl_mk_karr_invariants.h b/src/muz/transforms/dl_mk_karr_invariants.h index ac1015d147d..9ba579a603f 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.h +++ b/src/muz/transforms/dl_mk_karr_invariants.h @@ -62,8 +62,6 @@ namespace datalog { rule_set* update_rules(rule_set const& src); public: mk_karr_invariants(context & ctx, unsigned priority); - - ~mk_karr_invariants() override; rule_set * operator()(rule_set const & source) override; diff --git a/src/muz/transforms/dl_mk_loop_counter.cpp b/src/muz/transforms/dl_mk_loop_counter.cpp index 91aaa8d8b11..a20224eb29a 100644 --- a/src/muz/transforms/dl_mk_loop_counter.cpp +++ b/src/muz/transforms/dl_mk_loop_counter.cpp @@ -30,8 +30,6 @@ namespace datalog { m_refs(m) { } - mk_loop_counter::~mk_loop_counter() { } - app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { expr_ref_vector args(m); func_decl* new_fn, *old_fn = fn->get_decl(); diff --git a/src/muz/transforms/dl_mk_loop_counter.h b/src/muz/transforms/dl_mk_loop_counter.h index 067945caff0..b2758df67a6 100644 --- a/src/muz/transforms/dl_mk_loop_counter.h +++ b/src/muz/transforms/dl_mk_loop_counter.h @@ -35,7 +35,6 @@ namespace datalog { app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); - ~mk_loop_counter() override; rule_set * operator()(rule_set const & source) override; diff --git a/src/muz/transforms/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp index 59529e63014..afadfd6c3d5 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.cpp +++ b/src/muz/transforms/dl_mk_magic_symbolic.cpp @@ -63,8 +63,6 @@ namespace datalog { m(ctx.get_manager()), m_ctx(ctx) { } - - mk_magic_symbolic::~mk_magic_symbolic() { } rule_set * mk_magic_symbolic::operator()(rule_set const & source) { if (!m_ctx.magic()) { diff --git a/src/muz/transforms/dl_mk_magic_symbolic.h b/src/muz/transforms/dl_mk_magic_symbolic.h index 208f79f4579..ac29194bcc3 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.h +++ b/src/muz/transforms/dl_mk_magic_symbolic.h @@ -29,7 +29,6 @@ namespace datalog { app_ref mk_query(app* q); public: mk_magic_symbolic(context & ctx, unsigned priority = 33037); - ~mk_magic_symbolic() override; rule_set * operator()(rule_set const & source) override; }; diff --git a/src/qe/mbp/mbp_arith.cpp b/src/qe/mbp/mbp_arith.cpp index 255c4f8144e..ef4428fa74c 100644 --- a/src/qe/mbp/mbp_arith.cpp +++ b/src/qe/mbp/mbp_arith.cpp @@ -43,8 +43,6 @@ namespace mbp { imp(ast_manager& m) : m(m), a(m) {} - ~imp() {} - void insert_mul(expr* x, rational const& v, obj_map& ts) { rational w; if (ts.find(x, w)) diff --git a/src/qe/mbp/mbp_arrays.cpp b/src/qe/mbp/mbp_arrays.cpp index bf3ad08edc6..fa7ec8cb5d9 100644 --- a/src/qe/mbp/mbp_arrays.cpp +++ b/src/qe/mbp/mbp_arrays.cpp @@ -1058,7 +1058,6 @@ namespace mbp { scoped_ptr m_var; imp(ast_manager& m): m(m), a(m), m_stores(m) {} - ~imp() {} bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index e5e5309f357..6ecaa5de226 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -187,8 +187,6 @@ class term { m_is_peq = is_partial_eq(to_app(m_expr)); } - ~term() {} - class parents { term const &t; diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 8a6f910bc26..37cf62c615b 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -558,7 +558,6 @@ namespace qe { vector
m_divs; public: div_rewriter_cfg(nlqsat& s): m(s.m), a(s.m), m_zero(a.mk_real(0), m) {} - ~div_rewriter_cfg() {} br_status reduce_app(func_decl* f, unsigned sz, expr* const* args, expr_ref& result, proof_ref& pr) { rational r1, r(1); if (a.is_div(f) && sz == 2 && a.is_numeral(args[0], r1) && a.is_numeral(args[1], r) && !r.is_zero()) { diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index a331bef8e57..7393dc1bb28 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -58,7 +58,6 @@ namespace sat { m_stack(std::move(stack)) { m_counter = ++counter; } - ~elim_stack() { } void inc_ref() { ++m_refcount; } void dec_ref() { if (0 == --m_refcount) { dealloc(this); } } elim_stackv const& stack() const { return m_stack; } diff --git a/src/sat/sat_mus.cpp b/src/sat/sat_mus.cpp index cbbf01b2068..5b7542cb205 100644 --- a/src/sat/sat_mus.cpp +++ b/src/sat/sat_mus.cpp @@ -24,8 +24,6 @@ Module Name: namespace sat { mus::mus(solver& s):s(s), m_is_active(false), m_max_num_restarts(UINT_MAX) {} - - mus::~mus() {} void mus::reset() { m_core.reset(); diff --git a/src/sat/sat_mus.h b/src/sat/sat_mus.h index 5f67f6acd80..672e90185ff 100644 --- a/src/sat/sat_mus.h +++ b/src/sat/sat_mus.h @@ -30,7 +30,6 @@ namespace sat { public: mus(solver& s); - ~mus(); lbool operator()(); bool is_active() const { return m_is_active; } model const& get_model() const { return m_model; } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 55d39b25205..a65ca686d70 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -153,7 +153,6 @@ namespace arith { public: sls(solver& s); - ~sls() override {} void set(sat::ddfw* d); void init_search() override; void finish_search() override; diff --git a/src/sat/smt/array_solver.cpp b/src/sat/smt/array_solver.cpp index 7a8f31f1e40..1343577d99b 100644 --- a/src/sat/smt/array_solver.cpp +++ b/src/sat/smt/array_solver.cpp @@ -88,8 +88,6 @@ namespace array { m_constraint->initialize(m_constraint.get(), this); } - solver::~solver() {} - sat::check_result solver::check() { force_push(); // flet _is_redundant(m_is_redundant, true); diff --git a/src/sat/smt/array_solver.h b/src/sat/smt/array_solver.h index 8dc6e4e8446..0a7c854fdd5 100644 --- a/src/sat/smt/array_solver.h +++ b/src/sat/smt/array_solver.h @@ -268,7 +268,6 @@ namespace array { void validate_extensionality(euf::enode* s, euf::enode* t) const; public: solver(euf::solver& ctx, theory_id id); - ~solver() override; bool is_external(bool_var v) override { return false; } void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override {} void asserted(literal l) override {} diff --git a/src/sat/smt/intblast_solver.h b/src/sat/smt/intblast_solver.h index 34f876be6a6..ee8d6fb1935 100644 --- a/src/sat/smt/intblast_solver.h +++ b/src/sat/smt/intblast_solver.h @@ -103,8 +103,6 @@ namespace intblast { public: solver(euf::solver& ctx); - - ~solver() override {} lbool check_axiom(sat::literal_vector const& lits); lbool check_core(sat::literal_vector const& lits, euf::enode_pair_vector const& eqs); diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp index f251d01ae01..b77a389270e 100644 --- a/src/smt/arith_eq_adapter.cpp +++ b/src/smt/arith_eq_adapter.cpp @@ -67,8 +67,6 @@ namespace smt { m_ge(ge) { } - ~arith_eq_relevancy_eh() override {} - void operator()(relevancy_propagator & rp) override { if (!rp.is_relevant(m_n1)) return; diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 4139b310971..7a567d5a94b 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -223,8 +223,6 @@ namespace smt { m_sort(s) { } - ~node() {} - unsigned get_id() const { return m_id; } sort* get_sort() const { return m_sort; } diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index f6e3c46592f..8dea2842ff6 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -48,7 +48,6 @@ namespace smt { expr * m_target; public: simple_relevancy_eh(expr * t):m_target(t) {} - ~simple_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; @@ -61,7 +60,6 @@ namespace smt { expr * m_target; public: pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {} - ~pair_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 6ed4fc41f27..d469097d84a 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -134,7 +134,6 @@ namespace smt { unsigned_vector m_limit; public: exclusion_table(ast_manager& m): m(m), m_lhs(m), m_rhs(m) {} - ~exclusion_table() { } bool empty() const { return m_table.empty(); } void update(expr* e, expr* r); bool contains(expr* e, expr* r) const; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index ed645dae0b2..8995049f087 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -38,7 +38,6 @@ class simplifier_solver : public solver { model_reconstruction_trail m_reconstruction_trail; bool m_updated = false; dep_expr_state(simplifier_solver& s) :dependent_expr_state(s.m), s(s), m_reconstruction_trail(s.m, m_trail) {} - ~dep_expr_state() override {} unsigned qtail() const override { return s.m_fmls.size(); } dependent_expr const& operator[](unsigned i) override { return s.m_fmls[i]; } void update(unsigned i, dependent_expr const& j) override { diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 605a32ae19c..31c4ad9f321 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -28,8 +28,6 @@ solver_na2as::solver_na2as(ast_manager & m): m_assumptions(m) { } -solver_na2as::~solver_na2as() {} - void solver_na2as::assert_expr_core2(expr * t, expr * a) { if (a == nullptr) { assert_expr_core(t); diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 81a58fb39c0..fd0a7dc5e63 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -30,7 +30,6 @@ class solver_na2as : public solver { void restore_assumptions(unsigned old_sz); public: solver_na2as(ast_manager & m); - ~solver_na2as() override; void assert_expr_core2(expr * t, expr * a) override; diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index c791841ec2f..31ca836acc3 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -79,8 +79,6 @@ class nla2bv_tactic : public tactic { m_default_bv_size = m_num_bits = p.get_uint("nla2bv_bv_size", 4); } - ~imp() {} - void updt_params(params_ref const& p) {} void operator()(goal & g, model_converter_ref & mc) { diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp index f6db3c30e46..93491f7b253 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.cpp +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -41,8 +41,6 @@ struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg { bv_bound_chk_rewriter_cfg(ast_manager & m, bv_bound_chk_stats& stats) : m_m(m), m_b_rw(m), m_stats(stats) {} - ~bv_bound_chk_rewriter_cfg() {} - void updt_params(params_ref const & _p) { rewriter_params p(_p); m_bv_ineq_consistency_test_max = p.bv_ineq_consistency_test_max(); @@ -146,8 +144,6 @@ class bv_bound_chk_tactic::imp { imp(ast_manager & m, params_ref const & p, bv_bound_chk_stats& stats) : m_rw(m, p, stats) { } - virtual ~imp() = default; - ast_manager& m() { return m_rw.m(); } void operator()(goal_ref const & g) { @@ -164,7 +160,7 @@ class bv_bound_chk_tactic::imp { m_rw.m_cfg.cleanup(); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) { m_rw.updt_params(p); } diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 5f856800e8b..6ddc49c7f18 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -48,8 +48,6 @@ namespace { r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false"); } - ~bv_bounds_simplifier() override {} - bool assert_expr(expr * t, bool sign) override { return assert_expr_core(t, sign); } diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index aa4358e9cb1..e3cd7893a84 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -144,9 +144,8 @@ struct ctx_simplify_tactic::imp { }; struct cache_cell { - expr * m_from; - cached_result * m_result; - cache_cell():m_from(nullptr), m_result(nullptr) {} + expr * m_from = nullptr; + cached_result * m_result = nullptr; }; ast_manager & m; diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 9ad12d504dc..c5cba0b9a08 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -117,8 +117,6 @@ class symmetry_reduce_tactic::imp { m_replace = mk_default_expr_replacer(m, false); } - ~imp() {} - void operator()(goal & g) { if (g.inconsistent()) return; @@ -202,10 +200,10 @@ class symmetry_reduce_tactic::imp { // a |-> c1, b |-> c2 |-> c // struct u_pair { - unsigned m_first; - unsigned m_second; + unsigned m_first = 0; + unsigned m_second = 0; u_pair(unsigned f, unsigned s) : m_first(f), m_second(s) {} - u_pair(): m_first(0), m_second(0) {} + u_pair() = default; struct hash { unsigned operator()(u_pair const& p) const { @@ -324,8 +322,6 @@ class symmetry_reduce_tactic::imp { class parents { app_parents m_use_funs; public: - parents() {} - app_parents const& get_parents() { return m_use_funs; } void operator()(app* n) { diff --git a/src/tactic/probe.h b/src/tactic/probe.h index 4d1af66a8a9..452f2cb7058 100644 --- a/src/tactic/probe.h +++ b/src/tactic/probe.h @@ -40,10 +40,9 @@ class probe { }; private: - unsigned m_ref_count; + unsigned m_ref_count = 0; public: - probe():m_ref_count(0) {} virtual ~probe() = default; void inc_ref() { ++m_ref_count; } diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 179a42ab857..f7dcfb3ea02 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -146,8 +146,6 @@ tactic * mk_trace_tactic(char const * tag) { class fail_if_undecided_tactic : public skip_tactic { public: - fail_if_undecided_tactic() {} - void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (!in->is_decided()) throw tactic_exception("undecided"); diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 652bf8130bd..e62f93ecf06 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -32,10 +32,8 @@ class progress_callback; typedef ptr_buffer goal_buffer; class tactic : public user_propagator::core { - unsigned m_ref_count; + unsigned m_ref_count = 0; public: - tactic():m_ref_count(0) {} - void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } diff --git a/src/util/chashtable.h b/src/util/chashtable.h index 394e5998c4f..da7cb5b316e 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -609,7 +609,7 @@ class cmap { struct key_value { Key m_key; Value m_value; - key_value() {} + key_value() = default; key_value(Key const & k):m_key(k) {} key_value(Key const & k, Value const & v):m_key(k), m_value(v) {} }; diff --git a/src/util/optional.h b/src/util/optional.h index 8e515f4be7e..c2a0fd3efb3 100644 --- a/src/util/optional.h +++ b/src/util/optional.h @@ -30,7 +30,7 @@ class optional { } public: - optional() {} + optional() = default; explicit optional(const T & val) { m_obj = alloc(T, val); @@ -116,13 +116,13 @@ class optional { */ template class optional { - T * m_ptr; + T * m_ptr = nullptr; static optional m_undef; public: - optional():m_ptr(nullptr) {} + optional() = default; explicit optional(T * val):m_ptr(val) {} diff --git a/src/util/params.cpp b/src/util/params.cpp index 700a5310928..3440673b787 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -366,12 +366,11 @@ class params { }; typedef std::pair entry; svector m_entries; - std::atomic m_ref_count; + std::atomic m_ref_count = 0; void del_value(entry & e); void del_values(); public: - params():m_ref_count(0) {} ~params() { reset(); } diff --git a/src/util/queue.h b/src/util/queue.h index 059dea573c0..771ebbcd4cb 100644 --- a/src/util/queue.h +++ b/src/util/queue.h @@ -23,13 +23,10 @@ Module Name: template class queue { vector m_elems; - unsigned m_head; - unsigned m_capacity; + unsigned m_head = 0; + unsigned m_capacity = 0; public: - - queue(): m_head(0), m_capacity(0) {} - void push(T const& t) { m_elems.push_back(t); } bool empty() const { diff --git a/src/util/rational.h b/src/util/rational.h index e9924bca751..61569f40c99 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -41,8 +41,8 @@ class rational { ADD_INITIALIZER('rational::initialize();') ADD_FINALIZER('rational::finalize();') */ - rational() {} - + rational() = default; + rational(rational const & r) { m().set(m_val, r.m_val); } rational(rational&&) = default; diff --git a/src/util/s_integer.h b/src/util/s_integer.h index 6ddd2bf6716..b102d6d4b58 100644 --- a/src/util/s_integer.h +++ b/src/util/s_integer.h @@ -21,7 +21,7 @@ Revision History: #include "util/rational.h" class s_integer { - int m_val; + int m_val = 0; static s_integer m_zero; static s_integer m_one; static s_integer m_minus_one; @@ -41,7 +41,7 @@ class s_integer { std::string to_string() const; public: - s_integer(): m_val(0) {} + s_integer() = default; explicit s_integer(int n):m_val(n) {} struct i64 {}; explicit s_integer(int64_t i, i64):m_val(static_cast(i)) {} diff --git a/src/util/small_object_allocator.h b/src/util/small_object_allocator.h index 05fe32ef0e6..646a203c154 100644 --- a/src/util/small_object_allocator.h +++ b/src/util/small_object_allocator.h @@ -29,9 +29,8 @@ class small_object_allocator { static const unsigned NUM_SLOTS = (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); struct chunk { chunk* m_next{ nullptr }; - char* m_curr{ nullptr }; + char* m_curr = m_data; char m_data[CHUNK_SIZE]; - chunk():m_curr(m_data) {} }; chunk * m_chunks[NUM_SLOTS]; void * m_free_list[NUM_SLOTS]; diff --git a/src/util/vector.h b/src/util/vector.h index 31ced7f4bab..79e51d545ef 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -1170,7 +1170,7 @@ class vector { template class ptr_vector : public vector { public: - ptr_vector():vector() {} + ptr_vector() = default; ptr_vector(unsigned s):vector(s) {} ptr_vector(unsigned s, T * elem):vector(s, elem) {} ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} @@ -1190,7 +1190,7 @@ class ptr_vector : public vector { template class svector : public vector { public: - svector():vector() {} + svector() = default; svector(SZ s):vector(s) {} svector(SZ s, T const & elem):vector(s, elem) {} svector(SZ s, T const * data):vector(s, data) {} From 1ace3d0cf3f1a0f1c1c4a39a581674ebe7fa5306 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2024 10:37:19 -0700 Subject: [PATCH 185/414] fix #7372 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_config.cpp | 1 + src/util/gparams.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 73516f66d12..ac9a688788b 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -158,6 +158,7 @@ namespace sat { m_lookahead_delta_fraction = p.lookahead_delta_fraction(); m_lookahead_use_learned = p.lookahead_use_learned(); if (m_lookahead_delta_fraction < 0 || m_lookahead_delta_fraction > 1.0) { + verbose_stream() << m_lookahead_delta_fraction << "\n"; throw sat_param_exception("invalid value for delta fraction. It should be a number in the interval 0 to 1"); } diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 839d02ab05a..171cae60467 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -22,6 +22,7 @@ Module Name: #include "util/mutex.h" #include "util/region.h" #include "util/map.h" +#include "util/rational.h" static DECLARE_MUTEX(gparams_mux); @@ -351,8 +352,8 @@ struct gparams::imp { ps.set_uint(param_name, static_cast(val)); } else if (k == CPK_DOUBLE) { - char * aux; - double val = strtod(value, &aux); + rational r(value); + double val = r.get_double(); ps.set_double(param_name, val); } else if (k == CPK_BOOL) { From 99a9a4af03ed34fd4bd13b9881ca5910646d4c54 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2024 10:37:50 -0700 Subject: [PATCH 186/414] fix #7372 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index ac9a688788b..73516f66d12 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -158,7 +158,6 @@ namespace sat { m_lookahead_delta_fraction = p.lookahead_delta_fraction(); m_lookahead_use_learned = p.lookahead_use_learned(); if (m_lookahead_delta_fraction < 0 || m_lookahead_delta_fraction > 1.0) { - verbose_stream() << m_lookahead_delta_fraction << "\n"; throw sat_param_exception("invalid value for delta fraction. It should be a number in the interval 0 to 1"); } From 0ba306e7b320901540162bfd7cf70c7b7399be17 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Sep 2024 12:27:13 +0300 Subject: [PATCH 187/414] y --- src/api/python/z3/z3.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index bd38d5baa68..25dc341b84a 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7926,9 +7926,12 @@ def _global_on_model(ctx): class Optimize(Z3PPObject): """Optimize API provides methods for solving using objective functions and weighted soft constraints""" - def __init__(self, ctx=None): + def __init__(self, optimize=None, ctx=None): self.ctx = _get_ctx(ctx) - self.optimize = Z3_mk_optimize(self.ctx.ref()) + if optimize is None: + self.optimize = Z3_mk_optimize(self.ctx.ref()) + else: + self.optimize = optimize self._on_models_id = None Z3_optimize_inc_ref(self.ctx.ref(), self.optimize) From 48712b4f60e090b3e95bb71063cbfe37c1c1d353 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Sep 2024 16:13:15 +0300 Subject: [PATCH 188/414] Add initial value setting for variables in Z3 API, solver, and optimize modules --- src/api/api_opt.cpp | 17 ++++++++++- src/api/api_solver.cpp | 18 ++++++++++++ src/api/c++/z3++.h | 22 ++++++++++++++ src/api/python/z3/z3.py | 14 +++++++++ src/api/z3_api.h | 12 ++++++++ src/api/z3_optimization.h | 12 ++++++++ src/math/lp/lar_solver.cpp | 18 ++++++++++++ src/math/lp/lar_solver.h | 1 + src/opt/opt_context.cpp | 19 ++++++++----- src/opt/opt_context.h | 4 +++ src/opt/opt_solver.h | 1 + src/sat/sat_solver/inc_sat_solver.cpp | 4 +++ src/sat/sat_solver/sat_smt_solver.cpp | 5 ++++ src/sat/smt/euf_solver.cpp | 25 ++++++++++++++++ src/sat/smt/euf_solver.h | 2 ++ src/smt/smt_context.cpp | 41 +++++++++++++++++++++++++++ src/smt/smt_context.h | 13 +++++++++ src/smt/smt_kernel.cpp | 4 +++ src/smt/smt_kernel.h | 2 ++ src/smt/smt_solver.cpp | 4 +++ src/smt/smt_theory.h | 4 +++ src/smt/tactic/smt_tactic_core.cpp | 7 +++++ src/smt/theory_arith.h | 1 + src/smt/theory_arith_aux.h | 15 ++++++++++ src/smt/theory_lra.cpp | 19 ++++++++++++- src/smt/theory_lra.h | 2 ++ src/solver/combined_solver.cpp | 5 ++++ src/solver/simplifier_solver.cpp | 2 ++ src/solver/tactic2solver.cpp | 4 +++ src/tactic/tactical.cpp | 5 ++++ src/tactic/user_propagator_base.h | 4 +++ 31 files changed, 297 insertions(+), 9 deletions(-) diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 5854bdaca17..a9a0b3230b5 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -459,6 +459,21 @@ extern "C" { Z3_CATCH; } - + void Z3_API Z3_optimize_set_initial_value(Z3_context c, Z3_optimize o, Z3_ast var, Z3_ast value) { + Z3_TRY; + LOG_Z3_optimize_set_initial_value(c, o, var, value); + RESET_ERROR_CODE(); + if (to_expr(var)->get_sort() != to_expr(value)->get_sort()) { + SET_ERROR_CODE(Z3_INVALID_USAGE, "variable and value should have same sort"); + return; + } + ast_manager& m = mk_c(c)->m(); + if (!m.is_value(to_expr(value))) { + SET_ERROR_CODE(Z3_INVALID_USAGE, "a proper value was not supplied"); + return; + } + to_optimize_ptr(o)->initialize_value(to_expr(var), to_expr(value)); + Z3_CATCH; + } }; diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index f18edd96b2a..f226529de5a 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -1143,5 +1143,23 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + void Z3_API Z3_solver_set_initial_value(Z3_context c, Z3_solver s, Z3_ast var, Z3_ast value) { + Z3_TRY; + LOG_Z3_solver_set_initial_value(c, s, var, value); + RESET_ERROR_CODE(); + if (to_expr(var)->get_sort() != to_expr(value)->get_sort()) { + SET_ERROR_CODE(Z3_INVALID_USAGE, "variable and value should have same sort"); + return; + } + ast_manager& m = mk_c(c)->m(); + if (!m.is_value(to_expr(value))) { + SET_ERROR_CODE(Z3_INVALID_USAGE, "a proper value was not supplied"); + return; + } + to_solver_ref(s)->user_propagate_initialize_value(to_expr(var), to_expr(value)); + Z3_CATCH; + } + + }; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 81d5bcaa9b3..bb148d71288 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2865,6 +2865,17 @@ namespace z3 { check_error(); return result; } + void set_initial_value(expr const& var, expr const& value) { + Z3_solver_set_initial_value(ctx(), m_solver, var, value); + check_error(); + } + void set_initial_value(expr const& var, int i) { + set_initial_value(var, ctx().num_val(i, var.get_sort())); + } + void set_initial_value(expr const& var, bool b) { + set_initial_value(var, ctx().bool_val(b)); + } + expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, solver const & s); @@ -3330,6 +3341,17 @@ namespace z3 { handle add(expr const& e, unsigned weight) { return add_soft(e, weight); } + void set_initial_value(expr const& var, expr const& value) { + Z3_optimize_set_initial_value(ctx(), m_opt, var, value); + check_error(); + } + void set_initial_value(expr const& var, int i) { + set_initial_value(var, ctx().num_val(i, var.get_sort())); + } + void set_initial_value(expr const& var, bool b) { + set_initial_value(var, ctx().bool_val(b)); + } + handle maximize(expr const& e) { return handle(Z3_optimize_maximize(ctx(), m_opt, e)); } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 25dc341b84a..5c2a3599522 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7353,6 +7353,13 @@ def trail_levels(self): Z3_solver_get_levels(self.ctx.ref(), self.solver, trail.vector, len(trail), levels) return trail, levels + def set_initial_value(self, var, value): + """initialize the solver's state by setting the initial value of var to value + """ + s = var.sort() + value = s.cast(value) + Z3_solver_set_initial_value(self.ctx.ref(), self.solver, var.ast, value.ast) + def trail(self): """Return trail of the solver state after a check() call. """ @@ -8032,6 +8039,13 @@ def asoft(a): return [asoft(a) for a in arg] return asoft(arg) + def set_initial_value(self, var, value): + """initialize the solver's state by setting the initial value of var to value + """ + s = var.sort() + value = s.cast(value) + Z3_optimize_set_initial_value(self.ctx.ref(), self.optimize, var.ast, value.ast) + def maximize(self, arg): """Add objective function to maximize.""" return OptimizeObjective( diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 1f0daf8b505..fdc25ef46cf 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7241,6 +7241,18 @@ extern "C" { bool Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback cb, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); + + /** + \brief provide an initialization hint to the solver. The initialization hint is used to calibrate an initial value of the expression that + represents a variable. If the variable is Boolean, the initial phase is set according to \c value. If the variable is an integer or real, + the initial Simplex tableau is recalibrated to attempt to follow the value assignment. + + def_API('Z3_solver_set_initial_value', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(AST))) + */ + + void Z3_API Z3_solver_set_initial_value(Z3_context c, Z3_solver s, Z3_ast var, Z3_ast value); + + /** \brief Check whether the assertions in a given solver are consistent or not. diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index 8bf0e9da50a..ad55cab1d62 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -139,6 +139,18 @@ extern "C" { */ void Z3_API Z3_optimize_pop(Z3_context c, Z3_optimize d); + /** + \brief provide an initialization hint to the solver. + The initialization hint is used to calibrate an initial value of the expression that + represents a variable. If the variable is Boolean, the initial phase is set + according to \c value. If the variable is an integer or real, + the initial Simplex tableau is recalibrated to attempt to follow the value assignment. + + def_API('Z3_optimize_set_initial_value', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(AST))) + */ + + void Z3_API Z3_optimize_set_initial_value(Z3_context c, Z3_optimize o, Z3_ast var, Z3_ast value); + /** \brief Check consistency and produce optimal values. \param c - context diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9272e0298ae..b0f153e465f 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2081,6 +2081,24 @@ namespace lp { lpvar lar_solver::to_column(unsigned ext_j) const { return m_var_register.external_to_local(ext_j); } + + bool lar_solver::move_lpvar_to_value(lpvar j, mpq const& value) { + if (is_base(j)) + return false; + + impq ivalue(value); + auto& lcs = m_mpq_lar_core_solver; + auto& slv = m_mpq_lar_core_solver.m_r_solver; + + if (slv.column_has_upper_bound(j) && lcs.m_r_upper_bounds()[j] < ivalue) + return false; + if (slv.column_has_lower_bound(j) && lcs.m_r_lower_bounds()[j] > ivalue) + return false; + + set_value_for_nbasic_column(j, ivalue); + return true; + } + bool lar_solver::tighten_term_bounds_by_delta(lpvar j, const impq& delta) { SASSERT(column_has_term(j)); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index f223b5cc5a4..c58fe7917d7 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -623,6 +623,7 @@ class lar_solver : public column_namer { lp_status find_feasible_solution(); void move_non_basic_columns_to_bounds(); bool move_non_basic_column_to_bounds(unsigned j); + bool move_lpvar_to_value(lpvar j, mpq const& value); inline bool r_basis_has_inf_int() const { for (unsigned j : r_basis()) { if (column_is_int(j) && !column_value_is_int(j)) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 1b57a7200aa..90bc0ddd640 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -58,8 +58,9 @@ namespace opt { } void context::scoped_state::pop() { - m_hard.resize(m_hard_lim.back()); - m_asms.resize(m_asms_lim.back()); + m_hard.shrink(m_hard_lim.back()); + m_asms.shrink(m_asms_lim.back()); + m_values.shrink(m_values_lim.back()); unsigned k = m_objectives_term_trail_lim.back(); while (m_objectives_term_trail.size() > k) { unsigned idx = m_objectives_term_trail.back(); @@ -79,6 +80,7 @@ namespace opt { m_objectives_lim.pop_back(); m_hard_lim.pop_back(); m_asms_lim.pop_back(); + m_values_lim.pop_back(); } void context::scoped_state::add(expr* hard) { @@ -306,13 +308,11 @@ namespace opt { if (contains_quantifiers()) { warning_msg("optimization with quantified constraints is not supported"); } -#if 0 - if (is_qsat_opt()) { - return run_qsat_opt(); - } -#endif solver& s = get_solver(); s.assert_expr(m_hard_constraints); + for (auto const& [var, value] : m_scoped_state.m_values) { + s.user_propagate_initialize_value(var, value); + } opt_params optp(m_params); symbol pri = optp.priority(); @@ -697,6 +697,11 @@ namespace opt { } } + void context::initialize_value(expr* var, expr* value) { + m_scoped_state.m_values.push_back({expr_ref(var, m), expr_ref(value, m)}); + } + + /** * Set the solver to the SAT core. * It requres: diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 4e791531e3f..845fd39689a 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -140,12 +140,14 @@ namespace opt { unsigned_vector m_objectives_lim; unsigned_vector m_objectives_term_trail; unsigned_vector m_objectives_term_trail_lim; + unsigned_vector m_values_lim; map_id m_indices; public: expr_ref_vector m_hard; expr_ref_vector m_asms; vector m_objectives; + vector> m_values; scoped_state(ast_manager& m): m(m), @@ -275,6 +277,8 @@ namespace opt { void add_offset(unsigned id, rational const& o) override; + void initialize_value(expr* var, expr* value); + void register_on_model(on_model_t& ctx, std::function& on_model) { m_on_model_ctx = ctx; m_on_model_eh = on_model; diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 2682fca0951..66835df489d 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -116,6 +116,7 @@ namespace opt { phase* get_phase() override { return m_context.get_phase(); } void set_phase(phase* p) override { m_context.set_phase(p); } void move_to_front(expr* e) override { m_context.move_to_front(e); } + void user_propagate_initialize_value(expr* var, expr* value) override { m_context.user_propagate_initialize_value(var, value); } void set_logic(symbol const& logic); diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 4574d3da384..0129a026e54 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -702,6 +702,10 @@ class inc_sat_solver : public solver { ensure_euf()->user_propagate_register_decide(r); } + void user_propagate_initialize_value(expr* var, expr* value) override { + ensure_euf()->user_propagate_initialize_value(var, value); + } + private: diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 19b10eb3e91..1c141a80115 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -577,6 +577,11 @@ class sat_smt_solver : public solver { ensure_euf()->user_propagate_register_decide(r); } + void user_propagate_initialize_value(expr* var, expr* value) override { + ensure_euf()->user_propagate_initialize_value(var, value); + } + + private: void add_assumption(expr* a) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index b866990af43..0a58d7d1dfe 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -1256,6 +1256,31 @@ namespace euf { add_solver(m_user_propagator); } + void solver::user_propagate_initialize_value(expr* var, expr* value) { + if (m.is_bool(var)) { + auto lit = expr2literal(var); + if (lit == sat::null_literal) { + IF_VERBOSE(5, verbose_stream() << "no literal associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + if (m.is_true(value)) + s().set_phase(lit); + else if (m.is_false(value)) + s().set_phase(~lit); + else + IF_VERBOSE(5, verbose_stream() << "malformed value " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + auto* th = m_id2solver.get(var->get_sort()->get_family_id(), nullptr); + if (!th) { + IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + // th->initialize_value(var, value); + IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + } + + bool solver::watches_fixed(enode* n) const { return m_user_propagator && m_user_propagator->has_fixed() && n->get_th_var(m_user_propagator->get_id()) != null_theory_var; } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 1b13d513796..8c436b942ff 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -564,6 +564,8 @@ namespace euf { m_user_propagator->add_expr(e); } + void user_propagate_initialize_value(expr* var, expr* value); + // solver factory ::solver* mk_solver() { return m_mk_solver(); } void set_mk_solver(std::function<::solver*(void)>& mk) { m_mk_solver = mk; } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index c11084e5669..163da9b1683 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -2914,6 +2914,44 @@ namespace smt { register_plugin(m_user_propagator); } + void context::user_propagate_initialize_value(expr* var, expr* value) { + m_values.push_back({expr_ref(var, m), expr_ref(value, m)}); + push_trail(push_back_vector(m_values)); + } + + void context::initialize_value(expr* var, expr* value) { + IF_VERBOSE(10, verbose_stream() << "context initialize " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + sort* s = var->get_sort(); + ensure_internalized(var); + + if (m.is_bool(s)) { + auto v = get_bool_var_of_id_option(var->get_id()); + if (v == null_bool_var) { + IF_VERBOSE(5, verbose_stream() << "Boolean variable has no literal " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + m_bdata[v].m_phase_available = true; + if (m.is_true(value)) + m_bdata[v].m_phase = true; + else if (m.is_false(value)) + m_bdata[v].m_phase = false; + else + IF_VERBOSE(5, verbose_stream() << "Boolean value is not constant " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + + if (!e_internalized(var)) + return; + enode* n = get_enode(var); + theory* th = m_theories.get_plugin(s->get_family_id()); + if (!th) { + IF_VERBOSE(5, verbose_stream() << "No theory is attached to variable " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + th->initialize_value(var, value); + + } + bool context::watches_fixed(enode* n) const { return m_user_propagator && m_user_propagator->has_fixed() && n->get_th_var(m_user_propagator->get_family_id()) != null_theory_var; } @@ -3756,6 +3794,9 @@ namespace smt { TRACE("search", display(tout); display_enodes_lbls(tout);); TRACE("search_detail", m_asserted_formulas.display(tout);); init_search(); + for (auto const& [var, value] : m_values) + initialize_value(var, value); + flet l(m_searching, true); TRACE("after_init_search", display(tout);); IF_VERBOSE(2, verbose_stream() << "(smt.searching)\n";); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index da7cde7e76c..afbfd0e853a 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -123,6 +123,7 @@ namespace smt { unsigned m_par_index = 0; bool m_internalizing_assertions = false; + // ----------------------------------- // // Equality & Uninterpreted functions @@ -246,6 +247,16 @@ namespace smt { vector m_th_case_split_sets; u_map< vector > m_literal2casesplitsets; // returns the case split literal sets that a literal participates in + + // ---------------------------------- + // + // Value initialization + // + // ---------------------------------- + vector> m_values; + void initialize_value(expr* var, expr* value); + + // ----------------------------------- // // Accessors @@ -1777,6 +1788,8 @@ namespace smt { m_user_propagator->register_decide(r); } + void user_propagate_initialize_value(expr* var, expr* value); + bool watches_fixed(enode* n) const; bool has_split_candidate(bool_var& var, bool& is_pos); diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 74f0bded65d..2d6c2953273 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -305,5 +305,9 @@ namespace smt { void kernel::user_propagate_register_decide(user_propagator::decide_eh_t& r) { m_imp->m_kernel.user_propagate_register_decide(r); } + + void kernel::user_propagate_initialize_value(expr* var, expr* value) { + m_imp->m_kernel.user_propagate_initialize_value(var, value); + } }; diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index eec74f8b1f7..539a3275077 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -322,6 +322,8 @@ namespace smt { void user_propagate_register_decide(user_propagator::decide_eh_t& r); + void user_propagate_initialize_value(expr* var, expr* value); + /** \brief Return a reference to smt::context. This breaks abstractions. diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index f91a3111184..7b9d416f374 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -252,6 +252,10 @@ namespace { m_context.user_propagate_register_decide(c); } + void user_propagate_initialize_value(expr* var, expr* value) override { + m_context.user_propagate_initialize_value(var, value); + } + struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index d0e73cc9296..416d626f738 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -549,6 +549,10 @@ namespace smt { return get_manager().mk_eq(lhs, rhs); } + virtual void initialize_value(expr* var, expr* value) { + IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + } + literal mk_eq(expr * a, expr * b, bool gate_ctx); literal mk_preferred_eq(expr* a, expr* b); diff --git a/src/smt/tactic/smt_tactic_core.cpp b/src/smt/tactic/smt_tactic_core.cpp index 2be2ace5841..89b2b713566 100644 --- a/src/smt/tactic/smt_tactic_core.cpp +++ b/src/smt/tactic/smt_tactic_core.cpp @@ -41,6 +41,7 @@ class smt_tactic : public tactic { smt_params m_params; params_ref m_params_ref; expr_ref_vector m_vars; + vector> m_values; statistics m_stats; smt::kernel* m_ctx = nullptr; symbol m_logic; @@ -344,6 +345,8 @@ class smt_tactic : public tactic { for (expr* v : m_vars) m_ctx->user_propagate_register_expr(v); + for (auto& [var, value] : m_values) + m_ctx->user_propagate_initialize_value(var, value); } void user_propagate_clear() override { @@ -403,6 +406,10 @@ class smt_tactic : public tactic { void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override { m_decide_eh = decide_eh; } + + void user_propagate_initialize_value(expr* var, expr* value) override { + m_values.push_back({expr_ref(var, m), expr_ref(value, m)}); + } }; static tactic * mk_seq_smt_tactic(ast_manager& m, params_ref const & p) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 86c05aec64e..e68f0f53fd5 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -662,6 +662,7 @@ namespace smt { void restart_eh() override; void init_search_eh() override; + void initialize_value(expr* var, expr* value) override; /** \brief True if the assignment may be changed during final check. assume_eqs, check_int_feasibility, diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 8141377c455..acb036d2aa4 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -2249,6 +2249,21 @@ namespace smt { return false; } + template + void theory_arith::initialize_value(expr* var, expr* value) { + theory_var v = expr2var(var); + rational r; + if (!m_util.is_numeral(value, r)) { + IF_VERBOSE(5, verbose_stream() << "numeric constant expected in initialization " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + if (v == null_theory_var) + return; + if (is_base(v)) + return; + update_value(v, inf_numeral(r)); + } + #if 0 /** diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f0a96ddd1ba..8f334276d14 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -154,6 +154,7 @@ class theory_lra::imp { svector m_asserted_atoms; ptr_vector m_not_handled; ptr_vector m_underspecified; + vector> m_values; vector > m_use_list; // bounds where variables are used. // attributes for incremental version: @@ -991,6 +992,16 @@ class theory_lra::imp { return lp().compare_values(vi, k, b->get_value()) ? l_true : l_false; } + void initialize_value(expr* var, expr* value) { + rational r; + if (!a.is_numeral(value, r)) { + IF_VERBOSE(5, verbose_stream() << "numeric constant expected in initialization " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + ctx().push_trail(push_back_vector(m_values)); + m_values.push_back({get_lpvar(var), r}); + } + void new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith", tout << "eq " << v1 << " == " << v2 << "\n";); if (!is_int(v1) && !is_real(v1)) @@ -1409,6 +1420,9 @@ class theory_lra::imp { void init_search_eh() { m_arith_eq_adapter.init_search_eh(); m_num_conflicts = 0; + for (auto const& [v, r] : m_values) + lp().move_lpvar_to_value(v, r); + display(verbose_stream() << "init search\n"); } bool can_get_value(theory_var v) const { @@ -3878,6 +3892,9 @@ void theory_lra::assign_eh(bool_var v, bool is_true) { lbool theory_lra::get_phase(bool_var v) { return m_imp->get_phase(v); } +void theory_lra::initialize_value(expr* var, expr* value) { + m_imp->initialize_value(var, value); +} void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { m_imp->new_eq_eh(v1, v2); } @@ -3912,7 +3929,7 @@ final_check_status theory_lra::final_check_eh() { } bool theory_lra::is_shared(theory_var v) const { return m_imp->is_shared(v); -} +} bool theory_lra::can_propagate() { return m_imp->can_propagate(); } diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index 4c2351c85d5..96988f957c6 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -80,6 +80,8 @@ namespace smt { void apply_sort_cnstr(enode * n, sort * s) override; void init_model(model_generator & m) override; + + void initialize_value(expr* var, expr* value) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void validate_model(proto_model& mdl) override; diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 53aa5675300..051dfd4eb9d 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -394,6 +394,11 @@ class combined_solver : public solver { void user_propagate_clear() override { m_solver2->user_propagate_clear(); } + + void user_propagate_initialize_value(expr* var, expr* value) override { + m_solver2->user_propagate_initialize_value(var, value); + } + }; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 8995049f087..ae1d6b8a289 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -390,6 +390,8 @@ class simplifier_solver : public solver { void user_propagate_register_expr(expr* e) override { m_preprocess_state.freeze(e); s->user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& r) override { s->user_propagate_register_created(r); } void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { s->user_propagate_register_decide(r); } + void user_propagate_initialize_value(expr* var, expr* value) override { m_preprocess_state.freeze(var); s->user_propagate_initialize_value(var, value); } + }; diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 861a8318545..cdabecb2753 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -119,6 +119,10 @@ class tactic2solver : public solver_na2as { void user_propagate_register_expr(expr* e) override { m_tactic->user_propagate_register_expr(e); } + + void user_propagate_initialize_value(expr* var, expr* value) override { + m_tactic->user_propagate_initialize_value(var, value); + } void user_propagate_register_created(user_propagator::created_eh_t& created_eh) override { m_tactic->user_propagate_register_created(created_eh); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 0b8189e8df4..626380913f6 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -213,6 +213,10 @@ class and_then_tactical : public binary_tactical { m_t2->user_propagate_register_decide(decide_eh); } + void user_propagate_initialize_value(expr* var, expr* value) override { + m_t2->user_propagate_initialize_value(var, value); + } + }; tactic * and_then(tactic * t1, tactic * t2) { @@ -884,6 +888,7 @@ class unary_tactical : public tactic { void set_progress_callback(progress_callback * callback) override { m_t->set_progress_callback(callback); } void user_propagate_register_expr(expr* e) override { m_t->user_propagate_register_expr(e); } void user_propagate_clear() override { m_t->user_propagate_clear(); } + void user_propagate_initialize_value(expr* var, expr* value) override { m_t->user_propagate_initialize_value(var, value); } protected: diff --git a/src/tactic/user_propagator_base.h b/src/tactic/user_propagator_base.h index 58904a12d16..968196f63ec 100644 --- a/src/tactic/user_propagator_base.h +++ b/src/tactic/user_propagator_base.h @@ -99,6 +99,10 @@ namespace user_propagator { throw default_exception("clause logging is only supported on the SMT solver"); } + virtual void user_propagate_initialize_value(expr* var, expr* value) { + throw default_exception("value initialization is only supported on the SMT solver"); + } + }; From 0f896503a9ffe875936ec4f80094ea0e587e833a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Sep 2024 16:18:47 +0300 Subject: [PATCH 189/414] Add initial value setting API for solver and optimize contexts and update related function signatures --- RELEASE_NOTES.md | 5 ++++- src/api/z3_api.h | 2 +- src/api/z3_optimization.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b69772dcd5f..cc961a760fe 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -15,7 +15,10 @@ Version 4.13.1 - single-sample cell projection in nlsat was designed by Haokun Li and Bican Xia. - using simple-checker together with and variable ordering supported by qfnra_tactic was developed by Mengyu Zhao (Linxi) and Shaowei Cai. - The projection is described in paper by Haokun Li and Bican Xia, [Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection](https://arxiv.org/abs/2003.00409). The code ported from https://github.com/hybridSMT/hybridSMT.git + The projection is described in paper by Haokun Li and Bican Xia, [Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection](https://arxiv.org/abs/2003.00409). The code ported from https://github.com/hybridSMT/hybridSMT.git + +- Add API for providing hints for the solver/optimize contexts for which initial values to attempt to use for variables. + The new API function are Z3_solver_set_initial_value and Z3_optimize_set_initial_value, respectively. Supply these functions with a Boolean or numeric variable, and a value. The solver will then attempt to use these values in the initial phase of search. The feature is aimed at resolving nearly similar problems, or problems with a predicted model and the intent is that restarting the solver based on a near solution can avoid prune the space of constraints that are initially infeasible. Version 4.13.0 ============== diff --git a/src/api/z3_api.h b/src/api/z3_api.h index fdc25ef46cf..6c3efe7fc36 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7250,7 +7250,7 @@ extern "C" { def_API('Z3_solver_set_initial_value', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(AST))) */ - void Z3_API Z3_solver_set_initial_value(Z3_context c, Z3_solver s, Z3_ast var, Z3_ast value); + void Z3_API Z3_solver_set_initial_value(Z3_context c, Z3_solver s, Z3_ast v, Z3_ast val); /** diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index ad55cab1d62..4e585efb2a0 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -149,7 +149,7 @@ extern "C" { def_API('Z3_optimize_set_initial_value', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(AST))) */ - void Z3_API Z3_optimize_set_initial_value(Z3_context c, Z3_optimize o, Z3_ast var, Z3_ast value); + void Z3_API Z3_optimize_set_initial_value(Z3_context c, Z3_optimize o, Z3_ast v, Z3_ast val); /** \brief Check consistency and produce optimal values. From 1c163dbad2d2d9716a6a9eb7654a6a4387104ee3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Sep 2024 16:41:00 +0300 Subject: [PATCH 190/414] remove output Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_internalize.cpp | 10 +++++++ src/sat/smt/arith_solver.h | 3 ++ src/sat/smt/euf_solver.cpp | 49 ++++++++++++++++++------------- src/sat/smt/euf_solver.h | 2 ++ src/sat/smt/sat_th.h | 2 ++ src/smt/theory_lra.cpp | 1 - 6 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index a389d13b889..9c4e5c9bee1 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -51,6 +51,16 @@ namespace arith { } } + void solver::initialize_value(expr* var, expr* value) { + rational r; + if (!a.is_numeral(value, r)) { + IF_VERBOSE(5, verbose_stream() << "numeric constant expected in initialization " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + return; + } + lp().move_lpvar_to_value(get_lpvar(mk_evar(var)), r); + } + + lpvar solver::get_one(bool is_int) { return add_const(1, is_int ? m_one_var : m_rone_var, is_int); } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 755611474bd..f940af8b4ab 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -485,6 +485,8 @@ namespace arith { bool validate_conflict(); + + public: solver(euf::solver& ctx, theory_id id); ~solver() override; @@ -512,6 +514,7 @@ namespace arith { void internalize(expr* e) override; void eq_internalized(euf::enode* n) override; void apply_sort_cnstr(euf::enode* n, sort* s) override {} + void initialize_value(expr* var, expr* value) override; bool is_shared(theory_var v) const override; lbool get_phase(bool_var v) override; bool include_func_interp(func_decl* f) const override; diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 0a58d7d1dfe..b443d836e8b 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -195,6 +195,30 @@ namespace euf { m_reason_unknown.clear(); for (auto* s : m_solvers) s->init_search(); + + for (auto const& [var, value] : m_initial_values) { + if (m.is_bool(var)) { + auto lit = expr2literal(var); + if (lit == sat::null_literal) { + IF_VERBOSE(5, verbose_stream() << "no literal associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + continue; + } + if (m.is_true(value)) + s().set_phase(lit); + else if (m.is_false(value)) + s().set_phase(~lit); + else + IF_VERBOSE(5, verbose_stream() << "malformed value " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + continue; + } + auto* th = m_id2solver.get(var->get_sort()->get_family_id(), nullptr); + if (!th) { + IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + continue; + } + th->initialize_value(var, value); + } + } bool solver::is_external(bool_var v) { @@ -1257,27 +1281,10 @@ namespace euf { } void solver::user_propagate_initialize_value(expr* var, expr* value) { - if (m.is_bool(var)) { - auto lit = expr2literal(var); - if (lit == sat::null_literal) { - IF_VERBOSE(5, verbose_stream() << "no literal associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); - return; - } - if (m.is_true(value)) - s().set_phase(lit); - else if (m.is_false(value)) - s().set_phase(~lit); - else - IF_VERBOSE(5, verbose_stream() << "malformed value " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); - return; - } - auto* th = m_id2solver.get(var->get_sort()->get_family_id(), nullptr); - if (!th) { - IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); - return; - } - // th->initialize_value(var, value); - IF_VERBOSE(5, verbose_stream() << "no default initialization associated with " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + + m_initial_values.push_back({expr_ref(var, m), expr_ref(value, m)}); + push(push_back_vector(m_initial_values)); + } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 8c436b942ff..650208183c0 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -189,6 +189,8 @@ namespace euf { euf::enode* mk_true(); euf::enode* mk_false(); + vector> m_initial_values; + // replay typedef std::tuple reinit_t; vector m_reinit; diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index 37394801452..24226eede6f 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -152,6 +152,8 @@ namespace euf { virtual void finalize() {} + virtual void initialize_value(expr* v, expr* value) { IF_VERBOSE(5, verbose_stream() << "value initialzation is not supported for theory\n"); } + }; class th_proof_hint : public sat::proof_hint { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8f334276d14..38291237d97 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1422,7 +1422,6 @@ class theory_lra::imp { m_num_conflicts = 0; for (auto const& [v, r] : m_values) lp().move_lpvar_to_value(v, r); - display(verbose_stream() << "init search\n"); } bool can_get_value(theory_var v) const { From a3f35b6830fb9639a3e1d0dc23250e91973cb244 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Sep 2024 17:48:03 +0300 Subject: [PATCH 191/414] Add command to set initial value hints for solver in various components --- src/cmd_context/basic_cmds.cpp | 20 +++++++++++++++++++ src/cmd_context/cmd_context.h | 2 ++ src/nlsat/tactic/nlsat_tactic.cpp | 3 +++ src/opt/opt_context.h | 4 ++-- src/qe/nlqsat.cpp | 2 ++ src/qe/qsat.cpp | 4 +++- src/sat/tactic/sat_tactic.cpp | 5 +++++ src/smt/theory_bv.cpp | 20 +++++++++++++++++++ src/smt/theory_bv.h | 1 + src/smt/theory_lra.cpp | 6 +----- src/solver/combined_solver.cpp | 1 + src/solver/solver2tactic.cpp | 5 +++++ src/tactic/.#tactic.cpp | 1 + .../fd_solver/bounded_int2bv_solver.cpp | 3 +++ src/tactic/tactic.cpp | 1 + src/tactic/tactic.h | 1 + src/tactic/tactical.cpp | 11 ++++++++++ 17 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 src/tactic/.#tactic.cpp diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index c93e4432f30..cf661df4980 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -318,6 +318,25 @@ UNARY_CMD(echo_cmd, "echo", "", "display the given string", CPK_STRING, else ctx.regular_stream() << arg << std::endl;); +class set_initial_value_cmd : public cmd { + expr* m_var = nullptr, *m_value = nullptr; +public: + set_initial_value_cmd(): cmd("set-initial-value") {} + char const* get_usage() const override { return " "; } + char const* get_descr(cmd_context& ctx) const { return "set an initial value for search as a hint to the solver"; } + unsigned get_arity() const { return 2; } + void prepare(cmd_context& ctx) { m_var = m_value = nullptr; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const { return CPK_EXPR; } + void set_next_arg(cmd_context& ctx, expr* e) { if (m_var) m_value = e; else m_var = e; } + void execute(cmd_context& ctx) { + SASSERT(m_var && m_value); + if (ctx.get_opt()) + ctx.get_opt()->initialize_value(m_var, m_value); + else if (ctx.get_solver()) + ctx.get_solver()->user_propagate_initialize_value(m_var, m_value); + } +}; + class set_get_option_cmd : public cmd { protected: symbol m_true; @@ -893,6 +912,7 @@ void install_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(get_option_cmd)); ctx.insert(alloc(get_info_cmd)); ctx.insert(alloc(set_info_cmd)); + ctx.insert(alloc(set_initial_value_cmd)); ctx.insert(alloc(get_consequences_cmd)); ctx.insert(alloc(builtin_cmd, "assert", "", "assert term.")); ctx.insert(alloc(builtin_cmd, "check-sat", "*", "check if the current context is satisfiable. If a list of boolean constants B is provided, then check if the current context is consistent with assigning every constant in B to true.")); diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index c07d888c7e4..fc3c9f310d4 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -173,6 +173,8 @@ class opt_wrapper : public check_sat_result { virtual void set_logic(symbol const& s) = 0; virtual void get_box_model(model_ref& mdl, unsigned index) = 0; virtual void updt_params(params_ref const& p) = 0; + virtual void initialize_value(expr* var, expr* value) = 0; + }; class ast_context_params : public context_params { diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 9426de78ee7..11c80de2abb 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -262,6 +262,9 @@ class nlsat_tactic : public tactic { void reset_statistics() override { m_stats.reset(); } + + void user_propagate_initialize_value(expr* var, expr* value) override { } + }; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 845fd39689a..991fe16e6ea 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -277,8 +277,8 @@ namespace opt { void add_offset(unsigned id, rational const& o) override; - void initialize_value(expr* var, expr* value); - + void initialize_value(expr* var, expr* value) override; + void register_on_model(on_model_t& ctx, std::function& on_model) { m_on_model_ctx = ctx; m_on_model_eh = on_model; diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 37cf62c615b..9fd1d4e0ec0 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -848,6 +848,8 @@ namespace qe { void collect_param_descrs(param_descrs & r) override { } + void user_propagate_initialize_value(expr* var, expr* value) override { } + void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index dc0613c59d8..0ff054a8940 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1377,7 +1377,9 @@ namespace qe { tactic * translate(ast_manager & m) override { return alloc(qsat, m, m_params, m_mode); - } + } + + void user_propagate_initialize_value(expr* var, expr* value) override { } lbool maximize(expr_ref_vector const& fmls, app* t, model_ref& mdl, opt::inf_eps& value) { expr_ref_vector defs(m); diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 562fa431eda..223222f9aa1 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -246,6 +246,11 @@ class sat_tactic : public tactic { m_stats.reset(); } + void user_propagate_initialize_value(expr* var, expr* value) override { + + } + + protected: }; diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 55d3a1d6245..cf110298f3b 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1789,6 +1789,26 @@ namespace smt { return false; } + void theory_bv::initialize_value(expr* var, expr* value) { + rational val; + unsigned sz; + if (!m_util.is_numeral(value, val, sz)) { + IF_VERBOSE(5, verbose_stream() << "value should be a bit-vector " << mk_pp(value, m) << "\n"); + return; + } + if (!is_app(var)) + return; + enode* n = mk_enode(to_app(var)); + auto v = get_var(n); + unsigned idx = 0; + for (auto lit : m_bits[v]) { + auto & b = ctx.get_bdata(lit.var()); + b.m_phase_available = true; + b.m_phase = val.get_bit(idx); + ++idx; + } + } + void theory_bv::init_model(model_generator & mg) { m_factory = alloc(bv_factory, m); mg.register_factory(m_factory); diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index 10cf005e343..72775c1d353 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -251,6 +251,7 @@ namespace smt { bool merge_zero_one_bits(theory_var r1, theory_var r2); bool can_propagate() override { return m_prop_diseqs_qhead < m_prop_diseqs.size(); } void propagate() override; + void initialize_value(expr* var, expr* value) override; // ----------------------------------- // diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 38291237d97..6de623bb780 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -154,7 +154,6 @@ class theory_lra::imp { svector m_asserted_atoms; ptr_vector m_not_handled; ptr_vector m_underspecified; - vector> m_values; vector > m_use_list; // bounds where variables are used. // attributes for incremental version: @@ -998,8 +997,7 @@ class theory_lra::imp { IF_VERBOSE(5, verbose_stream() << "numeric constant expected in initialization " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); return; } - ctx().push_trail(push_back_vector(m_values)); - m_values.push_back({get_lpvar(var), r}); + lp().move_lpvar_to_value(get_lpvar(var), r); } void new_eq_eh(theory_var v1, theory_var v2) { @@ -1420,8 +1418,6 @@ class theory_lra::imp { void init_search_eh() { m_arith_eq_adapter.init_search_eh(); m_num_conflicts = 0; - for (auto const& [v, r] : m_values) - lp().move_lpvar_to_value(v, r); } bool can_get_value(theory_var v) const { diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 051dfd4eb9d..34d31ec84de 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -396,6 +396,7 @@ class combined_solver : public solver { } void user_propagate_initialize_value(expr* var, expr* value) override { + m_solver1->user_propagate_initialize_value(var, value); m_solver2->user_propagate_initialize_value(var, value); } diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index b8e3dd37a6e..add55c62846 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -189,6 +189,11 @@ class solver2tactic : public tactic { } char const* name() const override { return "solver2tactic"; } + + + void user_propagate_initialize_value(expr* var, expr* value) override { + m_solver->user_propagate_initialize_value(var, value); + } }; tactic* mk_solver2tactic(solver* s) { return alloc(solver2tactic, s); } diff --git a/src/tactic/.#tactic.cpp b/src/tactic/.#tactic.cpp new file mode 100644 index 00000000000..12fd8329bc4 --- /dev/null +++ b/src/tactic/.#tactic.cpp @@ -0,0 +1 @@ +nbjorner@LAPTOP-04AEAFKH.32880:1726092166 \ No newline at end of file diff --git a/src/tactic/fd_solver/bounded_int2bv_solver.cpp b/src/tactic/fd_solver/bounded_int2bv_solver.cpp index 317286e1e83..45b444fe9b2 100644 --- a/src/tactic/fd_solver/bounded_int2bv_solver.cpp +++ b/src/tactic/fd_solver/bounded_int2bv_solver.cpp @@ -375,6 +375,9 @@ class bounded_int2bv_solver : public solver_na2as { return m_assertions.get(idx); } } + + void user_propagate_initialize_value(expr* var, expr* value) override { + } }; solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s) { diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index f7dcfb3ea02..e4e7b246aef 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -151,6 +151,7 @@ class fail_if_undecided_tactic : public skip_tactic { throw tactic_exception("undecided"); skip_tactic::operator()(in, result); } + void user_propagate_initialize_value(expr* var, expr* value) override { } }; tactic * mk_fail_if_undecided_tactic() { diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index e62f93ecf06..c6927642476 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -129,6 +129,7 @@ class skip_tactic : public tactic { tactic * translate(ast_manager & m) override { return this; } char const* name() const override { return "skip"; } void collect_statistics(statistics& st) const override {} + void user_propagate_initialize_value(expr* var, expr* value) override { } }; tactic * mk_skip_tactic(); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 626380913f6..db0a6d44d76 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -382,6 +382,12 @@ class or_else_tactical : public nary_tactical { } tactic * translate(ast_manager & m) override { return translate_core(m); } + + void user_propagate_initialize_value(expr* var, expr* value) override { + for (auto t : m_ts) + t->user_propagate_initialize_value(var, value); + } + }; tactic * or_else(unsigned num, tactic * const * ts) { @@ -1163,6 +1169,11 @@ class cond_tactical : public binary_tactical { tactic * new_t2 = m_t2->translate(m); return alloc(cond_tactical, m_p.get(), new_t1, new_t2); } + + void user_propagate_initialize_value(expr* var, expr* value) override { + m_t1->user_propagate_initialize_value(var, value); + m_t2->user_propagate_initialize_value(var, value); + } }; tactic * cond(probe * p, tactic * t1, tactic * t2) { From 4896edfb042a44c05b57770d4f77298723071c28 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Sep 2024 11:27:17 +0300 Subject: [PATCH 192/414] Add tracking of values size in scoped_state push method in opt_context --- src/opt/opt_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 90bc0ddd640..0c18580e597 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -53,6 +53,7 @@ namespace opt { void context::scoped_state::push() { m_asms_lim.push_back(m_asms.size()); m_hard_lim.push_back(m_hard.size()); + m_values_lim.push_back(m_values.size()); m_objectives_lim.push_back(m_objectives.size()); m_objectives_term_trail_lim.push_back(m_objectives_term_trail.size()); } From 8349ee006936f8088e2cbba66504bd2c8fc458a4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Sep 2024 11:44:18 +0300 Subject: [PATCH 193/414] Add support for const array in all logics as per issue #7383 --- src/ast/array_decl_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index bd9d954c774..c013036f463 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -577,9 +577,9 @@ void array_decl_plugin::get_sort_names(svector& sort_names, symbol void array_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { op_names.push_back(builtin_name("store",OP_STORE)); op_names.push_back(builtin_name("select",OP_SELECT)); + op_names.push_back(builtin_name("const",OP_CONST_ARRAY)); // github issue #7383 if (logic == symbol::null || logic == symbol("HORN") || logic == symbol("ALL")) { // none of the SMT2 logics support these extensions - op_names.push_back(builtin_name("const",OP_CONST_ARRAY)); op_names.push_back(builtin_name("map",OP_ARRAY_MAP)); op_names.push_back(builtin_name("default",OP_ARRAY_DEFAULT)); op_names.push_back(builtin_name("union",OP_SET_UNION)); From b99c4a47a4097cc6a671808f1e8abafcfd54bd06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Sep 2024 15:11:59 +0300 Subject: [PATCH 194/414] Add override specifiers to methods in set_initial_value_cmd class for clarity and consistency --- src/cmd_context/basic_cmds.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index cf661df4980..402799e849c 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -323,12 +323,12 @@ class set_initial_value_cmd : public cmd { public: set_initial_value_cmd(): cmd("set-initial-value") {} char const* get_usage() const override { return " "; } - char const* get_descr(cmd_context& ctx) const { return "set an initial value for search as a hint to the solver"; } - unsigned get_arity() const { return 2; } - void prepare(cmd_context& ctx) { m_var = m_value = nullptr; } - cmd_arg_kind next_arg_kind(cmd_context& ctx) const { return CPK_EXPR; } - void set_next_arg(cmd_context& ctx, expr* e) { if (m_var) m_value = e; else m_var = e; } - void execute(cmd_context& ctx) { + char const* get_descr(cmd_context& ctx) const override { return "set an initial value for search as a hint to the solver"; } + unsigned get_arity() const override { return 2; } + void prepare(cmd_context& ctx) override { m_var = m_value = nullptr; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { return CPK_EXPR; } + void set_next_arg(cmd_context& ctx, expr* e) override { if (m_var) m_value = e; else m_var = e; } + void execute(cmd_context& ctx) override { SASSERT(m_var && m_value); if (ctx.get_opt()) ctx.get_opt()->initialize_value(m_var, m_value); From 342dccdc0223b10f03369b037d529b396f70843b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 19 Sep 2024 14:11:27 -0700 Subject: [PATCH 195/414] correctly process cancellation in gomory cuts Signed-off-by: Lev Nachmanson --- src/math/lp/gomory.cpp | 5 ++++- src/math/lp/lia_move.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 7b4347af513..6b781776995 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -527,7 +527,7 @@ struct create_cut { has_small_cut = true; add_cut(cc.m_t, cc.m_k, cc.m_dep); if (lia.settings().get_cancel_flag()) - return lia_move::undef; + return lia_move::cancelled; } if (big_cuts.size()) { @@ -544,6 +544,9 @@ struct create_cut { if (!_check_feasible()) return lia_move::conflict; + + if (lra.get_status() == lp_status::CANCELLED) + return lia_move::cancelled; if (!lia.has_inf_int()) return lia_move::sat; diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index 12e3d8e35ea..75ff67dcdd1 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -26,7 +26,8 @@ namespace lp { conflict, continue_with_check, undef, - unsat + unsat, + cancelled }; inline std::string lia_move_to_string(lia_move m) { switch (m) { From 0c48a50d59f207e3f1dee38a02391fd21f038d5b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Sep 2024 22:44:16 +0300 Subject: [PATCH 196/414] Add support for initializing variable values in solver and optimize contexts in Z3 --- RELEASE_NOTES.md | 13 +++++- src/ast/ast.cpp | 7 +++- .../converters/generic_model_converter.cpp | 41 +++++++++++++++++++ src/ast/converters/generic_model_converter.h | 3 ++ src/ast/converters/model_converter.cpp | 6 +++ src/ast/converters/model_converter.h | 2 + src/cmd_context/basic_cmds.cpp | 5 +-- src/cmd_context/cmd_context.cpp | 14 +++++++ src/cmd_context/cmd_context.h | 2 + src/opt/opt_context.cpp | 4 +- src/sat/sat_solver/inc_sat_solver.cpp | 9 +++- src/tactic/.#tactic.cpp | 1 - 12 files changed, 98 insertions(+), 9 deletions(-) delete mode 100644 src/tactic/.#tactic.cpp diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index cc961a760fe..50e181aeb71 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -18,7 +18,18 @@ Version 4.13.1 The projection is described in paper by Haokun Li and Bican Xia, [Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection](https://arxiv.org/abs/2003.00409). The code ported from https://github.com/hybridSMT/hybridSMT.git - Add API for providing hints for the solver/optimize contexts for which initial values to attempt to use for variables. - The new API function are Z3_solver_set_initial_value and Z3_optimize_set_initial_value, respectively. Supply these functions with a Boolean or numeric variable, and a value. The solver will then attempt to use these values in the initial phase of search. The feature is aimed at resolving nearly similar problems, or problems with a predicted model and the intent is that restarting the solver based on a near solution can avoid prune the space of constraints that are initially infeasible. + The new API function are Z3_solver_set_initial_value and Z3_optimize_set_initial_value, respectively. Supply these functions with a Boolean or numeric variable, and a value. The solver will then attempt to use these values in the initial phase of search. The feature is aimed at resolving nearly similar problems, or problems with a predicted model and the intent is that restarting the solver based on a near solution can avoid prune the space of constraints that are initially infeasible. + The SMTLIB front-end contains the new command (set-initial-value var value). For example, + (declare-const x Int) + (set-initial-value x 10) + (push) + (assert (> x 0)) + (check-sat) + (get-model) + produces a model where x = 10. We use (push) to ensure that z3 doesn't run a + specialized pre-processor that eliminates x, which renders the initialization + without effect. + Version 4.13.0 ============== diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index e1e3efe99ee..5194154f0c5 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1008,7 +1008,8 @@ sort* basic_decl_plugin::join(unsigned n, expr* const* es) { } sort* basic_decl_plugin::join(sort* s1, sort* s2) { - if (s1 == s2) return s1; + if (s1 == s2) + return s1; if (s1->get_family_id() == arith_family_id && s2->get_family_id() == arith_family_id) { if (s1->get_decl_kind() == REAL_SORT) { @@ -1016,6 +1017,10 @@ sort* basic_decl_plugin::join(sort* s1, sort* s2) { } return s2; } + if (s1 == m_bool_sort && s2->get_family_id() == arith_family_id) + return s2; + if (s2 == m_bool_sort && s1->get_family_id() == arith_family_id) + return s1; std::ostringstream buffer; buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible"; throw ast_exception(buffer.str()); diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index c50d86cae6d..81f24ed8ad5 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -130,6 +130,47 @@ generic_model_converter * generic_model_converter::copy(ast_translation & transl return res; } +void generic_model_converter::convert_initialize_value(expr_ref& var, expr_ref& value) { + for (auto const& e : m_entries) { + switch (e.m_instruction) { + case HIDE: + break; + case ADD: + if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl()) + convert_initialize_value(e.m_def, var, value); + break; + } + } +} + +void generic_model_converter::convert_initialize_value(expr* def, expr_ref& var, expr_ref& value) { + + // var = if(c, th, el) = value + // th = value => c = true + // el = value => c = false + expr* c = nullptr, *th = nullptr, *el = nullptr; + if (m.is_ite(def, c, th, el)) { + if (value == th) { + var = c; + value = m.mk_true(); + return; + } + if (value == el) { + var = c; + value = m.mk_false(); + return; + } + } + + // var = def = value + // => def = value + if (is_uninterp(def)) + var = def; + + +} + + void generic_model_converter::set_env(ast_pp_util* visitor) { if (!visitor) { diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index 0bc6b21b407..e778828cd54 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -37,6 +37,7 @@ class generic_model_converter : public model_converter { vector m_entries; expr_ref simplify_def(entry const& e); + void convert_initialize_value(expr* def, expr_ref& var, expr_ref& value); public: generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} @@ -61,6 +62,8 @@ class generic_model_converter : public model_converter { model_converter * translate(ast_translation & translator) override { return copy(translator); } + void convert_initialize_value(expr_ref& var, expr_ref& value) override; + generic_model_converter* copy(ast_translation & translator); void set_env(ast_pp_util* visitor) override; diff --git a/src/ast/converters/model_converter.cpp b/src/ast/converters/model_converter.cpp index d053394cac9..f03ef53279d 100644 --- a/src/ast/converters/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -107,6 +107,12 @@ class concat_model_converter : public concat_converter { m_c2->get_units(fmls); m_c1->get_units(fmls); } + + void convert_initialize_value(expr_ref& var, expr_ref& value) override { + m_c2->convert_initialize_value(var, value); + m_c1->convert_initialize_value(var, value); + } + char const * get_name() const override { return "concat-model-converter"; } diff --git a/src/ast/converters/model_converter.h b/src/ast/converters/model_converter.h index 720324919c4..0165cfbfcec 100644 --- a/src/ast/converters/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -86,6 +86,8 @@ class model_converter : public converter { virtual void set_env(ast_pp_util* visitor); + virtual void convert_initialize_value(expr_ref& var, expr_ref& value) { } + /** \brief we are adding a formula to the context of the model converter. The operator has as side effect of adding definitions as assertions to the diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 402799e849c..beb3aa0bb41 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -330,10 +330,7 @@ class set_initial_value_cmd : public cmd { void set_next_arg(cmd_context& ctx, expr* e) override { if (m_var) m_value = e; else m_var = e; } void execute(cmd_context& ctx) override { SASSERT(m_var && m_value); - if (ctx.get_opt()) - ctx.get_opt()->initialize_value(m_var, m_value); - else if (ctx.get_solver()) - ctx.get_solver()->user_propagate_initialize_value(m_var, m_value); + ctx.set_initial_value(m_var, m_value); } }; diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 231c78bc26e..e9ca5595fb7 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -629,6 +629,7 @@ cmd_context::~cmd_context() { finalize_cmds(); finalize_tactic_manager(); m_proof_cmds = nullptr; + m_var2values.reset(); reset(true); m_mcs.reset(); m_solver = nullptr; @@ -654,6 +655,8 @@ void cmd_context::set_opt(opt_wrapper* opt) { m_opt = opt; for (unsigned i = 0; i < m_scopes.size(); ++i) m_opt->push(); + for (auto const& [var, value] : m_var2values) + m_opt->initialize_value(var, value); m_opt->set_logic(m_logic); } @@ -1874,6 +1877,17 @@ void cmd_context::display_dimacs() { } } +void cmd_context::set_initial_value(expr* var, expr* value) { + if (get_opt()) { + get_opt()->initialize_value(var, value); + return; + } + if (get_solver()) + get_solver()->user_propagate_initialize_value(var, value); + m_var2values.push_back({expr_ref(var, m()), expr_ref(value, m())}); +} + + void cmd_context::display_model(model_ref& mdl) { if (mdl) { if (mc0()) (*mc0())(mdl); diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index fc3c9f310d4..d113eb8810a 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -262,6 +262,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_ scoped_ptr_vector m_extra_builtin_decls; // make sure that dynamically allocated builtin_decls are deleted dictionary m_object_refs; // anything that can be named. dictionary m_user_tactic_decls; + vector> m_var2values; dictionary m_func_decls; obj_map m_func_decl2alias; @@ -421,6 +422,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_ solver* get_solver() { return m_solver.get(); } void set_solver(solver* s) { m_solver = s; } void set_proof_cmds(proof_cmds* pc) { m_proof_cmds = pc; } + void set_initial_value(expr* var, expr* value); void set_solver_factory(solver_factory * s); void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 0c18580e597..6eb1de59822 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -311,7 +311,9 @@ namespace opt { } solver& s = get_solver(); s.assert_expr(m_hard_constraints); - for (auto const& [var, value] : m_scoped_state.m_values) { + for (auto & [var, value] : m_scoped_state.m_values) { + if (m_model_converter) + m_model_converter->convert_initialize_value(var, value); s.user_propagate_initialize_value(var, value); } diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 0129a026e54..8c4a79b76a4 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -703,7 +703,14 @@ class inc_sat_solver : public solver { } void user_propagate_initialize_value(expr* var, expr* value) override { - ensure_euf()->user_propagate_initialize_value(var, value); + expr_ref _var(var, m), _value(value, m); + if (m_mcs.back()) + m_mcs.back()->convert_initialize_value(_var, _value); + sat::bool_var b = m_map.to_bool_var(_var); + if (b != sat::null_bool_var) + m_solver.set_phase(sat::literal(b, m.is_false(_value))); + else if (get_euf()) + ensure_euf()->user_propagate_initialize_value(_var, _value); } diff --git a/src/tactic/.#tactic.cpp b/src/tactic/.#tactic.cpp deleted file mode 100644 index 12fd8329bc4..00000000000 --- a/src/tactic/.#tactic.cpp +++ /dev/null @@ -1 +0,0 @@ -nbjorner@LAPTOP-04AEAFKH.32880:1726092166 \ No newline at end of file From d66609ea14e8d329bb1ab730c93c98f3f67250be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 02:41:11 +0300 Subject: [PATCH 197/414] fix #7389 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bv_rewriter.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index db87cd0084f..96fb1f8565b 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -618,6 +618,31 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref return BR_REWRITE3; } + // #x000f <=_u a <=> not (a <=_u #x000f) or a = #x000f + i = bv_sz; + first_non_zero = UINT_MAX; + while (i > 0) { + --i; + if (!is_zero_bit(a, i)) { + first_non_zero = i; + break; + } + } + + if (first_non_zero == UINT_MAX) { + // all bits are zero + result = m.mk_eq(b, mk_zero(bv_sz)); + return BR_REWRITE1; + } + else if (first_non_zero < bv_sz - 1 && m_le2extract) { + result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, b), mk_zero(bv_sz - first_non_zero - 1)), + m_util.mk_ule(m_mk_extract(first_non_zero, 0, b), m_mk_extract(first_non_zero, 0, a))); + result = m.mk_or(m.mk_not(result), m.mk_eq(a, b)); + return BR_REWRITE_FULL; + } + + + } #endif From fa7fc8ef5ed98849f1a1408130f61a1b98bf58ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 13:04:49 +0300 Subject: [PATCH 198/414] Refactor bv_rewriter functions using unified variable assignment and early break logic --- src/ast/rewriter/bv_rewriter.cpp | 82 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 96fb1f8565b..018587afc9e 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -109,17 +109,20 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons break; case OP_BNEG_OVFL: SASSERT(num_args == 1); - return mk_bvneg_overflow(args[0], result); - + st = mk_bvneg_overflow(args[0], result); + break; case OP_BSHL: SASSERT(num_args == 2); - return mk_bv_shl(args[0], args[1], result); + st = mk_bv_shl(args[0], args[1], result); + break; case OP_BLSHR: SASSERT(num_args == 2); - return mk_bv_lshr(args[0], args[1], result); + st = mk_bv_lshr(args[0], args[1], result); + break; case OP_BASHR: SASSERT(num_args == 2); - return mk_bv_ashr(args[0], args[1], result); + st = mk_bv_ashr(args[0], args[1], result); + break; case OP_BSDIV: SASSERT(num_args == 2); return mk_bv_sdiv(args[0], args[1], result); @@ -151,13 +154,16 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons SASSERT(num_args == 2); return mk_bv_smod_i(args[0], args[1], result); case OP_CONCAT: - return mk_concat(num_args, args, result); + st = mk_concat(num_args, args, result); + break; case OP_EXTRACT: SASSERT(num_args == 1); - return mk_extract(m_util.get_extract_high(f), m_util.get_extract_low(f), args[0], result); + st = mk_extract(m_util.get_extract_high(f), m_util.get_extract_low(f), args[0], result); + break; case OP_REPEAT: SASSERT(num_args == 1); - return mk_repeat(f->get_parameter(0).get_int(), args[0], result); + st = mk_repeat(f->get_parameter(0).get_int(), args[0], result); + break; case OP_ZERO_EXT: SASSERT(num_args == 1); return mk_zero_extend(f->get_parameter(0).get_int(), args[0], result); @@ -596,53 +602,45 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref // // a <=_u #x000f // - unsigned bv_sz = m_util.get_bv_size(b); - unsigned i = bv_sz; - unsigned first_non_zero = UINT_MAX; - while (i > 0) { - --i; - if (!is_zero_bit(b, i)) { - first_non_zero = i; - break; - } - } + unsigned bv_sz = m_util.get_bv_size(a); + auto last_non_zero = [&](expr* x) { + for (unsigned i = bv_sz; i-- > 0; ) + if (!is_zero_bit(x, i)) + return i; + return UINT_MAX; + }; + + unsigned lnz = last_non_zero(b); - if (first_non_zero == UINT_MAX) { + if (lnz == UINT_MAX) { // all bits are zero result = m.mk_eq(a, mk_zero(bv_sz)); return BR_REWRITE1; } - else if (first_non_zero < bv_sz - 1 && m_le2extract) { - result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), mk_zero(bv_sz - first_non_zero - 1)), - m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); + else if (lnz < bv_sz - 1 && m_le2extract) { + // a[sz-1:lnz+1] = 0 & a[lnz:0] <= b[lnz:0] + result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, lnz + 1, a), mk_zero(bv_sz - lnz - 1)), + m_util.mk_ule(m_mk_extract(lnz, 0, a), m_mk_extract(lnz, 0, b))); + return BR_REWRITE3; } - // #x000f <=_u a <=> not (a <=_u #x000f) or a = #x000f - i = bv_sz; - first_non_zero = UINT_MAX; - while (i > 0) { - --i; - if (!is_zero_bit(a, i)) { - first_non_zero = i; - break; - } - } - if (first_non_zero == UINT_MAX) { + lnz = last_non_zero(a); + + if (lnz == UINT_MAX) { // all bits are zero - result = m.mk_eq(b, mk_zero(bv_sz)); - return BR_REWRITE1; + result = m.mk_true(); + return BR_DONE; } - else if (first_non_zero < bv_sz - 1 && m_le2extract) { - result = m.mk_and(m.mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, b), mk_zero(bv_sz - first_non_zero - 1)), - m_util.mk_ule(m_mk_extract(first_non_zero, 0, b), m_mk_extract(first_non_zero, 0, a))); - result = m.mk_or(m.mk_not(result), m.mk_eq(a, b)); + else if (lnz < bv_sz - 1 && m_le2extract) { + // use the equivalence to simplify: + // #x000f <=_u b <=> b[sz-1:lnz+1] != 0 or #xf <= b[lnz:0]) + + result = m.mk_implies(m.mk_eq(m_mk_extract(bv_sz - 1, lnz + 1, b), mk_zero(bv_sz - lnz - 1)), + m_util.mk_ule(m_mk_extract(lnz, 0, a), m_mk_extract(lnz, 0, b))); return BR_REWRITE_FULL; } - - - } #endif From ba5cec77045720d9cb81edbf102e473762762f37 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 21:29:12 +0300 Subject: [PATCH 199/414] additional rewrites for bv2int --- src/ast/rewriter/bv_rewriter.cpp | 61 +++++++++++++++++++++++++++++--- src/ast/rewriter/bv_rewriter.h | 1 + src/ast/rewriter/th_rewriter.cpp | 9 +++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 018587afc9e..7b54f76a10e 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1445,19 +1445,50 @@ br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, e br_status bv_rewriter::mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result) { numeral val; bool is_int; - + expr* x; if (m_autil.is_numeral(arg, val, is_int)) { val = m_util.norm(val, bv_size); result = mk_numeral(val, bv_size); return BR_DONE; } - // (int2bv (bv2int x)) --> x - if (m_util.is_bv2int(arg) && bv_size == get_bv_size(to_app(arg)->get_arg(0))) { - result = to_app(arg)->get_arg(0); + // int2bv (bv2int x) --> x + if (m_util.is_bv2int(arg, x) && bv_size == get_bv_size(x)) { + result = x; return BR_DONE; } + // int2bv (bv2int x) --> 0000x + if (m_util.is_bv2int(arg, x) && bv_size > get_bv_size(x)) { + mk_zero_extend(bv_size - get_bv_size(x), x, result); + return BR_REWRITE1; + } + + // int2bv (bv2int x) --> x[sz-1:0] + if (m_util.is_bv2int(arg, x) && bv_size < get_bv_size(x)) { + result = m_mk_extract(bv_size - 1, 0, x); + return BR_REWRITE1; + } + +#if 0 + // int2bv (a + b) --> int2bv(a) + int2bv(b) + if (m_autil.is_add(arg)) { + expr_ref_vector args(m); + for (expr* e : *to_app(arg)) + args.push_back(m_util.mk_int2bv(bv_size, e)); + result = m_util.mk_bv_add(args); + return BR_REWRITE3; + } + // int2bv (a * b) --> int2bv(a) * int2bv(b) + if (m_autil.is_mul(arg)) { + expr_ref_vector args(m); + for (expr* e : *to_app(arg)) + args.push_back(m_util.mk_int2bv(bv_size, e)); + result = m_util.mk_bv_mul(args); + return BR_REWRITE3; + } +#endif + return BR_FAILED; } @@ -2740,6 +2771,27 @@ bool bv_rewriter::is_urem_any(expr * e, expr * & dividend, expr * & divisor) { return true; } +br_status bv_rewriter::mk_eq_bv2int(expr* lhs, expr* rhs, expr_ref& result) { + rational r; + expr* x, *y; + if (m_autil.is_numeral(lhs)) + std::swap(lhs, rhs); + + if (m_autil.is_numeral(rhs, r) && m_util.is_bv2int(lhs, x)) { + unsigned bv_size = m_util.get_bv_size(x); + if (0 <= r && r < rational::power_of_two(bv_size)) + result = m.mk_eq(m_util.mk_numeral(r, bv_size), x); + else + result = m.mk_false(); + return BR_REWRITE1; + } + if (m_util.is_bv2int(lhs, x) && m_util.is_bv2int(rhs, y)) { + result = m.mk_eq(x, y); + return BR_REWRITE1; + } + return BR_FAILED; +} + br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (lhs == rhs) { result = m.mk_true(); @@ -2783,6 +2835,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { return st; } + if (m_blast_eq_value) { st = mk_blast_eq_value(lhs, rhs, result); if (st != BR_FAILED) diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 67ed1da8706..73710f5c6e0 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -203,6 +203,7 @@ class bv_rewriter : public poly_rewriter { bool is_urem_any(expr * e, expr * & dividend, expr * & divisor); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); + br_status mk_eq_bv2int(expr* lhs, expr* rhs, expr_ref& result); br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result); br_status mk_distinct(unsigned num_args, expr * const * args, expr_ref & result); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 844c51940e9..483e2d5fb5a 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -685,9 +685,18 @@ struct th_rewriter_cfg : public default_rewriter_cfg { st = m_seq_rw.mk_eq_core(a, b, result); if (st != BR_FAILED) return st; + st = extended_bv_eq(a, b, result); + if (st != BR_FAILED) + return st; return apply_tamagotchi(a, b, result); } + br_status extended_bv_eq(expr* a, expr* b, expr_ref& result) { + if (m_bv_util.is_bv2int(a) || m_bv_util.is_bv2int(b)) + return m_bv_rw.mk_eq_bv2int(a, b, result); + return BR_FAILED; + } + expr_ref mk_eq(expr* a, expr* b) { expr_ref result(m()); br_status st = reduce_eq(a, b, result); From a9f8ec1bcb997ab7d386e67ff4825450b110728a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 21:30:11 +0300 Subject: [PATCH 200/414] updated handling of value initialization for bit-vectors --- .../converters/generic_model_converter.cpp | 39 +++++++++++++------ src/ast/converters/generic_model_converter.h | 4 +- src/ast/converters/model_converter.cpp | 6 +-- src/ast/converters/model_converter.h | 2 +- src/opt/opt_context.cpp | 7 ++-- src/sat/sat_solver/inc_sat_solver.cpp | 30 ++++++++++---- src/sat/tactic/sat_tactic.cpp | 29 +++++++++++--- src/smt/smt_context.cpp | 2 +- src/smt/theory_bv.cpp | 1 + src/tactic/bv/bit_blaster_model_converter.cpp | 28 +++++++++++++ 10 files changed, 112 insertions(+), 36 deletions(-) diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index 81f24ed8ad5..f64e798b599 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -22,6 +22,7 @@ Module Name: #include "ast/for_each_expr.h" #include "ast/ast_util.h" #include "ast/occurs.h" +#include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" #include "ast/converters/generic_model_converter.h" @@ -130,25 +131,32 @@ generic_model_converter * generic_model_converter::copy(ast_translation & transl return res; } -void generic_model_converter::convert_initialize_value(expr_ref& var, expr_ref& value) { - for (auto const& e : m_entries) { - switch (e.m_instruction) { - case HIDE: - break; - case ADD: - if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl()) - convert_initialize_value(e.m_def, var, value); - break; +void generic_model_converter::convert_initialize_value(vector> & var2value) { + if (var2value.empty() || m_entries.empty()) + return; + for (unsigned i = 0; i < var2value.size(); ++i) { + auto& [var, value] = var2value[i]; + for (auto const& e : m_entries) { + switch (e.m_instruction) { + case HIDE: + break; + case ADD: + if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl()) + convert_initialize_value(e.m_def, i, var2value); + break; + } } } } -void generic_model_converter::convert_initialize_value(expr* def, expr_ref& var, expr_ref& value) { +void generic_model_converter::convert_initialize_value(expr* def, unsigned i, vector>& var2value) { // var = if(c, th, el) = value // th = value => c = true // el = value => c = false expr* c = nullptr, *th = nullptr, *el = nullptr; + auto& [var, value] = var2value[i]; + verbose_stream() << "def " << mk_pp(def, m) << "\n"; if (m.is_ite(def, c, th, el)) { if (value == th) { var = c; @@ -164,8 +172,15 @@ void generic_model_converter::convert_initialize_value(expr* def, expr_ref& var, // var = def = value // => def = value - if (is_uninterp(def)) - var = def; + if (is_uninterp(def)) { + var = def; + return; + } + + bv_util bv(m); + if (bv.is_mkbv(def)) { + verbose_stream() << "def\n"; + } } diff --git a/src/ast/converters/generic_model_converter.h b/src/ast/converters/generic_model_converter.h index e778828cd54..18ec16fe0e5 100644 --- a/src/ast/converters/generic_model_converter.h +++ b/src/ast/converters/generic_model_converter.h @@ -37,7 +37,7 @@ class generic_model_converter : public model_converter { vector m_entries; expr_ref simplify_def(entry const& e); - void convert_initialize_value(expr* def, expr_ref& var, expr_ref& value); + void convert_initialize_value(expr* def, unsigned i, vector>& var2value); public: generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} @@ -62,7 +62,7 @@ class generic_model_converter : public model_converter { model_converter * translate(ast_translation & translator) override { return copy(translator); } - void convert_initialize_value(expr_ref& var, expr_ref& value) override; + void convert_initialize_value(vector>& var2value) override; generic_model_converter* copy(ast_translation & translator); diff --git a/src/ast/converters/model_converter.cpp b/src/ast/converters/model_converter.cpp index f03ef53279d..c309bbb7922 100644 --- a/src/ast/converters/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -108,9 +108,9 @@ class concat_model_converter : public concat_converter { m_c1->get_units(fmls); } - void convert_initialize_value(expr_ref& var, expr_ref& value) override { - m_c2->convert_initialize_value(var, value); - m_c1->convert_initialize_value(var, value); + void convert_initialize_value(vector>& var2value) override { + m_c2->convert_initialize_value(var2value); + m_c1->convert_initialize_value(var2value); } diff --git a/src/ast/converters/model_converter.h b/src/ast/converters/model_converter.h index 0165cfbfcec..354b5185436 100644 --- a/src/ast/converters/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -86,7 +86,7 @@ class model_converter : public converter { virtual void set_env(ast_pp_util* visitor); - virtual void convert_initialize_value(expr_ref& var, expr_ref& value) { } + virtual void convert_initialize_value(vector> & var2value) { } /** \brief we are adding a formula to the context of the model converter. diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 6eb1de59822..c571753e96e 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -311,11 +311,10 @@ namespace opt { } solver& s = get_solver(); s.assert_expr(m_hard_constraints); - for (auto & [var, value] : m_scoped_state.m_values) { - if (m_model_converter) - m_model_converter->convert_initialize_value(var, value); + if (m_model_converter) + m_model_converter->convert_initialize_value(m_scoped_state.m_values); + for (auto & [var, value] : m_scoped_state.m_values) s.user_propagate_initialize_value(var, value); - } opt_params optp(m_params); symbol pri = optp.priority(); diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 8c4a79b76a4..19482c2863a 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -78,6 +78,7 @@ class inc_sat_solver : public solver { // this allows to access the internal state of the SAT solver and carry on partial results. bool m_internalized_converted; // have internalized formulas been converted back expr_ref_vector m_internalized_fmls; // formulas in internalized format + vector> m_var2value; typedef obj_map dep2asm_t; @@ -175,9 +176,27 @@ class inc_sat_solver : public solver { (m.is_not(e, e) && is_uninterp_const(e)); } + void initialize_values() { + if (m_mcs.back()) + m_mcs.back()->convert_initialize_value(m_var2value); + if (m_mcs.back()) + m_mcs.back()->display(verbose_stream()); + + for (auto & [var, value] : m_var2value) { + sat::bool_var b = m_map.to_bool_var(var); + if (b != sat::null_bool_var) + m_solver.set_phase(sat::literal(b, m.is_false(value))); + else if (get_euf()) + ensure_euf()->user_propagate_initialize_value(var, value); + } + } + lbool check_sat_core(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); m_core.reset(); + + + if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; @@ -202,6 +221,8 @@ class inc_sat_solver : public solver { r = internalize_assumptions(sz, _assumptions.data()); if (r != l_true) return r; + initialize_values(); + init_reason_unknown(); m_internalized_converted = false; bool reason_set = false; @@ -703,14 +724,7 @@ class inc_sat_solver : public solver { } void user_propagate_initialize_value(expr* var, expr* value) override { - expr_ref _var(var, m), _value(value, m); - if (m_mcs.back()) - m_mcs.back()->convert_initialize_value(_var, _value); - sat::bool_var b = m_map.to_bool_var(_var); - if (b != sat::null_bool_var) - m_solver.set_phase(sat::literal(b, m.is_false(_value))); - else if (get_euf()) - ensure_euf()->user_propagate_initialize_value(_var, _value); + m_var2value.push_back({expr_ref(var, m), expr_ref(value, m) }); } diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 223222f9aa1..e997cbc2cba 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -32,13 +32,26 @@ class sat_tactic : public tactic { sat2goal m_sat2goal; scoped_ptr m_solver; params_ref m_params; + vector>& m_var2value; - imp(ast_manager & _m, params_ref const & p): + imp(ast_manager & _m, params_ref const & p, vector>& var2value): m(_m), m_solver(alloc(sat::solver, p, m.limit())), - m_params(p) { + m_params(p), + m_var2value(var2value) { updt_params(p); } + + void initialize_values(goal_ref const& g, atom2bool_var& map) { + g->mc()->convert_initialize_value(m_var2value); + for (auto & [var, value] : m_var2value) { + if (!m.is_bool(var)) + continue; + sat::bool_var b = map.to_bool_var(var); + if (b != sat::null_bool_var) + m_solver->set_phase(sat::literal(b, m.is_false(value))); + } + } void operator()(goal_ref const & g, goal_ref_buffer & result) { @@ -66,6 +79,8 @@ class sat_tactic : public tactic { g->reset(); g->m().compact_memory(); + initialize_values(g, map); + CASSERT("sat_solver", m_solver->check_invariant()); IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver->display_status(verbose_stream());); TRACE("sat_dimacs", m_solver->display_dimacs(tout);); @@ -184,11 +199,14 @@ class sat_tactic : public tactic { imp * m_imp; params_ref m_params; statistics m_stats; + ast_manager& m; + vector> m_var2value; public: sat_tactic(ast_manager & m, params_ref const & p): m_imp(nullptr), - m_params(p) { + m_params(p), + m(m) { sat_params p1(p); } @@ -215,7 +233,7 @@ class sat_tactic : public tactic { void operator()(goal_ref const & g, goal_ref_buffer & result) override { - imp proc(g->m(), m_params); + imp proc(g->m(), m_params, m_var2value); scoped_set_imp set(this, &proc); try { proc(g, result); @@ -247,7 +265,8 @@ class sat_tactic : public tactic { } void user_propagate_initialize_value(expr* var, expr* value) override { - + verbose_stream() << "initialize-value\n"; + m_var2value.push_back({ expr_ref(var, m), expr_ref(value, m) }); } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 163da9b1683..deee05f6d30 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -2920,7 +2920,7 @@ namespace smt { } void context::initialize_value(expr* var, expr* value) { - IF_VERBOSE(10, verbose_stream() << "context initialize " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); + IF_VERBOSE(10, verbose_stream() << "initialize " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); sort* s = var->get_sort(); ensure_internalized(var); diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index cf110298f3b..ca327891c9c 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1792,6 +1792,7 @@ namespace smt { void theory_bv::initialize_value(expr* var, expr* value) { rational val; unsigned sz; + TRACE("bv", tout << "initializing " << mk_pp(var, m) << " := " << mk_pp(value, m) << "\n"); if (!m_util.is_numeral(value, val, sz)) { IF_VERBOSE(5, verbose_stream() << "value should be a bit-vector " << mk_pp(value, m) << "\n"); return; diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 5958a9d3867..5bc874abb34 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -222,6 +222,34 @@ struct bit_blaster_model_converter : public model_converter { // no-op } + void convert_initialize_value(vector>& var2value) { + if (m_vars.empty() || var2value.empty()) + return; + rational r; + bv_util util(m()); + for (unsigned j = 0; j < var2value.size(); ++j) { + auto& [var, value] = var2value[j]; + if (!is_uninterp_const(var)) + continue; + if (!util.is_numeral(value, r)) + continue; + unsigned sz = m_vars.size(); + for (unsigned i = 0; i < sz; i++) { + if (m_vars.get(i) != to_app(var)->get_decl()) + continue; + unsigned k = 0; + expr_ref bit_k(m()); + for (auto arg : *to_app(m_bits.get(i))) { + bit_k = m().mk_bool_val(r.get_bit(k)); + var2value.push_back({ expr_ref(arg, m()), bit_k }); + ++k; + } + var2value[i] = var2value.back(); + var2value.pop_back(); + } + } + } + protected: bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m), m_newbits(m) { } public: From 96c13757864c3fb5ec91e811fb4b4186fcd4ce91 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 19:35:03 +0100 Subject: [PATCH 201/414] #7391 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 50f0bc05a25..7e39931f4a5 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -2060,6 +2060,10 @@ br_status seq_rewriter::mk_seq_replace_all(expr* a, expr* b, expr* c, expr_ref& result = m().mk_ite(str().mk_is_empty(b), str().mk_empty(a->get_sort()), c); return BR_REWRITE2; } + if (str().is_empty(a) && str().is_empty(c)) { + result = a; + return BR_DONE; + } zstring s1, s2; expr_ref_vector strs(m()); if (str().is_string(a, s1) && str().is_string(b, s2)) { From 1e580a7f12cb1cbba6aed4d3484bae4988e06806 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 21:30:44 +0100 Subject: [PATCH 202/414] update to c++20, remove debug output Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- scripts/mk_util.py | 10 +++++----- src/ast/converters/generic_model_converter.cpp | 9 +-------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cdccb8e4b9a..4603ae1de79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,7 +178,7 @@ include(${PROJECT_SOURCE_DIR}/cmake/z3_add_cxx_flag.cmake) ################################################################################ # C++ language version ################################################################################ -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) ################################################################################ diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 3e6da0b22b8..e0f029a3bcd 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2007,11 +2007,11 @@ def mk_makefile(self, out): src_dir = self.to_src_dir mk_dir(os.path.join(BUILD_DIR, self.sub_dir)) api_src = get_component(API_COMPONENT).to_src_dir - # remove /GL and -std=c++17; the ocaml tools don't like them. + # remove /GL and -std=c++20; the ocaml tools don't like them. if IS_WINDOWS: out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n') else: - out.write('CXXFLAGS_OCAML=$(subst -std=c++17,,$(CXXFLAGS))\n') + out.write('CXXFLAGS_OCAML=$(subst -std=c++20,,$(CXXFLAGS))\n') substitutions = { 'VERSION': "{}.{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) } @@ -2500,7 +2500,7 @@ def mk_config(): config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') global CXX, CC, GMP, GUARD_CF, STATIC_BIN, GIT_HASH, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED, IS_ARCH_ARM64 if IS_WINDOWS: - CXXFLAGS = '/nologo /Zi /D WIN32 /D _WINDOWS /EHsc /GS /Gd /std:c++17' + CXXFLAGS = '/nologo /Zi /D WIN32 /D _WINDOWS /EHsc /GS /Gd /std:C++20' config.write( 'CC=cl\n' 'CXX=cl\n' @@ -2616,7 +2616,7 @@ def mk_config(): CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) - CXXFLAGS = '%s -std=c++17' % CXXFLAGS + CXXFLAGS = '%s -std=C++20' % CXXFLAGS CXXFLAGS = '%s -fvisibility=hidden -fvisibility-inlines-hidden -c' % CXXFLAGS FPMATH = test_fpmath(CXX) CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) @@ -2699,7 +2699,7 @@ def mk_config(): config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) - config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=c++17', ''))) + config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=C++20', ''))) config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('C_OUT_FLAG=-o \n') diff --git a/src/ast/converters/generic_model_converter.cpp b/src/ast/converters/generic_model_converter.cpp index f64e798b599..03624c9de91 100644 --- a/src/ast/converters/generic_model_converter.cpp +++ b/src/ast/converters/generic_model_converter.cpp @@ -156,7 +156,6 @@ void generic_model_converter::convert_initialize_value(expr* def, unsigned i, ve // el = value => c = false expr* c = nullptr, *th = nullptr, *el = nullptr; auto& [var, value] = var2value[i]; - verbose_stream() << "def " << mk_pp(def, m) << "\n"; if (m.is_ite(def, c, th, el)) { if (value == th) { var = c; @@ -175,13 +174,7 @@ void generic_model_converter::convert_initialize_value(expr* def, unsigned i, ve if (is_uninterp(def)) { var = def; return; - } - - bv_util bv(m); - if (bv.is_mkbv(def)) { - verbose_stream() << "def\n"; - } - + } } From 11218154391d5137ab35a3281fb6283587c08ba0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 22 Sep 2024 21:45:10 +0100 Subject: [PATCH 203/414] Standardize C++20 flag across different platforms in build script --- scripts/mk_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index e0f029a3bcd..0177dc564f4 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2500,7 +2500,7 @@ def mk_config(): config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') global CXX, CC, GMP, GUARD_CF, STATIC_BIN, GIT_HASH, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED, IS_ARCH_ARM64 if IS_WINDOWS: - CXXFLAGS = '/nologo /Zi /D WIN32 /D _WINDOWS /EHsc /GS /Gd /std:C++20' + CXXFLAGS = '/nologo /Zi /D WIN32 /D _WINDOWS /EHsc /GS /Gd /std:c++20' config.write( 'CC=cl\n' 'CXX=cl\n' @@ -2616,7 +2616,7 @@ def mk_config(): CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) - CXXFLAGS = '%s -std=C++20' % CXXFLAGS + CXXFLAGS = '%s -std=c++20' % CXXFLAGS CXXFLAGS = '%s -fvisibility=hidden -fvisibility-inlines-hidden -c' % CXXFLAGS FPMATH = test_fpmath(CXX) CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) @@ -2699,7 +2699,7 @@ def mk_config(): config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) - config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=C++20', ''))) + config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=c++20', ''))) config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('C_OUT_FLAG=-o \n') From 22d9bfad358eefaa2f7aa490983463f6b0dd022f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 23 Sep 2024 08:10:56 +0100 Subject: [PATCH 204/414] fix warning with iterators due to non-const comparator --- src/ast/rewriter/factor_equivs.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ast/rewriter/factor_equivs.h b/src/ast/rewriter/factor_equivs.h index 7c58f4abde8..d4d566ed6fe 100644 --- a/src/ast/rewriter/factor_equivs.h +++ b/src/ast/rewriter/factor_equivs.h @@ -103,11 +103,10 @@ class obj_equiv_class { m_first = false; return *this; } - bool operator==(const iterator& o) { + bool operator!=(const iterator& o) const { SASSERT(&m_ouf == &o.m_ouf); - return m_first == o.m_first && m_curr_id == o.m_curr_id; + return m_first != o.m_first || m_curr_id != o.m_curr_id; } - bool operator!=(const iterator& o) {return !(*this == o);} }; iterator begin(OBJ*o) { @@ -152,11 +151,10 @@ class obj_equiv_class { m_ouf.m_uf.is_root(m_rootnb) != true); return *this; } - bool operator==(const equiv_iterator& o) { + bool operator!=(const equiv_iterator& o) const { SASSERT(&m_ouf == &o.m_ouf); - return m_rootnb == o.m_rootnb; + return m_rootnb != o.m_rootnb; } - bool operator!=(const equiv_iterator& o) {return !(*this == o);} }; equiv_iterator begin() {return equiv_iterator(*this, 0);} From a62fede64ba641cb6158f3bfdd22fd86f87d55f9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 23 Sep 2024 08:17:58 +0100 Subject: [PATCH 205/414] remove a few default constructors --- src/ast/recfun_decl_plugin.cpp | 2 -- src/ast/seq_decl_plugin.h | 2 +- src/ast/sls/sls_valuation.h | 2 +- src/math/grobner/grobner.h | 4 ++-- src/math/lp/explanation.h | 2 +- src/math/lp/general_matrix.h | 2 +- src/math/lp/hnf_cutter.cpp | 3 +-- src/math/lp/implied_bound.h | 2 +- src/math/lp/indexed_vector.h | 2 +- src/math/lp/lar_term.h | 2 +- src/math/lp/nla_tangent_lemmas.h | 2 +- src/muz/base/dl_util.h | 1 - src/muz/spacer/spacer_unsat_core_plugin.h | 2 +- src/smt/fingerprints.h | 2 +- src/tactic/aig/aig.cpp | 1 - src/util/mpq.h | 6 +++--- 16 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 37e5d359ea4..a976ee3c9c8 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -170,8 +170,6 @@ namespace recfun { vector m_branches; public: - case_state() : m_reg(), m_branches() {} - bool empty() const { return m_branches.empty(); } branch pop_branch() { diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index c6550d33aad..c08ed6f7a5e 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -446,7 +446,7 @@ class seq_util { /* Default constructor of invalid info. */ - info() {} + info() = default; /* Used for constructing either an invalid info that is only used to indicate uninitialized entry, or valid but unknown info value. diff --git a/src/ast/sls/sls_valuation.h b/src/ast/sls/sls_valuation.h index 66d7e0281cc..f10fd8f0bfc 100644 --- a/src/ast/sls/sls_valuation.h +++ b/src/ast/sls/sls_valuation.h @@ -33,7 +33,7 @@ namespace bv { unsigned nw = 0; unsigned mask = 0; - bvect() {} + bvect() = default; bvect(unsigned sz) : svector(sz, (unsigned)0) {} void set_bw(unsigned bw); diff --git a/src/math/grobner/grobner.h b/src/math/grobner/grobner.h index 6adffea7cc5..83c0d8a5e83 100644 --- a/src/math/grobner/grobner.h +++ b/src/math/grobner/grobner.h @@ -48,7 +48,7 @@ class grobner { friend class grobner; friend struct monomial_lt; - monomial() {} + monomial() = default; public: rational const & get_coeff() const { return m_coeff; } unsigned get_degree() const { return m_vars.size(); } @@ -63,7 +63,7 @@ class grobner { ptr_vector m_monomials; //!< sorted monomials v_dependency * m_dep; //!< justification for the equality friend class grobner; - equation() {} + equation() = default; public: unsigned get_num_monomials() const { return m_monomials.size(); } monomial const * get_monomial(unsigned idx) const { return m_monomials[idx]; } diff --git a/src/math/lp/explanation.h b/src/math/lp/explanation.h index 960c5fb4ab5..9d8099c1e9c 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -29,7 +29,7 @@ class explanation { vector> m_vector; ci_set m_set; public: - explanation() {} + explanation() = default; template explanation(const T& t) { diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index fb1030e6b10..e42f01ad72c 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -180,7 +180,7 @@ class general_matrix { m_column_permutation.transpose_from_left(j, k); } - general_matrix(){} + general_matrix() = default; general_matrix(unsigned n) : m_row_permutation(n), m_column_permutation(n), diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index b120b7aac0e..7f1af2aaa67 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -19,8 +19,7 @@ namespace lp { lia(lia), lra(lia.lra), m_settings(lia.settings()), - m_abs_max(zero_of_type()), - m_var_register() {} + m_abs_max(zero_of_type()) {} bool hnf_cutter::is_full() const { return diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 195ec03599d..6d7fc54b9d1 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -44,7 +44,7 @@ class implied_bound { k = static_cast(k / 2); return k; } - implied_bound(){} + implied_bound() = default; implied_bound(const mpq & a, unsigned j, bool is_lower_bound, diff --git a/src/math/lp/indexed_vector.h b/src/math/lp/indexed_vector.h index 9f3119e9a16..5740fdc217a 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -76,7 +76,7 @@ class indexed_vector { } - indexed_vector() {} + indexed_vector() = default; void resize(unsigned data_size); unsigned data_size() const { diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 6547377d365..87a0c282a37 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -70,7 +70,7 @@ class lar_term { } } // constructors - lar_term() {} + lar_term() = default; lar_term(const vector>& coeffs) { for (auto const& p : coeffs) { add_monomial(p.first, p.second); diff --git a/src/math/lp/nla_tangent_lemmas.h b/src/math/lp/nla_tangent_lemmas.h index 1895b3fcba6..daa45a41aec 100644 --- a/src/math/lp/nla_tangent_lemmas.h +++ b/src/math/lp/nla_tangent_lemmas.h @@ -17,7 +17,7 @@ struct point { rational x; rational y; point(const rational& a, const rational& b): x(a), y(b) {} - point() {} + point() = default; inline point& operator*=(rational a) { x *= a; y *= a; diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 401d8e81670..ac1bed51e67 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -347,7 +347,6 @@ namespace datalog { class rule_counter : public var_counter { public: - rule_counter(){} void count_rule_vars(const rule * r, int coef = 1); unsigned get_max_rule_var(const rule& r); }; diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index a7ea4c89bb6..b844cdd469a 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -33,7 +33,7 @@ namespace spacer { unsat_core_plugin(unsat_core_learner& learner); virtual ~unsat_core_plugin() = default; virtual void compute_partial_core(proof* step) = 0; - virtual void finalize(){}; + virtual void finalize(){} unsat_core_learner& m_ctx; }; diff --git a/src/smt/fingerprints.h b/src/smt/fingerprints.h index a602f25cd65..6a0bc1ccd2a 100644 --- a/src/smt/fingerprints.h +++ b/src/smt/fingerprints.h @@ -32,7 +32,7 @@ namespace smt { enode** m_args = nullptr; friend class fingerprint_set; - fingerprint() {} + fingerprint() = default; public: fingerprint(region & r, void * d, unsigned d_hash, expr* def, unsigned n, enode * const * args); void * get_data() const { return m_data; } diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 433120b48b6..e599829694a 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -50,7 +50,6 @@ struct aig { unsigned m_ref_count; aig_lit m_children[2]; unsigned m_mark:1; - aig() {} }; #if Z3DEBUG diff --git a/src/util/mpq.h b/src/util/mpq.h index 286c2758d86..87ee5e17da4 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -23,12 +23,12 @@ Revision History: class mpq { mpz m_num; - mpz m_den; + mpz m_den = 1; friend class mpq_manager; friend class mpq_manager; public: - mpq(int v):m_num(v), m_den(1) {} - mpq():m_den(1) {} + mpq(int v) : m_num(v) {} + mpq() = default; mpq(mpq &&) noexcept = default; mpq & operator=(mpq&&) = default; mpq & operator=(mpq const&) = delete; From 4b4a28239f9a06e9e10049f73f65e9859268b0a6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 11:45:11 +0100 Subject: [PATCH 206/414] Add const qualifiers to comparison operators and update iterator equality checks in various classes --- src/api/c++/z3++.h | 26 +++++++++++++------------- src/muz/rel/dl_base.h | 16 ++++++---------- src/sat/sat_solver/inc_sat_solver.cpp | 2 -- src/sat/tactic/sat_tactic.cpp | 1 - 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index bb148d71288..85d5fce6285 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1603,10 +1603,10 @@ namespace z3 { unsigned i; public: iterator(expr& e, unsigned i): e(e), i(i) {} - bool operator==(iterator const& other) noexcept { + bool operator==(iterator const& other) const noexcept { return i == other.i; } - bool operator!=(iterator const& other) noexcept { + bool operator!=(iterator const& other) const noexcept { return i != other.i; } expr operator*() const { return e.arg(i); } @@ -1703,28 +1703,28 @@ namespace z3 { inline expr operator||(bool a, expr const & b) { return b.ctx().bool_val(a) || b; } - inline expr operator==(expr const & a, expr const & b) { + inline expr operator==(expr const & a, expr const & b) const { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } - inline expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } + inline expr operator==(expr const & a, int b) const { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } inline expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) == b; } - inline expr operator==(expr const & a, double b) { assert(a.is_fpa()); return a == a.ctx().fpa_val(b); } - inline expr operator==(double a, expr const & b) { assert(b.is_fpa()); return b.ctx().fpa_val(a) == b; } + inline expr operator==(expr const & a, double b) const { assert(a.is_fpa()); return a == a.ctx().fpa_val(b); } + inline expr operator==(double a, expr const & b) const { assert(b.is_fpa()); return b.ctx().fpa_val(a) == b; } - inline expr operator!=(expr const & a, expr const & b) { + inline expr operator!=(expr const & a, expr const & b) const { check_context(a, b); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_distinct(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } - inline expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } - inline expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } - inline expr operator!=(expr const & a, double b) { assert(a.is_fpa()); return a != a.ctx().fpa_val(b); } - inline expr operator!=(double a, expr const & b) { assert(b.is_fpa()); return b.ctx().fpa_val(a) != b; } + inline expr operator!=(expr const & a, int b) const { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } + inline expr operator!=(int a, expr const & b) const { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } + inline expr operator!=(expr const & a, double b) const { assert(a.is_fpa()); return a != a.ctx().fpa_val(b); } + inline expr operator!=(double a, expr const & b) const { assert(b.is_fpa()); return b.ctx().fpa_val(a) != b; } inline expr operator+(expr const & a, expr const & b) { check_context(a, b); @@ -2957,10 +2957,10 @@ namespace z3 { expr_vector const * operator->() const { return &(operator*()); } expr_vector const& operator*() const noexcept { return m_cube; } - bool operator==(cube_iterator const& other) noexcept { + bool operator==(cube_iterator const& other) const noexcept { return other.m_end == m_end; }; - bool operator!=(cube_iterator const& other) noexcept { + bool operator!=(cube_iterator const& other) const noexcept { return other.m_end != m_end; }; diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index 158df4906ff..d1d946d106f 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -1113,7 +1113,7 @@ namespace datalog { virtual row_interface & operator*() = 0; virtual void operator++() = 0; - virtual bool operator==(const iterator_core & it) { + virtual bool operator==(const iterator_core & it) const { //we worry about the equality operator only because of checking //the equality with the end() iterator return is_finished() && it.is_finished(); @@ -1142,7 +1142,7 @@ namespace datalog { virtual table_element operator*() = 0; virtual void operator++() = 0; - virtual bool operator==(const row_iterator_core & it) { + virtual bool operator==(const row_iterator_core & it) const { //we worry about the equality operator only because of checking //the equality with the end() iterator return is_finished() && it.is_finished(); @@ -1169,10 +1169,8 @@ namespace datalog { { return &(*(*m_core)); } iterator & operator++() { ++(*m_core); return *this; } - bool operator==(const iterator & it) - { return (*m_core)==(*it.m_core); } - bool operator!=(const iterator & it) - { return !operator==(it); } + bool operator==(const iterator & it) const { return (*m_core)==(*it.m_core); } + bool operator!=(const iterator & it) const { return !operator==(it); } }; class row_iterator { @@ -1187,10 +1185,8 @@ namespace datalog { { return *(*m_core); } row_iterator & operator++() { ++(*m_core); return *this; } - bool operator==(const row_iterator & it) - { return (*m_core)==(*it.m_core); } - bool operator!=(const row_iterator & it) - { return !operator==(it); } + bool operator==(const row_iterator & it) const { return (*m_core)==(*it.m_core); } + bool operator!=(const row_iterator & it) const { return !operator==(it); } }; virtual iterator begin() const = 0; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 19482c2863a..5e0d109183b 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -179,8 +179,6 @@ class inc_sat_solver : public solver { void initialize_values() { if (m_mcs.back()) m_mcs.back()->convert_initialize_value(m_var2value); - if (m_mcs.back()) - m_mcs.back()->display(verbose_stream()); for (auto & [var, value] : m_var2value) { sat::bool_var b = m_map.to_bool_var(var); diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index e997cbc2cba..6a48bc8ed96 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -265,7 +265,6 @@ class sat_tactic : public tactic { } void user_propagate_initialize_value(expr* var, expr* value) override { - verbose_stream() << "initialize-value\n"; m_var2value.push_back({ expr_ref(var, m), expr_ref(value, m) }); } From 737c2208fad6c006a391897663adba71215f066f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 23 Sep 2024 12:13:52 +0100 Subject: [PATCH 207/414] delete more default constructors reduces code size by 0.1% --- src/ackermannization/ackr_bound_probe.cpp | 2 -- src/ast/converters/model_converter.h | 3 --- src/ast/recfun_decl_plugin.cpp | 1 - src/ast/recfun_decl_plugin.h | 1 - src/ast/rewriter/var_subst.h | 2 +- src/ast/simplifiers/linear_equation.h | 2 +- src/cmd_context/cmd_context.h | 2 +- src/math/interval/mod_interval.h | 4 ++-- src/math/lp/factorization.h | 2 +- src/math/lp/indexed_value.h | 2 +- src/math/lp/nex.h | 2 +- src/math/lp/numeric_pair.h | 5 +---- src/math/lp/permutation_matrix.h | 2 +- src/math/lp/stacked_vector.h | 3 --- src/math/lp/static_matrix.h | 2 +- src/math/lp/var_eqs.h | 2 +- src/math/lp/var_register.h | 2 +- src/math/polynomial/polynomial.cpp | 14 ++++---------- .../polynomial/upolynomial_factorization_int.h | 2 +- src/math/simplex/model_based_opt.h | 6 +++--- src/muz/base/dl_util.cpp | 2 -- src/muz/fp/dl_cmds.cpp | 3 +-- src/muz/rel/dl_mk_simple_joins.cpp | 2 -- src/muz/rel/dl_relation_manager.cpp | 1 - src/muz/rel/dl_sieve_relation.h | 2 +- src/muz/rel/karr_relation.cpp | 2 -- src/muz/rel/udoc_relation.cpp | 4 ---- src/muz/spacer/spacer_arith_kernel.cpp | 2 -- src/muz/transforms/dl_mk_magic_sets.h | 2 +- src/opt/maxcore.cpp | 2 +- src/sat/sat_ddfw.h | 1 - src/sat/smt/arith_solver.h | 8 -------- src/sat/smt/q_mam.h | 2 +- src/smt/params/theory_array_params.h | 2 -- src/smt/theory_lra.cpp | 11 ----------- src/tactic/core/pb_preprocess_tactic.cpp | 2 +- src/util/array.h | 8 ++++---- src/util/buffer.h | 2 +- src/util/inf_rational.h | 2 +- src/util/sat_literal.h | 3 +-- src/util/stacked_value.h | 2 +- 41 files changed, 35 insertions(+), 91 deletions(-) diff --git a/src/ackermannization/ackr_bound_probe.cpp b/src/ackermannization/ackr_bound_probe.cpp index b518adccce6..48a54543fe5 100644 --- a/src/ackermannization/ackr_bound_probe.cpp +++ b/src/ackermannization/ackr_bound_probe.cpp @@ -60,8 +60,6 @@ class ackr_bound_probe : public probe { }; public: - ackr_bound_probe() {} - result operator()(goal const & g) override { proc p(g.m()); unsigned sz = g.size(); diff --git a/src/ast/converters/model_converter.h b/src/ast/converters/model_converter.h index 354b5185436..baf5e422d52 100644 --- a/src/ast/converters/model_converter.h +++ b/src/ast/converters/model_converter.h @@ -71,9 +71,6 @@ class model_converter : public converter { void display_del(std::ostream& out, func_decl* f) const; void display_add(std::ostream& out, ast_manager& m); public: - - model_converter() {} - void set_completion(bool f) { m_completion = f; } virtual void operator()(model_ref & m) = 0; diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index a976ee3c9c8..5f2534a5107 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -417,7 +417,6 @@ namespace recfun { } namespace decl { - plugin::plugin() : decl_plugin(), m_defs(), m_case_defs() {} plugin::~plugin() { finalize(); } void plugin::finalize() { diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index e078c7ef98b..73e2b5244f8 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -173,7 +173,6 @@ namespace recfun { void compute_scores(expr* e, obj_map& scores); public: - plugin(); ~plugin() override; void finalize() override; diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index e9d39740dd0..aa905cb0e3d 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -94,7 +94,7 @@ class expr_free_vars { ptr_vector m_sorts; ptr_vector m_todo; public: - expr_free_vars() {} + expr_free_vars() = default; expr_free_vars(expr* e) { (*this)(e); } void reset(); void operator()(expr* e); diff --git a/src/ast/simplifiers/linear_equation.h b/src/ast/simplifiers/linear_equation.h index 5220a1e46fc..662c8c9fcc3 100644 --- a/src/ast/simplifiers/linear_equation.h +++ b/src/ast/simplifiers/linear_equation.h @@ -35,7 +35,7 @@ class linear_equation { mpz * m_as; // precise coefficients double * m_approx_as; // approximated coefficients var * m_xs; // var ids - linear_equation() {} + linear_equation() = default; public: unsigned size() const { return m_size; } mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index d113eb8810a..6dc60c9dd96 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -47,7 +47,7 @@ class func_decls { bool signatures_collide(func_decl* f, func_decl* g) const; bool signatures_collide(unsigned n, sort*const* domain, sort* range, func_decl* g) const; public: - func_decls() {} + func_decls() = default; func_decls(ast_manager & m, func_decl * f); void finalize(ast_manager & m); bool contains(func_decl * f) const; diff --git a/src/math/interval/mod_interval.h b/src/math/interval/mod_interval.h index ed189fe7b6e..28b69b003e3 100644 --- a/src/math/interval/mod_interval.h +++ b/src/math/interval/mod_interval.h @@ -26,7 +26,7 @@ namespace bv { bool tight = true; interval_tpl(T const& l, T const& h, unsigned sz, bool tight = false): l(l), h(h), sz(sz), tight(tight) {} - interval_tpl() {} + interval_tpl() = default; bool invariant() const { return @@ -167,7 +167,7 @@ namespace bv { iinterval i; rinterval r; - interval() {} + interval() = default; interval(rational const& l, rational const& h, unsigned sz, bool tight = false) { if (sz <= 64) { diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index e1096a75f60..be3a024e3a4 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -24,7 +24,7 @@ class factor { factor_type m_type = factor_type::VAR; bool m_sign = false; public: - factor() { } + factor() = default; explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t) {} unsigned var() const { return m_var; } factor_type type() const { return m_type; } diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index 92d8f2adf1f..c0194670d66 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -29,7 +29,7 @@ class indexed_value { // m_other is the offset of the corresponding element in its vector : for a row element it points to the column element offset, // for a column element it points to the row element offset unsigned m_other; - indexed_value() {} + indexed_value() = default; indexed_value(T v, unsigned i, unsigned other) : m_value(v), m_index(i), m_other(other) { diff --git a/src/math/lp/nex.h b/src/math/lp/nex.h index e4087a407fd..7217374d5f4 100644 --- a/src/math/lp/nex.h +++ b/src/math/lp/nex.h @@ -92,7 +92,7 @@ class nex { virtual const rational& coeff() const { return rational::one(); } #ifdef Z3DEBUG - virtual void sort() {}; + virtual void sort() {} #endif bool virtual is_linear() const = 0; }; diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 93780f7be39..dbebbf99862 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -116,10 +116,7 @@ template struct numeric_pair { T x; T y; - // empty constructor - numeric_pair() {} - // another constructor - + numeric_pair() = default; numeric_pair(T xp, T yp) : x(xp), y(yp) {} template diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index a3fff4f7fa5..df0897dbec7 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -52,7 +52,7 @@ class permutation_matrix }; public: - permutation_matrix() {} + permutation_matrix() = default; permutation_matrix(unsigned length); permutation_matrix(unsigned length, vector const & values); diff --git a/src/math/lp/stacked_vector.h b/src/math/lp/stacked_vector.h index ecd61eb10fb..69ff9926388 100644 --- a/src/math/lp/stacked_vector.h +++ b/src/math/lp/stacked_vector.h @@ -98,9 +98,6 @@ template < typename B> class stacked_vector { } } public: - - stacked_vector() { } - ref operator[] (unsigned a) { return ref(*this, a); } diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index 42dd476b5d4..5bbd000cabb 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -105,7 +105,7 @@ class static_matrix void init_row_columns(unsigned m, unsigned n); // constructor with no parameters - static_matrix() {} + static_matrix() = default; // constructor static_matrix(unsigned m, unsigned n): m_vector_of_row_offsets(n, -1) { diff --git a/src/math/lp/var_eqs.h b/src/math/lp/var_eqs.h index a639a9a5ee2..56c39c46028 100644 --- a/src/math/lp/var_eqs.h +++ b/src/math/lp/var_eqs.h @@ -81,7 +81,7 @@ class var_eqs { mutable stats m_stats; public: - var_eqs(): m_merge_handler(nullptr), m_uf(*this), m_stack() {} + var_eqs(): m_merge_handler(nullptr), m_uf(*this) {} /** \brief push a scope */ void push() { diff --git a/src/math/lp/var_register.h b/src/math/lp/var_register.h index bd7e6efe830..84ac8236d5f 100644 --- a/src/math/lp/var_register.h +++ b/src/math/lp/var_register.h @@ -27,7 +27,7 @@ class ext_var_info { bool m_is_integer; std::string m_name; public: - ext_var_info() {} + ext_var_info() = default; ext_var_info(unsigned j): ext_var_info(j, true) {} ext_var_info(unsigned j , bool is_int) : m_external_j(j), m_is_integer(is_int) {} ext_var_info(unsigned j , bool is_int, std::string name) : m_external_j(j), m_is_integer(is_int), m_name(name) {} diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 74c5e05d785..76f717da08c 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -91,7 +91,7 @@ namespace polynomial { */ class power : public std::pair { public: - power():std::pair() {} + power() = default; power(var v, unsigned d):std::pair(v, d) {} var get_var() const { return first; } unsigned degree() const { return second; } @@ -1895,7 +1895,7 @@ namespace polynomial { Invoke add(...), addmul(...) several times, and then invoke mk() to obtain the final polynomial. */ class som_buffer { - imp * m_owner; + imp * m_owner = nullptr; monomial2pos m_m2pos; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; @@ -1939,8 +1939,6 @@ namespace polynomial { } public: - som_buffer():m_owner(nullptr) {} - void reset() { if (empty()) return; @@ -2236,12 +2234,10 @@ namespace polynomial { In this buffer, each monomial can be added at most once. */ class cheap_som_buffer { - imp * m_owner; + imp * m_owner = nullptr; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; public: - cheap_som_buffer():m_owner(nullptr) {} - void set_owner(imp * o) { m_owner = o; } bool empty() const { return m_tmp_ms.empty(); } @@ -3072,11 +3068,9 @@ namespace polynomial { } class newton_interpolator_vector { - imp * m_imp; + imp * m_imp = nullptr; ptr_vector m_data; public: - newton_interpolator_vector():m_imp(nullptr) {} - ~newton_interpolator_vector() { flush(); } diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index a65e5ee62f7..f47610b65dc 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -64,7 +64,7 @@ namespace upolynomial { public: - factorization_degree_set() { } + factorization_degree_set() = default; factorization_degree_set(zp_factors const & factors) { diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 35516283d95..891df598b48 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -43,7 +43,7 @@ namespace opt { struct var { unsigned m_id { 0 }; rational m_coeff; - var() {} + var() = default; var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} struct compare { bool operator()(var x, var y) { @@ -76,11 +76,11 @@ namespace opt { // A definition is a linear term of the form (vars + coeff) / div struct def { - def(): m_div(1) {} + def() = default; def(row const& r, unsigned x); vector m_vars; rational m_coeff; - rational m_div; + rational m_div{1}; def operator+(def const& other) const; def operator/(unsigned n) const { return *this / rational(n); } def operator/(rational const& n) const; diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 03a8f1c9956..7b01c2b3f4d 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -393,8 +393,6 @@ namespace datalog { class skip_model_converter : public model_converter { public: - skip_model_converter() {} - model_converter * translate(ast_translation & translator) override { return alloc(skip_model_converter); } diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index f40400167e6..508b79acea2 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -56,8 +56,7 @@ struct dl_context { m_cmd(ctx), m_collected_cmds(collected_cmds), m_ref_count(0), - m_decl_plugin(nullptr), - m_trail() {} + m_decl_plugin(nullptr) {} void inc_ref() { ++m_ref_count; diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index 37fd3ef3616..f60760fab54 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -54,8 +54,6 @@ namespace datalog { var_idx_set m_all_nonlocal_vars; rule_vector m_rules; - pair_info() {} - pair_info & operator=(const pair_info &) = delete; bool can_be_joined() const { return m_consumers > 0; diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 8de25c8f38e..5e96dc487b0 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -1043,7 +1043,6 @@ namespace datalog { class relation_manager::null_signature_table_project_fn : public table_transformer_fn { const table_signature m_empty_sig; public: - null_signature_table_project_fn() : m_empty_sig() {} table_base * operator()(const table_base & t) override { relation_manager & m = t.get_plugin().get_manager(); table_base * res = m.mk_empty_table(m_empty_sig); diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h index 72c246e99cd..0166bec981e 100644 --- a/src/muz/rel/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -36,7 +36,7 @@ namespace datalog { /** Create uninitialized rel_spec. */ - rel_spec() {} + rel_spec() = default; /** \c inner_kind==null_family_id means we will not specify a relation kind when requesting the relation object from the relation_manager. diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp index d0105696083..7a2ba51797d 100644 --- a/src/muz/rel/karr_relation.cpp +++ b/src/muz/rel/karr_relation.cpp @@ -674,8 +674,6 @@ namespace datalog { class karr_relation_plugin::union_fn : public relation_union_fn { public: - union_fn() {} - void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { karr_relation& r = get(_r); diff --git a/src/muz/rel/udoc_relation.cpp b/src/muz/rel/udoc_relation.cpp index 2b28a6c8213..8240f7f9e10 100644 --- a/src/muz/rel/udoc_relation.cpp +++ b/src/muz/rel/udoc_relation.cpp @@ -491,8 +491,6 @@ namespace datalog { } class udoc_plugin::union_fn : public relation_union_fn { public: - union_fn() {} - void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); udoc_relation& r = get(_r); @@ -1040,8 +1038,6 @@ namespace datalog { class udoc_plugin::join_project_and_fn : public relation_join_fn { public: - join_project_and_fn() {} - relation_base* operator()(relation_base const& t1, relation_base const& t2) override { udoc_relation *result = get(t1.clone()); result->get_udoc().intersect(result->get_dm(), get(t2).get_udoc()); diff --git a/src/muz/spacer/spacer_arith_kernel.cpp b/src/muz/spacer/spacer_arith_kernel.cpp index fdcb6b0d650..25aff02735f 100644 --- a/src/muz/spacer/spacer_arith_kernel.cpp +++ b/src/muz/spacer/spacer_arith_kernel.cpp @@ -45,8 +45,6 @@ bool spacer_arith_kernel::compute_kernel() { namespace { class simplex_arith_kernel_plugin : public spacer_arith_kernel::plugin { public: - simplex_arith_kernel_plugin() {} - bool compute_kernel(const spacer_matrix &in, spacer_matrix &out, vector &basics) override { using qmatrix = simplex::sparse_matrix; diff --git a/src/muz/transforms/dl_mk_magic_sets.h b/src/muz/transforms/dl_mk_magic_sets.h index aa4c302745c..3a8a92580dc 100644 --- a/src/muz/transforms/dl_mk_magic_sets.h +++ b/src/muz/transforms/dl_mk_magic_sets.h @@ -65,7 +65,7 @@ namespace datalog { func_decl * m_pred; adornment m_adornment; - adornment_desc() {} + adornment_desc() = default; adornment_desc(func_decl * pred) : m_pred(pred) {} adornment_desc(func_decl * pred, const adornment & a) : m_pred(pred), m_adornment(a) {} diff --git a/src/opt/maxcore.cpp b/src/opt/maxcore.cpp index d29da86f723..84906cbaa65 100644 --- a/src/opt/maxcore.cpp +++ b/src/opt/maxcore.cpp @@ -777,7 +777,7 @@ class maxcore : public maxsmt_solver_base { ptr_vector es; unsigned k = 0; rational weight; - bound_info() {} + bound_info() = default; bound_info(ptr_vector const& es, unsigned k, rational const& weight): es(es), k(k), weight(weight) {} bound_info(expr_ref_vector const& es, unsigned k, rational const& weight): diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ff86e9b8c17..3454d47dade 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -95,7 +95,6 @@ namespace sat { }; struct var_info { - var_info() {} bool m_value = false; double m_reward = 0; double m_last_reward = 0; diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index f940af8b4ab..d4850e67d72 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -428,19 +428,11 @@ namespace arith { * Facility to put a small box around integer variables used in branch and bounds. */ - struct bound_info { - rational m_offset; - unsigned m_range; - bound_info() {} - bound_info(rational const& o, unsigned r) :m_offset(o), m_range(r) {} - }; unsigned m_bounded_range_idx; // current size of bounded range. literal m_bounded_range_lit; // current bounded range literal expr_ref_vector m_bound_terms; // predicates used for bounds expr_ref m_bound_predicate; - obj_map m_predicate2term; - obj_map m_term2bound_info; bool m_model_is_initialized = false; unsigned small_lemma_size() const { return get_config().m_arith_small_lemma_size; } diff --git a/src/sat/smt/q_mam.h b/src/sat/smt/q_mam.h index 9fc6d18f18f..e642f9e44aa 100644 --- a/src/sat/smt/q_mam.h +++ b/src/sat/smt/q_mam.h @@ -39,7 +39,7 @@ namespace q { class mam { friend class mam_impl; - mam() {} + mam() = default; public: diff --git a/src/smt/params/theory_array_params.h b/src/smt/params/theory_array_params.h index b6da32ba5f8..8c56093a838 100644 --- a/src/smt/params/theory_array_params.h +++ b/src/smt/params/theory_array_params.h @@ -41,8 +41,6 @@ struct theory_array_params { unsigned m_array_lazy_ieq_delay = 10; bool m_array_fake_support = false; // fake support for all array operations to pretend they are satisfiable. - theory_array_params() {} - void updt_params(params_ref const & _p); void display(std::ostream & out) const; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6de623bb780..135744f0256 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3806,19 +3806,10 @@ class theory_lra::imp { * Facility to put a small box around integer variables used in branch and bounds. */ - struct bound_info { - rational m_offset; - unsigned m_range; - bound_info() {} - bound_info(rational const& o, unsigned r):m_offset(o), m_range(r) {} - }; unsigned m_bounded_range_idx; // current size of bounded range. literal m_bounded_range_lit; // current bounded range literal expr_ref_vector m_bound_terms; // predicates used for bounds expr_ref m_bound_predicate; - - obj_map m_predicate2term; - obj_map m_term2bound_info; unsigned init_range() const { return 5; } unsigned max_range() const { return 20; } @@ -3828,8 +3819,6 @@ class theory_lra::imp { m_bounded_range_lit = null_literal; m_bound_terms.reset(); m_bound_predicate = nullptr; - m_predicate2term.reset(); - m_term2bound_info.reset(); } diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 05ed6eee903..2d82a12a16a 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -25,7 +25,7 @@ Module Name: #include "ast/ast_pp.h" class pb_preprocess_tactic : public tactic { - struct rec { unsigned_vector pos, neg; rec() { } }; + struct rec { unsigned_vector pos, neg; }; typedef obj_map var_map; ast_manager& m; expr_ref_vector m_trail; diff --git a/src/util/array.h b/src/util/array.h index 2954035f641..d844692ef7f 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -26,7 +26,7 @@ class array { private: #define ARRAY_SIZE_IDX -1 - T * m_data; + T * m_data = nullptr; void destroy_elements() { iterator it = begin(); iterator e = end(); @@ -71,7 +71,7 @@ class array { typedef T * iterator; typedef const T * const_iterator; - array():m_data(nullptr) {} + array() = default; /** \brief Store the array in the given chunk of memory (mem). @@ -193,7 +193,7 @@ class array { template class ptr_array : public array { public: - ptr_array() {} + ptr_array() = default; ptr_array(void * mem, unsigned sz, T * const * vs):array(mem, sz, vs) {} template ptr_array(Allocator & a, unsigned sz, T * const * vs):array(a, sz, vs) {} @@ -205,7 +205,7 @@ class ptr_array : public array { template class sarray : public array { public: - sarray() {} + sarray() = default; sarray(void * mem, unsigned sz, T const * vs):array(mem, sz, vs) {} template sarray(Allocator & a, unsigned sz, T const * vs):array(a, sz, vs) {} diff --git a/src/util/buffer.h b/src/util/buffer.h index 3ca597af2e9..b71717fe65e 100644 --- a/src/util/buffer.h +++ b/src/util/buffer.h @@ -275,6 +275,6 @@ class ptr_buffer : public buffer { template class sbuffer : public buffer { public: - sbuffer(): buffer() {} + sbuffer() = default; sbuffer(unsigned sz, const T& elem) : buffer(sz,elem) {} }; diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index 2d7963ff080..7e8487d9d90 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -65,7 +65,7 @@ class inf_rational { return s; } - inf_rational() {} + inf_rational() = default; explicit inf_rational(int n): m_first(rational(n)), diff --git a/src/util/sat_literal.h b/src/util/sat_literal.h index 58088e6281a..61ee5f65781 100644 --- a/src/util/sat_literal.h +++ b/src/util/sat_literal.h @@ -119,7 +119,7 @@ namespace sat { literal_set(literal_vector const& v) { for (unsigned i = 0; i < v.size(); ++i) insert(v[i]); } - literal_set() {} + literal_set() = default; literal_vector to_vector() const { literal_vector result; for (literal lit : *this) result.push_back(lit); @@ -146,7 +146,6 @@ namespace sat { literal operator*() const { return to_literal(*m_it); } iterator& operator++() { ++m_it; return *this; } iterator operator++(int) { iterator tmp = *this; ++m_it; return tmp; } - bool operator==(iterator const& it) const { return m_it == it.m_it; } bool operator!=(iterator const& it) const { return m_it != it.m_it; } }; iterator begin() const { return iterator(m_set.begin()); } diff --git a/src/util/stacked_value.h b/src/util/stacked_value.h index 89be6fa5384..a3a0d933b65 100644 --- a/src/util/stacked_value.h +++ b/src/util/stacked_value.h @@ -52,7 +52,7 @@ template class stacked_value { } } - stacked_value() {} + stacked_value() = default; stacked_value(const T& m) { m_value = m; } From 499ed5d8445607c03c4bd11e5cba27a91dfec578 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 23 Sep 2024 12:57:54 +0100 Subject: [PATCH 208/414] remove unneeded iterator functions --- src/ast/euf/euf_ac_plugin.h | 3 +-- src/ast/euf/euf_enode.h | 6 ++---- src/ast/for_each_expr.cpp | 24 ++++++++---------------- src/ast/for_each_expr.h | 2 -- src/math/dd/dd_pdd.h | 4 +--- src/math/hilbert/heap_trie.h | 1 - src/math/hilbert/hilbert_basis.cpp | 2 -- src/math/hilbert/hilbert_basis.h | 1 - src/math/lp/emonics.h | 7 ++----- src/math/lp/explanation.h | 5 ++--- src/math/lp/factorization.cpp | 10 ++++------ src/math/lp/factorization.h | 3 +-- src/math/lp/lar_constraints.h | 2 -- src/math/lp/lar_term.h | 3 +-- src/math/lp/var_eqs.h | 1 - src/math/simplex/bit_matrix.h | 2 -- src/math/simplex/sparse_matrix.h | 2 -- src/muz/rel/dl_table.cpp | 2 +- src/qe/qe.h | 1 - src/sat/dimacs.h | 1 - src/sat/sat_clause.h | 1 - src/sat/smt/pb_constraint.h | 1 - src/smt/smt_enode.h | 3 +-- src/util/approx_set.h | 4 ---- src/util/bit_vector.h | 1 - src/util/chashtable.h | 1 - src/util/distribution.h | 1 - src/util/dlist.h | 7 +------ src/util/list.h | 1 - src/util/scoped_vector.h | 3 +-- src/util/uint_set.h | 1 - src/util/vector.h | 6 ------ 32 files changed, 26 insertions(+), 86 deletions(-) diff --git a/src/ast/euf/euf_ac_plugin.h b/src/ast/euf/euf_ac_plugin.h index 2963c1f8b0e..d55d7dbb465 100644 --- a/src/ast/euf/euf_ac_plugin.h +++ b/src/ast/euf/euf_ac_plugin.h @@ -61,8 +61,7 @@ namespace euf { node* operator*() { return m_first; } iterator& operator++() { if (!m_last) m_last = m_first; m_first = m_first->next; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& other) const { return m_last == other.m_last && m_first == other.m_first; } - bool operator!=(iterator const& other) const { return !(*this == other); } + bool operator!=(iterator const& other) const { return m_last != other.m_last || m_first != other.m_first; } }; equiv(node& _n) :n(_n) {} equiv(node* _n) :n(*_n) {} diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index 50a7ce479e3..af22a5b2c39 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -280,8 +280,7 @@ namespace euf { enode* operator*() { return m_first; } iterator& operator++() { if (!m_last) m_last = m_first; m_first = m_first->m_next; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& other) const { return m_last == other.m_last && m_first == other.m_first; } - bool operator!=(iterator const& other) const { return !(*this == other); } + bool operator!=(iterator const& other) const { return m_last != other.m_last || m_first != other.m_first; } }; enode_class(enode & _n):n(_n) {} enode_class(enode * _n):n(*_n) {} @@ -300,8 +299,7 @@ namespace euf { th_var_list const& operator*() { return *m_th_vars; } iterator& operator++() { m_th_vars = m_th_vars->get_next(); return *this; } iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; } - bool operator==(iterator const& other) const { return m_th_vars == other.m_th_vars; } - bool operator!=(iterator const& other) const { return !(*this == other); } + bool operator!=(iterator const& other) const { return m_th_vars != other.m_th_vars; } }; enode_th_vars(enode& _n) :n(_n) {} enode_th_vars(enode* _n) :n(*_n) {} diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 8feb217dbb3..ebad5760cb3 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -146,20 +146,16 @@ subterms::iterator& subterms::iterator::operator++() { return *this; } -bool subterms::iterator::operator==(iterator const& other) const { +bool subterms::iterator::operator!=(iterator const& other) const { // ignore state of visited if (other.m_esp->size() != m_esp->size()) { - return false; + return true; } for (unsigned i = m_esp->size(); i-- > 0; ) { if (m_esp->get(i) != other.m_esp->get(i)) - return false; + return true; } - return true; -} - -bool subterms::iterator::operator!=(iterator const& other) const { - return !(*this == other); + return false; } @@ -216,18 +212,14 @@ subterms_postorder::iterator& subterms_postorder::iterator::operator++() { return *this; } -bool subterms_postorder::iterator::operator==(iterator const& other) const { +bool subterms_postorder::iterator::operator!=(iterator const& other) const { // ignore state of visited if (other.m_es.size() != m_es.size()) { - return false; + return true; } for (unsigned i = m_es.size(); i-- > 0; ) { if (m_es.get(i) != other.m_es.get(i)) - return false; + return true; } - return true; -} - -bool subterms_postorder::iterator::operator!=(iterator const& other) const { - return !(*this == other); + return false; } diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 77b01e93928..97a1717558f 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -190,7 +190,6 @@ class subterms { expr* operator*(); iterator operator++(int); iterator& operator++(); - bool operator==(iterator const& other) const; bool operator!=(iterator const& other) const; }; @@ -220,7 +219,6 @@ class subterms_postorder { expr* operator*(); iterator operator++(int); iterator& operator++(); - bool operator==(iterator const& other) const; bool operator!=(iterator const& other) const; }; static subterms_postorder all(expr_ref_vector const& es) { return subterms_postorder(es, true); } diff --git a/src/math/dd/dd_pdd.h b/src/math/dd/dd_pdd.h index fa545c06ec0..4fe208429b9 100644 --- a/src/math/dd/dd_pdd.h +++ b/src/math/dd/dd_pdd.h @@ -571,8 +571,7 @@ namespace dd { pdd_monomial const* operator->() const { return &m_mono; } pdd_iterator& operator++() { next(); return *this; } pdd_iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(pdd_iterator const& other) const { return m_nodes == other.m_nodes; } - bool operator!=(pdd_iterator const& other) const { return !operator==(other); } + bool operator!=(pdd_iterator const& other) const { return m_nodes != other.m_nodes; } }; class pdd_linear_iterator { @@ -591,7 +590,6 @@ namespace dd { pointer operator->() const { return &m_mono; } pdd_linear_iterator& operator++() { next(); return *this; } pdd_linear_iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(pdd_linear_iterator const& other) const { return m_next == other.m_next; } bool operator!=(pdd_linear_iterator const& other) const { return m_next != other.m_next; } }; diff --git a/src/math/hilbert/heap_trie.h b/src/math/hilbert/heap_trie.h index b492fb7ee44..3feb68ba6df 100644 --- a/src/math/hilbert/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -369,7 +369,6 @@ class heap_trie { } iterator& operator++() { fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& it) const {return m_count == it.m_count; } bool operator!=(iterator const& it) const {return m_count != it.m_count; } private: diff --git a/src/math/hilbert/hilbert_basis.cpp b/src/math/hilbert/hilbert_basis.cpp index 586027103a5..308adf54fd1 100644 --- a/src/math/hilbert/hilbert_basis.cpp +++ b/src/math/hilbert/hilbert_basis.cpp @@ -455,7 +455,6 @@ class hilbert_basis::passive { offset_t operator*() const { return p.m_passive[m_idx]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; @@ -614,7 +613,6 @@ class hilbert_basis::passive2 { offset_t sos() const { return (p.hb.vec(pas()).weight().is_pos()?p.m_neg_sos:p.m_pos_sos)[p.m_psos[m_idx]]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; diff --git a/src/math/hilbert/hilbert_basis.h b/src/math/hilbert/hilbert_basis.h index 1bd87d60cb8..f743f32ca01 100644 --- a/src/math/hilbert/hilbert_basis.h +++ b/src/math/hilbert/hilbert_basis.h @@ -115,7 +115,6 @@ class hilbert_basis { offset_t operator*() const { return hb.m_basis[m_idx]; } iterator& operator++() { ++m_idx; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index 55086515dc8..f6326c0441f 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -51,9 +51,8 @@ class emonics { unsigned m_index; }; struct head_tail { - head_tail(): m_head(nullptr), m_tail(nullptr) {} - cell* m_head; - cell* m_tail; + cell* m_head = nullptr; + cell* m_tail = nullptr; }; struct hash_canonical { emonics& em; @@ -205,7 +204,6 @@ class emonics { monic & operator*() { return m.m_monics[m_cell->m_index]; } iterator& operator++() { m_touched = true; m_cell = m_cell->m_next; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& other) const { return m_cell == other.m_cell && m_touched == other.m_touched; } bool operator!=(iterator const& other) const { return m_cell != other.m_cell || m_touched != other.m_touched; } }; @@ -239,7 +237,6 @@ class emonics { } pf_iterator& operator++() { ++m_it; fast_forward(); return *this; } pf_iterator operator++(int) { pf_iterator tmp = *this; ++*this; return tmp; } - bool operator==(pf_iterator const& other) const { return m_it == other.m_it; } bool operator!=(pf_iterator const& other) const { return m_it != other.m_it; } }; diff --git a/src/math/lp/explanation.h b/src/math/lp/explanation.h index 9d8099c1e9c..850e59cd471 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -106,11 +106,10 @@ class explanation { iterator(bool run_on_vector, pair_vec::const_iterator vi, ci_set::iterator cii) : m_run_on_vector(run_on_vector), m_vi(vi), m_ci(cii) {} - bool operator==(const iterator &other) const { + bool operator!=(const iterator &other) const { SASSERT(m_run_on_vector == other.m_run_on_vector); - return m_run_on_vector? m_vi == other.m_vi : m_ci == other.m_ci; + return m_run_on_vector ? m_vi != other.m_vi : m_ci != other.m_ci; } - bool operator!=(const iterator &other) const { return !(*this == other); } }; iterator begin() const { diff --git a/src/math/lp/factorization.cpp b/src/math/lp/factorization.cpp index e1dcff626dd..89c16ec91ee 100644 --- a/src/math/lp/factorization.cpp +++ b/src/math/lp/factorization.cpp @@ -97,14 +97,12 @@ const_iterator_mon::const_iterator_mon(const bool_vector& mask, const factorizat m_ff(f) , m_full_factorization_returned(false) {} - -bool const_iterator_mon::operator==(const const_iterator_mon::self_type &other) const { + +bool const_iterator_mon::operator!=(const const_iterator_mon::self_type &other) const { return - m_full_factorization_returned == other.m_full_factorization_returned && - m_mask == other.m_mask; + m_full_factorization_returned != other.m_full_factorization_returned || + m_mask != other.m_mask; } - -bool const_iterator_mon::operator!=(const const_iterator_mon::self_type &other) const { return !(*this == other); } factorization const_iterator_mon::create_binary_factorization(factor j, factor k) const { factorization f(nullptr); diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index be3a024e3a4..128b269fae6 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -88,8 +88,7 @@ struct const_iterator_mon { self_type operator++(int); const_iterator_mon(const bool_vector& mask, const factorization_factory *f); - - bool operator==(const self_type &other) const; + bool operator!=(const self_type &other) const; factorization create_binary_factorization(factor j, factor k) const; diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index b9069625f5c..1105382e7ac 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -203,7 +203,6 @@ class constraint_set { lar_base_constraint const* operator->() const { return &cs[m_index]; } iterator& operator++() { next(); return *this; } iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(iterator const& other) const { return m_index == other.m_index; } bool operator!=(iterator const& other) const { return m_index != other.m_index; } }; iterator begin() const { return iterator(cs, 0); } @@ -229,7 +228,6 @@ class constraint_set { constraint_index const* operator->() const { return &m_index; } iterator& operator++() { next(); return *this; } iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(iterator const& other) const { return m_index == other.m_index; } bool operator!=(iterator const& other) const { return m_index != other.m_index; } }; iterator begin() const { return iterator(cs, 0); } diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 87a0c282a37..8f8dd2f4aad 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -170,8 +170,7 @@ class lar_term { const_iterator operator++() { const_iterator i = *this; m_it++; return i; } const_iterator operator++(int) { m_it++; return *this; } const_iterator(u_map::iterator it) : m_it(it) {} - bool operator==(const const_iterator &other) const { return m_it == other.m_it; } - bool operator!=(const const_iterator &other) const { return !(*this == other); } + bool operator!=(const const_iterator &other) const { return m_it != other.m_it; } }; bool is_normalized() const { diff --git a/src/math/lp/var_eqs.h b/src/math/lp/var_eqs.h index 56c39c46028..b2b3f24cf51 100644 --- a/src/math/lp/var_eqs.h +++ b/src/math/lp/var_eqs.h @@ -302,7 +302,6 @@ class var_eqs { return signed_var(m_idx); } iterator& operator++() { m_idx = m_ve.m_uf.next(m_idx); m_touched = true; return *this; } - bool operator==(iterator const& other) const { return m_idx == other.m_idx && m_touched == other.m_touched; } bool operator!=(iterator const& other) const { return m_idx != other.m_idx || m_touched != other.m_touched; } }; diff --git a/src/math/simplex/bit_matrix.h b/src/math/simplex/bit_matrix.h index c827690ec43..13ca2b7bafc 100644 --- a/src/math/simplex/bit_matrix.h +++ b/src/math/simplex/bit_matrix.h @@ -71,7 +71,6 @@ class bit_matrix { unsigned const* operator->() const { return &m_column; } col_iterator& operator++() { next(); return *this; } col_iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(col_iterator const& other) const { return m_column == other.m_column; } bool operator!=(col_iterator const& other) const { return m_column != other.m_column; } }; @@ -88,7 +87,6 @@ class bit_matrix { row* operator->() { return &m_row; } row_iterator& operator++() { next(); return *this; } row_iterator operator++(int) { auto tmp = *this; next(); return tmp; } - bool operator==(row_iterator const& other) const { return m_index == other.m_index; } bool operator!=(row_iterator const& other) const { return m_index != other.m_index; } }; diff --git a/src/math/simplex/sparse_matrix.h b/src/math/simplex/sparse_matrix.h index 9333e9ddb3c..0dfd1a2ad5a 100644 --- a/src/math/simplex/sparse_matrix.h +++ b/src/math/simplex/sparse_matrix.h @@ -198,7 +198,6 @@ namespace simplex { row_entry * operator->() const { return &(operator*()); } row_iterator & operator++() { ++m_curr; move_to_used(); return *this; } row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; } - bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; } }; @@ -308,7 +307,6 @@ namespace simplex { row operator*() { return row(m_curr); } all_row_iterator & operator++() { m_curr++; move_to_next(); return *this; } all_row_iterator operator++(int) { all_row_iterator tmp = *this; ++*this; return tmp; } - bool operator==(all_row_iterator const& it) const { return m_curr == it.m_curr; } bool operator!=(all_row_iterator const& it) const { return m_curr != it.m_curr; } }; diff --git a/src/muz/rel/dl_table.cpp b/src/muz/rel/dl_table.cpp index e0ea17739eb..fec4e5c98b0 100644 --- a/src/muz/rel/dl_table.cpp +++ b/src/muz/rel/dl_table.cpp @@ -122,7 +122,7 @@ namespace datalog { m_end(t.m_data.end()), m_row_obj(*this) {} bool is_finished() const override { - return m_inner==m_end; + return !(m_inner != m_end); } row_interface & operator*() override { diff --git a/src/qe/qe.h b/src/qe/qe.h index 55729d0b5a8..05251a2e0b4 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -123,7 +123,6 @@ namespace qe { iterator(conj_enum& c, bool first) : m_super(&c), m_index(first?0:c.m_conjs.size()) {} expr* operator*() { return m_super->m_conjs[m_index].get(); } iterator& operator++() { m_index++; return *this; } - bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } }; diff --git a/src/sat/dimacs.h b/src/sat/dimacs.h index ca6aae07b19..3ca8102ffb2 100644 --- a/src/sat/dimacs.h +++ b/src/sat/dimacs.h @@ -94,7 +94,6 @@ namespace dimacs { iterator(drat_parser& p, bool is_eof):p(p), m_eof(is_eof) { if (!m_eof) m_eof = !p.next(); } drat_record const& operator*() { return p.m_record; } iterator& operator++() { if (!p.next()) m_eof = true; return *this;} - bool operator==(iterator const& other) const { return m_eof == other.m_eof; } bool operator!=(iterator const& other) const { return m_eof != other.m_eof; } }; diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index f1a44296ec0..0129febbf8e 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -188,7 +188,6 @@ namespace sat { iterator(clause_wrapper const& c, unsigned idx): m_idx(idx), m_cw(c) {} iterator& operator++() { ++m_idx; return *this; } literal operator*() { return m_cw[m_idx]; } - bool operator==(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx == other.m_idx; } bool operator!=(iterator const& other) const { SASSERT(&m_cw == &other.m_cw); return m_idx != other.m_idx; } }; iterator begin() const { return iterator(*this, 0); } diff --git a/src/sat/smt/pb_constraint.h b/src/sat/smt/pb_constraint.h index 26c5f8b0886..4c6f69e0776 100644 --- a/src/sat/smt/pb_constraint.h +++ b/src/sat/smt/pb_constraint.h @@ -125,7 +125,6 @@ namespace pb { iterator(constraint const& c, unsigned idx) : c(c), idx(idx) {} literal operator*() { return c.get_lit(idx); } iterator& operator++() { ++idx; return *this; } - bool operator==(iterator const& other) const { SASSERT(&c == &other.c); return idx == other.idx; } bool operator!=(iterator const& other) const { SASSERT(&c == &other.c); return idx != other.idx; } }; diff --git a/src/smt/smt_enode.h b/src/smt/smt_enode.h index 92902ea0b1b..3f488a3b709 100644 --- a/src/smt/smt_enode.h +++ b/src/smt/smt_enode.h @@ -360,8 +360,7 @@ namespace smt { enode* operator*() { return m_first; } iterator& operator++() { if (!m_last) m_last = m_first; m_first = m_first->m_next; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const& other) const { return m_last == other.m_last && m_first == other.m_first; } - bool operator!=(iterator const& other) const { return !(*this == other); } + bool operator!=(iterator const& other) const { return m_last != other.m_last || m_first != other.m_first; } }; iterator begin() { return iterator(this, nullptr); } diff --git a/src/util/approx_set.h b/src/util/approx_set.h index a1835be6f2d..011a8017df9 100644 --- a/src/util/approx_set.h +++ b/src/util/approx_set.h @@ -197,10 +197,6 @@ class approx_set : public u_approx_set { ++*this; return tmp; } - - bool operator==(iterator const & it) const { - return m_set == it.m_set; - } bool operator!=(iterator const & it) const { return m_set != it.m_set; diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 12f86dd0084..7b87e045a92 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -220,7 +220,6 @@ class bit_vector { bool operator*() const { return b.get(m_curr); } iterator& operator++() { ++m_curr; return *this; } iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; } - bool operator==(iterator const& it) const { return m_curr == it.m_curr; } bool operator!=(iterator const& it) const { return m_curr != it.m_curr; } }; diff --git a/src/util/chashtable.h b/src/util/chashtable.h index da7cb5b316e..f245a047ca0 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -551,7 +551,6 @@ class chashtable : private HashProc, private EqProc { return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const & it) const { return m_list_it == it.m_list_it; } bool operator!=(iterator const & it) const { return m_list_it != it.m_list_it; } }; diff --git a/src/util/distribution.h b/src/util/distribution.h index 0ed63d510d6..92aa4d805d9 100644 --- a/src/util/distribution.h +++ b/src/util/distribution.h @@ -95,7 +95,6 @@ class distribution { next_index(); return *this; } - bool operator==(iterator const& other) const { return m_sz == other.m_sz; } bool operator!=(iterator const& other) const { return m_sz != other.m_sz; } }; diff --git a/src/util/dlist.h b/src/util/dlist.h index 4c0e51e5898..1ed183d3d5c 100644 --- a/src/util/dlist.h +++ b/src/util/dlist.h @@ -213,13 +213,8 @@ class dll_iterator { T const& operator*() const { return *m_elem; } - - bool operator==(dll_iterator const& other) const { - return m_elem == other.m_elem && m_first == other.m_first; - } - bool operator!=(dll_iterator const& other) const { - return !operator==(other); + return m_elem != other.m_elem || m_first != other.m_first; } }; diff --git a/src/util/list.h b/src/util/list.h index 208828ea0ee..a9c33fd33c7 100644 --- a/src/util/list.h +++ b/src/util/list.h @@ -44,7 +44,6 @@ class list { T const & operator*() const { return m_curr->head(); } iterator & operator++() { m_curr = m_curr->tail(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const & it) { return m_curr == it.m_curr; } bool operator!=(iterator const & it) { return m_curr != it.m_curr; } }; diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index 7d0cec472b2..6a5c1228df4 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -106,8 +106,7 @@ class scoped_vector { unsigned m_index; public: iterator(scoped_vector const& v, unsigned idx): m_vec(v), m_index(idx) {} - - bool operator==(iterator const& other) const { return &other.m_vec == &m_vec && other.m_index == m_index; } + bool operator!=(iterator const& other) const { return &other.m_vec != &m_vec || other.m_index != m_index; } T const& operator*() { return m_vec[m_index]; } diff --git a/src/util/uint_set.h b/src/util/uint_set.h index 73c3bce1fed..f936f1134c1 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -194,7 +194,6 @@ class uint_set : unsigned_vector { SASSERT(invariant()); } unsigned operator*() const { return m_index; } - bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } iterator & operator++() { ++m_index; scan(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } diff --git a/src/util/vector.h b/src/util/vector.h index 79e51d545ef..1f51ac77548 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -311,9 +311,6 @@ class vector { return *this; } - bool operator==(reverse_iterator const& other) const { - return other.v == v; - } bool operator!=(reverse_iterator const& other) const { return other.v != v; } @@ -883,9 +880,6 @@ class vector { return *this; } - bool operator==(reverse_iterator const& other) const { - return other.v == v; - } bool operator!=(reverse_iterator const& other) const { return other.v != v; } From eb8c63080a9f37e023fce58caf76e82e4497c406 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 13:33:44 +0100 Subject: [PATCH 209/414] Refactor and fix uninitialized variables and improve function consistency across multiple modules --- genaisrc/.#gcm.genai.mts | 1 + genaisrc/fw.genai.mts | 37 +++++++++++++++++++++++++++ src/ast/bv_decl_plugin.cpp | 4 +-- src/ast/euf/euf_ac_plugin.cpp | 1 - src/ast/euf/euf_ac_plugin.h | 2 +- src/qe/mbp/mbp_arith.cpp | 8 +++--- src/qe/mbp/mbp_arith.h | 2 +- src/qe/mbp/mbp_arrays.cpp | 2 +- src/qe/mbp/mbp_arrays.h | 2 +- src/qe/mbp/mbp_datatypes.cpp | 6 ++--- src/qe/mbp/mbp_datatypes.h | 2 +- src/qe/mbp/mbp_plugin.h | 2 +- src/qe/qe_mbp.cpp | 2 +- src/sat/sat_solver/inc_sat_solver.cpp | 2 -- src/sat/smt/arith_axioms.cpp | 2 +- src/sat/smt/array_internalize.cpp | 2 +- src/sat/smt/intblast_solver.cpp | 8 +++--- 17 files changed, 60 insertions(+), 25 deletions(-) create mode 100644 genaisrc/.#gcm.genai.mts create mode 100644 genaisrc/fw.genai.mts diff --git a/genaisrc/.#gcm.genai.mts b/genaisrc/.#gcm.genai.mts new file mode 100644 index 00000000000..9102e16bea2 --- /dev/null +++ b/genaisrc/.#gcm.genai.mts @@ -0,0 +1 @@ +nbjorner@LAPTOP-04AEAFKH.21956:1726928207 \ No newline at end of file diff --git a/genaisrc/fw.genai.mts b/genaisrc/fw.genai.mts new file mode 100644 index 00000000000..11f4ed57925 --- /dev/null +++ b/genaisrc/fw.genai.mts @@ -0,0 +1,37 @@ +// This script is used to invoke ninja and automatically suggest fixes to build warnings +import { select, input, confirm } from "@inquirer/prompts" + + +// TODO: invoke ninja in z3 build directory +// - pipe output of build to a string buffer +// - chunk up warning/error messages one by one +// - create AI query to have the warning/error fixed +// - stage the changes +// - recompile, rinse repeat until fixes +// - backtrack from failed fixes? + +// let ninjaout = await host.exec("ninja", []) +// console.log(ninjaout.stdout) +// await runPrompt( (_) => { _.def("BUILDMSG", ninjaout, { maxTokens: 20000}) +// _.$`BUILDMSG is the output of a ninja build. Please generate fixes for the warning messages, stage the changes. Repeat the build process for up to three iterations to fix error or warning messages` } + + + +defData("EXAMPLEMSG"," +/home/nbjorner/z3/src/smt/theory_str.cpp: In member function ‘void smt::theory_str::instantiate_axiom_CharAt(smt::enode*)’: +/home/nbjorner/z3/src/smt/theory_str.cpp:1092:15: warning: ‘arg0’ may be used uninitialized [-Wmaybe-uninitialized] + 1092 | expr* arg0, *arg1; + | ^~~~ +In file included from /home/nbjorner/z3/src/ast/ast_smt2_pp.h:26, + from /home/nbjorner/z3/src/smt/theory_str.cpp:17: +In member function ‘app* arith_util::mk_lt(expr*, expr*) const’, + inlined from ‘void smt::theory_str::instantiate_axiom_CharAt(smt::enode*)’ at /home/nbjorner/z3/src/smt/theory_str.cpp:1110:40: +") + +// TODO: script to extract file contents +// TODO: script what to update. + +$` +You are a helpful AI assistant who knows C++ and can fix build warnings. +You are given the following warning message ${EXAMPLEMSG}. Create a fix. +` diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 5dd9f60806f..8c9a5652fc1 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -927,7 +927,7 @@ sort * bv_util::mk_sort(unsigned bv_size) { } unsigned bv_util::get_int2bv_size(parameter const& p) { - int sz; + int sz = 0; VERIFY(m_plugin->get_int2bv_size(1, &p, sz)); return static_cast(sz); } @@ -951,4 +951,4 @@ app* bv_util::mk_bv_rotate_left(expr* arg, unsigned n) { app* bv_util::mk_bv_rotate_right(expr* arg, unsigned n) { parameter p(n); return m_manager.mk_app(get_fid(), OP_ROTATE_RIGHT, 1, &p, 1, &arg); -} \ No newline at end of file +} diff --git a/src/ast/euf/euf_ac_plugin.cpp b/src/ast/euf/euf_ac_plugin.cpp index 174ef363b21..69d0e657279 100644 --- a/src/ast/euf/euf_ac_plugin.cpp +++ b/src/ast/euf/euf_ac_plugin.cpp @@ -907,7 +907,6 @@ namespace euf { m_dst_r.reset(); m_dst_r.append(monomial(dst.r).m_nodes); unsigned src_r_size = m_src_r.size(); - unsigned dst_r_size = m_dst_r.size(); SASSERT(src_r_size == monomial(src.r).size()); // dst_r contains C // src_r contains E diff --git a/src/ast/euf/euf_ac_plugin.h b/src/ast/euf/euf_ac_plugin.h index d55d7dbb465..809ac55cf77 100644 --- a/src/ast/euf/euf_ac_plugin.h +++ b/src/ast/euf/euf_ac_plugin.h @@ -136,7 +136,7 @@ namespace euf { }; theory_id m_fid = 0; - unsigned m_op = null_decl_kind; + decl_kind m_op = null_decl_kind; func_decl* m_decl = nullptr; vector m_eqs; ptr_vector m_nodes; diff --git a/src/qe/mbp/mbp_arith.cpp b/src/qe/mbp/mbp_arith.cpp index ef4428fa74c..8bbf3a0027d 100644 --- a/src/qe/mbp/mbp_arith.cpp +++ b/src/qe/mbp/mbp_arith.cpp @@ -304,7 +304,7 @@ namespace mbp { return rational(b.is_pos() ? -1 : 1); } - bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { + bool project1(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { app_ref_vector vs(m); vs.push_back(v); vector defs; @@ -710,8 +710,8 @@ namespace mbp { dealloc(m_imp); } - bool arith_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); + bool arith_project_plugin::project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->project1(model, var, vars, lits); } bool arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { @@ -743,6 +743,6 @@ namespace mbp { ast_manager& m = lits.get_manager(); arith_project_plugin ap(m); app_ref_vector vars(m); - return ap(model, var, vars, lits); + return ap.project1(model, var, vars, lits); } } diff --git a/src/qe/mbp/mbp_arith.h b/src/qe/mbp/mbp_arith.h index ca4cccb7478..666ffffc46b 100644 --- a/src/qe/mbp/mbp_arith.h +++ b/src/qe/mbp/mbp_arith.h @@ -26,7 +26,7 @@ namespace mbp { arith_project_plugin(ast_manager& m); ~arith_project_plugin() override; - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; } family_id get_family_id() override; bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; diff --git a/src/qe/mbp/mbp_arrays.cpp b/src/qe/mbp/mbp_arrays.cpp index fa7ec8cb5d9..6e036a71490 100644 --- a/src/qe/mbp/mbp_arrays.cpp +++ b/src/qe/mbp/mbp_arrays.cpp @@ -1444,7 +1444,7 @@ namespace mbp { dealloc(m_imp); } - bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + bool array_project_plugin::project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { ast_manager& m = vars.get_manager(); app_ref_vector vvars(m, 1, &var); expr_ref fml = mk_and(lits); diff --git a/src/qe/mbp/mbp_arrays.h b/src/qe/mbp/mbp_arrays.h index 7dd904108e8..ed06ba78b35 100644 --- a/src/qe/mbp/mbp_arrays.h +++ b/src/qe/mbp/mbp_arrays.h @@ -31,7 +31,7 @@ namespace mbp { public: array_project_plugin(ast_manager& m); ~array_project_plugin() override; - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects); family_id get_family_id() override; diff --git a/src/qe/mbp/mbp_datatypes.cpp b/src/qe/mbp/mbp_datatypes.cpp index 32e2b9f27ed..5a88016dbb1 100644 --- a/src/qe/mbp/mbp_datatypes.cpp +++ b/src/qe/mbp/mbp_datatypes.cpp @@ -40,7 +40,7 @@ namespace mbp { return lift_foreign(vars, lits); } - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { expr_ref val = model(var); SASSERT(is_app(val)); TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); @@ -292,8 +292,8 @@ namespace mbp { dealloc(m_imp); } - bool datatype_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); + bool datatype_project_plugin::project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->project1(model, var, vars, lits); } bool datatype_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { diff --git a/src/qe/mbp/mbp_datatypes.h b/src/qe/mbp/mbp_datatypes.h index f30aaaa9faf..28f93089d55 100644 --- a/src/qe/mbp/mbp_datatypes.h +++ b/src/qe/mbp/mbp_datatypes.h @@ -31,7 +31,7 @@ namespace mbp { public: datatype_project_plugin(ast_manager& m); ~datatype_project_plugin() override; - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; family_id get_family_id() override; bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector& defs) override; diff --git a/src/qe/mbp/mbp_plugin.h b/src/qe/mbp/mbp_plugin.h index 4f73b92e635..c270489e85a 100644 --- a/src/qe/mbp/mbp_plugin.h +++ b/src/qe/mbp/mbp_plugin.h @@ -60,7 +60,7 @@ namespace mbp { public: project_plugin(ast_manager& m) :m(m), m_cache(m), m_args(m), m_pure_eqs(m) {} virtual ~project_plugin() = default; - virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return false; } + virtual bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return false; } /** \brief partial solver. */ diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 97f12238d41..b5fafeeac27 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -452,7 +452,7 @@ class mbproj::impl { var = vars.back(); vars.pop_back(); mbp::project_plugin* p = get_plugin(var); - if (p && (*p)(model, var, vars, fmls)) { + if (p && p->project1(model, var, vars, fmls)) { progress = true; } else { diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 5e0d109183b..522638c1d3d 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -193,8 +193,6 @@ class inc_sat_solver : public solver { m_solver.pop_to_base_level(); m_core.reset(); - - if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index 7270e708083..f35f3851a03 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -325,7 +325,7 @@ namespace arith { void solver::mk_bv_axiom(app* n) { unsigned sz; - expr* _x, * _y; + expr* _x = nullptr, * _y = nullptr; VERIFY(a.is_band(n, sz, _x, _y) || a.is_shl(n, sz, _x, _y) || a.is_ashr(n, sz, _x, _y) || a.is_lshr(n, sz, _x, _y)); rational N = rational::power_of_two(sz); expr_ref x(a.mk_mod(_x, a.mk_int(N)), m); diff --git a/src/sat/smt/array_internalize.cpp b/src/sat/smt/array_internalize.cpp index d5a1dd2c556..9e41be3bbc1 100644 --- a/src/sat/smt/array_internalize.cpp +++ b/src/sat/smt/array_internalize.cpp @@ -128,7 +128,7 @@ namespace array { internalize_lambda_eh(n); break; case OP_SET_SUBSET: { - expr* x, *y; + expr* x = nullptr, *y = nullptr; VERIFY(a.is_subset(n->get_expr(), x, y)); expr_ref diff(a.mk_setminus(x, y), m); expr_ref emp(a.mk_empty_set(x->get_sort()), m); diff --git a/src/sat/smt/intblast_solver.cpp b/src/sat/smt/intblast_solver.cpp index f91113d591c..d6dfe57d7c1 100644 --- a/src/sat/smt/intblast_solver.cpp +++ b/src/sat/smt/intblast_solver.cpp @@ -91,7 +91,7 @@ namespace intblast { void solver::eq_internalized(euf::enode* n) { expr* e = n->get_expr(); - expr* x, * y; + expr* x = nullptr, * y = nullptr; VERIFY(m.is_eq(n->get_expr(), x, y)); SASSERT(bv.is_bv(x)); if (!is_translated(e)) { @@ -482,7 +482,7 @@ namespace intblast { return r >= 0; if (is_bounded(e, N)) return true; - expr* x, * y; + expr* x = nullptr, * y = nullptr; if (a.is_mul(e, x, y)) return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y); if (a.is_add(e, x, y)) @@ -544,7 +544,7 @@ namespace intblast { */ expr* solver::amod(expr* bv_expr, expr* x, rational const& N) { rational v; - expr* r, *c, * t, * e; + expr* r = nullptr, *c = nullptr, * t = nullptr, * e = nullptr; if (m.is_ite(x, c, t, e)) r = m.mk_ite(c, amod(bv_expr, t, N), amod(bv_expr, e, N)); else if (a.is_idiv(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N && is_non_negative(bv_expr, e)) @@ -880,7 +880,7 @@ namespace intblast { r = umod(bv_expr, 0); SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr)); unsigned arg_sz = bv.get_bv_size(bv_expr); - unsigned sz = bv.get_bv_size(e); + //unsigned sz = bv.get_bv_size(e); // rational N = rational::power_of_two(sz); rational M = rational::power_of_two(arg_sz); expr* signbit = a.mk_ge(r, a.mk_int(M / 2)); From ee347735be8246d7150fa90b1e35cfc5f02bb835 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 13:34:26 +0100 Subject: [PATCH 210/414] remove junk Signed-off-by: Nikolaj Bjorner --- genaisrc/.#gcm.genai.mts | 1 - genaisrc/fw.genai.mts | 37 ------------------------------------- 2 files changed, 38 deletions(-) delete mode 100644 genaisrc/.#gcm.genai.mts delete mode 100644 genaisrc/fw.genai.mts diff --git a/genaisrc/.#gcm.genai.mts b/genaisrc/.#gcm.genai.mts deleted file mode 100644 index 9102e16bea2..00000000000 --- a/genaisrc/.#gcm.genai.mts +++ /dev/null @@ -1 +0,0 @@ -nbjorner@LAPTOP-04AEAFKH.21956:1726928207 \ No newline at end of file diff --git a/genaisrc/fw.genai.mts b/genaisrc/fw.genai.mts deleted file mode 100644 index 11f4ed57925..00000000000 --- a/genaisrc/fw.genai.mts +++ /dev/null @@ -1,37 +0,0 @@ -// This script is used to invoke ninja and automatically suggest fixes to build warnings -import { select, input, confirm } from "@inquirer/prompts" - - -// TODO: invoke ninja in z3 build directory -// - pipe output of build to a string buffer -// - chunk up warning/error messages one by one -// - create AI query to have the warning/error fixed -// - stage the changes -// - recompile, rinse repeat until fixes -// - backtrack from failed fixes? - -// let ninjaout = await host.exec("ninja", []) -// console.log(ninjaout.stdout) -// await runPrompt( (_) => { _.def("BUILDMSG", ninjaout, { maxTokens: 20000}) -// _.$`BUILDMSG is the output of a ninja build. Please generate fixes for the warning messages, stage the changes. Repeat the build process for up to three iterations to fix error or warning messages` } - - - -defData("EXAMPLEMSG"," -/home/nbjorner/z3/src/smt/theory_str.cpp: In member function ‘void smt::theory_str::instantiate_axiom_CharAt(smt::enode*)’: -/home/nbjorner/z3/src/smt/theory_str.cpp:1092:15: warning: ‘arg0’ may be used uninitialized [-Wmaybe-uninitialized] - 1092 | expr* arg0, *arg1; - | ^~~~ -In file included from /home/nbjorner/z3/src/ast/ast_smt2_pp.h:26, - from /home/nbjorner/z3/src/smt/theory_str.cpp:17: -In member function ‘app* arith_util::mk_lt(expr*, expr*) const’, - inlined from ‘void smt::theory_str::instantiate_axiom_CharAt(smt::enode*)’ at /home/nbjorner/z3/src/smt/theory_str.cpp:1110:40: -") - -// TODO: script to extract file contents -// TODO: script what to update. - -$` -You are a helpful AI assistant who knows C++ and can fix build warnings. -You are given the following warning message ${EXAMPLEMSG}. Create a fix. -` From 5c583299f1983bcd7300f82d96b1b81e5634baee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 13:38:07 +0100 Subject: [PATCH 211/414] Remove unnecessary const qualifiers from comparison operator overloads in z3++.h --- src/api/c++/z3++.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 85d5fce6285..e29bde4a8dd 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1703,28 +1703,28 @@ namespace z3 { inline expr operator||(bool a, expr const & b) { return b.ctx().bool_val(a) || b; } - inline expr operator==(expr const & a, expr const & b) const { + inline expr operator==(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } - inline expr operator==(expr const & a, int b) const { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } + inline expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } inline expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) == b; } - inline expr operator==(expr const & a, double b) const { assert(a.is_fpa()); return a == a.ctx().fpa_val(b); } - inline expr operator==(double a, expr const & b) const { assert(b.is_fpa()); return b.ctx().fpa_val(a) == b; } + inline expr operator==(expr const & a, double b) { assert(a.is_fpa()); return a == a.ctx().fpa_val(b); } + inline expr operator==(double a, expr const & b) { assert(b.is_fpa()); return b.ctx().fpa_val(a) == b; } - inline expr operator!=(expr const & a, expr const & b) const { + inline expr operator!=(expr const & a, expr const & b) { check_context(a, b); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_distinct(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } - inline expr operator!=(expr const & a, int b) const { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } - inline expr operator!=(int a, expr const & b) const { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } - inline expr operator!=(expr const & a, double b) const { assert(a.is_fpa()); return a != a.ctx().fpa_val(b); } - inline expr operator!=(double a, expr const & b) const { assert(b.is_fpa()); return b.ctx().fpa_val(a) != b; } + inline expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } + inline expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } + inline expr operator!=(expr const & a, double b) { assert(a.is_fpa()); return a != a.ctx().fpa_val(b); } + inline expr operator!=(double a, expr const & b) { assert(b.is_fpa()); return b.ctx().fpa_val(a) != b; } inline expr operator+(expr const & a, expr const & b) { check_context(a, b); From 5a6dc18d0d7843a6b8583be193056fee467995f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 13:46:50 +0100 Subject: [PATCH 212/414] Override convert_initialize_value method in bit_blaster_model_converter.cpp --- src/tactic/bv/bit_blaster_model_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 5bc874abb34..ee48f48a155 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -222,7 +222,7 @@ struct bit_blaster_model_converter : public model_converter { // no-op } - void convert_initialize_value(vector>& var2value) { + void convert_initialize_value(vector>& var2value) override { if (m_vars.empty() || var2value.empty()) return; rational r; From 0604d23c570970155ed56d58f545b8533d336be1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 13:50:32 +0100 Subject: [PATCH 213/414] Check if model_converter is non-null before initializing values in sat_tactic --- src/sat/tactic/sat_tactic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 6a48bc8ed96..105fe94ba00 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -43,7 +43,8 @@ class sat_tactic : public tactic { } void initialize_values(goal_ref const& g, atom2bool_var& map) { - g->mc()->convert_initialize_value(m_var2value); + if (g->mc()) + g->mc()->convert_initialize_value(m_var2value); for (auto & [var, value] : m_var2value) { if (!m.is_bool(var)) continue; From 95d2e009ef93fbcd07d531fc4360249a0afc13fa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 17:25:44 +0100 Subject: [PATCH 214/414] Update OCaml jobs to use Ubuntu-latest in Azure Pipelines configuration --- azure-pipelines.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b7ad2ab1ed0..18ab31f500f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -76,10 +76,10 @@ jobs: - script: "pip install build git+https://github.com/rhelmot/auditwheel" - script: "cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../.." -- job: "Ubuntu20OCaml" - displayName: "Ubuntu 20 with OCaml" +- job: "UbuntuOCaml" + displayName: "Ubuntu with OCaml" pool: - vmImage: "Ubuntu-20.04" + vmImage: "Ubuntu-latest" steps: - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y @@ -100,9 +100,9 @@ jobs: - job: "Ubuntu20OCamlStatic" - displayName: "Ubuntu 20 with OCaml on z3-static" + displayName: "Ubuntu with OCaml on z3-static" pool: - vmImage: "Ubuntu-20.04" + vmImage: "Ubuntu-latest" steps: - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y From ec14ef765e20a1280d956c57458d3bda52e6ff5b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2024 17:42:39 +0100 Subject: [PATCH 215/414] Update Ubuntu job name in Azure pipeline and add string variable creation in C API example --- azure-pipelines.yml | 2 +- examples/c/test_capi.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 18ab31f500f..d12872f07a3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -99,7 +99,7 @@ jobs: - template: scripts/generate-doc.yml -- job: "Ubuntu20OCamlStatic" +- job: "UbuntuOCamlStatic" displayName: "Ubuntu with OCaml on z3-static" pool: vmImage: "Ubuntu-latest" diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 98842bb9cf5..fe4834f578e 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -165,6 +165,15 @@ Z3_ast mk_int_var(Z3_context ctx, const char * name) return mk_var(ctx, name, ty); } +/** + \brief Create a string variable using the given name. +*/ +Z3_ast mk_string_var(Z3_context ctx, const char * name) +{ + Z3_sort ty = Z3_mk_string_sort(ctx); + return mk_var(ctx, name, ty); +} + /** \brief Create a Z3 integer node using a C int. */ @@ -1615,7 +1624,7 @@ void error_code_example2() { Z3_del_config(cfg); x = mk_int_var(ctx, "x"); - y = mk_bool_var(ctx, "y"); + y = mk_string_var(ctx, "y"); printf("before Z3_mk_iff\n"); /* the next call will produce an error */ app = Z3_mk_iff(ctx, x, y); From c34c8477f3d10444f8142e6917a752f06af1e246 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Mon, 23 Sep 2024 09:47:34 -0700 Subject: [PATCH 216/414] Add .gitattributes for genaiscript and update git commit flow script. (#7396) --- genaisrc/.gitattributes | 1 + genaisrc/gcm.genai.mts | 99 +- genaisrc/genaiscript.d.ts | 2631 +++++++++++++++++++++++++++++++++++++ genaisrc/tsconfig.json | 21 + 4 files changed, 2705 insertions(+), 47 deletions(-) create mode 100644 genaisrc/.gitattributes create mode 100644 genaisrc/genaiscript.d.ts create mode 100644 genaisrc/tsconfig.json diff --git a/genaisrc/.gitattributes b/genaisrc/.gitattributes new file mode 100644 index 00000000000..b89350c92bf --- /dev/null +++ b/genaisrc/.gitattributes @@ -0,0 +1 @@ +genaiscript.d.ts -diff merge=ours linguist-generated \ No newline at end of file diff --git a/genaisrc/gcm.genai.mts b/genaisrc/gcm.genai.mts index 54f6762a8e9..e1ebd1197ab 100644 --- a/genaisrc/gcm.genai.mts +++ b/genaisrc/gcm.genai.mts @@ -1,73 +1,78 @@ -// https://microsoft.github.io/genaiscript/guides/auto-git-commit-message/ -import { select, input, confirm } from "@inquirer/prompts" +/** + * git commit flow with auto-generated commit message + */ +script({ + title: "git commit message", + description: "Generate a commit message for all staged changes", +}) + +// TODO: update this diff command to match your workspace +const diffCmd = "git diff --cached -- . :!**/genaiscript.d.ts" // Check for staged changes and stage all changes if none are staged -let diff = await host.exec("git", ["diff", "--cached"]) +let diff = await host.exec(diffCmd) if (!diff.stdout) { - const stage = await confirm({ - message: "No staged changes. Stage all changes?", + /** + * Ask user to stage all changes if none are staged + */ + const stage = await host.confirm("No staged changes. Stage all changes?", { default: true, }) if (stage) { - await host.exec("git", ["add", "."]) - diff = await host.exec("git", [ - "diff", - "--cached", - "--", - ".", - ":!**/genaiscript.d.ts", - ]) + // Stage all changes and recompute diff + await host.exec("git add .") + diff = await host.exec(diffCmd) } if (!diff.stdout) cancel("no staged changes") } +// show diff in the console console.log(diff.stdout) let choice let message do { // Generate commit message - message = ( - await runPrompt( - (_) => { - _.def("GIT_DIFF", diff, { maxTokens: 20000 }) - _.$`GIT_DIFF is a diff of all staged changes, coming from the command: + const res = await runPrompt( + (_) => { + _.def("GIT_DIFF", diff, { maxTokens: 20000 }) + _.$`GIT_DIFF is a diff of all staged changes, coming from the command: \`\`\` git diff --cached \`\`\` Please generate a concise, one-line commit message for these changes. -- do NOT add quotes` - }, - { cache: false, temperature: 0.8 } - ) - ).text +- do NOT add quotes +` // TODO: add a better prompt + }, + { cache: false, temperature: 0.8 } + ) + if (res.error) throw res.error + + message = res.text + if (!message) { + console.log("No message generated, did you configure the LLM model?") + break + } // Prompt user for commit message - choice = await select({ - message, - choices: [ - { - name: "commit", - value: "commit", - description: "accept message and commit", - }, - { - name: "edit", - value: "edit", - description: "edit message and commit", - }, - { - name: "regenerate", - value: "regenerate", - description: "regenerate message", - }, - ], - }) + choice = await host.select(message, [ + { + value: "commit", + description: "accept message and commit", + }, + { + value: "edit", + description: "edit message and commit", + }, + { + value: "regenerate", + description: "regenerate message", + }, + ]) // Handle user choice if (choice === "edit") { - message = await input({ - message: "Edit commit message", + message = await host.input("Edit commit message", { required: true, }) choice = "commit" @@ -75,8 +80,8 @@ Please generate a concise, one-line commit message for these changes. // Regenerate message if (choice === "commit" && message) { console.log((await host.exec("git", ["commit", "-m", message])).stdout) - if (await confirm({ message: "Push changes?", default: true })) - console.log((await host.exec("git", ["push"])).stdout) + if (await host.confirm("Push changes?", { default: true })) + console.log((await host.exec("git push")).stdout) break } } while (choice !== "commit") diff --git a/genaisrc/genaiscript.d.ts b/genaisrc/genaiscript.d.ts new file mode 100644 index 00000000000..df355fd9fa1 --- /dev/null +++ b/genaisrc/genaiscript.d.ts @@ -0,0 +1,2631 @@ +type OptionsOrString = (string & {}) | TOptions + +type ElementOrArray = T | T[] + +interface PromptGenerationConsole { + log(...data: any[]): void + warn(...data: any[]): void + debug(...data: any[]): void + error(...data: any[]): void +} + +type DiagnosticSeverity = "error" | "warning" | "info" + +interface Diagnostic { + filename: string + range: CharRange + severity: DiagnosticSeverity + message: string + /** + * error or warning code + */ + code?: string +} + +type Awaitable = T | PromiseLike + +interface SerializedError { + name?: string + message?: string + stack?: string + cause?: unknown + code?: string + line?: number + column?: number +} + +interface PromptDefinition { + /** + * Based on file name. + */ + id: string + + /** + * Something like "Summarize children", show in UI. + */ + title?: string + + /** + * Longer description of the prompt. Shows in UI grayed-out. + */ + description?: string +} + +interface PromptLike extends PromptDefinition { + /** + * File where the prompt comes from (if any). + */ + filename?: string + + /** + * The text of the prompt JS source code. + */ + jsSource: string + + /** + * The actual text of the prompt template. + * Only used for system prompts. + */ + text?: string +} + +type SystemPromptId = OptionsOrString< + | "system" + | "system.annotations" + | "system.changelog" + | "system.diagrams" + | "system.diff" + | "system.explanations" + | "system.files" + | "system.files_schema" + | "system.fs_find_files" + | "system.fs_read_file" + | "system.math" + | "system.md_frontmatter" + | "system.python" + | "system.python_code_interpreter" + | "system.retrieval_fuzz_search" + | "system.retrieval_vector_search" + | "system.retrieval_web_search" + | "system.schema" + | "system.tasks" + | "system.technical" + | "system.tools" + | "system.typescript" + | "system.zero_shot_cot" +> + +type SystemToolId = OptionsOrString< + | "fs_find_files" + | "fs_read_file" + | "math_eval" + | "md_read_frontmatter" + | "python_code_interpreter" + | "retrieval_fuzz_search" + | "retrieval_vector_search" + | "retrieval_web_search" +> + +type FileMergeHandler = ( + filename: string, + label: string, + before: string, + generated: string +) => Awaitable + +interface PromptOutputProcessorResult { + /** + * Updated text + */ + text?: string + /** + * Generated files from the output + */ + files?: Record + + /** + * User defined errors + */ + annotations?: Diagnostic[] +} + +type PromptOutputProcessorHandler = ( + output: GenerationOutput +) => + | PromptOutputProcessorResult + | Promise + | undefined + | Promise + | void + | Promise + +type PromptTemplateResponseType = "json_object" | "json_schema" | undefined + +interface ModelConnectionOptions { + /** + * Which LLM model to use. + * + * @default gpt-4 + * @example gpt-4 + */ + model?: OptionsOrString< + | "openai:gpt-4o" + | "openai:gpt-4o-mini" + | "openai:gpt-4" + | "openai:gpt-4-turbo" + | "openai:gpt-3.5-turbo" + | "ollama:phi3" + | "ollama:llama3" + | "ollama:mixtral" + > +} + +interface ModelOptions extends ModelConnectionOptions { + /** + * Temperature to use. Higher temperature means more hallucination/creativity. + * Range 0.0-2.0. + * + * @default 0.2 + */ + temperature?: number + + /** + * Specifies the type of output. Default is plain text. + * - `json_object` enables JSON mode + * - `json_schema` enables structured outputs + * Use `responseSchema` to specify an output schema. + */ + responseType?: PromptTemplateResponseType + + /** + * JSON object schema for the output. Enables the `JSON` output mode by default. + */ + responseSchema?: PromptParametersSchema | JSONSchemaObject + + /** + * “Top_p” or nucleus sampling is a setting that decides how many possible words to consider. + * A high “top_p” value means the model looks at more possible words, even the less likely ones, + * which makes the generated text more diverse. + */ + topP?: number + + /** + * When to stop producing output. + * + */ + maxTokens?: number + + /** + * Maximum number of tool calls to make. + */ + maxToolCalls?: number + + /** + * Maximum number of data repairs to attempt. + */ + maxDataRepairs?: number + + /** + * A deterministic integer seed to use for the model. + */ + seed?: number + + /** + * By default, LLM queries are not cached. If true, the LLM request will be cached. Use a string to override the default cache name + */ + cache?: boolean | string + + /** + * Custom cache name. If not set, the default cache is used. + * @deprecated Use `cache` instead with a string + */ + cacheName?: string + + /** + * Budget of tokens to apply the prompt flex renderer. + */ + flexTokens?: number +} + +interface EmbeddingsModelConnectionOptions { + /** + * LLM model to use for embeddings. + */ + embeddingsModel?: OptionsOrString< + "openai:text-embedding-3-small", + "openai:text-embedding-3-large", + "openai:text-embedding-ada-002", + "github:text-embedding-3-small", + "github:text-embedding-3-large", + "ollama:nomic-embed-text" + > +} + +interface EmbeddingsModelOptions extends EmbeddingsModelConnectionOptions {} + +interface PromptSystemOptions { + /** + * List of system script ids used by the prompt. + */ + system?: SystemPromptId | SystemPromptId[] + + /** + * List of tools used by the prompt. + */ + tools?: SystemToolId | SystemToolId[] +} + +interface ScriptRuntimeOptions { + /** + * Secrets required by the prompt + */ + secrets?: string[] + + /** + * Default value for emitting line numbers in fenced code blocks. + */ + lineNumbers?: boolean +} + +type PromptParameterType = + | string + | number + | boolean + | object + | JSONSchemaNumber + | JSONSchemaString + | JSONSchemaBoolean +type PromptParametersSchema = Record< + string, + PromptParameterType | PromptParameterType[] +> +type PromptParameters = Record + +type PromptAssertion = { + // How heavily to weigh the assertion. Defaults to 1.0 + weight?: number + /** + * The transformation to apply to the output before checking the assertion. + */ + transform?: string +} & ( + | { + // type of assertion + type: + | "icontains" + | "not-icontains" + | "equals" + | "not-equals" + | "starts-with" + | "not-starts-with" + // The expected value + value: string + } + | { + // type of assertion + type: + | "contains-all" + | "not-contains-all" + | "contains-any" + | "not-contains-any" + | "icontains-all" + | "not-icontains-all" + // The expected values + value: string[] + } + | { + // type of assertion + type: "levenshtein" | "not-levenshtein" + // The expected value + value: string + // The threshold value + threshold?: number + } + | { + type: "javascript" + /** + * JavaScript expression to evaluate. + */ + value: string + /** + * Optional threshold if the javascript expression returns a number + */ + threshold?: number + } +) + +interface PromptTest { + /** + * Short name of the test + */ + name?: string + /** + * Description of the test. + */ + description?: string + /** + * List of files to apply the test to. + */ + files?: string | string[] + /** + * Extra set of variables for this scenario + */ + vars?: Record + /** + * LLM output matches a given rubric, using a Language Model to grade output. + */ + rubrics?: string | string[] + /** + * LLM output adheres to the given facts, using Factuality method from OpenAI evaluation. + */ + facts?: string | string[] + /** + * List of keywords that should be contained in the LLM output. + */ + keywords?: string | string[] + /** + * List of keywords that should not be contained in the LLM output. + */ + forbidden?: string | string[] + /** + * Additional deterministic assertions. + */ + asserts?: PromptAssertion | PromptAssertion[] +} + +interface PromptScript + extends PromptLike, + ModelOptions, + PromptSystemOptions, + EmbeddingsModelOptions, + ScriptRuntimeOptions { + /** + * Groups template in UI + */ + group?: string + + /** + * Additional template parameters that will populate `env.vars` + */ + parameters?: PromptParametersSchema + + /** + * A file path or list of file paths or globs. + * The content of these files will be by the files selected in the UI by the user or the cli arguments. + */ + files?: string | string[] + + /** + * Extra variable values that can be used to configure system prompts. + */ + vars?: Record + + /** + * Tests to validate this script. + */ + tests?: PromptTest | PromptTest[] + + /** + * Don't show it to the user in lists. Template `system.*` are automatically unlisted. + */ + unlisted?: boolean + + /** + * Set if this is a system prompt. + */ + isSystem?: boolean +} + +/** + * Represent a workspace file and optional content. + */ +interface WorkspaceFile { + /** + * Name of the file, relative to project root. + */ + filename: string + + /** + * Content of the file. + */ + content?: string +} + +interface WorkspaceFileWithScore extends WorkspaceFile { + /** + * Score allocated by search algorithm + */ + score?: number +} + +interface ToolDefinition { + /** + * The name of the function to be called. Must be a-z, A-Z, 0-9, or contain + * underscores and dashes, with a maximum length of 64. + */ + name: string + + /** + * A description of what the function does, used by the model to choose when and + * how to call the function. + */ + description?: string + + /** + * The parameters the functions accepts, described as a JSON Schema object. See the + * [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) + * for examples, and the + * [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + * documentation about the format. + * + * Omitting `parameters` defines a function with an empty parameter list. + */ + parameters?: JSONSchema +} + +interface ToolCallTrace { + log(message: string): void + item(message: string): void + tip(message: string): void + fence(message: string, contentType?: string): void +} + +/** + * Position (line, character) in a file. Both are 0-based. + */ +type CharPosition = [number, number] + +/** + * Describes a run of text. + */ +type CharRange = [CharPosition, CharPosition] + +/** + * 0-based line numbers. + */ +type LineRange = [number, number] + +interface FileEdit { + type: string + filename: string + label?: string + validated?: boolean +} + +interface ReplaceEdit extends FileEdit { + type: "replace" + range: CharRange | LineRange + text: string +} + +interface InsertEdit extends FileEdit { + type: "insert" + pos: CharPosition | number + text: string +} + +interface DeleteEdit extends FileEdit { + type: "delete" + range: CharRange | LineRange +} + +interface CreateFileEdit extends FileEdit { + type: "createfile" + overwrite?: boolean + ignoreIfExists?: boolean + text: string +} + +type Edits = InsertEdit | ReplaceEdit | DeleteEdit | CreateFileEdit + +interface ToolCallContent { + type?: "content" + content: string + edits?: Edits[] +} + +type ToolCallOutput = + | string + | number + | boolean + | ToolCallContent + | ShellOutput + | WorkspaceFile + | RunPromptResult + | undefined + +interface WorkspaceFileCache { + /** + * Gets the value associated with the key, or undefined if there is none. + * @param key + */ + get(key: K): Promise + /** + * Sets the value associated with the key. + * @param key + * @param value + */ + set(key: K, value: V): Promise + + /** + * List of keys + */ + keys(): Promise + + /** + * List the values in the cache. + */ + values(): Promise +} + +interface WorkspaceFileSystem { + /** + * Searches for files using the glob pattern and returns a list of files. + * Ignore `.env` files and apply `.gitignore` if present. + * @param glob + */ + findFiles( + glob: string, + options?: { + /** + * Set to false to skip read text content. True by default + */ + readText?: boolean + } + ): Promise + + /** + * Performs a grep search over the files in the workspace + * @param query + * @param globs + */ + grep( + query: string | RegExp, + globs: string | string[], + options?: { + /** + * Set to false to skip read text content. True by default + */ + readText?: boolean + } + ): Promise<{ files: WorkspaceFile[] }> + + /** + * Reads the content of a file as text + * @param path + */ + readText(path: string | Awaitable): Promise + + /** + * Reads the content of a file and parses to JSON, using the JSON5 parser. + * @param path + */ + readJSON(path: string | Awaitable): Promise + + /** + * Reads the content of a file and parses to XML, using the XML parser. + */ + readXML(path: string | Awaitable): Promise + + /** + * Writes a file as text to the file system + * @param path + * @param content + */ + writeText(path: string, content: string): Promise + + /** + * Opens a key-value cache for the given cache name. + * The cache is persisted across runs of the script. Entries are dropped when the cache grows too large. + * @param cacheName + */ + cache( + cacheName: string + ): Promise> +} + +interface ToolCallContext { + trace: ToolCallTrace +} + +interface ToolCallback { + spec: ToolDefinition + impl: ( + args: { context: ToolCallContext } & Record + ) => Awaitable +} + +type AgenticToolCallback = Omit & { + spec: Omit & { + parameters: Record + } +} + +interface AgenticToolProviderCallback { + functions: Iterable +} + +type ChatParticipantHandler = ( + context: ChatTurnGenerationContext, + messages: ChatCompletionMessageParam[] +) => Awaitable + +interface ChatParticipantOptions { + label?: string +} + +interface ChatParticipant { + generator: ChatParticipantHandler + options: ChatParticipantOptions +} + +/** + * A set of text extracted from the context of the prompt execution + */ +interface ExpansionVariables { + /** + * Directory where the prompt is executed + */ + dir: string + + /** + * List of linked files parsed in context + */ + files: WorkspaceFile[] + + /** + * current prompt template + */ + template: PromptDefinition + + /** + * User defined variables + */ + vars?: Record + + /** + * List of secrets used by the prompt, must be registered in `genaiscript`. + */ + secrets?: Record + + /** + * Root prompt generation context + */ + generator: ChatGenerationContext +} + +type MakeOptional = Partial> & Omit + +type PromptArgs = Omit + +type PromptSystemArgs = Omit< + PromptArgs, + | "model" + | "embeddingsModel" + | "temperature" + | "topP" + | "maxTokens" + | "seed" + | "tests" + | "responseLanguage" + | "responseType" + | "responseSchema" + | "files" +> + +type StringLike = string | WorkspaceFile | WorkspaceFile[] + +interface FenceOptions { + /** + * Language of the fenced code block. Defaults to "markdown". + */ + language?: + | "markdown" + | "json" + | "yaml" + | "javascript" + | "typescript" + | "python" + | "shell" + | "toml" + | string + + /** + * Prepend each line with a line numbers. Helps with generating diffs. + */ + lineNumbers?: boolean + + /** + * JSON schema identifier + */ + schema?: string +} + +interface ContextExpansionOptions { + /** + * Specifies an maximum of estimated tokens for this entry; after which it will be truncated. + */ + maxTokens?: number + /* + * Value that is conceptually similar to a zIndex (higher number == higher priority). + * If a rendered prompt has more message tokens than can fit into the available context window, the prompt renderer prunes messages with the lowest priority from the ChatMessages result, preserving the order in which they were declared. This means your extension code can safely declare TSX components for potentially large pieces of context like conversation history and codebase context. + */ + priority?: number + /** + * Controls the proportion of tokens allocated from the container's budget to this element. + * It defaults to 1 on all elements. + */ + flex?: number +} + +interface DefOptions extends FenceOptions, ContextExpansionOptions, DataFilter { + /** + * Filename filter based on file suffix. Case insensitive. + */ + endsWith?: string + + /** + * Filename filter using glob syntax. + */ + glob?: string + + /** + * By default, throws an error if the value in def is empty. + */ + ignoreEmpty?: boolean +} + +interface DefImagesOptions { + detail?: "high" | "low" + /** + * Maximum width of the image + */ + maxWidth?: number + /** + * Maximum height of the image + */ + maxHeight?: number + /** + * Auto cropping same color on the edges of the image + */ + autoCrop?: boolean +} + +interface ChatTaskOptions { + command: string + cwd?: string + env?: Record + args?: string[] +} + +type JSONSchemaTypeName = + | "string" + | "number" + | "integer" + | "boolean" + | "object" + | "array" + | "null" + +type JSONSchemaType = + | JSONSchemaString + | JSONSchemaNumber + | JSONSchemaBoolean + | JSONSchemaObject + | JSONSchemaArray + | null + +interface JSONSchemaString { + type: "string" + description?: string + default?: string +} + +interface JSONSchemaNumber { + type: "number" | "integer" + description?: string + default?: number + minimum?: number + exclusiveMinimum?: number + maximum?: number + exclusiveMaximum?: number +} + +interface JSONSchemaBoolean { + type: "boolean" + description?: string + default?: boolean +} + +interface JSONSchemaObject { + $schema?: string + type: "object" + description?: string + properties?: { + [key: string]: JSONSchemaType + } + required?: string[] + additionalProperties?: boolean +} + +interface JSONSchemaArray { + $schema?: string + type: "array" + description?: string + items?: JSONSchemaType +} + +type JSONSchema = JSONSchemaObject | JSONSchemaArray + +interface JSONSchemaValidation { + schema?: JSONSchema + valid: boolean + error?: string +} + +interface DataFrame { + schema?: string + data: unknown + validation?: JSONSchemaValidation +} + +interface RunPromptResult { + text: string + annotations?: Diagnostic[] + fences?: Fenced[] + frames?: DataFrame[] + json?: any + error?: SerializedError + genVars?: Record + schemas?: Record + finishReason: + | "stop" + | "length" + | "tool_calls" + | "content_filter" + | "cancel" + | "fail" +} + +/** + * Path manipulation functions. + */ +interface Path { + /** + * Returns the last portion of a path. Similar to the Unix basename command. + * @param path + */ + dirname(path: string): string + + /** + * Returns the extension of the path, from the last '.' to end of string in the last portion of the path. + * @param path + */ + extname(path: string): string + + /** + * Returns the last portion of a path, similar to the Unix basename command. + */ + basename(path: string, suffix?: string): string + + /** + * The path.join() method joins all given path segments together using the platform-specific separator as a delimiter, then normalizes the resulting path. + * @param paths + */ + join(...paths: string[]): string + + /** + * The path.normalize() method normalizes the given path, resolving '..' and '.' segments. + */ + normalize(...paths: string[]): string + + /** + * The path.relative() method returns the relative path from from to to based on the current working directory. If from and to each resolve to the same path (after calling path.resolve() on each), a zero-length string is returned. + */ + relative(from: string, to: string): string + + /** + * The path.resolve() method resolves a sequence of paths or path segments into an absolute path. + * @param pathSegments + */ + resolve(...pathSegments: string[]): string + + /** + * Determines whether the path is an absolute path. + * @param path + */ + isAbsolute(path: string): boolean +} + +interface Fenced { + label: string + language?: string + content: string + args?: { schema?: string } & Record + + validation?: JSONSchemaValidation +} + +interface XMLParseOptions { + allowBooleanAttributes?: boolean + ignoreAttributes?: boolean + ignoreDeclaration?: boolean + ignorePiTags?: boolean + parseAttributeValue?: boolean + removeNSPrefix?: boolean + unpairedTags?: string[] +} + +interface ParsePDFOptions { + filter?: (pageIndex: number, text?: string) => boolean +} + +interface HTMLToTextOptions { + /** + * After how many chars a line break should follow in `p` elements. + * + * Set to `null` or `false` to disable word-wrapping. + */ + wordwrap?: number | false | null | undefined +} + +interface ParseXLSXOptions { + // specific worksheet name + sheet?: string + // Use specified range (A1-style bounded range string) + range?: string +} + +interface WorkbookSheet { + name: string + rows: object[] +} + +interface ParseZipOptions { + glob?: string +} + +type TokenEncoder = (text: string) => number[] + +interface Parsers { + /** + * Parses text as a JSON5 payload + */ + JSON5( + content: string | WorkspaceFile, + options?: { defaultValue?: any } + ): any | undefined + + /** + * Parses text or file as a JSONL payload. Empty lines are ignore, and JSON5 is used for parsing. + * @param content + */ + JSONL(content: string | WorkspaceFile): any[] | undefined + + /** + * Parses text as a YAML payload + */ + YAML( + content: string | WorkspaceFile, + options?: { defaultValue?: any } + ): any | undefined + + /** + * Parses text as TOML payload + * @param text text as TOML payload + */ + TOML( + content: string | WorkspaceFile, + options?: { defaultValue?: any } + ): any | undefined + + /** + * Parses the front matter of a markdown file + * @param content + * @param defaultValue + */ + frontmatter( + content: string | WorkspaceFile, + options?: { defaultValue?: any; format: "yaml" | "json" | "toml" } + ): any | undefined + + /** + * Parses a file or URL as PDF + * @param content + */ + PDF( + content: string | WorkspaceFile, + options?: ParsePDFOptions + ): Promise<{ file: WorkspaceFile; pages: string[] } | undefined> + + /** + * Parses a .docx file + * @param content + */ + DOCX( + content: string | WorkspaceFile + ): Promise<{ file: WorkspaceFile } | undefined> + + /** + * Parses a CSV file or text + * @param content + */ + CSV( + content: string | WorkspaceFile, + options?: { delimiter?: string; headers?: string[] } + ): object[] | undefined + + /** + * Parses a XLSX file and a given worksheet + * @param content + */ + XLSX( + content: WorkspaceFile, + options?: ParseXLSXOptions + ): Promise + + /** + * Parses a .env file + * @param content + */ + dotEnv(content: string | WorkspaceFile): Record + + /** + * Parses a .ini file + * @param content + */ + INI( + content: string | WorkspaceFile, + options?: { defaultValue?: any } + ): any | undefined + + /** + * Parses a .xml file + * @param content + */ + XML( + content: string | WorkspaceFile, + options?: { defaultValue?: any } & XMLParseOptions + ): any | undefined + + /** + * Convert HTML to text + * @param content html string or file + * @param options + */ + HTMLToText( + content: string | WorkspaceFile, + options?: HTMLToTextOptions + ): string + + /** + * Convert HTML to markdown + * @param content html string or file + * @param options + */ + HTMLToMarkdown(content: string | WorkspaceFile): string + + /** + * Extracts the contents of a zip archive file + * @param file + * @param options + */ + unzip( + file: WorkspaceFile, + options?: ParseZipOptions + ): Promise + + /** + * Estimates the number of tokens in the content. + * @param content content to tokenize + */ + tokens(content: string | WorkspaceFile): number + + /** + * Parses fenced code sections in a markdown text + */ + fences(content: string | WorkspaceFile): Fenced[] + + /** + * Parses various format of annotations (error, warning, ...) + * @param content + */ + annotations(content: string | WorkspaceFile): Diagnostic[] + + /** + * Executes a tree-sitter query on a code file + * @param file + * @param query tree sitter query; if missing, returns the entire tree + */ + code(file: WorkspaceFile, query?: string): Promise + + /** + * Parses and evaluates a math expression + * @param expression math expression compatible with mathjs + */ + math(expression: string): Promise + + /** + * Using the JSON schema, validates the content + * @param schema JSON schema instance + * @param content object to validate + */ + validateJSON(schema: JSONSchema, content: any): JSONSchemaValidation + + /** + * Renders a mustache template + * @param text template text + * @param data data to render + */ + mustache(text: string | WorkspaceFile, data: Record): str + /** + * Renders a jinja template + */ + jinja(text: string | WorkspaceFile, data: Record): string +} + +interface AICIGenOptions { + /** + * Make sure the generated text is one of the options. + */ + options?: string[] + /** + * Make sure the generated text matches given regular expression. + */ + regex?: string | RegExp + /** + * Make sure the generated text matches given yacc-like grammar. + */ + yacc?: string + /** + * Make sure the generated text is a substring of the given string. + */ + substring?: string + /** + * Used together with `substring` - treat the substring as ending the substring + * (typically '"' or similar). + */ + substringEnd?: string + /** + * Store result of the generation (as bytes) into a shared variable. + */ + storeVar?: string + /** + * Stop generation when the string is generated (the result includes the string and any following bytes (from the same token)). + */ + stopAt?: string + /** + * Stop generation when the given number of tokens have been generated. + */ + maxTokens?: number +} + +interface AICINode { + type: "aici" + name: "gen" +} + +interface AICIGenNode extends AICINode { + name: "gen" + options: AICIGenOptions +} + +interface AICI { + /** + * Generate a string that matches given constraints. + * If the tokens do not map cleanly into strings, it will contain Unicode replacement characters. + */ + gen(options: AICIGenOptions): AICIGenNode +} + +interface YAML { + /** + * Converts an object to its YAML representation + * @param obj + */ + stringify(obj: any): string + /** + * Parses a YAML string to object + */ + parse(text: string): any +} + +interface XML { + /** + * Parses an XML payload to an object + * @param text + */ + parse(text: string, options?: XMLParseOptions): any +} + +interface HTMLTableToJSONOptions { + useFirstRowForHeadings?: boolean + headers?: HeaderRows + stripHtmlFromHeadings?: boolean + stripHtmlFromCells?: boolean + stripHtml?: boolean | null + forceIndexAsNumber?: boolean + countDuplicateHeadings?: boolean + ignoreColumns?: number[] | null + onlyColumns?: number[] | null + ignoreHiddenRows?: boolean + id?: string[] | null + headings?: string[] | null + containsClasses?: string[] | null + limitrows?: number | null +} + +interface HTML { + /** + * Converts all HTML tables to JSON. + * @param html + * @param options + */ + convertTablesToJSON( + html: string, + options?: HTMLTableToJSONOptions + ): object[][] + /** + * Converts HTML markup to plain text + * @param html + */ + convertToText(html: string): string + /** + * Converts HTML markup to markdown + * @param html + */ + convertToMarkdown(html: string): string +} + +interface MD { + /** + * Parses front matter from markdown + * @param text + */ + frontmatter(text: string, format?: "yaml" | "json" | "toml" | "text"): any + + /** + * Removes the front matter from the markdown text + */ + content(text: string): string + + /** + * Merges frontmatter with the existing text + * @param text + * @param frontmatter + * @param format + */ + updateFrontmatter( + text: string, + frontmatter: any, + format?: "yaml" | "json" + ): string +} + +interface JSONL { + /** + * Parses a JSONL string to an array of objects + * @param text + */ + parse(text: string): any[] + /** + * Converts objects to JSONL format + * @param objs + */ + stringify(objs: any[]): string +} + +interface INI { + /** + * Parses a .ini file + * @param text + */ + parse(text: string): any + + /** + * Converts an object to.ini string + * @param value + */ + stringify(value: any): string +} + +interface CSV { + /** + * Parses a CSV string to an array of objects + * @param text + * @param options + */ + parse( + text: string, + options?: { + delimiter?: string + headers?: string[] + } + ): object[] + + /** + * Converts an array of object that represents a data table to a markdown table + * @param csv + * @param options + */ + markdownify(csv: object[], options?: { headers?: string[] }): string +} + +interface HighlightOptions { + maxLength?: number +} + +interface WebSearchResult { + webPages: WorkspaceFile[] +} + +interface VectorSearchOptions extends EmbeddingsModelOptions { + /** + * Maximum number of embeddings to use + */ + topK?: number + /** + * Minimum similarity score + */ + minScore?: number +} + +interface FuzzSearchOptions { + /** + * Controls whether to perform prefix search. It can be a simple boolean, or a + * function. + * + * If a boolean is passed, prefix search is performed if true. + * + * If a function is passed, it is called upon search with a search term, the + * positional index of that search term in the tokenized search query, and the + * tokenized search query. + */ + prefix?: boolean + /** + * Controls whether to perform fuzzy search. It can be a simple boolean, or a + * number, or a function. + * + * If a boolean is given, fuzzy search with a default fuzziness parameter is + * performed if true. + * + * If a number higher or equal to 1 is given, fuzzy search is performed, with + * a maximum edit distance (Levenshtein) equal to the number. + * + * If a number between 0 and 1 is given, fuzzy search is performed within a + * maximum edit distance corresponding to that fraction of the term length, + * approximated to the nearest integer. For example, 0.2 would mean an edit + * distance of 20% of the term length, so 1 character in a 5-characters term. + * The calculated fuzziness value is limited by the `maxFuzzy` option, to + * prevent slowdown for very long queries. + */ + fuzzy?: boolean | number + /** + * Controls the maximum fuzziness when using a fractional fuzzy value. This is + * set to 6 by default. Very high edit distances usually don't produce + * meaningful results, but can excessively impact search performance. + */ + maxFuzzy?: number + /** + * Maximum number of results to return + */ + topK?: number +} + +interface Retrieval { + /** + * Executers a Bing web search. Requires to configure the BING_SEARCH_API_KEY secret. + * @param query + */ + webSearch(query: string): Promise + + /** + * Search using similarity distance on embeddings + */ + vectorSearch( + query: string, + files: (string | WorkspaceFile) | (string | WorkspaceFile)[], + options?: VectorSearchOptions + ): Promise + + /** + * Performs a fuzzy search over the files + * @param query keywords to search + * @param files list of files + * @param options fuzzing configuration + */ + fuzzSearch( + query: string, + files: WorkspaceFile | WorkspaceFile[], + options?: FuzzSearchOptions + ): Promise +} + +type FetchTextOptions = Omit + +interface DataFilter { + /** + * The keys to select from the object. + * If a key is prefixed with -, it will be removed from the object. + */ + headers?: string[] + /** + * Selects the first N elements from the data + */ + sliceHead?: number + /** + * Selects the last N elements from the data + */ + sliceTail?: number + /** + * Selects the a random sample of N items in the collection. + */ + sliceSample?: number + + /** + * Removes items with duplicate values for the specified keys. + */ + distinct?: string[] +} + +interface DefDataOptions + extends Omit, + DataFilter { + /** + * Output format in the prompt. Defaults to Markdown table rendering. + */ + format?: "json" | "yaml" | "csv" +} + +interface DefSchemaOptions { + /** + * Output format in the prompt. + */ + format?: "typescript" | "json" | "yaml" +} + +type ChatFunctionHandler = ( + args: { context: ToolCallContext } & Record +) => Awaitable + +interface WriteTextOptions extends ContextExpansionOptions { + /** + * Append text to the assistant response + */ + assistant?: boolean +} + +type PromptGenerator = (ctx: ChatGenerationContext) => Awaitable + +interface PromptGeneratorOptions extends ModelOptions, PromptSystemOptions { + /** + * Label for trace + */ + label?: string +} + +interface FileOutputOptions { + /** + * Schema identifier to validate the generated file + */ + schema?: string +} + +interface FileOutput { + pattern: string + description?: string + options?: FileOutputOptions +} + +interface ImportTemplateOptions {} + +interface PromptTemplateString { + /** + * Set a priority similar to CSS z-index + * to control the trimming of the prompt when the context is full + * @param priority + */ + priority(value: number): PromptTemplateString + /** + * Sets the context layout flex weight + */ + flex(value: number): PromptTemplateString + /** + * Applies jinja template to the string lazily + * @param data jinja data + */ + jinja(data: Record): PromptTemplateString + /** + * Applies mustache template to the string lazily + * @param data mustache data + */ + mustache(data: Record): PromptTemplateString + /** + * Sets the max tokens for this string + * @param tokens + */ + maxTokens(tokens: number): PromptTemplateString +} + +interface ChatTurnGenerationContext { + importTemplate( + files: string | string[], + arguments?: Record< + string | number | boolean | (() => string | number | boolean) + >, + options?: ImportTemplateOptions + ): void + writeText(body: Awaitable, options?: WriteTextOptions): void + $(strings: TemplateStringsArray, ...args: any[]): PromptTemplateString + fence(body: StringLike, options?: FenceOptions): void + def( + name: string, + body: string | WorkspaceFile | WorkspaceFile[] | ShellOutput | Fenced, + options?: DefOptions + ): string + defData( + name: string, + data: object[] | object, + options?: DefDataOptions + ): string + console: PromptGenerationConsole +} + +interface FileUpdate { + before: string + after: string + validation?: JSONSchemaValidation +} + +interface RunPromptResultPromiseWithOptions extends Promise { + options(values?: PromptGeneratorOptions): RunPromptResultPromiseWithOptions +} + +interface ChatGenerationContext extends ChatTurnGenerationContext { + defSchema( + name: string, + schema: JSONSchema, + options?: DefSchemaOptions + ): string + defImages( + files: ElementOrArray, + options?: DefImagesOptions + ): void + defTool( + tool: ToolCallback | AgenticToolCallback | AgenticToolProviderCallback + ): void + defTool( + name: string, + description: string, + parameters: PromptParametersSchema | JSONSchema, + fn: ChatFunctionHandler + ): void + defChatParticipant( + participant: ChatParticipantHandler, + options?: ChatParticipantOptions + ): void + defFileOutput( + pattern: string, + description?: string, + options?: FileOutputOptions + ): void + runPrompt( + generator: string | PromptGenerator, + options?: PromptGeneratorOptions + ): Promise + prompt( + strings: TemplateStringsArray, + ...args: any[] + ): RunPromptResultPromiseWithOptions +} + +interface GenerationOutput { + /** + * LLM output. + */ + text: string + + /** + * Parsed fence sections + */ + fences: Fenced[] + + /** + * Parsed data sections + */ + frames: DataFrame[] + + /** + * A map of file updates + */ + fileEdits: Record + + /** + * Generated variables, typically from AICI.gen + */ + genVars: Record + + /** + * Generated annotations + */ + annotations: Diagnostic[] + + /** + * Schema definition used in the generation + */ + schemas: Record + + /** + * Output as JSON if parsable + */ + json?: any +} + +type Point = { + row: number + column: number +} + +interface SyntaxNode { + id: number + typeId: number + grammarId: number + type: string + grammarType: string + isNamed: boolean + isMissing: boolean + isExtra: boolean + hasChanges: boolean + hasError: boolean + isError: boolean + text: string + parseState: number + nextParseState: number + startPosition: Point + endPosition: Point + startIndex: number + endIndex: number + parent: SyntaxNode | null + children: Array + namedChildren: Array + childCount: number + namedChildCount: number + firstChild: SyntaxNode | null + firstNamedChild: SyntaxNode | null + lastChild: SyntaxNode | null + lastNamedChild: SyntaxNode | null + nextSibling: SyntaxNode | null + nextNamedSibling: SyntaxNode | null + previousSibling: SyntaxNode | null + previousNamedSibling: SyntaxNode | null + descendantCount: number + + equals(other: SyntaxNode): boolean + toString(): string + child(index: number): SyntaxNode | null + namedChild(index: number): SyntaxNode | null + childForFieldName(fieldName: string): SyntaxNode | null + childForFieldId(fieldId: number): SyntaxNode | null + fieldNameForChild(childIndex: number): string | null + childrenForFieldName( + fieldName: string, + cursor: TreeCursor + ): Array + childrenForFieldId(fieldId: number, cursor: TreeCursor): Array + firstChildForIndex(index: number): SyntaxNode | null + firstNamedChildForIndex(index: number): SyntaxNode | null + + descendantForIndex(index: number): SyntaxNode + descendantForIndex(startIndex: number, endIndex: number): SyntaxNode + namedDescendantForIndex(index: number): SyntaxNode + namedDescendantForIndex(startIndex: number, endIndex: number): SyntaxNode + descendantForPosition(position: Point): SyntaxNode + descendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode + namedDescendantForPosition(position: Point): SyntaxNode + namedDescendantForPosition( + startPosition: Point, + endPosition: Point + ): SyntaxNode + descendantsOfType( + types: String | Array, + startPosition?: Point, + endPosition?: Point + ): Array + + walk(): TreeCursor +} + +interface TreeCursor { + nodeType: string + nodeTypeId: number + nodeStateId: number + nodeText: string + nodeId: number + nodeIsNamed: boolean + nodeIsMissing: boolean + startPosition: Point + endPosition: Point + startIndex: number + endIndex: number + readonly currentNode: SyntaxNode + readonly currentFieldName: string + readonly currentFieldId: number + readonly currentDepth: number + readonly currentDescendantIndex: number + + reset(node: SyntaxNode): void + resetTo(cursor: TreeCursor): void + gotoParent(): boolean + gotoFirstChild(): boolean + gotoLastChild(): boolean + gotoFirstChildForIndex(goalIndex: number): boolean + gotoFirstChildForPosition(goalPosition: Point): boolean + gotoNextSibling(): boolean + gotoPreviousSibling(): boolean + gotoDescendant(goalDescendantIndex: number): void +} + +interface QueryCapture { + name: string + node: SyntaxNode +} + +interface ShellOptions { + cwd?: string + stdin?: string + /** + * Process timeout in milliseconds, default is 60s + */ + timeout?: number + /** + * trace label + */ + label?: string +} + +interface ShellOutput { + stdout?: string + stderr?: string + exitCode: number + failed: boolean +} + +interface BrowserOptions { + /** + * Browser engine for this page. Defaults to chromium + * + */ + browser?: "chromium" | "firefox" | "webkit" + + /** + * If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in is closed. + */ + downloadsPath?: string + + /** + * Whether to run browser in headless mode. More details for Chromium and Firefox. Defaults to true unless the devtools option is true. + */ + headless?: boolean + + /** + * Specify environment variables that will be visible to the browser. Defaults to process.env. + */ + env?: Record +} + +interface BrowseSessionOptions extends BrowserOptions, TimeoutOptions { + /** + * Creates a new context for the browser session + */ + incognito?: boolean + + /** + * Base url to use for relative urls + * @link https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url + */ + baseUrl?: string + + /** + * Toggles bypassing page's Content-Security-Policy. Defaults to false. + * @link https://playwright.dev/docs/api/class-browser#browser-new-context-option-bypass-csp + */ + bypassCSP?: boolean + + /** + * Whether to ignore HTTPS errors when sending network requests. Defaults to false. + * @link https://playwright.dev/docs/api/class-browser#browser-new-context-option-ignore-https-errors + */ + ignoreHTTPSErrors?: boolean + + /** + * Whether or not to enable JavaScript in the context. Defaults to true. + * @link https://playwright.dev/docs/api/class-browser#browser-new-context-option-java-script-enabled + */ + javaScriptEnabled?: boolean +} + +interface TimeoutOptions { + /** + * Maximum time in milliseconds. Default to no timeout + */ + timeout?: number +} + +interface ScreenshotOptions extends TimeoutOptions { + quality?: number + scale?: "css" | "device" + type?: "png" | "jpeg" + style?: string +} + +interface PageScreenshotOptions extends ScreenshotOptions { + fullPage?: boolean + omitBackground?: boolean + clip?: { + x: number + y: number + width: number + height: number + } +} + +interface BrowserLocatorSelector { + /** + * Allows locating elements by their ARIA role, ARIA attributes and accessible name. + * @param role + * @param options + */ + getByRole( + role: + | "alert" + | "alertdialog" + | "application" + | "article" + | "banner" + | "blockquote" + | "button" + | "caption" + | "cell" + | "checkbox" + | "code" + | "columnheader" + | "combobox" + | "complementary" + | "contentinfo" + | "definition" + | "deletion" + | "dialog" + | "directory" + | "document" + | "emphasis" + | "feed" + | "figure" + | "form" + | "generic" + | "grid" + | "gridcell" + | "group" + | "heading" + | "img" + | "insertion" + | "link" + | "list" + | "listbox" + | "listitem" + | "log" + | "main" + | "marquee" + | "math" + | "meter" + | "menu" + | "menubar" + | "menuitem" + | "menuitemcheckbox" + | "menuitemradio" + | "navigation" + | "none" + | "note" + | "option" + | "paragraph" + | "presentation" + | "progressbar" + | "radio" + | "radiogroup" + | "region" + | "row" + | "rowgroup" + | "rowheader" + | "scrollbar" + | "search" + | "searchbox" + | "separator" + | "slider" + | "spinbutton" + | "status" + | "strong" + | "subscript" + | "superscript" + | "switch" + | "tab" + | "table" + | "tablist" + | "tabpanel" + | "term" + | "textbox" + | "time" + | "timer" + | "toolbar" + | "tooltip" + | "tree" + | "treegrid" + | "treeitem", + options?: { + checked?: boolean + disabled?: boolean + exact?: boolean + expanded?: boolean + name?: string + selected?: boolean + } & TimeoutOptions + ): Locator + + /** + * Allows locating input elements by the text of the associated