From c1c26f07269539707b3dda264f8bac1843ff8fc2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 09:21:35 -0800 Subject: [PATCH 001/220] restart after sat solution Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 61fc816e1..29710ed29 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1351,9 +1351,15 @@ namespace sat { scoped_rl.push_child(&(m_local_search->rlimit())); m_local_search->rlimit().push(500000); m_local_search->reinit(*this); - m_local_search->check(_lits.size(), _lits.data(), nullptr); + lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); for (unsigned i = 0; i < m_phase.size(); ++i) m_best_phase[i] = m_local_search->get_value(i); + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + do_restart(true); + } } From a7231027c369ff8d7b4a7aee10b70eb8ff96f956 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:04:54 -0800 Subject: [PATCH 002/220] try side-by-side nightly --- scripts/mk_nuget_task.py | 2 +- scripts/nightly.yaml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index e22057a0f..b48f37759 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -25,7 +25,7 @@ os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), 'glibc-2.31' : ('so', 'linux-x64'), - 'glibc' : ('so', 'linux-x64'), + 'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 4925ce45d..dc7f923ba 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,6 +35,20 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu + displayName: "Ubuntu build" + pool: + vmImage: "ubuntu-20.04" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - 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)/. + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu displayName: "Ubuntu build" pool: @@ -512,6 +526,11 @@ stages: inputs: artifactName: 'Ubuntu' targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download Ubuntu-20.04" + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Doc" inputs: From c1cadd37cc1284e22b15c50d24a76206bd0ea77e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:07:12 -0800 Subject: [PATCH 003/220] update stage name --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index dc7f923ba..d8ff50b92 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,8 +35,8 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) - - job: Ubuntu - displayName: "Ubuntu build" + - job: Ubuntu-20 + displayName: "Ubuntu-20 build" pool: vmImage: "ubuntu-20.04" steps: From f3ae7692ca6bfe40f677245e8b98d127b709991d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Feb 2023 16:08:14 -0800 Subject: [PATCH 004/220] update stage name --- scripts/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index d8ff50b92..4aeb44b35 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -35,8 +35,8 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) - - job: Ubuntu-20 - displayName: "Ubuntu-20 build" + - job: Ubuntu20 + displayName: "Ubuntu20 build" pool: vmImage: "ubuntu-20.04" steps: From 90a75866fbe1eb66813444ea7c7d0d9a26046fb4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 03:17:52 -0800 Subject: [PATCH 005/220] elaborating on local-search rephase strategy --- src/sat/sat_ddfw.cpp | 52 +++++++++++++------------- src/sat/sat_ddfw.h | 2 + src/sat/sat_parallel.cpp | 20 ++++++---- src/sat/sat_parallel.h | 4 +- src/sat/sat_params.pyg | 2 +- src/sat/sat_prob.h | 10 +++-- src/sat/sat_solver.cpp | 80 +++++++++++++++++++++++++++++++++------- src/sat/sat_solver.h | 15 ++++++++ src/sat/sat_types.h | 2 +- 9 files changed, 131 insertions(+), 56 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index f1493232c..418070c64 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -57,11 +57,11 @@ namespace sat { double sec = m_stopwatch.get_current_seconds(); double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec); if (m_last_flips == 0) { - IF_VERBOSE(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; + IF_VERBOSE(1, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; if (m_par) verbose_stream() << " :par"; verbose_stream() << ")\n"); } - IF_VERBOSE(0, verbose_stream() << "(sat.ddfw " + IF_VERBOSE(1, verbose_stream() << "(sat.ddfw " << std::setw(07) << m_min_sz << std::setw(07) << m_models.size() << std::setw(10) << kflips_per_sec @@ -106,7 +106,6 @@ namespace sat { if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) { - if (m_par) update_reward_avg(v); return v; } } @@ -139,9 +138,8 @@ namespace sat { } void ddfw::add(solver const& s) { - for (auto& ci : m_clauses) { + for (auto& ci : m_clauses) m_alloc.del_clause(ci.m_clause); - } m_clauses.reset(); m_use_list.reset(); m_num_non_binary_clauses = 0; @@ -281,6 +279,7 @@ namespace sat { ci.add(nlit); } value(v) = !value(v); + update_reward_avg(v); } bool ddfw::should_reinit_weights() { @@ -379,36 +378,35 @@ namespace sat { return m_par != nullptr && m_flips >= m_parsync_next; } + void ddfw::save_priorities() { + m_probs.reset(); + for (unsigned v = 0; v < num_vars(); ++v) + m_probs.push_back(-m_vars[v].m_reward_avg); + } + void ddfw::do_parallel_sync() { - if (m_par->from_solver(*this)) { - // Sum exp(xi) / exp(a) = Sum exp(xi - a) - double max_avg = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg); - } - double sum = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)); - } - if (sum == 0) { - sum = 0.01; - } - m_probs.reset(); - for (unsigned v = 0; v < num_vars(); ++v) { - m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum); - } + if (m_par->from_solver(*this)) m_par->to_solver(*this); - } + ++m_parsync_count; m_parsync_next *= 3; m_parsync_next /= 2; } + void ddfw::save_model() { + m_model.reserve(num_vars()); + for (unsigned i = 0; i < num_vars(); ++i) + m_model[i] = to_lbool(value(i)); + save_priorities(); + } + + void ddfw::save_best_values() { - if (m_unsat.empty()) { - m_model.reserve(num_vars()); - for (unsigned i = 0; i < num_vars(); ++i) - m_model[i] = to_lbool(value(i)); + if (m_unsat.empty()) + save_model(); + else if (m_unsat.size() < m_min_sz) { + if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) + save_model(); } if (m_unsat.size() < m_min_sz) { m_models.reset(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ed9936f0a..d5e7df773 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -164,6 +164,8 @@ namespace sat { bool_var pick_var(); void flip(bool_var v); void save_best_values(); + void save_model(); + void save_priorities(); // shift activity void shift_weights(); diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 2f7a19558..3e493168a 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -214,14 +214,17 @@ namespace sat { } - bool parallel::_to_solver(solver& s) { - if (m_priorities.empty()) { - return false; - } + void parallel::_to_solver(solver& s) { + return; +#if 0 + if (m_priorities.empty()) + return; + for (bool_var v = 0; v < m_priorities.size(); ++v) { s.update_activity(v, m_priorities[v]); } - return true; + s.m_activity_inc = 128; +#endif } void parallel::from_solver(solver& s) { @@ -229,16 +232,19 @@ namespace sat { _from_solver(s); } - bool parallel::to_solver(solver& s) { + void parallel::to_solver(solver& s) { lock_guard lock(m_mux); - return _to_solver(s); + _to_solver(s); } void parallel::_to_solver(i_local_search& s) { + return; +#if 0 m_priorities.reset(); for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) { m_priorities.push_back(s.get_priority(v)); } +#endif } bool parallel::_from_solver(i_local_search& s) { diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h index 68266760b..65ae09183 100644 --- a/src/sat/sat_parallel.h +++ b/src/sat/sat_parallel.h @@ -51,7 +51,7 @@ namespace sat { bool enable_add(clause const& c) const; void _get_clauses(solver& s); void _from_solver(solver& s); - bool _to_solver(solver& s); + void _to_solver(solver& s); bool _from_solver(i_local_search& s); void _to_solver(i_local_search& s); @@ -102,7 +102,7 @@ namespace sat { // exchange from solver state to local search and back. void from_solver(solver& s); - bool to_solver(solver& s); + void to_solver(solver& s); bool from_solver(i_local_search& s); void to_solver(i_local_search& s); diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index f9d7c643a..6aedf1c89 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -2,7 +2,7 @@ def_module_params('sat', export=True, description='propositional SAT solver', params=(max_memory_param(), - ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching'), + ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching, local_search'), ('phase.sticky', BOOL, True, 'use sticky phase caching'), ('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'), ('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'), diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index 305e76b8b..f05365e39 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -58,7 +58,7 @@ namespace sat { clause_vector m_clause_db; svector m_clauses; bool_vector m_values, m_best_values; - unsigned m_best_min_unsat{ 0 }; + unsigned m_best_min_unsat = 0; vector m_use_list; unsigned_vector m_flat_use_list; unsigned_vector m_use_list_index; @@ -67,9 +67,9 @@ namespace sat { indexed_uint_set m_unsat; random_gen m_rand; unsigned_vector m_breaks; - uint64_t m_flips{ 0 }; - uint64_t m_next_restart{ 0 }; - unsigned m_restart_count{ 0 }; + uint64_t m_flips = 0; + uint64_t m_next_restart = 0; + unsigned m_restart_count = 0; stopwatch m_stopwatch; model m_model; @@ -139,6 +139,8 @@ namespace sat { void add(solver const& s) override; model const& get_model() const override { return m_model; } + + double get_priority(bool_var v) const { return 0; } std::ostream& display(std::ostream& out) const; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 29710ed29..373885f34 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -40,6 +40,26 @@ Revision History: namespace sat { + /** + * Special cases of kissat style general backoff calculation. + * The version here calculates + * limit := value*log(C)^2*n*log(n) + * (effort calculation in kissat is based on ticks not clauses) + * + * respectively + * limit := conflicts + value*log(C)^2*n*log(n) + */ + void backoff::delta_effort(solver& s) { + count++; + unsigned d = value * count * log2(count + 1); + unsigned cl = log2(s.num_clauses() + 2); + limit = cl * cl * d; + } + + void backoff::delta_conflicts(solver& s) { + delta_effort(s); + limit += s.m_conflicts_since_init; + } solver::solver(params_ref const & p, reslimit& l): solver_core(l), @@ -1349,16 +1369,43 @@ namespace sat { m_local_search->updt_params(m_params); m_local_search->set_seed(m_rand()); scoped_rl.push_child(&(m_local_search->rlimit())); - m_local_search->rlimit().push(500000); + + m_backoffs.m_local_search.delta_effort(*this); + m_local_search->rlimit().push(m_backoffs.m_local_search.limit); + m_local_search->reinit(*this); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); - for (unsigned i = 0; i < m_phase.size(); ++i) - m_best_phase[i] = m_local_search->get_value(i); - if (r == l_true) { - m_conflicts_since_restart = 0; - m_conflicts_since_gc = 0; - m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + auto const& mdl = m_local_search->get_model(); + if (mdl.size() == m_best_phase.size()) { + for (unsigned i = 0; i < m_best_phase.size(); ++i) + m_best_phase[i] = l_true == mdl[i]; + + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } do_restart(true); +#if 0 + // move higher priority variables to front + // eg., move the first 10% variables to front + svector> priorities(mdl.size()); + for (unsigned i = 0; i < mdl.size(); ++i) + priorities[i] = { m_local_search->get_priority(i), i }; + std::sort(priorities.begin(), priorities.end(), [](auto& x, auto& y) { return x.first > y.first; }); + for (unsigned i = priorities.size() / 10; i-- > 0; ) + move_to_front(priorities[i].second); +#endif + + + if (l_true == r) { + for (clause const* cp : m_clauses) { + bool is_true = any_of(*cp, [&](auto lit) { return lit.sign() != m_best_phase[lit.var()]; }); + if (!is_true) { + verbose_stream() << "clause is false " << *cp << "\n"; + } + } + } } } @@ -1693,7 +1740,7 @@ namespace sat { if (!is_pos) next_lit.neg(); - + TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); assign_scoped(next_lit); return true; @@ -1913,6 +1960,7 @@ namespace sat { m_rephase_lim = 0; m_rephase_inc = 0; m_reorder_lim = m_config.m_reorder_base; + m_backoffs.m_local_search.value = 500; m_reorder_inc = 0; m_conflicts_since_restart = 0; m_force_conflict_analysis = false; @@ -1928,6 +1976,7 @@ namespace sat { m_next_simplify = m_config.m_simplify_delay; m_min_d_tk = 1.0; m_search_lvl = 0; + if (m_learned.size() <= 2*m_clauses.size()) m_conflicts_since_gc = 0; m_restart_next_out = 0; @@ -2030,9 +2079,7 @@ namespace sat { if (m_par) { m_par->from_solver(*this); - if (m_par->to_solver(*this)) { - m_activity_inc = 128; - } + m_par->to_solver(*this); } if (m_config.m_binspr && !inconsistent()) { @@ -2944,14 +2991,19 @@ namespace sat { case PS_SAT_CACHING: if (m_search_state == s_sat) for (unsigned i = 0; i < m_phase.size(); ++i) - m_phase[i] = m_best_phase[i]; + m_phase[i] = m_best_phase[i]; break; case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; case PS_LOCAL_SEARCH: - if (m_search_state == s_sat) - bounded_local_search(); + if (m_search_state == s_sat) { + if (m_rand() % 2 == 0) + bounded_local_search(); + for (unsigned i = 0; i < m_phase.size(); ++i) + m_phase[i] = m_best_phase[i]; + } + break; default: UNREACHABLE(); diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 524f6b06d..ca738ce9b 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -87,10 +87,23 @@ namespace sat { struct no_drat_params : public params_ref { no_drat_params() { set_bool("drat.disable", true); } }; + + struct backoff { + unsigned value = 1; + unsigned lo = 0; + unsigned hi = 0; + unsigned limit = 0; + unsigned count = 0; + void delta_effort(solver& s); + void delta_conflicts(solver& s); + }; class solver : public solver_core { public: struct abort_solver {}; + struct backoffs { + backoff m_local_search; + }; protected: enum search_state { s_sat, s_unsat }; @@ -159,6 +172,7 @@ namespace sat { unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; + backoffs m_backoffs; unsigned m_rephase_lim; unsigned m_rephase_inc; unsigned m_reorder_lim; @@ -237,6 +251,7 @@ namespace sat { friend class lut_finder; friend class npn3_finder; friend class proof_trim; + friend struct backoff; public: solver(params_ref const & p, reslimit& l); ~solver() override; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index c92a8bbeb..3026b3c5e 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -90,7 +90,7 @@ namespace sat { virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; - virtual double get_priority(bool_var v) const { return 0; } + virtual double get_priority(bool_var v) const = 0; virtual bool get_value(bool_var v) const { return true; } }; From a150e58893b242b093d627b32f1aacd500fc9945 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 09:21:02 -0800 Subject: [PATCH 006/220] update release script Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 4 ++-- scripts/release.yml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index b48f37759..073b6b99a 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,8 +24,8 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'glibc-2.31' : ('so', 'linux-x64'), - 'glibc-2.35' : ('so', 'linux-x64'), + 'glibc' : ('so', 'linux-x64'), + #'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), diff --git a/scripts/release.yml b/scripts/release.yml index 10ae24577..7d3ec1085 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -85,6 +85,35 @@ stages: artifactName: 'UbuntuBuild' targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuBuild20 + displayName: "Ubuntu build 20" + pool: + vmImage: "ubuntu-20.04" + steps: + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'UbuntuBuild20' + targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuDoc displayName: "Ubuntu Doc build" pool: @@ -191,6 +220,11 @@ stages: inputs: artifact: 'UbuntuBuild' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu20 Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: @@ -436,6 +470,11 @@ stages: pool: vmImage: "windows-latest" steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu Build' inputs: From 601e506d54874fe9216ca9881134d517ea34a83d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 10:40:49 -0800 Subject: [PATCH 007/220] remove debug out --- src/muz/base/dl_util.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 623f287f7..401d8e816 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -324,17 +324,14 @@ namespace datalog { if (cycle_len < 2) return; auto aux = container[permutation_cycle[0]]; - verbose_stream() << "xx " << cycle_len << "\n"; for (unsigned i = 1; i < cycle_len; i++) container[permutation_cycle[i-1]] = container[permutation_cycle[i]]; container[permutation_cycle[cycle_len-1]] = aux; } template void permute_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { - if (cycle_len<2) { + if (cycle_len < 2) return; - } - verbose_stream() << "ptr\n"; T * aux = container.get(permutation_cycle[0]); for (unsigned i=1; i < cycle_len; i++) { container.set(permutation_cycle[i-1], container.get(permutation_cycle[i])); From 6a2d60a6ba2c8b7012cd3afd32366991d2eb08c3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 11:04:58 -0800 Subject: [PATCH 008/220] fix #6571 most solvers don't support background properties --- src/muz/base/dl_context.cpp | 5 +++++ src/muz/base/rule_properties.cpp | 6 ++++++ src/muz/base/rule_properties.h | 1 + 3 files changed, 12 insertions(+) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index c9d2c7797..230a452df 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -577,6 +577,7 @@ namespace datalog { m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); + m_rule_properties.check_background_free(); break; case SPACER_ENGINE: m_rule_properties.collect(r); @@ -584,6 +585,7 @@ namespace datalog { m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_quantifier_free(exists_k); + m_rule_properties.check_background_free(); break; case BMC_ENGINE: m_rule_properties.collect(r); @@ -598,13 +600,16 @@ namespace datalog { m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case DDNF_ENGINE: + m_rule_properties.check_background_free(); break; case LAST_ENGINE: default: diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 7632a0c2f..239fa73b6 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -139,6 +139,12 @@ void rule_properties::check_nested_free() { } } +void rule_properties::check_background_free() { + if (m_ctx.get_num_assertions() > 0) + throw default_exception("engine does not support background assertions"); +} + + void rule_properties::check_existential_tail() { ast_mark visited; ptr_vector todo, tocheck; diff --git a/src/muz/base/rule_properties.h b/src/muz/base/rule_properties.h index 896b1bb16..a7ef9a0de 100644 --- a/src/muz/base/rule_properties.h +++ b/src/muz/base/rule_properties.h @@ -68,6 +68,7 @@ namespace datalog { void check_for_negated_predicates(); void check_nested_free(); void check_infinite_sorts(); + void check_background_free(); bool is_monotone() { return m_is_monotone; } void operator()(var* n); void operator()(quantifier* n); From 96d815b9049e7feba128fa32c27a81aa2c42350c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:27:19 -0800 Subject: [PATCH 009/220] adding arith sls --- src/sat/sat_ddfw.cpp | 14 +- src/sat/sat_ddfw.h | 2 +- src/sat/sat_extension.h | 1 + src/sat/sat_local_search.cpp | 11 +- src/sat/sat_local_search.h | 2 +- src/sat/sat_parallel.cpp | 2 +- src/sat/sat_prob.h | 2 +- src/sat/sat_solver.cpp | 2 +- src/sat/sat_types.h | 2 +- src/sat/smt/CMakeLists.txt | 2 + src/sat/smt/arith_diagnostics.cpp | 11 + src/sat/smt/arith_local_search.cpp | 352 +++++++++++++++++++++++++++++ src/sat/smt/arith_solver.cpp | 1 + src/sat/smt/arith_solver.h | 105 +++++++-- src/sat/smt/euf_local_search.cpp | 126 +++++++++++ src/sat/smt/euf_solver.h | 16 +- src/sat/smt/sat_th.h | 9 + 17 files changed, 625 insertions(+), 35 deletions(-) create mode 100644 src/sat/smt/arith_local_search.cpp create mode 100644 src/sat/smt/euf_local_search.cpp diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 418070c64..723b38586 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -200,16 +200,14 @@ namespace sat { m_shifts = 0; m_stopwatch.start(); } - - void ddfw::reinit(solver& s) { + + void ddfw::reinit(solver& s, bool_vector const& phase) { add(s); add_assumptions(); - if (s.m_best_phase_size > 0) { - for (unsigned v = 0; v < num_vars(); ++v) { - value(v) = s.m_best_phase[v]; - reward(v) = 0; - make_count(v) = 0; - } + for (unsigned v = 0; v < phase.size(); ++v) { + value(v) = phase[v]; + reward(v) = 0; + make_count(v) = 0; } init_clause_data(); flatten_use_list(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index d5e7df773..ce5ff9fdf 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -227,7 +227,7 @@ namespace sat { // for parallel integration unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; } - void reinit(solver& s) override; + void reinit(solver& s, bool_vector const& phase) override; void collect_statistics(statistics& st) const override {} diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index d6a956a32..3a1f363a3 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -126,6 +126,7 @@ namespace sat { virtual void add_assumptions(literal_set& ext_assumptions) {} virtual bool tracking_assumptions() { return false; } virtual bool enable_self_propagate() const { return false; } + virtual void local_search(bool_vector& phase) {} virtual bool extract_pb(std::function& card, std::function& pb) { diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 61ddd13d8..8cc90f05e 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -359,13 +359,10 @@ namespace sat { m_par(nullptr) { } - void local_search::reinit(solver& s) { - import(s, true); - if (s.m_best_phase_size > 0) { - for (unsigned i = num_vars(); i-- > 0; ) { - set_phase(i, s.m_best_phase[i]); - } - } + void local_search::reinit(solver& s, bool_vector const& phase) { + import(s, true); + for (unsigned i = phase.size(); i-- > 0; ) + set_phase(i, phase[i]); } void local_search::import(solver const& s, bool _init) { diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index e46d4b009..7295b851a 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -248,7 +248,7 @@ namespace sat { void set_seed(unsigned n) override { config().set_random_seed(n); } - void reinit(solver& s) override; + void reinit(solver& s, bool_vector const& phase) override; // used by unit-walk void set_phase(bool_var v, bool f); diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 3e493168a..cdb13706f 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -252,7 +252,7 @@ namespace sat { m_consumer_ready = true; if (m_solver_copy) { copied = true; - s.reinit(*m_solver_copy.get()); + s.reinit(*m_solver_copy.get(), m_solver_copy->m_best_phase); } return copied; } diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index f05365e39..d8d58d091 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -150,7 +150,7 @@ namespace sat { void collect_statistics(statistics& st) const override {} - void reinit(solver& s) override { UNREACHABLE(); } + void reinit(solver& s, bool_vector const& phase) override { UNREACHABLE(); } }; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 373885f34..10aac6dcb 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1373,7 +1373,7 @@ namespace sat { m_backoffs.m_local_search.delta_effort(*this); m_local_search->rlimit().push(m_backoffs.m_local_search.limit); - m_local_search->reinit(*this); + m_local_search->reinit(*this, m_best_phase); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); auto const& mdl = m_local_search->get_model(); if (mdl.size() == m_best_phase.size()) { diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 3026b3c5e..d5d457cb0 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -85,7 +85,7 @@ namespace sat { virtual void updt_params(params_ref const& p) = 0; virtual void set_seed(unsigned s) = 0; virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0; - virtual void reinit(solver& s) = 0; + virtual void reinit(solver& s, bool_vector const& phase) = 0; virtual unsigned num_non_binary_clauses() const = 0; virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index 22fc9963c..dbbfc3856 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(sat_smt arith_axioms.cpp arith_diagnostics.cpp arith_internalize.cpp + arith_local_search.cpp arith_solver.cpp array_axioms.cpp array_diagnostics.cpp @@ -20,6 +21,7 @@ z3_add_component(sat_smt euf_ackerman.cpp euf_internalize.cpp euf_invariant.cpp + euf_local_search.cpp euf_model.cpp euf_proof.cpp euf_proof_checker.cpp diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 8ead3d980..a3e48256d 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -23,6 +23,17 @@ Author: namespace arith { + + void arith_proof_hint_builder::set_type(euf::solver& ctx, hint_type ty) { + ctx.push(value_trail(m_eq_tail)); + ctx.push(value_trail(m_lit_tail)); + m_ty = ty; + reset(); + } + + arith_proof_hint* arith_proof_hint_builder::mk(euf::solver& s) { + return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); + } std::ostream& solver::display(std::ostream& out) const { lp().display(out); diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp new file mode 100644 index 000000000..c6d02e436 --- /dev/null +++ b/src/sat/smt/arith_local_search.cpp @@ -0,0 +1,352 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/smt/arith_solver.h" + + +namespace arith { + + + /// + /// need access to clauses + /// need access to m_unsat + /// need update of phase + /// need to initialize ineqs (arithmetical atoms) + /// + + solver::sls::sls(solver& s): + s(s), m(s.m) {} + + void solver::sls::operator()(bool_vector& phase) { + + // need to init variables/atoms/ineqs + + m.limit().push(m_max_arith_steps); + + unsigned m_best_min_unsat = 1; + unsigned best = m_best_min_unsat; + + while (m.inc() && m_best_min_unsat > 0) { + // unsigned prev = m_unsat.size(); + if (!flip()) + return; +#if 0 + if (m_unsat.size() < best) { + best = m_unsat.size(); + num_steps = 0; + } + if (m_unsat.size() < m_best_min_unsat) + save_best_values(); +#endif + } + } + + void solver::sls::set_bounds_begin() { + m_max_arith_steps = 0; + } + + void solver::sls::set_bounds_end(unsigned num_literals) { + // m_max_arith_steps = s.ctx.m_sl_config.L * + } + + void solver::sls::set_bounds(enode* n) { + ++m_max_arith_steps; + } + + bool solver::sls::flip() { + ++m_stats.m_num_flips; + log(); + if (flip_unsat()) + return true; + if (flip_clauses()) + return true; + if (flip_dscore()) + return true; + return false; + } + + // distance to true + rational solver::sls::dtt(rational const& args, ineq const& ineq) const { + switch (ineq.m_op) { + case ineq_kind::LE: + if (args <= ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound; + case ineq_kind::EQ: + if (args == ineq.m_bound) + return rational::zero(); + return rational::one(); + case ineq_kind::NE: + if (args == ineq.m_bound) + return rational::one(); + return rational::zero(); + case ineq_kind::LT: + default: + if (args < ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound + 1; + } + } + + rational solver::sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + auto new_args_value = ineq.m_args_value; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + new_args_value += coeff * (new_value - m_vars[w].m_value); + break; + } + } + return dtt(new_args_value, ineq); + } + + // critical move + bool solver::sls::cm(ineq const& ineq, var_t v, rational& new_value) { + SASSERT(!ineq.is_true()); + auto delta = ineq.m_args_value - ineq.m_bound; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + if (coeff > 0) + new_value = value(v) - abs(ceil(delta / coeff)); + else + new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(delta + coeff * (new_value - value(v)) <= 0); + return true; + case ineq_kind::EQ: + return delta + coeff * (new_value - value(v)) == 0; + case ineq_kind::NE: + return delta + coeff * (new_value - value(v)) != 0; + case ineq_kind::LT: + return delta + coeff * (new_value - value(v)) < 0; + default: + UNREACHABLE(); break; + } + } + } + return false; + } + +#if 0 + + bool solver::sls::flip_unsat() { + unsigned start = m_rand(); + for (unsigned i = m_unsat.size(); i-- > 0; ) { + unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); + if (flip(m_clauses[cl])) + return true; + } + return false; + } + + bool solver::sls::flip_clauses() { + unsigned start = m_rand(); + for (unsigned i = m_clauses.size(); i-- > 0; ) + if (flip_arith(m_clauses[(i + start) % m_clauses.size()])) + return true; + return false; + } + + bool solver::sls::flip_dscore() { + paws(); + unsigned start = m_rand(); + for (unsigned i = m_unsat.size(); i-- > 0; ) { + unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); + if (flip_dscore(m_clauses[cl])) + return true; + } + std::cout << "flip dscore\n"; + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << m_unsat.size() << ")\n"); + return false; + } + + bool solver::sls::flip_dscore(clause const& clause) { + rational new_value, min_value, min_score(-1); + var_t min_var = UINT_MAX; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + ineq const& ineq = ai.m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + rational score = dscore(v, new_value); + if (UINT_MAX == min_var || score < min_score) { + min_var = v; + min_value = new_value; + min_score = score; + } + } + } + } + if (min_var != UINT_MAX) { + update(min_var, min_value); + return true; + } + return false; + } + + void solver::sls::paws() { + for (auto& clause : m_clauses) { + bool above = 10000 * m_config.sp <= (m_rand() % 10000); + if (!above && clause.is_true() && clause.m_weight > 1) + clause.m_weight -= 1; + if (above && !clause.is_true()) + clause.m_weight += 1; + } + } + + void solver::sls::update(var_t v, rational const& new_value) { + auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto& ai = m_atoms[atm]; + SASSERT(!ai.m_is_bool); + auto& clause = m_clauses[ai.m_clause_idx]; + rational dtt_old = dtt(ai.m_ineq); + ai.m_ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ai.m_ineq); + bool was_true = clause.is_true(); + if (dtt_new < clause.m_dts) { + if (was_true && clause.m_dts > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { + for (auto lit : clause.m_bools) { + if (is_true(lit)) { + dec_break(lit); + break; + } + } + } + clause.m_dts = dtt_new; + if (!was_true && clause.is_true()) + m_unsat.remove(ai.m_clause_idx); + } + else if (clause.m_dts == dtt_old && dtt_old < dtt_new) { + clause.m_dts = dts(clause); + if (was_true && !clause.is_true()) + m_unsat.insert(ai.m_clause_idx); + if (was_true && clause.is_true() && clause.m_dts > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { + for (auto lit : clause.m_bools) { + if (is_true(lit)) { + inc_break(lit); + break; + } + } + } + } + SASSERT(clause.m_dts >= 0); + } + vi.m_value = new_value; + } + + bool solver::sls::flip_arith(clause const& clause) { + rational new_value; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + ineq const& ineq = ai.m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = m_unsat.size(); + update(v, new_value); + std::cout << "score " << v << " " << score << "\n"; + std::cout << num_unsat << " -> " << m_unsat.size() << "\n"; + return true; + } + } + } + return false; + } + + + + rational solver::sls::dts(clause const& cl) const { + rational d(1), d2; + bool first = true; + for (auto a : cl.m_arith) { + auto const& ai = m_atoms[a]; + d2 = dtt(ai.m_ineq); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + rational solver::sls::dts(clause const& cl, var_t v, rational const& new_value) const { + rational d(1), d2; + bool first = true; + for (auto a : cl.m_arith) { + auto const& ai = m_atoms[a]; + d2 = dtt(ai.m_ineq, v, new_value); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + // + // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // + rational solver::sls::dscore(var_t v, rational const& new_value) const { + auto const& vi = m_vars[v]; + rational score(0); + for (auto const& [coeff, atm] : vi.m_atoms) { + auto const& ai = m_atoms[atm]; + auto const& cl = m_clauses[ai.m_clause_idx]; + score += (cl.m_dts - dts(cl, v, new_value)) * rational(cl.m_weight); + } + return score; + } + + int solver::sls::cm_score(var_t v, rational const& new_value) { + int score = 0; + auto& vi = m_vars[v]; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto const& ai = m_atoms[atm]; + auto const& clause = m_clauses[ai.m_clause_idx]; + rational dtt_old = dtt(ai.m_ineq); + rational dtt_new = dtt(ai.m_ineq, v, new_value); + if (!clause.is_true()) { + if (dtt_new == 0) + ++score; + } + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) + continue; + else { + bool has_true = false; + for (auto a : clause.m_arith) { + auto const& ai = m_atoms[a]; + rational d = dtt(ai.m_ineq, v, new_value); + has_true |= (d == 0); + } + if (!has_true) + --score; + } + } + return score; + } + +#endif +} + diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 35e0795b7..fd507ed15 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -24,6 +24,7 @@ namespace arith { solver::solver(euf::solver& ctx, theory_id id) : th_euf_solver(ctx, symbol("arith"), id), m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), + m_local_search(*this), m_resource_limit(*this), m_bp(*this), a(m), diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a13ef6684..1717b93d1 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -78,12 +78,7 @@ namespace arith { m_eq_tail++; } public: - void set_type(euf::solver& ctx, hint_type ty) { - ctx.push(value_trail(m_eq_tail)); - ctx.push(value_trail(m_lit_tail)); - m_ty = ty; - reset(); - } + void set_type(euf::solver& ctx, hint_type ty); void set_num_le(unsigned n) { m_num_le = n; } void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); } void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); } @@ -96,12 +91,9 @@ namespace arith { } std::pair const& lit(unsigned i) const { return m_literals[i]; } std::tuple const& eq(unsigned i) const { return m_eqs[i]; } - arith_proof_hint* mk(euf::solver& s) { - return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); - } + arith_proof_hint* mk(euf::solver& s); }; - class solver : public euf::th_euf_solver { friend struct arith_proof_hint; @@ -144,7 +136,7 @@ namespace arith { }; int_hashtable m_model_eqs; - bool m_new_eq { false }; + bool m_new_eq = false; // temporary values kept during internalization @@ -198,6 +190,85 @@ namespace arith { } }; + // local search portion for arithmetic + class sls { + enum class ineq_kind { EQ, LE, LT, NE }; + enum class var_kind { INT, REAL }; + typedef unsigned var_t; + typedef unsigned atom_t; + + struct stats { + unsigned m_num_flips = 0; + }; + // encode args <= bound, args = bound, args < bound + struct ineq { + vector> m_args; + ineq_kind m_op = ineq_kind::LE; + rational m_bound; + rational m_args_value; + + bool is_true() const { + switch (m_op) { + case ineq_kind::LE: + return m_args_value <= m_bound; + case ineq_kind::EQ: + return m_args_value == m_bound; + case ineq_kind::NE: + return m_args_value != m_bound; + default: + return m_args_value < m_bound; + } + } + }; + + struct var_info { + rational m_value; + rational m_best_value; + var_kind m_kind = var_kind::INT; + vector> m_atoms; + }; + + struct atom_info { + ineq m_ineq; + unsigned m_clause_idx; + bool m_is_bool = false; + bool m_phase = false; + bool m_best_phase = false; + unsigned m_breaks = 0; + }; + + solver& s; + ast_manager& m; + unsigned m_max_arith_steps = 0; + stats m_stats; + vector m_atoms; + vector m_vars; + + bool flip(); + void log() {} + bool flip_unsat() { return false; } + bool flip_clauses() { return false; } + bool flip_dscore() { return false; } +// bool flip_dscore(clause const&); +// bool flip(clause const&); + rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + rational dtt(rational const& args, ineq const& ineq) const; + rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; +// rational dts(clause const& cl, var_t v, rational const& new_value) const; +// rational dts(clause const& cl) const; + bool cm(ineq const& ineq, var_t v, rational& new_value); + + rational value(var_t v) const { return m_vars[v].m_value; } + public: + sls(solver& s); + void operator ()(bool_vector& phase); + void set_bounds_begin(); + void set_bounds_end(unsigned num_literals); + void set_bounds(enode* n); + }; + + sls m_local_search; + typedef vector> var_coeffs; vector m_columns; var_coeffs m_left_side; // constraint left side @@ -233,10 +304,10 @@ namespace arith { unsigned m_asserted_qhead = 0; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head{ 0 }; + unsigned m_assume_eq_head = 0; lp::u_set m_tmp_var_set; - unsigned m_num_conflicts{ 0 }; + unsigned m_num_conflicts = 0; lp_api::stats m_stats; svector m_scopes; @@ -515,6 +586,11 @@ namespace arith { bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); } bool has_unhandled() const override { return m_not_handled != nullptr; } + void set_bounds_begin() override { m_local_search.set_bounds_begin(); } + void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } + void set_bounds(enode* n) override { m_local_search.set_bounds(n); } + void local_search(bool_vector& phase) override { m_local_search(phase); } + // bounds and equality propagation callbacks lp::lar_solver& lp() { return *m_solver; } lp::lar_solver const& lp() const { return *m_solver; } @@ -523,4 +599,7 @@ namespace arith { void consume(rational const& v, lp::constraint_index j); bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const; }; + + + } diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp new file mode 100644 index 000000000..5ea30b96c --- /dev/null +++ b/src/sat/smt/euf_local_search.cpp @@ -0,0 +1,126 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + euf_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/sat_ddfw.h" +#include "sat/smt/euf_solver.h" + + +namespace euf { + + void solver::local_search(bool_vector& phase) { + + + scoped_limits scoped_rl(m.limit()); + sat::ddfw bool_search; + bool_search.add(s()); + bool_search.updt_params(s().params()); + bool_search.set_seed(rand()); + scoped_rl.push_child(&(bool_search.rlimit())); + + unsigned rounds = 0; + unsigned max_rounds = 30; + + sat::model mdl(s().num_vars()); + for (unsigned v = 0; v < s().num_vars(); ++v) + mdl[v] = s().value(v); + + + while (m.inc() && rounds < max_rounds) { + setup_bounds(mdl); + bool_search.reinit(s(), phase); + + // Non-boolean literals are assumptions to Boolean search + literal_vector _lits; + for (unsigned v = 0; v < mdl.size(); ++v) + if (!is_propositional(literal(v))) + _lits.push_back(literal(v, mdl[v] == l_false)); + + bool_search.rlimit().push(m_max_bool_steps); + + lbool r = bool_search.check(_lits.size(), _lits.data(), nullptr); + + + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + phase[i] = mdl[i] == l_true; + + for (auto* th : m_solvers) + th->local_search(phase); + ++rounds; + // if is_sat break; + } + + } + + bool solver::is_propositional(sat::literal lit) { + expr* e = m_bool_var2expr.get(lit.var(), nullptr); + if (!e) + return true; + if (is_uninterp_const(e)) + return true; + euf::enode* n = m_egraph.find(e); + if (!n) + return true; + } + + void solver::setup_bounds(sat::model const& mdl) { + unsigned num_literals = 0; + unsigned num_bool = 0; + for (auto* th : m_solvers) + th->set_bounds_begin(); + + auto init_literal = [&](sat::literal l) { + if (is_propositional(l)) { + ++num_bool; + return; + } + euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); + for (auto const& thv : enode_th_vars(n)) { + auto* th = m_id2solver.get(thv.get_id(), nullptr); + if (th) + th->set_bounds(n); + } + }; + + auto is_true = [&](auto lit) { + return mdl[lit.var()] == to_lbool(!lit.sign()); + }; + + svector bin_clauses; + s().collect_bin_clauses(bin_clauses, false, false); + for (auto* cp : s().clauses()) { + if (any_of(*cp, [&](auto lit) { return is_true(lit); })) + continue; + num_literals += cp->size(); + for (auto l : *cp) + init_literal(l); + } + + for (auto [l1, l2] : bin_clauses) { + if (is_true(l1) || is_true(l2)) + continue; + num_literals += 2; + init_literal(l1); + init_literal(l2); + }; + + m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; + + for (auto* th : m_solvers) + th->set_bounds_end(num_literals); + } +} diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 96079fbec..44e3df4b0 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -100,6 +100,14 @@ namespace euf { scope(unsigned l) : m_var_lim(l) {} }; + struct local_search_config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast((size_t)(l.index() << 4)), 1); } size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast(jst), 2); } @@ -119,6 +127,7 @@ namespace euf { sat::sat_internalizer& si; relevancy m_relevancy; smt_params m_config; + local_search_config m_ls_config; euf::egraph m_egraph; trail_stack m_trail; stats m_stats; @@ -253,6 +262,11 @@ namespace euf { constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } constraint& lit_constraint(enode* n); + // local search + unsigned m_max_bool_steps = 10; + bool is_propositional(sat::literal lit); + void setup_bounds(sat::model const& mdl); + // user propagator void check_for_user_propagator() { if (!m_user_propagator) @@ -339,6 +353,7 @@ namespace euf { void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; std::string reason_unknown() override { return m_reason_unknown; } + void local_search(bool_vector& phase) override; void propagate(literal lit, ext_justification_idx idx); bool propagate(enode* a, enode* b, ext_justification_idx idx); @@ -551,4 +566,3 @@ namespace euf { inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) { return s.display(out); } - diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index a3b81a08d..2101dfd64 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -136,6 +136,15 @@ namespace euf { sat::status status() const { return sat::status::th(false, get_id()); } + /** + * Local search interface + */ + virtual void set_bounds_begin() {} + + virtual void set_bounds_end(unsigned num_literals) {} + + virtual void set_bounds(enode* n) {} + }; class th_proof_hint : public sat::proof_hint { From b3ebce3966aa4923550076064ab66193dc5c8632 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:30:45 -0800 Subject: [PATCH 010/220] fix compilation --- src/sat/smt/euf_local_search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 5ea30b96c..a614d1656 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -72,9 +72,7 @@ namespace euf { return true; if (is_uninterp_const(e)) return true; - euf::enode* n = m_egraph.find(e); - if (!n) - return true; + return !m_egraph.find(e); } void solver::setup_bounds(sat::model const& mdl) { From a8335f2d5eb634a0ed6591cf96c8c2fcff8e00a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Feb 2023 19:50:45 -0800 Subject: [PATCH 011/220] use phase --- src/sat/smt/euf_local_search.cpp | 27 +++++++-------------------- src/sat/smt/euf_solver.h | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index a614d1656..90889ca3e 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -22,8 +22,6 @@ Author: namespace euf { void solver::local_search(bool_vector& phase) { - - scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; bool_search.add(s()); @@ -31,23 +29,17 @@ namespace euf { bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); - unsigned rounds = 0; unsigned max_rounds = 30; - sat::model mdl(s().num_vars()); - for (unsigned v = 0; v < s().num_vars(); ++v) - mdl[v] = s().value(v); - - - while (m.inc() && rounds < max_rounds) { - setup_bounds(mdl); + for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { + setup_bounds(phase); bool_search.reinit(s(), phase); // Non-boolean literals are assumptions to Boolean search literal_vector _lits; - for (unsigned v = 0; v < mdl.size(); ++v) + for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - _lits.push_back(literal(v, mdl[v] == l_false)); + _lits.push_back(literal(v, !phase[v])); bool_search.rlimit().push(m_max_bool_steps); @@ -60,7 +52,6 @@ namespace euf { for (auto* th : m_solvers) th->local_search(phase); - ++rounds; // if is_sat break; } @@ -68,14 +59,10 @@ namespace euf { bool solver::is_propositional(sat::literal lit) { expr* e = m_bool_var2expr.get(lit.var(), nullptr); - if (!e) - return true; - if (is_uninterp_const(e)) - return true; - return !m_egraph.find(e); + return !e || is_uninterp_const(e) || !m_egraph.find(e); } - void solver::setup_bounds(sat::model const& mdl) { + void solver::setup_bounds(bool_vector const& phase) { unsigned num_literals = 0; unsigned num_bool = 0; for (auto* th : m_solvers) @@ -95,7 +82,7 @@ namespace euf { }; auto is_true = [&](auto lit) { - return mdl[lit.var()] == to_lbool(!lit.sign()); + return phase[lit.var()] == !lit.sign(); }; svector bin_clauses; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 44e3df4b0..a19dbca5d 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -265,7 +265,7 @@ namespace euf { // local search unsigned m_max_bool_steps = 10; bool is_propositional(sat::literal lit); - void setup_bounds(sat::model const& mdl); + void setup_bounds(bool_vector const& mdl); // user propagator void check_for_user_propagator() { From 02d48adae556ca0e472ec19dfc1dab414e2e55b1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Feb 2023 08:24:33 -0800 Subject: [PATCH 012/220] fix #6573 --- src/ast/seq_decl_plugin.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 54ef58e32..9292a7be6 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -663,19 +663,21 @@ void seq_decl_plugin::add_map_sig() { m_sigs[OP_SEQ_MAP] = alloc(psig, m, "seq.map", 2, 2, arrABseqA, seqB); m_sigs[OP_SEQ_MAPI] = alloc(psig, m, "seq.mapi", 2, 3, arrIABintTseqA, seqB); m_sigs[OP_SEQ_FOLDL] = alloc(psig, m, "seq.fold_left", 2, 3, arrBAB_BseqA, B); - m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_leftli", 2, 4, arrIBABintTBseqA, B); + m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_lefti", 2, 4, arrIBABintTBseqA, B); } void seq_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { init(); for (unsigned i = 0; i < m_sigs.size(); ++i) { - if (m_sigs[i]) - op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); + if (m_sigs[i]) + op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); } op_names.push_back(builtin_name("seq.map", OP_SEQ_MAP)); op_names.push_back(builtin_name("seq.mapi", OP_SEQ_MAPI)); op_names.push_back(builtin_name("seq.foldl", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("seq.foldli", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_lefti", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_left", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("str.in.re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.in-re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.to.re", _OP_STRING_TO_REGEXP)); From d52e893528249b512ef9a302ac291e6ffc1f4ba7 Mon Sep 17 00:00:00 2001 From: Julian Parsert Date: Fri, 10 Feb 2023 18:00:26 +0000 Subject: [PATCH 013/220] Added overloaded versions of context::recfun in the c++ api that allow for the declaration of recursive functions where the domain is given by a z3::sort_vector instead of an arity and sort* (#6576) Co-authored-by: Julian Parsert --- src/api/c++/z3++.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 820818168..f2c0f3f24 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -360,6 +360,8 @@ namespace z3 { func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range); + func_decl recfun(symbol const & name, const sort_vector& domain, sort const & range); + func_decl recfun(char const * name, sort_vector const& domain, sort const & range); func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); @@ -2712,6 +2714,16 @@ namespace z3 { void set(char const * k, double v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, symbol const & v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, char const* v) { params p(ctx()); p.set(k, v); set(p); } + /** + \brief Create a backtracking point. + + The solver contains a stack of assertions. + + \sa Z3_solver_get_num_scopes + \sa Z3_solver_pop + + def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) + */ void push() { Z3_solver_push(ctx(), m_solver); check_error(); } void pop(unsigned n = 1) { Z3_solver_pop(ctx(), m_solver, n); check_error(); } void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } @@ -3604,6 +3616,19 @@ namespace z3 { } + inline func_decl context::recfun(symbol const & name, sort_vector const& domain, sort const & range) { + check_context(domain, range); + array domain1(domain); + Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, domain1.size(), domain1.ptr(), range); + check_error(); + return func_decl(*this, f); + } + + inline func_decl context::recfun(char const * name, sort_vector const& domain, sort const & range) { + return recfun(str_symbol(name), domain, range); + + } + inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { return recfun(str_symbol(name), arity, domain, range); } From 1b0c76e3f0585cde680efcf14bb6b304fca09fc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 10 Feb 2023 16:55:55 -0800 Subject: [PATCH 014/220] fixes to mbqi in the new core based on #6575 --- src/ast/ast.cpp | 2 +- src/ast/ast.h | 2 +- src/ast/ast_pp_util.cpp | 6 +++--- src/ast/decl_collector.cpp | 19 ++++++++++++------- src/ast/decl_collector.h | 6 +++--- src/sat/smt/q_mbi.cpp | 3 +-- src/smt/smt_clause_proof.cpp | 2 +- src/solver/check_sat_result.cpp | 6 ++++-- src/solver/check_sat_result.h | 2 +- 9 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index dee55cf98..0a34d3e12 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -3296,7 +3296,7 @@ proof * ast_manager::mk_redundant_del(expr* e) { return mk_clause_trail_elem(nullptr, e, PR_REDUNDANT_DEL); } -proof * ast_manager::mk_clause_trail(unsigned n, proof* const* ps) { +proof * ast_manager::mk_clause_trail(unsigned n, expr* const* ps) { ptr_buffer args; args.append(n, (expr**) ps); return mk_app(basic_family_id, PR_CLAUSE_TRAIL, 0, nullptr, args.size(), args.data()); diff --git a/src/ast/ast.h b/src/ast/ast.h index 2400e05a6..8fae83b26 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2335,7 +2335,7 @@ public: proof * mk_th_assumption_add(proof* pr, expr* e); proof * mk_th_lemma_add(proof* pr, expr* e); proof * mk_redundant_del(expr* e); - proof * mk_clause_trail(unsigned n, proof* const* ps); + proof * mk_clause_trail(unsigned n, expr* const* ps); proof * mk_def_axiom(expr * ax); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs); diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index c0608522f..2b7e72a91 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -43,11 +43,11 @@ void ast_pp_util::display_decls(std::ostream& out) { for (unsigned i = m_sorts; i < n; ++i) pp.display_sort_decl(out, coll.get_sorts()[i], seen); m_sorts = n; - + n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f)) + if (coll.should_declare(f) && !m_removed.contains(f)) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; @@ -80,7 +80,7 @@ void ast_pp_util::display_skolem_decls(std::ostream& out) { unsigned n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f) && f->is_skolem()) + if (coll.should_declare(f) && !m_removed.contains(f) && f->is_skolem()) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index 7786a79d4..5619f546b 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -24,7 +24,7 @@ Revision History: void decl_collector::visit_sort(sort * n) { SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); - if (m().is_uninterp(n)) + if (m.is_uninterp(n)) m_sorts.push_back(n); else if (fid == m_dt_fid) { m_sorts.push_back(n); @@ -43,7 +43,7 @@ void decl_collector::visit_sort(sort * n) { } bool decl_collector::is_bool(sort * s) { - return m().is_bool(s); + return m.is_bool(s); } void decl_collector::visit_func(func_decl * n) { @@ -51,10 +51,10 @@ void decl_collector::visit_func(func_decl * n) { if (!m_visited.is_marked(n)) { family_id fid = n->get_family_id(); - if (fid == null_family_id) + if (should_declare(n)) m_decls.push_back(n); else if (fid == m_rec_fid) { - recfun::util u(m()); + recfun::util u(m); if (u.has_def(n)) { m_rec_decls.push_back(n); m_todo.push_back(u.get_def(n).get_rhs()); @@ -69,12 +69,17 @@ void decl_collector::visit_func(func_decl * n) { } } +bool decl_collector::should_declare(func_decl* f) const { + return f->get_family_id() == null_family_id || m.is_model_value(f); +} + + decl_collector::decl_collector(ast_manager & m): - m_manager(m), + m(m), m_trail(m), m_dt_util(m), m_ar_util(m) { - m_basic_fid = m_manager.get_basic_family_id(); + m_basic_fid = m.get_basic_family_id(); m_dt_fid = m_dt_util.get_family_id(); recfun::util rec_util(m); m_rec_fid = rec_util.get_family_id(); @@ -83,7 +88,7 @@ decl_collector::decl_collector(ast_manager & m): void decl_collector::visit(ast* n) { if (m_visited.is_marked(n)) return; - datatype_util util(m()); + datatype_util util(m); m_todo.push_back(n); while (!m_todo.empty()) { n = m_todo.back(); diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 31cabe796..a7f79d008 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -26,7 +26,7 @@ Revision History: #include "ast/array_decl_plugin.h" class decl_collector { - ast_manager & m_manager; + ast_manager & m; lim_svector m_sorts; lim_svector m_decls; lim_svector m_rec_decls; @@ -48,10 +48,10 @@ class decl_collector { void collect_deps(top_sort& st); void collect_deps(sort* s, sort_set& set); - public: decl_collector(ast_manager & m); - ast_manager & m() { return m_manager; } + + bool should_declare(func_decl* f) const; void reset() { m_sorts.reset(); m_decls.reset(); m_visited.reset(); m_trail.reset(); } void visit_func(func_decl* n); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 21830c162..21842ec76 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -357,8 +357,7 @@ namespace q { return expr_ref(m); } else if (!(*p)(*m_model, vars, fmls)) { - TRACE("q", tout << "theory projection failed\n"); - return expr_ref(m); + TRACE("q", tout << "theory projection failed - use value\n"); } } for (app* v : vars) { diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 521510b59..3bb2a1fdf 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -233,7 +233,7 @@ namespace smt { TRACE("context", tout << "get-proof " << ctx.get_fparams().m_clause_proof << "\n";); if (!ctx.get_fparams().m_clause_proof) return proof_ref(m); - proof_ref_vector ps(m); + expr_ref_vector ps(m); for (auto& info : m_trail) { expr_ref fact = mk_or(info.m_clause); proof* pr = info.m_proof; diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index e946dd430..c0f3979aa 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -41,8 +41,10 @@ void check_sat_result::set_reason_unknown(event_handler& eh) { proof* check_sat_result::get_proof() { if (!m_log.empty() && !m_proof) { - app* last = m_log.back(); - m_log.push_back(to_app(m.get_fact(last))); + SASSERT(is_app(m_log.back())); + app* last = to_app(m_log.back()); + expr* fact = m.get_fact(last); + m_log.push_back(fact); m_proof = m.mk_clause_trail(m_log.size(), m_log.data()); } if (m_proof) diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 936f6d3df..2269a1444 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -40,7 +40,7 @@ Notes: class check_sat_result { protected: ast_manager& m; - proof_ref_vector m_log; + expr_ref_vector m_log; proof_ref m_proof; unsigned m_ref_count = 0; lbool m_status = l_undef; From d22e4aa5259137961bc5ae2c45403b9655aa5f1e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 Feb 2023 15:52:32 -0800 Subject: [PATCH 015/220] wip - integrating arithmetic local search --- src/sat/sat_ddfw.cpp | 27 ++- src/sat/sat_ddfw.h | 15 +- src/sat/sat_solver.h | 1 + src/sat/smt/arith_local_search.cpp | 320 ++++++++++++++++------------- src/sat/smt/arith_solver.h | 60 ++++-- src/sat/smt/euf_local_search.cpp | 23 +-- src/sat/smt/sat_th.h | 3 + 7 files changed, 280 insertions(+), 169 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 723b38586..74a0e7777 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -129,6 +129,7 @@ namespace sat { void ddfw::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); unsigned idx = m_clauses.size(); + m_clauses.push_back(clause_info(cls, m_config.m_init_clause_weight)); for (literal lit : *cls) { m_use_list.reserve(2*(lit.var()+1)); @@ -137,6 +138,18 @@ namespace sat { } } + /** + * Remove the last clause that was added + */ + void ddfw::del() { + auto& info = m_clauses.back(); + for (literal lit : *info.m_clause) + m_use_list[lit.index()].pop_back(); + m_alloc.del_clause(info.m_clause); + m_clauses.pop_back(); + m_unsat.remove(m_clauses.size()); + } + void ddfw::add(solver const& s) { for (auto& ci : m_clauses) m_alloc.del_clause(ci.m_clause); @@ -169,9 +182,17 @@ namespace sat { } void ddfw::add_assumptions() { - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - add(1, m_assumptions.data() + i); - } + for (unsigned i = 0; i < m_assumptions.size(); ++i) + add(1, m_assumptions.data() + i); + } + + void ddfw::remove_assumptions() { + for (unsigned i = 0; i < m_assumptions.size(); ++i) + del(); + m_unsat_vars.reset(); + for (auto idx : m_unsat) + for (auto lit : get_clause(idx)) + m_unsat_vars.insert(lit.var()); } void ddfw::init(unsigned sz, literal const* assumptions) { diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ce5ff9fdf..7dd69de81 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -32,7 +32,7 @@ namespace sat { class parallel; class ddfw : public i_local_search { - + public: struct clause_info { clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {} double m_weight; // weight of clause @@ -43,6 +43,7 @@ namespace sat { void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; + protected: struct config { config() { reset(); } @@ -197,6 +198,8 @@ namespace sat { void add(unsigned sz, literal const* c); + void del(); + void add_assumptions(); inline void transfer_weight(unsigned from, unsigned to, double w); @@ -232,6 +235,16 @@ namespace sat { void collect_statistics(statistics& st) const override {} double get_priority(bool_var v) const override { return m_probs[v]; } + + // access clause information and state of Boolean search + indexed_uint_set& unsat_set() { return m_unsat; } + + unsigned num_clauses() const { return m_clauses.size(); } + + clause_info& get_clause_info(unsigned idx) { return m_clauses[idx]; } + + void remove_assumptions(); + }; } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index ca738ce9b..703b36dd0 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -347,6 +347,7 @@ namespace sat { s.m_checkpoint_enabled = true; } }; + unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp index c6d02e436..787280e73 100644 --- a/src/sat/smt/arith_local_search.cpp +++ b/src/sat/smt/arith_local_search.cpp @@ -36,22 +36,19 @@ namespace arith { // need to init variables/atoms/ineqs m.limit().push(m_max_arith_steps); - - unsigned m_best_min_unsat = 1; - unsigned best = m_best_min_unsat; - - while (m.inc() && m_best_min_unsat > 0) { - // unsigned prev = m_unsat.size(); + m_best_min_unsat = unsat().size(); + unsigned num_steps = 0; + while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) return; -#if 0 - if (m_unsat.size() < best) { - best = m_unsat.size(); + ++m_stats.m_num_flips; + ++num_steps; + unsigned num_unsat = unsat().size(); + if (num_unsat < m_best_min_unsat) { + m_best_min_unsat = num_unsat; num_steps = 0; - } - if (m_unsat.size() < m_best_min_unsat) save_best_values(); -#endif + } } } @@ -68,7 +65,6 @@ namespace arith { } bool solver::sls::flip() { - ++m_stats.m_num_flips; log(); if (flip_unsat()) return true; @@ -141,45 +137,72 @@ namespace arith { return false; } -#if 0 - bool solver::sls::flip_unsat() { - unsigned start = m_rand(); - for (unsigned i = m_unsat.size(); i-- > 0; ) { - unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); - if (flip(m_clauses[cl])) + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) { + unsigned cl = unsat().elem_at((i + start) % sz); + if (flip(cl)) return true; } return false; } + + bool solver::sls::flip(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value; + for (literal lit : clause) { + auto const* ai = atom(lit); + if (!ai) + continue; + ineq const& ineq = ai->m_ineq; + for (auto const& [coeff, v] : ineq.m_args) { + if (!ineq.is_true() && cm(ineq, v, new_value)) { + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(0, + verbose_stream() << "score " << v << " " << score << "\n" + << num_unsat << " -> " << unsat().size() << "\n"); + return true; + } + } + } + return false; + } + bool solver::sls::flip_clauses() { - unsigned start = m_rand(); - for (unsigned i = m_clauses.size(); i-- > 0; ) - if (flip_arith(m_clauses[(i + start) % m_clauses.size()])) + unsigned start = s.random(); + for (unsigned i = num_clauses(); i-- > 0; ) + if (flip((i + start) % num_clauses())) return true; return false; } bool solver::sls::flip_dscore() { paws(); - unsigned start = m_rand(); - for (unsigned i = m_unsat.size(); i-- > 0; ) { - unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size()); - if (flip_dscore(m_clauses[cl])) + unsigned start = s.random(); + for (unsigned i = unsat().size(); i-- > 0; ) { + unsigned cl = unsat().elem_at((i + start) % unsat().size()); + if (flip_dscore(cl)) return true; } - std::cout << "flip dscore\n"; - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << m_unsat.size() << ")\n"); + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); return false; } - bool solver::sls::flip_dscore(clause const& clause) { + bool solver::sls::flip_dscore(unsigned cl) { + auto const& clause = get_clause(cl); rational new_value, min_value, min_score(-1); var_t min_var = UINT_MAX; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; - ineq const& ineq = ai.m_ineq; + for (auto lit : clause) { + auto const* ai = atom(lit); + if (!ai) + continue; + ineq const& ineq = ai->m_ineq; for (auto const& [coeff, v] : ineq.m_args) { if (!ineq.is_true() && cm(ineq, v, new_value)) { rational score = dscore(v, new_value); @@ -199,8 +222,9 @@ namespace arith { } void solver::sls::paws() { - for (auto& clause : m_clauses) { - bool above = 10000 * m_config.sp <= (m_rand() % 10000); + for (unsigned cl = num_clauses(); cl-- > 0; ) { + auto& clause = get_clause_info(cl); + bool above = 10000 * m_config.sp <= (s.random() % 10000); if (!above && clause.is_true() && clause.m_weight > 1) clause.m_weight -= 1; if (above && !clause.is_true()) @@ -208,103 +232,6 @@ namespace arith { } } - void solver::sls::update(var_t v, rational const& new_value) { - auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; - for (auto const& [coeff, atm] : vi.m_atoms) { - auto& ai = m_atoms[atm]; - SASSERT(!ai.m_is_bool); - auto& clause = m_clauses[ai.m_clause_idx]; - rational dtt_old = dtt(ai.m_ineq); - ai.m_ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ai.m_ineq); - bool was_true = clause.is_true(); - if (dtt_new < clause.m_dts) { - if (was_true && clause.m_dts > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { - for (auto lit : clause.m_bools) { - if (is_true(lit)) { - dec_break(lit); - break; - } - } - } - clause.m_dts = dtt_new; - if (!was_true && clause.is_true()) - m_unsat.remove(ai.m_clause_idx); - } - else if (clause.m_dts == dtt_old && dtt_old < dtt_new) { - clause.m_dts = dts(clause); - if (was_true && !clause.is_true()) - m_unsat.insert(ai.m_clause_idx); - if (was_true && clause.is_true() && clause.m_dts > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { - for (auto lit : clause.m_bools) { - if (is_true(lit)) { - inc_break(lit); - break; - } - } - } - } - SASSERT(clause.m_dts >= 0); - } - vi.m_value = new_value; - } - - bool solver::sls::flip_arith(clause const& clause) { - rational new_value; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; - ineq const& ineq = ai.m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = m_unsat.size(); - update(v, new_value); - std::cout << "score " << v << " " << score << "\n"; - std::cout << num_unsat << " -> " << m_unsat.size() << "\n"; - return true; - } - } - } - return false; - } - - - - rational solver::sls::dts(clause const& cl) const { - rational d(1), d2; - bool first = true; - for (auto a : cl.m_arith) { - auto const& ai = m_atoms[a]; - d2 = dtt(ai.m_ineq); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - - rational solver::sls::dts(clause const& cl, var_t v, rational const& new_value) const { - rational d(1), d2; - bool first = true; - for (auto a : cl.m_arith) { - auto const& ai = m_atoms[a]; - d2 = dtt(ai.m_ineq, v, new_value); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - // // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) // @@ -312,9 +239,9 @@ namespace arith { auto const& vi = m_vars[v]; rational score(0); for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = m_atoms[atm]; - auto const& cl = m_clauses[ai.m_clause_idx]; - score += (cl.m_dts - dts(cl, v, new_value)) * rational(cl.m_weight); + auto const& ai = *m_atoms[atm]; + auto const& cl = get_clause_info(ai.m_clause_idx); + // score += (dts(cl) - dts(cl, v, new_value)) * rational(cl.m_weight); } return score; } @@ -323,8 +250,8 @@ namespace arith { int score = 0; auto& vi = m_vars[v]; for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = m_atoms[atm]; - auto const& clause = m_clauses[ai.m_clause_idx]; + auto const& ai = *m_atoms[atm]; + auto const& clause = get_clause_info(ai.m_clause_idx); rational dtt_old = dtt(ai.m_ineq); rational dtt_new = dtt(ai.m_ineq, v, new_value); if (!clause.is_true()) { @@ -335,8 +262,10 @@ namespace arith { continue; else { bool has_true = false; - for (auto a : clause.m_arith) { - auto const& ai = m_atoms[a]; + for (auto lit : *clause.m_clause) { + if (!atom(lit)) + continue; + auto const& ai = *atom(lit); rational d = dtt(ai.m_ineq, v, new_value); has_true |= (d == 0); } @@ -347,6 +276,121 @@ namespace arith { return score; } + rational solver::sls::dts(unsigned cl) const { + rational d(1), d2; + bool first = true; + for (auto a : get_clause(cl)) { + auto const* ai = atom(a); + if (!ai) + continue; + d2 = dtt(ai->m_ineq); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + rational solver::sls::dts(unsigned cl, var_t v, rational const& new_value) const { + rational d(1), d2; + bool first = true; + for (auto lit : get_clause(cl)) { + auto const* ai = atom(lit); + if (!ai) + continue; + d2 = dtt(ai->m_ineq, v, new_value); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + void solver::sls::update(var_t v, rational const& new_value) { + auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; + for (auto const& [coeff, atm] : vi.m_atoms) { + auto& ai = *m_atoms[atm]; + SASSERT(!ai.m_is_bool); + auto& clause = get_clause_info(ai.m_clause_idx); + rational dtt_old = dtt(ai.m_ineq); + ai.m_ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ai.m_ineq); + bool was_true = clause.is_true(); + auto& dts_value = dts(ai.m_clause_idx); + if (dtt_new < dts_value) { + if (was_true && dts_value > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { + for (auto lit : *clause.m_clause) { +#if false + TODO + if (is_true(lit)) { + dec_break(lit); + break; + } +#endif + } + } + dts_value = dtt_new; + if (!was_true && clause.is_true()) + unsat().remove(ai.m_clause_idx); + } + else if (dts_value == dtt_old && dtt_old < dtt_new) { + dts_value = dts(ai.m_clause_idx); + if (was_true && !clause.is_true()) + unsat().insert(ai.m_clause_idx); + if (was_true && clause.is_true() && dts_value > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { + for (auto lit : *clause.m_clause) { +#if false + TODO + if (is_true(lit)) { + inc_break(lit); + break; + } +#endif + } + } + } + SASSERT(dts_value >= 0); + } + vi.m_value = new_value; + } + +#if 0 + + + + + + + void solver::sls::add_clause(sat::clause* cl) { + unsigned clause_idx = m_clauses.size(); + m_clauses.push_back({ cl, 1, rational::zero() }); + clause& cls = m_clauses.back(); + cls.m_dts = dts(cls); + for (sat::literal lit : *cl) { + if (is_true(lit)) + cls.add(lit); + } + + for (auto a : arith) + m_atoms[a].m_clause_idx = clause_idx; + + if (!cl.is_true()) { + m_best_min_unsat++; + m_unsat.insert(clause_idx); + } + else if (cl.m_dts > 0 && cl.m_num_trues == 1) + inc_break(sat::to_literal(cl.m_trues)); + + } + + #endif } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 1717b93d1..17207bd80 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -30,6 +30,7 @@ Author: #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" #include "sat/smt/sat_th.h" +#include "sat/sat_ddfw.h" namespace euf { class solver; @@ -197,6 +198,14 @@ namespace arith { typedef unsigned var_t; typedef unsigned atom_t; + struct config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + struct stats { unsigned m_num_flips = 0; }; @@ -237,26 +246,49 @@ namespace arith { unsigned m_breaks = 0; }; - solver& s; - ast_manager& m; - unsigned m_max_arith_steps = 0; - stats m_stats; - vector m_atoms; - vector m_vars; + struct clause { + unsigned m_weight = 1; + rational m_dts = rational::one(); + }; + solver& s; + ast_manager& m; + sat::ddfw* m_bool_search = nullptr; + unsigned m_max_arith_steps = 0; + unsigned m_best_min_unsat = UINT_MAX; + stats m_stats; + config m_config; + scoped_ptr_vector m_atoms; + vector m_vars; + vector m_clauses; + + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } + unsigned num_clauses() const { return m_bool_search->num_clauses(); } + sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } + sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } + sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } + sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + + atom_info* atom(sat::literal lit) const { return m_atoms[lit.index()]; } + rational& dts(unsigned idx) { return m_clauses[idx].m_dts; } bool flip(); void log() {} - bool flip_unsat() { return false; } - bool flip_clauses() { return false; } - bool flip_dscore() { return false; } -// bool flip_dscore(clause const&); -// bool flip(clause const&); + bool flip_unsat(); + bool flip_clauses(); + bool flip_dscore(); + bool flip_dscore(unsigned cl); + bool flip(unsigned cl); rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } rational dtt(rational const& args, ineq const& ineq) const; rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; -// rational dts(clause const& cl, var_t v, rational const& new_value) const; -// rational dts(clause const& cl) const; + rational dts(unsigned cl, var_t v, rational const& new_value) const; + rational dts(unsigned cl) const; bool cm(ineq const& ineq, var_t v, rational& new_value); + int cm_score(var_t v, rational const& new_value); + void update(var_t v, rational const& new_value); + void paws(); + rational dscore(var_t v, rational const& new_value) const; + void save_best_values() {} rational value(var_t v) const { return m_vars[v].m_value; } public: @@ -265,6 +297,7 @@ namespace arith { void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(enode* n); + void set(sat::ddfw* d) { m_bool_search = d; } }; sls m_local_search; @@ -590,6 +623,7 @@ namespace arith { void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } void set_bounds(enode* n) override { m_local_search.set_bounds(n); } void local_search(bool_vector& phase) override { m_local_search(phase); } + void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks lp::lar_solver& lp() { return *m_solver; } diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 90889ca3e..4ee6490b7 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -31,19 +31,24 @@ namespace euf { unsigned max_rounds = 30; + for (auto* th : m_solvers) + th->set_bool_search(&bool_search); + for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - setup_bounds(phase); + bool_search.reinit(s(), phase); + setup_bounds(phase); + // Non-boolean literals are assumptions to Boolean search - literal_vector _lits; + literal_vector assumptions; for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - _lits.push_back(literal(v, !phase[v])); + assumptions.push_back(literal(v, !phase[v])); bool_search.rlimit().push(m_max_bool_steps); - lbool r = bool_search.check(_lits.size(), _lits.data(), nullptr); + lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); auto const& mdl = bool_search.get_model(); @@ -85,8 +90,6 @@ namespace euf { return phase[lit.var()] == !lit.sign(); }; - svector bin_clauses; - s().collect_bin_clauses(bin_clauses, false, false); for (auto* cp : s().clauses()) { if (any_of(*cp, [&](auto lit) { return is_true(lit); })) continue; @@ -95,14 +98,6 @@ namespace euf { init_literal(l); } - for (auto [l1, l2] : bin_clauses) { - if (is_true(l1) || is_true(l2)) - continue; - num_literals += 2; - init_literal(l1); - init_literal(l2); - }; - m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; for (auto* th : m_solvers) diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index 2101dfd64..e226566b8 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -18,6 +18,7 @@ Author: #include "util/top_sort.h" #include "sat/smt/sat_smt.h" +#include "sat/sat_ddfw.h" #include "ast/euf/euf_egraph.h" #include "model/model.h" #include "smt/params/smt_params.h" @@ -139,6 +140,8 @@ namespace euf { /** * Local search interface */ + virtual void set_bool_search(sat::ddfw* ddfw) {} + virtual void set_bounds_begin() {} virtual void set_bounds_end(unsigned num_literals) {} From 46c8d78ecef31bb24577370400acda501c59522c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 09:32:53 -0800 Subject: [PATCH 016/220] fixes for #6577 - the literal false should not appear in clauses - the literal true forces a tautology - fix early return in is_cnf check. It should check all clauses for nested Booleans. --- src/api/api_goal.cpp | 2 +- src/ast/display_dimacs.cpp | 8 ++++++++ src/tactic/goal.cpp | 19 +++++++------------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 086e8f302..cfe0974df 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -198,7 +198,7 @@ extern "C" { RESET_ERROR_CODE(); std::ostringstream buffer; if (!to_goal_ref(g)->is_cnf()) { - SET_ERROR_CODE(Z3_INVALID_ARG, "If this is not what you want, then preprocess by optional bit-blasting and applying tseitin-cnf"); + SET_ERROR_CODE(Z3_INVALID_ARG, "Goal is not converted into CNF. Preprocess by optional bit-blasting and applying tseitin-cnf"); RETURN_Z3(nullptr); } to_goal_ref(g)->display_dimacs(buffer, include_names); diff --git a/src/ast/display_dimacs.cpp b/src/ast/display_dimacs.cpp index 6b2ef6658..9440987ea 100644 --- a/src/ast/display_dimacs.cpp +++ b/src/ast/display_dimacs.cpp @@ -47,6 +47,8 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; if (m.is_not(l)) l = to_app(l)->get_arg(0); if (!is_uninterp_const(l)) @@ -101,6 +103,12 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; + if (m.is_true(l)) { + out << "1 -1 "; + continue; + } if (m.is_not(l)) { out << "-"; l = to_app(l)->get_arg(0); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index b6fe76f6a..23e3ff969 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -692,28 +692,23 @@ bool goal::is_cnf() const { for (unsigned i = 0; i < size(); i++) { expr * f = form(i); if (m_manager.is_or(f)) { - for (expr* lit : *to_app(f)) { - if (!is_literal(lit)) { + for (expr* lit : *to_app(f)) + if (!is_literal(lit)) return false; - } - } - return true; } - if (!is_literal(f)) { + if (!is_literal(f)) return false; - } } return true; } bool goal::is_literal(expr* f) const { m_manager.is_not(f, f); - if (!is_app(f)) return false; - if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) { + if (!is_app(f)) + return false; + if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) for (expr* arg : *to_app(f)) - if (m_manager.is_bool(arg)) { + if (m_manager.is_bool(arg)) return false; - } - } return true; } From 7bef2f3e6f312b979077ed53dfb4218659321b84 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 09:33:35 -0800 Subject: [PATCH 017/220] wip - local search for euf/arithmetic --- src/sat/sat_ddfw.cpp | 38 +-- src/sat/sat_ddfw.h | 26 +- src/sat/sat_local_search.cpp | 17 +- src/sat/sat_local_search.h | 16 +- src/sat/smt/CMakeLists.txt | 2 +- src/sat/smt/arith_sls.cpp | 511 +++++++++++++++++++++++++++++++ src/sat/smt/arith_sls.h | 147 +++++++++ src/sat/smt/arith_solver.h | 113 +------ src/sat/smt/euf_local_search.cpp | 18 +- 9 files changed, 716 insertions(+), 172 deletions(-) create mode 100644 src/sat/smt/arith_sls.cpp create mode 100644 src/sat/smt/arith_sls.h diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 74a0e7777..98e3ce2bd 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -49,6 +49,7 @@ namespace sat { else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(); } + remove_assumptions(); log(); return m_min_sz == 0 ? l_true : l_undef; } @@ -244,7 +245,6 @@ namespace sat { m_use_list_index.push_back(m_flat_use_list.size()); } - void ddfw::flip(bool_var v) { ++m_flips; literal lit = literal(v, !value(v)); @@ -309,19 +309,15 @@ namespace sat { log(); if (m_reinit_count % 2 == 0) { - for (auto& ci : m_clauses) { - ci.m_weight += 1; - } + for (auto& ci : m_clauses) + ci.m_weight += 1; } else { - for (auto& ci : m_clauses) { - if (ci.is_true()) { - ci.m_weight = m_config.m_init_clause_weight; - } - else { - ci.m_weight = m_config.m_init_clause_weight + 1; - } - } + for (auto& ci : m_clauses) + if (ci.is_true()) + ci.m_weight = m_config.m_init_clause_weight; + else + ci.m_weight = m_config.m_init_clause_weight + 1; } init_clause_data(); ++m_reinit_count; @@ -341,11 +337,9 @@ namespace sat { clause const& c = get_clause(i); ci.m_trues = 0; ci.m_num_trues = 0; - for (literal lit : c) { - if (is_true(lit)) { - ci.add(lit); - } - } + for (literal lit : c) + if (is_true(lit)) + ci.add(lit); switch (ci.m_num_trues) { case 0: for (literal lit : c) { @@ -384,12 +378,10 @@ namespace sat { void ddfw::reinit_values() { for (unsigned i = 0; i < num_vars(); ++i) { int b = bias(i); - if (0 == (m_rand() % (1 + abs(b)))) { - value(i) = (m_rand() % 2) == 0; - } - else { - value(i) = bias(i) > 0; - } + if (0 == (m_rand() % (1 + abs(b)))) + value(i) = (m_rand() % 2) == 0; + else + value(i) = bias(i) > 0; } } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 7dd69de81..9971033e3 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -43,6 +43,17 @@ namespace sat { void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; + + class use_list { + ddfw& p; + unsigned i; + public: + use_list(ddfw& p, literal lit) : + p(p), i(lit.index()) {} + unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } + unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } + }; + protected: struct config { @@ -102,15 +113,7 @@ namespace sat { parallel* m_par; - class use_list { - ddfw& p; - unsigned i; - public: - use_list(ddfw& p, literal lit): - p(p), i(lit.index()) {} - unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } - unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } - }; + void flatten_use_list(); @@ -163,7 +166,6 @@ namespace sat { // flip activity bool do_flip(); bool_var pick_var(); - void flip(bool_var v); void save_best_values(); void save_model(); void save_priorities(); @@ -245,6 +247,10 @@ namespace sat { void remove_assumptions(); + void flip(bool_var v); + + use_list get_use_list(literal lit) { return use_list(*this, lit); } + }; } diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 8cc90f05e..c3cb0fb37 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -353,10 +353,7 @@ namespace sat { DEBUG_CODE(verify_unsat_stack();); } - local_search::local_search() : - m_is_unsat(false), - m_initializing(false), - m_par(nullptr) { + local_search::local_search() { } void local_search::reinit(solver& s, bool_vector const& phase) { @@ -375,11 +372,10 @@ namespace sat { m_vars.reserve(s.num_vars()); m_config.set_config(s.get_config()); - if (m_config.phase_sticky()) { - unsigned v = 0; + unsigned v = 0; + if (m_config.phase_sticky()) for (var_info& vi : m_vars) - vi.m_bias = s.m_phase[v++] ? 98 : 2; - } + vi.m_bias = s.m_phase[v++] ? 98 : 2; // copy units unsigned trail_sz = s.init_trail_size(); @@ -419,9 +415,8 @@ namespace sat { if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) throw default_exception("local search is incomplete with extensions beyond PB"); - if (_init) { - init(); - } + if (_init) + init(); } local_search::~local_search() { diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index 7295b851a..b62234522 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -133,21 +133,21 @@ namespace sat { vector m_constraints; // all constraints literal_vector m_assumptions; // temporary assumptions literal_vector m_prop_queue; // propagation queue - unsigned m_num_non_binary_clauses; - bool m_is_pb; - bool m_is_unsat; + unsigned m_num_non_binary_clauses = 0; + bool m_is_pb = false; + bool m_is_unsat = false; unsigned_vector m_unsat_stack; // store all the unsat constraints unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack // configuration changed decreasing variables (score>0 and conf_change==true) bool_var_vector m_goodvar_stack; - bool m_initializing; + bool m_initializing = false; // information about solution - unsigned m_best_unsat; - double m_best_unsat_rate; - double m_last_best_unsat_rate; + unsigned m_best_unsat = 0; + double m_best_unsat_rate = 0; + double m_last_best_unsat_rate = 0; // for non-known instance, set as maximal int m_best_known_value = INT_MAX; // best known value for this instance @@ -159,7 +159,7 @@ namespace sat { reslimit m_limit; random_gen m_rand; - parallel* m_par; + parallel* m_par = nullptr; model m_model; inline int score(bool_var v) const { return m_vars[v].m_score; } diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index dbbfc3856..4a899ca9d 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -3,7 +3,7 @@ z3_add_component(sat_smt arith_axioms.cpp arith_diagnostics.cpp arith_internalize.cpp - arith_local_search.cpp + arith_sls.cpp arith_solver.cpp array_axioms.cpp array_diagnostics.cpp diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp new file mode 100644 index 000000000..460bdedce --- /dev/null +++ b/src/sat/smt/arith_sls.cpp @@ -0,0 +1,511 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.cpp + +Abstract: + + Local search dispatch for SMT + +Author: + + Nikolaj Bjorner (nbjorner) 2023-02-07 + +--*/ +#include "sat/sat_solver.h" +#include "sat/smt/arith_solver.h" + + +namespace arith { + + + /// + /// need to initialize ineqs (arithmetical atoms) + /// + + + sls::sls(solver& s): + s(s), m(s.m) {} + + void sls::operator()(bool_vector& phase) { + + m_best_min_unsat = unsat().size(); + unsigned num_steps = 0; + while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { + if (!flip()) + break; + ++m_stats.m_num_flips; + ++num_steps; + unsigned num_unsat = unsat().size(); + if (num_unsat < m_best_min_unsat) { + m_best_min_unsat = num_unsat; + num_steps = 0; + save_best_values(); + } + } + IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); + } + + void sls::save_best_values() { + // first compute assignment to terms + // then update non-basic variables in tableau, assuming a sat solution was found. +#if false + for (auto const& [t, v] : terms) { + rational val; + lp::lar_term const& term = lp().get_term(t); + for (lp::lar_term::ival arg : term) { + auto t2 = lp().column2tv(arg.column()); + auto w = lp().local_to_external(t2.id()); + val += arg.coeff() * local_search.value(w); + } + update(v, val); + } +#endif + + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + if (s.is_bool(v)) + continue; + if (!s.lp().external_is_used(v)) + continue; + rational old_value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); + rational new_value = value(v); + if (old_value == new_value) + continue; + s.ensure_column(v); + lp::column_index vj = s.lp().to_column_index(v); + SASSERT(!vj.is_null()); + if (!s.lp().is_base(vj.index())) { + lp::impq val(new_value); + s.lp().set_value_for_nbasic_column(vj.index(), val); + } + } + } + + void sls::set(sat::ddfw* d) { + m_bool_search = d; + add_vars(); + m_clauses.resize(d->num_clauses()); + for (unsigned i = 0; i < d->num_clauses(); ++i) + for (sat::literal lit : *d->get_clause_info(i).m_clause) + init_literal(lit); + } + + void sls::set_bounds_begin() { + m_max_arith_steps = 0; + } + + void sls::set_bounds(enode* n) { + ++m_max_arith_steps; + } + + void sls::set_bounds_end(unsigned num_literals) { + m_max_arith_steps = (m_config.L * m_max_arith_steps) / num_literals; + } + + bool sls::flip() { + log(); + return flip_unsat() || flip_clauses() || flip_dscore(); + } + + // distance to true + rational sls::dtt(rational const& args, ineq const& ineq) const { + switch (ineq.m_op) { + case ineq_kind::LE: + if (args <= ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound; + case ineq_kind::EQ: + if (args == ineq.m_bound) + return rational::zero(); + return rational::one(); + case ineq_kind::NE: + if (args == ineq.m_bound) + return rational::one(); + return rational::zero(); + case ineq_kind::LT: + if (args < ineq.m_bound) + return rational::zero(); + return args - ineq.m_bound + 1; + default: + UNREACHABLE(); + return rational::zero(); + } + } + + rational sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + auto new_args_value = ineq.m_args_value; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + new_args_value += coeff * (new_value - m_vars[w].m_value); + break; + } + } + return dtt(new_args_value, ineq); + } + + // critical move + bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { + SASSERT(!ineq.is_true()); + auto delta = ineq.m_args_value - ineq.m_bound; + for (auto const& [coeff, w] : ineq.m_args) { + if (w == v) { + if (coeff > 0) + new_value = value(v) - abs(ceil(delta / coeff)); + else + new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(delta + coeff * (new_value - value(v)) <= 0); + return true; + case ineq_kind::EQ: + return delta + coeff * (new_value - value(v)) == 0; + case ineq_kind::NE: + return delta + coeff * (new_value - value(v)) != 0; + case ineq_kind::LT: + return delta + coeff * (new_value - value(v)) < 0; + default: + UNREACHABLE(); + break; + } + } + } + return false; + } + + bool sls::flip_unsat() { + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) + if (flip(unsat().elem_at((i + start) % sz))) + return true; + return false; + } + + bool sls::flip(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value; + for (literal lit : clause) { + auto const* ineq = atom(lit); + if (!ineq || ineq->is_true()) + continue; + for (auto const& [coeff, v] : ineq->m_args) { + if (!cm(*ineq, v, new_value)) + continue; + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(2, + verbose_stream() << "score " << v << " " << score << "\n" + << num_unsat << " -> " << unsat().size() << "\n"); + return true; + } + } + return false; + } + + bool sls::flip_clauses() { + unsigned start = s.random(); + unsigned sz = m_bool_search->num_clauses(); + for (unsigned i = sz; i-- > 0; ) + if (flip((i + start) % sz)) + return true; + return false; + } + + bool sls::flip_dscore() { + paws(); + unsigned start = s.random(); + unsigned sz = unsat().size(); + for (unsigned i = sz; i-- > 0; ) + if (flip_dscore(unsat().elem_at((i + start) % sz))) + return true; + return false; + } + + bool sls::flip_dscore(unsigned cl) { + auto const& clause = get_clause(cl); + rational new_value, min_value, min_score(-1); + var_t min_var = UINT_MAX; + for (auto lit : clause) { + auto const* ineq = atom(lit); + if (!ineq || ineq->is_true()) + continue; + for (auto const& [coeff, v] : ineq->m_args) { + if (cm(*ineq, v, new_value)) { + rational score = dscore(v, new_value); + if (UINT_MAX == min_var || score < min_score) { + min_var = v; + min_value = new_value; + min_score = score; + } + } + } + } + if (min_var != UINT_MAX) { + update(min_var, min_value); + return true; + } + return false; + } + + /** + * redistribute weights of clauses. TODO - re-use ddfw weights instead. + */ + void sls::paws() { + for (unsigned cl = num_clauses(); cl-- > 0; ) { + auto& clause = get_clause_info(cl); + bool above = 10000 * m_config.sp <= (s.random() % 10000); + if (!above && clause.is_true() && get_weight(cl) > 1) + get_weight(cl) -= 1; + if (above && !clause.is_true()) + get_weight(cl) += 1; + } + } + + // + // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // + rational sls::dscore(var_t v, rational const& new_value) const { + auto const& vi = m_vars[v]; + rational score(0); + for (auto const& [coeff, lit] : vi.m_literals) + for (auto cl : m_bool_search->get_use_list(lit)) + score += (dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + return score; + } + + int sls::cm_score(var_t v, rational const& new_value) { + int score = 0; + auto& vi = m_vars[v]; + for (auto const& [coeff, lit] : vi.m_literals) { + auto const& ineq = *atom(lit); + rational dtt_old = dtt(ineq); + rational dtt_new = dtt(ineq, v, new_value); + for (auto cl : m_bool_search->get_use_list(lit)) { + auto const& clause = get_clause_info(cl); + if (!clause.is_true()) { + if (dtt_new == 0) + ++score; // false -> true + } + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) // true -> true ?? TODO + continue; + else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO + --score; + } + } + return score; + } + + rational sls::dts(unsigned cl) const { + rational d(1), d2; + bool first = true; + for (auto a : get_clause(cl)) { + auto const* ineq = atom(a); + if (!ineq) + continue; + d2 = dtt(*ineq); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + rational sls::dts(unsigned cl, var_t v, rational const& new_value) const { + rational d(1), d2; + bool first = true; + for (auto lit : get_clause(cl)) { + auto const* ineq = atom(lit); + if (!ineq) + continue; + d2 = dtt(*ineq, v, new_value); + if (first) + d = d2, first = false; + else + d = std::min(d, d2); + if (d == 0) + break; + } + return d; + } + + void sls::update(var_t v, rational const& new_value) { + auto& vi = m_vars[v]; + auto const& old_value = vi.m_value; + for (auto const& [coeff, lit] : vi.m_literals) { + auto& ineq = *atom(lit); + rational dtt_old = dtt(ineq); + ineq.m_args_value += coeff * (new_value - old_value); + rational dtt_new = dtt(ineq); + SASSERT(!(dtt_new == 0 && dtt_new < dtt_old) || m_bool_search->get_value(lit.var()) == lit.sign()); + SASSERT(!(dtt_old == 0 && dtt_new > dtt_old) || m_bool_search->get_value(lit.var()) != lit.sign()); + if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true + m_bool_search->flip(lit.var()); + else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false + m_bool_search->flip(lit.var()); + dtt(ineq) = dtt_new; + SASSERT((dtt_new == 0) == (m_bool_search->get_value(lit.var()) != lit.sign())); + } + vi.m_value = new_value; + } + + void sls::add_vars() { + SASSERT(m_vars.empty()); + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + rational value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); + value = s.is_int(v) ? ceil(value) : value; + auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL; + m_vars.push_back({ value, value, k, {} }); + } + } + + sls::ineq& sls::new_ineq(ineq_kind op, rational const& bound) { + auto* i = alloc(ineq); + i->m_bound = bound; + i->m_op = op; + return *i; + } + + void sls::add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v) { + ineq.m_args.push_back({ c, v }); + ineq.m_args_value += c * value(v); + m_vars[v].m_literals.push_back({ c, lit }); + } + + void sls::add_bounds(sat::literal_vector& bounds) { + unsigned bvars = s.s().num_vars(); + auto add_ineq = [&](sat::literal lit, ineq& i) { + m_literals.set(lit.index(), &i); + bounds.push_back(lit); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + rational lo, hi; + bool is_strict_lo = false, is_strict_hi = false; + lp::constraint_index ci; + if (!s.is_registered_var(v)) + continue; + lp::column_index vi = s.lp().to_column_index(v); + if (vi.is_null()) + continue; + bool has_lo = s.lp().has_lower_bound(vi.index(), ci, lo, is_strict_lo); + bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); + + if (has_lo && has_hi && lo == hi) { + auto& ineq = new_ineq(sls::ineq_kind::EQ, lo); + sat::literal lit(bvars++); + add_arg(lit, ineq, rational::one(), v); + add_ineq(lit, ineq); + continue; + } + if (has_lo) { + auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, -lo); + sat::literal lit(bvars++); + add_arg(lit, ineq, -rational::one(), v); + add_ineq(lit, ineq); + } + if (has_hi) { + auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, hi); + sat::literal lit(bvars++); + add_arg(lit, ineq, rational::one(), v); + add_ineq(lit, ineq); + } + } + } + + + void sls::add_args(ineq& ineq, lp::tv t, theory_var v, rational sign) { + if (t.is_term()) { + lp::lar_term const& term = s.lp().get_term(t); + + for (lp::lar_term::ival arg : term) { + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + ineq.m_args.push_back({ sign * arg.coeff(), w }); + } + } + else + ineq.m_args.push_back({ sign, s.lp().local_to_external(t.id()) }); + } + + + void sls::init_literal(sat::literal lit) { + if (m_literals.get(lit.index(), nullptr)) + return; + api_bound* b = nullptr; + s.m_bool_var2bound.find(lit.var(), b); + if (b) { + auto t = b->tv(); + rational bound = b->get_value(); + bool should_minus = false; + sls::ineq_kind op; + if (!lit.sign()) { + should_minus = b->get_bound_kind() == lp::GE; + op = sls::ineq_kind::LE; + } + else { + should_minus = b->get_bound_kind() == lp::LE; + if (s.is_int(b->get_var())) { + bound -= 1; + op = sls::ineq_kind::LE; + } + else + op = sls::ineq_kind::LT; + + } + if (should_minus) + bound.neg(); + auto& ineq = new_ineq(op, bound); + + add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + set_literal(lit, ineq); + return; + } + + expr* e = s.bool_var2expr(lit.var()); + expr* l = nullptr, * r = nullptr; + if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) { + theory_var u = s.get_th_var(l); + theory_var v = s.get_th_var(r); + lp::tv tu = s.get_tv(u); + lp::tv tv = s.get_tv(v); + auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); + add_args(ineq, tu, u, rational::one()); + add_args(ineq, tv, v, -rational::one()); + set_literal(lit, ineq); + return; + } + } + + /** + * Associate literal with inequality and synchronize truth assignment based on arithmetic values. + */ + void sls::set_literal(sat::literal lit, ineq& ineq) { + m_literals.set(lit.index(), &ineq); + if (m_bool_search->get_value(lit.var())) { + if (dtt(ineq) != 0) + m_bool_search->flip(lit.var()); + } + else { + if (dtt(ineq) == 0) + m_bool_search->flip(lit.var()); + } + } + +#if 0 + + { + + } +} + + +#endif +} + diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h new file mode 100644 index 000000000..dcb935610 --- /dev/null +++ b/src/sat/smt/arith_sls.h @@ -0,0 +1,147 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + arith_local_search.h + +Abstract: + + Theory plugin for arithmetic local search + +Author: + + Nikolaj Bjorner (nbjorner) 2020-09-08 + +--*/ +#pragma once + +#include "util/obj_pair_set.h" +#include "ast/ast_trail.h" +#include "ast/arith_decl_plugin.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" +#include "math/lp/indexed_value.h" +#include "math/lp/lar_solver.h" +#include "math/lp/nla_solver.h" +#include "math/lp/lp_types.h" +#include "math/lp/lp_api.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial.h" +#include "sat/smt/sat_th.h" +#include "sat/sat_ddfw.h" + +namespace arith { + + class solver; + + // local search portion for arithmetic + class sls { + enum class ineq_kind { EQ, LE, LT, NE }; + enum class var_kind { INT, REAL }; + typedef unsigned var_t; + typedef unsigned atom_t; + + struct config { + double cb = 0.0; + unsigned L = 20; + unsigned t = 45; + unsigned max_no_improve = 500000; + double sp = 0.0003; + }; + + struct stats { + unsigned m_num_flips = 0; + }; + + // encode args <= bound, args = bound, args < bound + struct ineq { + vector> m_args; + ineq_kind m_op = ineq_kind::LE; + rational m_bound; + rational m_args_value; + + bool is_true() const { + switch (m_op) { + case ineq_kind::LE: + return m_args_value <= m_bound; + case ineq_kind::EQ: + return m_args_value == m_bound; + case ineq_kind::NE: + return m_args_value != m_bound; + default: + return m_args_value < m_bound; + } + } + }; + + struct var_info { + rational m_value; + rational m_best_value; + var_kind m_kind = var_kind::INT; + vector> m_literals; + }; + + struct clause { + unsigned m_weight = 1; + }; + + solver& s; + ast_manager& m; + sat::ddfw* m_bool_search = nullptr; + unsigned m_max_arith_steps = 0; + unsigned m_best_min_unsat = UINT_MAX; + stats m_stats; + config m_config; + scoped_ptr_vector m_literals; + vector m_vars; + vector m_clauses; + + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } + unsigned num_clauses() const { return m_bool_search->num_clauses(); } + sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } + sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } + sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } + sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + + ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } + unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } + unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } + bool flip(); + void log() {} + bool flip_unsat(); + bool flip_clauses(); + bool flip_dscore(); + bool flip_dscore(unsigned cl); + bool flip(unsigned cl); + rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + rational dtt(rational const& args, ineq const& ineq) const; + rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; + rational dts(unsigned cl, var_t v, rational const& new_value) const; + rational dts(unsigned cl) const; + bool cm(ineq const& ineq, var_t v, rational& new_value); + int cm_score(var_t v, rational const& new_value); + void update(var_t v, rational const& new_value); + void paws(); + rational dscore(var_t v, rational const& new_value) const; + void save_best_values(); + void add_vars(); + sls::ineq& new_ineq(ineq_kind op, rational const& bound); + void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); + void add_bounds(sat::literal_vector& bounds); + void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void init_literal(sat::literal lit); + void set_literal(sat::literal lit, ineq& ineq); + + rational value(var_t v) const { return m_vars[v].m_value; } + public: + sls(solver& s); + void operator ()(bool_vector& phase); + void set_bounds_begin(); + void set_bounds_end(unsigned num_literals); + void set_bounds(euf::enode* n); + void set(sat::ddfw* d); + }; + +} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 17207bd80..732e291b1 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -30,6 +30,7 @@ Author: #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" #include "sat/smt/sat_th.h" +#include "sat/smt/arith_sls.h" #include "sat/sat_ddfw.h" namespace euf { @@ -98,6 +99,7 @@ namespace arith { class solver : public euf::th_euf_solver { friend struct arith_proof_hint; + friend class sls; struct scope { unsigned m_bounds_lim; @@ -190,116 +192,7 @@ namespace arith { coeffs().pop_back(); } }; - - // local search portion for arithmetic - class sls { - enum class ineq_kind { EQ, LE, LT, NE }; - enum class var_kind { INT, REAL }; - typedef unsigned var_t; - typedef unsigned atom_t; - - struct config { - double cb = 0.0; - unsigned L = 20; - unsigned t = 45; - unsigned max_no_improve = 500000; - double sp = 0.0003; - }; - - struct stats { - unsigned m_num_flips = 0; - }; - // encode args <= bound, args = bound, args < bound - struct ineq { - vector> m_args; - ineq_kind m_op = ineq_kind::LE; - rational m_bound; - rational m_args_value; - - bool is_true() const { - switch (m_op) { - case ineq_kind::LE: - return m_args_value <= m_bound; - case ineq_kind::EQ: - return m_args_value == m_bound; - case ineq_kind::NE: - return m_args_value != m_bound; - default: - return m_args_value < m_bound; - } - } - }; - - struct var_info { - rational m_value; - rational m_best_value; - var_kind m_kind = var_kind::INT; - vector> m_atoms; - }; - - struct atom_info { - ineq m_ineq; - unsigned m_clause_idx; - bool m_is_bool = false; - bool m_phase = false; - bool m_best_phase = false; - unsigned m_breaks = 0; - }; - - struct clause { - unsigned m_weight = 1; - rational m_dts = rational::one(); - }; - - solver& s; - ast_manager& m; - sat::ddfw* m_bool_search = nullptr; - unsigned m_max_arith_steps = 0; - unsigned m_best_min_unsat = UINT_MAX; - stats m_stats; - config m_config; - scoped_ptr_vector m_atoms; - vector m_vars; - vector m_clauses; - - indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } - unsigned num_clauses() const { return m_bool_search->num_clauses(); } - sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; } - sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } - sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } - sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } - - atom_info* atom(sat::literal lit) const { return m_atoms[lit.index()]; } - rational& dts(unsigned idx) { return m_clauses[idx].m_dts; } - bool flip(); - void log() {} - bool flip_unsat(); - bool flip_clauses(); - bool flip_dscore(); - bool flip_dscore(unsigned cl); - bool flip(unsigned cl); - rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - rational dtt(rational const& args, ineq const& ineq) const; - rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; - rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, rational& new_value); - int cm_score(var_t v, rational const& new_value); - void update(var_t v, rational const& new_value); - void paws(); - rational dscore(var_t v, rational const& new_value) const; - void save_best_values() {} - - rational value(var_t v) const { return m_vars[v].m_value; } - public: - sls(solver& s); - void operator ()(bool_vector& phase); - void set_bounds_begin(); - void set_bounds_end(unsigned num_literals); - void set_bounds(enode* n); - void set(sat::ddfw* d) { m_bool_search = d; } - }; - + sls m_local_search; typedef vector> var_coeffs; diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 4ee6490b7..873a64b7e 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -24,7 +24,7 @@ namespace euf { void solver::local_search(bool_vector& phase) { scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; - bool_search.add(s()); + bool_search.reinit(s(), phase); bool_search.updt_params(s().params()); bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); @@ -36,29 +36,29 @@ namespace euf { for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - bool_search.reinit(s(), phase); - setup_bounds(phase); // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) - assumptions.push_back(literal(v, !phase[v])); + assumptions.push_back(literal(v, !bool_search.get_value(v))); bool_search.rlimit().push(m_max_bool_steps); lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); - - - auto const& mdl = bool_search.get_model(); - for (unsigned i = 0; i < mdl.size(); ++i) - phase[i] = mdl[i] == l_true; + bool_search.rlimit().pop(); for (auto* th : m_solvers) th->local_search(phase); // if is_sat break; + } + + + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + phase[i] = mdl[i] == l_true; } From 4b2c166e8b749f55e420b08653f3e7b7fbb919d6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 10:19:24 -0800 Subject: [PATCH 018/220] fixes to build Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_local_search.cpp | 396 ----------------------------- src/sat/smt/arith_sls.cpp | 40 +-- src/sat/smt/arith_sls.h | 3 +- 3 files changed, 16 insertions(+), 423 deletions(-) delete mode 100644 src/sat/smt/arith_local_search.cpp diff --git a/src/sat/smt/arith_local_search.cpp b/src/sat/smt/arith_local_search.cpp deleted file mode 100644 index 787280e73..000000000 --- a/src/sat/smt/arith_local_search.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/*++ -Copyright (c) 2020 Microsoft Corporation - -Module Name: - - arith_local_search.cpp - -Abstract: - - Local search dispatch for SMT - -Author: - - Nikolaj Bjorner (nbjorner) 2023-02-07 - ---*/ -#include "sat/sat_solver.h" -#include "sat/smt/arith_solver.h" - - -namespace arith { - - - /// - /// need access to clauses - /// need access to m_unsat - /// need update of phase - /// need to initialize ineqs (arithmetical atoms) - /// - - solver::sls::sls(solver& s): - s(s), m(s.m) {} - - void solver::sls::operator()(bool_vector& phase) { - - // need to init variables/atoms/ineqs - - m.limit().push(m_max_arith_steps); - m_best_min_unsat = unsat().size(); - unsigned num_steps = 0; - while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { - if (!flip()) - return; - ++m_stats.m_num_flips; - ++num_steps; - unsigned num_unsat = unsat().size(); - if (num_unsat < m_best_min_unsat) { - m_best_min_unsat = num_unsat; - num_steps = 0; - save_best_values(); - } - } - } - - void solver::sls::set_bounds_begin() { - m_max_arith_steps = 0; - } - - void solver::sls::set_bounds_end(unsigned num_literals) { - // m_max_arith_steps = s.ctx.m_sl_config.L * - } - - void solver::sls::set_bounds(enode* n) { - ++m_max_arith_steps; - } - - bool solver::sls::flip() { - log(); - if (flip_unsat()) - return true; - if (flip_clauses()) - return true; - if (flip_dscore()) - return true; - return false; - } - - // distance to true - rational solver::sls::dtt(rational const& args, ineq const& ineq) const { - switch (ineq.m_op) { - case ineq_kind::LE: - if (args <= ineq.m_bound) - return rational::zero(); - return args - ineq.m_bound; - case ineq_kind::EQ: - if (args == ineq.m_bound) - return rational::zero(); - return rational::one(); - case ineq_kind::NE: - if (args == ineq.m_bound) - return rational::one(); - return rational::zero(); - case ineq_kind::LT: - default: - if (args < ineq.m_bound) - return rational::zero(); - return args - ineq.m_bound + 1; - } - } - - rational solver::sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { - auto new_args_value = ineq.m_args_value; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - new_args_value += coeff * (new_value - m_vars[w].m_value); - break; - } - } - return dtt(new_args_value, ineq); - } - - // critical move - bool solver::sls::cm(ineq const& ineq, var_t v, rational& new_value) { - SASSERT(!ineq.is_true()); - auto delta = ineq.m_args_value - ineq.m_bound; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - if (coeff > 0) - new_value = value(v) - abs(ceil(delta / coeff)); - else - new_value = value(v) + abs(floor(delta / coeff)); - switch (ineq.m_op) { - case ineq_kind::LE: - SASSERT(delta + coeff * (new_value - value(v)) <= 0); - return true; - case ineq_kind::EQ: - return delta + coeff * (new_value - value(v)) == 0; - case ineq_kind::NE: - return delta + coeff * (new_value - value(v)) != 0; - case ineq_kind::LT: - return delta + coeff * (new_value - value(v)) < 0; - default: - UNREACHABLE(); break; - } - } - } - return false; - } - - bool solver::sls::flip_unsat() { - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) { - unsigned cl = unsat().elem_at((i + start) % sz); - if (flip(cl)) - return true; - } - return false; - } - - - bool solver::sls::flip(unsigned cl) { - auto const& clause = get_clause(cl); - rational new_value; - for (literal lit : clause) { - auto const* ai = atom(lit); - if (!ai) - continue; - ineq const& ineq = ai->m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(0, - verbose_stream() << "score " << v << " " << score << "\n" - << num_unsat << " -> " << unsat().size() << "\n"); - return true; - } - } - } - return false; - } - - bool solver::sls::flip_clauses() { - unsigned start = s.random(); - for (unsigned i = num_clauses(); i-- > 0; ) - if (flip((i + start) % num_clauses())) - return true; - return false; - } - - bool solver::sls::flip_dscore() { - paws(); - unsigned start = s.random(); - for (unsigned i = unsat().size(); i-- > 0; ) { - unsigned cl = unsat().elem_at((i + start) % unsat().size()); - if (flip_dscore(cl)) - return true; - } - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); - return false; - } - - bool solver::sls::flip_dscore(unsigned cl) { - auto const& clause = get_clause(cl); - rational new_value, min_value, min_score(-1); - var_t min_var = UINT_MAX; - for (auto lit : clause) { - auto const* ai = atom(lit); - if (!ai) - continue; - ineq const& ineq = ai->m_ineq; - for (auto const& [coeff, v] : ineq.m_args) { - if (!ineq.is_true() && cm(ineq, v, new_value)) { - rational score = dscore(v, new_value); - if (UINT_MAX == min_var || score < min_score) { - min_var = v; - min_value = new_value; - min_score = score; - } - } - } - } - if (min_var != UINT_MAX) { - update(min_var, min_value); - return true; - } - return false; - } - - void solver::sls::paws() { - for (unsigned cl = num_clauses(); cl-- > 0; ) { - auto& clause = get_clause_info(cl); - bool above = 10000 * m_config.sp <= (s.random() % 10000); - if (!above && clause.is_true() && clause.m_weight > 1) - clause.m_weight -= 1; - if (above && !clause.is_true()) - clause.m_weight += 1; - } - } - - // - // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) - // - rational solver::sls::dscore(var_t v, rational const& new_value) const { - auto const& vi = m_vars[v]; - rational score(0); - for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = *m_atoms[atm]; - auto const& cl = get_clause_info(ai.m_clause_idx); - // score += (dts(cl) - dts(cl, v, new_value)) * rational(cl.m_weight); - } - return score; - } - - int solver::sls::cm_score(var_t v, rational const& new_value) { - int score = 0; - auto& vi = m_vars[v]; - for (auto const& [coeff, atm] : vi.m_atoms) { - auto const& ai = *m_atoms[atm]; - auto const& clause = get_clause_info(ai.m_clause_idx); - rational dtt_old = dtt(ai.m_ineq); - rational dtt_new = dtt(ai.m_ineq, v, new_value); - if (!clause.is_true()) { - if (dtt_new == 0) - ++score; - } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) - continue; - else { - bool has_true = false; - for (auto lit : *clause.m_clause) { - if (!atom(lit)) - continue; - auto const& ai = *atom(lit); - rational d = dtt(ai.m_ineq, v, new_value); - has_true |= (d == 0); - } - if (!has_true) - --score; - } - } - return score; - } - - rational solver::sls::dts(unsigned cl) const { - rational d(1), d2; - bool first = true; - for (auto a : get_clause(cl)) { - auto const* ai = atom(a); - if (!ai) - continue; - d2 = dtt(ai->m_ineq); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - - rational solver::sls::dts(unsigned cl, var_t v, rational const& new_value) const { - rational d(1), d2; - bool first = true; - for (auto lit : get_clause(cl)) { - auto const* ai = atom(lit); - if (!ai) - continue; - d2 = dtt(ai->m_ineq, v, new_value); - if (first) - d = d2, first = false; - else - d = std::min(d, d2); - if (d == 0) - break; - } - return d; - } - - void solver::sls::update(var_t v, rational const& new_value) { - auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; - for (auto const& [coeff, atm] : vi.m_atoms) { - auto& ai = *m_atoms[atm]; - SASSERT(!ai.m_is_bool); - auto& clause = get_clause_info(ai.m_clause_idx); - rational dtt_old = dtt(ai.m_ineq); - ai.m_ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ai.m_ineq); - bool was_true = clause.is_true(); - auto& dts_value = dts(ai.m_clause_idx); - if (dtt_new < dts_value) { - if (was_true && dts_value > 0 && dtt_new == 0 && 1 == clause.m_num_trues) { - for (auto lit : *clause.m_clause) { -#if false - TODO - if (is_true(lit)) { - dec_break(lit); - break; - } -#endif - } - } - dts_value = dtt_new; - if (!was_true && clause.is_true()) - unsat().remove(ai.m_clause_idx); - } - else if (dts_value == dtt_old && dtt_old < dtt_new) { - dts_value = dts(ai.m_clause_idx); - if (was_true && !clause.is_true()) - unsat().insert(ai.m_clause_idx); - if (was_true && clause.is_true() && dts_value > 0 && dtt_old == 0 && 1 == clause.m_num_trues) { - for (auto lit : *clause.m_clause) { -#if false - TODO - if (is_true(lit)) { - inc_break(lit); - break; - } -#endif - } - } - } - SASSERT(dts_value >= 0); - } - vi.m_value = new_value; - } - -#if 0 - - - - - - - void solver::sls::add_clause(sat::clause* cl) { - unsigned clause_idx = m_clauses.size(); - m_clauses.push_back({ cl, 1, rational::zero() }); - clause& cls = m_clauses.back(); - cls.m_dts = dts(cls); - for (sat::literal lit : *cl) { - if (is_true(lit)) - cls.add(lit); - } - - for (auto a : arith) - m_atoms[a].m_clause_idx = clause_idx; - - if (!cl.is_true()) { - m_best_min_unsat++; - m_unsat.insert(clause_idx); - } - else if (cl.m_dts > 0 && cl.m_num_trues == 1) - inc_break(sat::to_literal(cl.m_trues)); - - } - - -#endif -} - diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 460bdedce..7e0997156 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -31,8 +31,10 @@ namespace arith { void sls::operator()(bool_vector& phase) { - m_best_min_unsat = unsat().size(); unsigned num_steps = 0; + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + m_best_min_unsat = unsat().size(); while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -446,11 +448,11 @@ namespace arith { bool should_minus = false; sls::ineq_kind op; if (!lit.sign()) { - should_minus = b->get_bound_kind() == lp::GE; + should_minus = b->get_bound_kind() == lp_api::bound_kind::upper_t; op = sls::ineq_kind::LE; } else { - should_minus = b->get_bound_kind() == lp::LE; + should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; if (s.is_int(b->get_var())) { bound -= 1; op = sls::ineq_kind::LE; @@ -464,7 +466,7 @@ namespace arith { auto& ineq = new_ineq(op, bound); add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); - set_literal(lit, ineq); + m_literals.set(lit.index(), &ineq); return; } @@ -478,34 +480,20 @@ namespace arith { auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); add_args(ineq, tu, u, rational::one()); add_args(ineq, tv, v, -rational::one()); - set_literal(lit, ineq); + m_literals.set(lit.index(), &ineq); return; } } - /** - * Associate literal with inequality and synchronize truth assignment based on arithmetic values. - */ - void sls::set_literal(sat::literal lit, ineq& ineq) { - m_literals.set(lit.index(), &ineq); - if (m_bool_search->get_value(lit.var())) { - if (dtt(ineq) != 0) - m_bool_search->flip(lit.var()); - } - else { - if (dtt(ineq) == 0) - m_bool_search->flip(lit.var()); - } + void sls::init_bool_var_assignment(sat::bool_var v) { + init_literal_assignment(literal(v, false)); + init_literal_assignment(literal(v, true)); } -#if 0 - - { - + void sls::init_literal_assignment(sat::literal lit) { + auto* ineq = m_literals.get(lit.index(), nullptr); + if (ineq && m_bool_search->get_value(lit.var()) != (dtt(*ineq) == 0)) + m_bool_search->flip(lit.var()); } } - -#endif -} - diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index dcb935610..16a8549f9 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -132,7 +132,8 @@ namespace arith { void add_bounds(sat::literal_vector& bounds); void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); void init_literal(sat::literal lit); - void set_literal(sat::literal lit, ineq& ineq); + void init_bool_var_assignment(sat::bool_var v); + void init_literal_assignment(sat::literal lit); rational value(var_t v) const { return m_vars[v].m_value; } public: From 5e30323b1ac7626634e2d1f167296d9f828ed676 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Feb 2023 15:46:39 -0800 Subject: [PATCH 019/220] wip - bounded local search for arithmetic --- src/sat/sat_ddfw.cpp | 10 ++-- src/sat/sat_extension.h | 2 +- src/sat/sat_solver.cpp | 9 +++ src/sat/smt/arith_sls.cpp | 95 ++++++++++++++++++++------------ src/sat/smt/arith_sls.h | 33 +++++++++-- src/sat/smt/arith_solver.h | 2 +- src/sat/smt/euf_local_search.cpp | 35 ++++++------ src/sat/smt/euf_solver.h | 4 +- 8 files changed, 124 insertions(+), 66 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 98e3ce2bd..747ea4940 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -148,7 +148,8 @@ namespace sat { m_use_list[lit.index()].pop_back(); m_alloc.del_clause(info.m_clause); m_clauses.pop_back(); - m_unsat.remove(m_clauses.size()); + if (m_unsat.contains(m_clauses.size())) + m_unsat.remove(m_clauses.size()); } void ddfw::add(solver const& s) { @@ -188,12 +189,11 @@ namespace sat { } void ddfw::remove_assumptions() { + if (m_assumptions.empty()) + return; for (unsigned i = 0; i < m_assumptions.size(); ++i) del(); - m_unsat_vars.reset(); - for (auto idx : m_unsat) - for (auto lit : get_clause(idx)) - m_unsat_vars.insert(lit.var()); + init(0, nullptr); } void ddfw::init(unsigned sz, literal const* assumptions) { diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 3a1f363a3..ae99cae12 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -126,7 +126,7 @@ namespace sat { virtual void add_assumptions(literal_set& ext_assumptions) {} virtual bool tracking_assumptions() { return false; } virtual bool enable_self_propagate() const { return false; } - virtual void local_search(bool_vector& phase) {} + virtual lbool local_search(bool_vector& phase) { return l_undef; } virtual bool extract_pb(std::function& card, std::function& pb) { diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 10aac6dcb..898c3a2a4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,6 +1302,9 @@ namespace sat { return l_undef; } + // uncomment this to test bounded local search: + // bounded_local_search(); + log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; @@ -1360,6 +1363,12 @@ namespace sat { }; void solver::bounded_local_search() { + if (m_ext) { + verbose_stream() << "bounded local search\n"; + do_restart(true); + m_ext->local_search(m_best_phase); + return; + } literal_vector _lits; scoped_limits scoped_rl(rlimit()); m_local_search = alloc(ddfw); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 7e0997156..79e6aec54 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -20,21 +20,24 @@ Author: namespace arith { - - /// - /// need to initialize ineqs (arithmetical atoms) - /// - - sls::sls(solver& s): s(s), m(s.m) {} - void sls::operator()(bool_vector& phase) { + void sls::reset() { + m_literals.reset(); + m_vars.reset(); + m_clauses.reset(); + m_terms.reset(); + } + + lbool sls::operator()(bool_vector& phase) { unsigned num_steps = 0; for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); m_best_min_unsat = unsat().size(); + verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; + //m_max_arith_steps = 10000; while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -47,24 +50,27 @@ namespace arith { save_best_values(); } } - IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n"); + log(); + return unsat().empty() ? l_true : l_undef; + } + + void sls::log() { + IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); } void sls::save_best_values() { // first compute assignment to terms - // then update non-basic variables in tableau, assuming a sat solution was found. -#if false - for (auto const& [t, v] : terms) { + // then update non-basic variables in tableau. + for (auto const& [t, v] : m_terms) { rational val; - lp::lar_term const& term = lp().get_term(t); + lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { - auto t2 = lp().column2tv(arg.column()); - auto w = lp().local_to_external(t2.id()); - val += arg.coeff() * local_search.value(w); + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + val += arg.coeff() * value(w); } update(v, val); } -#endif for (unsigned v = 0; v < s.get_num_vars(); ++v) { if (s.is_bool(v)) @@ -87,6 +93,8 @@ namespace arith { void sls::set(sat::ddfw* d) { m_bool_search = d; + reset(); + m_literals.reserve(s.s().num_vars() * 2); add_vars(); m_clauses.resize(d->num_clauses()); for (unsigned i = 0; i < d->num_clauses(); ++i) @@ -151,12 +159,16 @@ namespace arith { bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { SASSERT(!ineq.is_true()); auto delta = ineq.m_args_value - ineq.m_bound; + if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) + delta--; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { + if (coeff > 0) new_value = value(v) - abs(ceil(delta / coeff)); else new_value = value(v) + abs(floor(delta / coeff)); + switch (ineq.m_op) { case ineq_kind::LE: SASSERT(delta + coeff * (new_value - value(v)) <= 0); @@ -189,9 +201,12 @@ namespace arith { auto const& clause = get_clause(cl); rational new_value; for (literal lit : clause) { - auto const* ineq = atom(lit); - if (!ineq || ineq->is_true()) + if (is_true(lit)) continue; + auto const* ineq = atom(lit); + if (!ineq) + continue; + SASSERT(!ineq->is_true()); for (auto const& [coeff, v] : ineq->m_args) { if (!cm(*ineq, v, new_value)) continue; @@ -201,8 +216,9 @@ namespace arith { unsigned num_unsat = unsat().size(); update(v, new_value); IF_VERBOSE(2, - verbose_stream() << "score " << v << " " << score << "\n" + verbose_stream() << "v" << v << " score " << score << " " << num_unsat << " -> " << unsat().size() << "\n"); + SASSERT(num_unsat > unsat().size()); return true; } } @@ -255,7 +271,8 @@ namespace arith { } /** - * redistribute weights of clauses. TODO - re-use ddfw weights instead. + * redistribute weights of clauses. + * TODO - re-use ddfw weights instead. */ void sls::paws() { for (unsigned cl = num_clauses(); cl-- > 0; ) { @@ -270,13 +287,15 @@ namespace arith { // // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) + // TODO - use cached dts instead of computed dts + // cached dts has to be updated when the score of literals are updated. // rational sls::dscore(var_t v, rational const& new_value) const { auto const& vi = m_vars[v]; rational score(0); for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); return score; } @@ -290,10 +309,11 @@ namespace arith { for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { + VERIFY(dtt_old != 0); if (dtt_new == 0) ++score; // false -> true } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) // true -> true ?? TODO + else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO --score; @@ -302,7 +322,7 @@ namespace arith { return score; } - rational sls::dts(unsigned cl) const { + rational sls::compute_dts(unsigned cl) const { rational d(1), d2; bool first = true; for (auto a : get_clause(cl)) { @@ -346,14 +366,20 @@ namespace arith { rational dtt_old = dtt(ineq); ineq.m_args_value += coeff * (new_value - old_value); rational dtt_new = dtt(ineq); - SASSERT(!(dtt_new == 0 && dtt_new < dtt_old) || m_bool_search->get_value(lit.var()) == lit.sign()); - SASSERT(!(dtt_old == 0 && dtt_new > dtt_old) || m_bool_search->get_value(lit.var()) != lit.sign()); + if ((dtt_new == 0) == is_true(lit)) { + dtt(ineq) = dtt_new; + continue; + } + VERIFY((dtt_old == 0) == is_true(lit)); + VERIFY(!(dtt_new == 0 && dtt_new < dtt_old) || !is_true(lit)); + VERIFY(!(dtt_old == 0 && dtt_new > dtt_old) || is_true(lit)); if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true m_bool_search->flip(lit.var()); else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false m_bool_search->flip(lit.var()); dtt(ineq) = dtt_new; - SASSERT((dtt_new == 0) == (m_bool_search->get_value(lit.var()) != lit.sign())); + + VERIFY((dtt_new == 0) == is_true(lit)); } vi.m_value = new_value; } @@ -422,18 +448,18 @@ namespace arith { } - void sls::add_args(ineq& ineq, lp::tv t, theory_var v, rational sign) { + void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, rational sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - ineq.m_args.push_back({ sign * arg.coeff(), w }); + add_arg(lit, ineq, sign * arg.coeff(), w); } } else - ineq.m_args.push_back({ sign, s.lp().local_to_external(t.id()) }); + add_arg(lit, ineq, sign, s.lp().local_to_external(t.id())); } @@ -465,7 +491,7 @@ namespace arith { bound.neg(); auto& ineq = new_ineq(op, bound); - add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + add_args(lit, ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); m_literals.set(lit.index(), &ineq); return; } @@ -478,8 +504,8 @@ namespace arith { lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); - add_args(ineq, tu, u, rational::one()); - add_args(ineq, tv, v, -rational::one()); + add_args(lit, ineq, tu, u, rational::one()); + add_args(lit, ineq, tv, v, -rational::one()); m_literals.set(lit.index(), &ineq); return; } @@ -492,8 +518,9 @@ namespace arith { void sls::init_literal_assignment(sat::literal lit) { auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && m_bool_search->get_value(lit.var()) != (dtt(*ineq) == 0)) - m_bool_search->flip(lit.var()); + + if (ineq && is_true(lit) != (dtt(*ineq) == 0)) + m_bool_search->flip(lit.var()); } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 16a8549f9..9a4cfcd81 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -55,6 +55,7 @@ namespace arith { unsigned m_num_flips = 0; }; + public: // encode args <= bound, args = bound, args < bound struct ineq { vector> m_args; @@ -74,7 +75,23 @@ namespace arith { return m_args_value < m_bound; } } + std::ostream& display(std::ostream& out) const { + bool first = true; + for (auto const& [c, v] : m_args) + out << (first? "": " + ") << c << " * v" << v, first = false; + switch (m_op) { + case ineq_kind::LE: + return out << " <= " << m_bound << "(" << m_args_value << ")"; + case ineq_kind::EQ: + return out << " == " << m_bound << "(" << m_args_value << ")"; + case ineq_kind::NE: + return out << " != " << m_bound << "(" << m_args_value << ")"; + default: + return out << " < " << m_bound << "(" << m_args_value << ")"; + } + } }; + private: struct var_info { rational m_value; @@ -85,6 +102,7 @@ namespace arith { struct clause { unsigned m_weight = 1; + rational m_dts = rational::one(); }; solver& s; @@ -97,6 +115,8 @@ namespace arith { scoped_ptr_vector m_literals; vector m_vars; vector m_clauses; + svector> m_terms; + indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } unsigned num_clauses() const { return m_bool_search->num_clauses(); } @@ -104,12 +124,14 @@ namespace arith { sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; } sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } + bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } + void reset(); ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } bool flip(); - void log() {} + void log(); bool flip_unsat(); bool flip_clauses(); bool flip_dscore(); @@ -119,7 +141,7 @@ namespace arith { rational dtt(rational const& args, ineq const& ineq) const; rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational dts(unsigned cl) const; + rational compute_dts(unsigned cl) const; bool cm(ineq const& ineq, var_t v, rational& new_value); int cm_score(var_t v, rational const& new_value); void update(var_t v, rational const& new_value); @@ -130,7 +152,7 @@ namespace arith { sls::ineq& new_ineq(ineq_kind op, rational const& bound); void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); void add_bounds(sat::literal_vector& bounds); - void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, rational sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); void init_literal_assignment(sat::literal lit); @@ -138,11 +160,14 @@ namespace arith { rational value(var_t v) const { return m_vars[v].m_value; } public: sls(solver& s); - void operator ()(bool_vector& phase); + lbool operator ()(bool_vector& phase); void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(euf::enode* n); void set(sat::ddfw* d); }; + inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) { + return ineq.display(out); + } } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 732e291b1..a31ca844a 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -515,7 +515,7 @@ namespace arith { void set_bounds_begin() override { m_local_search.set_bounds_begin(); } void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } void set_bounds(enode* n) override { m_local_search.set_bounds(n); } - void local_search(bool_vector& phase) override { m_local_search(phase); } + lbool local_search(bool_vector& phase) override { return m_local_search(phase); } void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 873a64b7e..fab3b7815 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -21,7 +21,7 @@ Author: namespace euf { - void solver::local_search(bool_vector& phase) { + lbool solver::local_search(bool_vector& phase) { scoped_limits scoped_rl(m.limit()); sat::ddfw bool_search; bool_search.reinit(s(), phase); @@ -36,7 +36,7 @@ namespace euf { for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { - setup_bounds(phase); + setup_bounds(bool_search, phase); // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; @@ -44,6 +44,8 @@ namespace euf { if (!is_propositional(literal(v))) assumptions.push_back(literal(v, !bool_search.get_value(v))); + verbose_stream() << "assumptions " << assumptions.size() << "\n"; + bool_search.rlimit().push(m_max_bool_steps); lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); @@ -51,15 +53,15 @@ namespace euf { for (auto* th : m_solvers) th->local_search(phase); - // if is_sat break; + if (bool_search.unsat_set().empty()) + break; } - - auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) - phase[i] = mdl[i] == l_true; - + phase[i] = mdl[i] == l_true; + + return bool_search.unsat_set().empty() ? l_true : l_undef; } bool solver::is_propositional(sat::literal lit) { @@ -67,13 +69,13 @@ namespace euf { return !e || is_uninterp_const(e) || !m_egraph.find(e); } - void solver::setup_bounds(bool_vector const& phase) { + void solver::setup_bounds(sat::ddfw& bool_search, bool_vector const& phase) { unsigned num_literals = 0; unsigned num_bool = 0; for (auto* th : m_solvers) th->set_bounds_begin(); - auto init_literal = [&](sat::literal l) { + auto count_literal = [&](sat::literal l) { if (is_propositional(l)) { ++num_bool; return; @@ -86,16 +88,11 @@ namespace euf { } }; - auto is_true = [&](auto lit) { - return phase[lit.var()] == !lit.sign(); - }; - - for (auto* cp : s().clauses()) { - if (any_of(*cp, [&](auto lit) { return is_true(lit); })) - continue; - num_literals += cp->size(); - for (auto l : *cp) - init_literal(l); + for (auto cl : bool_search.unsat_set()) { + auto& c = *bool_search.get_clause_info(cl).m_clause; + num_literals += c.size(); + for (auto l : c) + count_literal(l); } m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index a19dbca5d..d62390329 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -265,7 +265,7 @@ namespace euf { // local search unsigned m_max_bool_steps = 10; bool is_propositional(sat::literal lit); - void setup_bounds(bool_vector const& mdl); + void setup_bounds(sat::ddfw& bool_search, bool_vector const& mdl); // user propagator void check_for_user_propagator() { @@ -353,7 +353,7 @@ namespace euf { void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; std::string reason_unknown() override { return m_reason_unknown; } - void local_search(bool_vector& phase) override; + lbool local_search(bool_vector& phase) override; void propagate(literal lit, ext_justification_idx idx); bool propagate(enode* a, enode* b, ext_justification_idx idx); From ede9e5ffc225f98a02cc27e35de8a99f6ca6c91c Mon Sep 17 00:00:00 2001 From: Walden Yan Date: Sat, 11 Feb 2023 18:48:29 -0500 Subject: [PATCH 020/220] [WIP] More TS Binding Features (#6412) * feat: basic quantfier support * feat: added isQuantifier * feat: expanded functions * wip: (lambda broken) * temp fix to LambdaImpl typing issue * feat: function type inference * formatting with prettier * fix: imported from invalid module * fix isBool bug and dumping to smtlib * substitution and model.updateValue * api to add custom func interps to model * fix: building * properly handling uint32 -> number conversion in z3 TS wrapper * added simplify * remame Add->Sum and Mul->Product * formatting --- .../js/examples/high-level/using_smtlib2.ts | 36 + src/api/js/examples/low-level/example-raw.ts | 1 + src/api/js/package-lock.json | 44 +- src/api/js/package.json | 9 +- src/api/js/scripts/make-ts-wrapper.ts | 12 +- src/api/js/scripts/parse-api.ts | 2 +- src/api/js/src/high-level/high-level.test.ts | 195 +++- src/api/js/src/high-level/high-level.ts | 935 +++++++++++++++--- src/api/js/src/high-level/types.ts | 481 ++++++--- 9 files changed, 1383 insertions(+), 332 deletions(-) create mode 100644 src/api/js/examples/high-level/using_smtlib2.ts diff --git a/src/api/js/examples/high-level/using_smtlib2.ts b/src/api/js/examples/high-level/using_smtlib2.ts new file mode 100644 index 000000000..e9275b7bf --- /dev/null +++ b/src/api/js/examples/high-level/using_smtlib2.ts @@ -0,0 +1,36 @@ +// @ts-ignore we're not going to bother with types for this +import process from 'process'; +import { init } from '../../build/node'; +import assert from 'assert'; + +(async () => { + let { Context, em } = await init(); + let z3 = Context('main'); + + const x = z3.BitVec.const('x', 256); + const y = z3.BitVec.const('y', 256); + const z = z3.BitVec.const('z', 256); + const xPlusY = x.add(y); + const xPlusZ = x.add(z); + const expr = xPlusY.mul(xPlusZ); + + const to_check = expr.eq(z3.Const('test', expr.sort)); + + const solver = new z3.Solver(); + solver.add(to_check); + const cr = await solver.check(); + console.log(cr); + assert(cr === 'sat'); + + const model = solver.model(); + let modelStr = model.sexpr(); + modelStr = modelStr.replace(/\n/g, ' '); + console.log("Model: ", modelStr); + + const exprs = z3.ast_from_string(modelStr); + console.log(exprs); + +})().catch(e => { + console.error('error', e); + process.exit(1); +}); \ No newline at end of file diff --git a/src/api/js/examples/low-level/example-raw.ts b/src/api/js/examples/low-level/example-raw.ts index 6e34b4aba..2790f9594 100644 --- a/src/api/js/examples/low-level/example-raw.ts +++ b/src/api/js/examples/low-level/example-raw.ts @@ -1,3 +1,4 @@ +// @ts-ignore we're not going to bother with types for this import process from 'process'; import { init, Z3_error_code } from '../../build/node'; diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index f6969a933..a46cae951 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -4461,14 +4461,14 @@ "dev": true }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "vscode-textmate": "^6.0.0" } }, "side-channel": { @@ -4826,16 +4826,15 @@ "dev": true }, "typedoc": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz", - "integrity": "sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA==", + "version": "0.23.16", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.16.tgz", + "integrity": "sha512-rumYsCeNRXlyuZVzefD7050n7ptL2uudsCJg50dY0v/stKniqIlRpvx/F/6expC0/Q6Dbab+g/JpZuB7Sw90FA==", "dev": true, "requires": { - "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^4.0.16", + "marked": "^4.0.19", "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "shiki": "^0.11.1" }, "dependencies": { "brace-expansion": { @@ -4847,19 +4846,6 @@ "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, "minimatch": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", @@ -4872,9 +4858,9 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true }, "typical": { @@ -4945,9 +4931,9 @@ "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "walker": { diff --git a/src/api/js/package.json b/src/api/js/package.json index 90a7bfa3d..3e79ba8f4 100644 --- a/src/api/js/package.json +++ b/src/api/js/package.json @@ -1,5 +1,6 @@ { "name": "z3-solver", + "version": "0.1.0", "keywords": [ "Z3", "theorem", @@ -26,8 +27,8 @@ "build:ts:generate": "ts-node --transpileOnly scripts/make-ts-wrapper.ts src/low-level/wrapper.__GENERATED__.ts src/low-level/types.__GENERATED__.ts", "build:wasm": "ts-node --transpileOnly ./scripts/build-wasm.ts", "clean": "rimraf build 'src/**/*.__GENERATED__.*'", - "lint": "prettier -c '{,src/,scripts/,examples}*.{js,ts}'", - "format": "prettier --write '{,src/,scripts/}*.{js,ts}'", + "lint": "prettier -c '{./,src/,scripts/,examples/}**/*.{js,ts}'", + "format": "prettier --write '{./,src/,scripts/}**/*.{js,ts}'", "test": "jest", "docs": "typedoc", "check-engine": "check-engine" @@ -53,8 +54,8 @@ "ts-expect": "^1.3.0", "ts-jest": "^28.0.3", "ts-node": "^10.8.0", - "typedoc": "^0.22.17", - "typescript": "^4.5.4" + "typedoc": "^0.23.16", + "typescript": "^4.8.4" }, "license": "MIT", "dependencies": { diff --git a/src/api/js/scripts/make-ts-wrapper.ts b/src/api/js/scripts/make-ts-wrapper.ts index 58b2ce0ae..568609467 100644 --- a/src/api/js/scripts/make-ts-wrapper.ts +++ b/src/api/js/scripts/make-ts-wrapper.ts @@ -76,6 +76,7 @@ function makeTsWrapper() { } const isInParam = (p: FuncParam) => p.kind !== undefined && ['in', 'in_array'].includes(p.kind); + function wrapFunction(fn: Func) { if (CUSTOM_IMPLEMENTATIONS.includes(fn.name)) { return null; @@ -104,7 +105,7 @@ function makeTsWrapper() { let isAsync = asyncFuncs.includes(fn.name); let trivial = - !['string', 'boolean'].includes(fn.ret) && + !['string', 'boolean', 'unsigned'].includes(fn.ret) && !fn.nullableRet && outParams.length === 0 && !inParams.some(p => p.type === 'string' || p.isArray || p.nullable); @@ -234,6 +235,7 @@ function makeTsWrapper() { function setArg() { args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`; } + let read, type; if (outParam.type === 'string') { read = `Mod.UTF8ToString(getOutUint(${memIdx}))`; @@ -330,11 +332,15 @@ function makeTsWrapper() { if (ret === 0) { return null; } - `.trim(); + `.trim(); + } else if (fn.ret === 'unsigned') { + infix += ` + ret = (new Uint32Array([ret]))[0]; + `.trim(); } // prettier-ignore - let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`; + let invocation = `Mod.ccall('${isAsync ? "async_" : ""}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(", ")}])`; if (isAsync) { invocation = `await Mod.async_call(() => ${invocation})`; diff --git a/src/api/js/scripts/parse-api.ts b/src/api/js/scripts/parse-api.ts index c3292583a..a3aa81acd 100644 --- a/src/api/js/scripts/parse-api.ts +++ b/src/api/js/scripts/parse-api.ts @@ -45,7 +45,7 @@ const types = { __proto__: null, // these are function types I can't be bothered to parse - // NSB: They can be extracted automatically from z3_api.h thanks to the use + // NSB: They can be extracted automatically from z3_api.h thanks to the use // of a macro. Z3_error_handler: 'Z3_error_handler', Z3_push_eh: 'Z3_push_eh', diff --git a/src/api/js/src/high-level/high-level.test.ts b/src/api/js/src/high-level/high-level.test.ts index 70c11b875..d42075169 100644 --- a/src/api/js/src/high-level/high-level.test.ts +++ b/src/api/js/src/high-level/high-level.test.ts @@ -1,8 +1,8 @@ import assert from 'assert'; import asyncToArray from 'iter-tools/methods/async-to-array'; import { init, killThreads } from '../jest'; -import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types'; -import { expectType } from "ts-expect"; +import { Arith, Bool, Model, Quantifier, Z3AssertionError, Z3HighLevel, AstVector } from './types'; +import { expectType } from 'ts-expect'; /** * Generate all possible solutions from given assumptions. @@ -58,6 +58,7 @@ async function* allSolutions(...assertions: Bool[]): async function prove(conjecture: Bool): Promise { const solver = new conjecture.ctx.Solver(); + solver.set('timeout', 1000); const { Not } = solver.ctx; solver.add(Not(conjecture)); expect(await solver.check()).toStrictEqual('unsat'); @@ -113,11 +114,11 @@ describe('high-level', () => { it('test loading a solver state from a string', async () => { const { Solver, Not, Int } = api.Context('main'); const solver = new Solver(); - solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))") - expect(await solver.check()).toStrictEqual('sat') - const x = Int.const('x') - solver.add(Not(x.eq(1))) - expect(await solver.check()).toStrictEqual('unsat') + solver.fromString('(declare-const x Int) (assert (and (< x 2) (> x 0)))'); + expect(await solver.check()).toStrictEqual('sat'); + const x = Int.const('x'); + solver.add(Not(x.eq(1))); + expect(await solver.check()).toStrictEqual('unsat'); }); it('disproves x = y implies g(g(x)) = g(y)', async () => { @@ -393,14 +394,13 @@ describe('high-level', () => { }); describe('arrays', () => { - it('Example 1', async () => { const Z3 = api.Context('main'); const arr = Z3.Array.const('arr', Z3.Int.sort(), Z3.Int.sort()); const [idx, val] = Z3.Int.consts('idx val'); - const conjecture = (arr.store(idx, val).select(idx).eq(val)); + const conjecture = arr.store(idx, val).select(idx).eq(val); await prove(conjecture); }); @@ -428,7 +428,7 @@ describe('high-level', () => { // and is detected at compile time // @ts-expect-error const arr3 = Array.const('arr3', BitVec.sort(1)); - }) + }); it('can do simple proofs', async () => { const { BitVec, Array, isArray, isArraySort, isConstArray, Eq, Not } = api.Context('main'); @@ -447,13 +447,6 @@ describe('high-level', () => { await prove(Eq(arr2.select(0), FIVE_VAL)); await prove(Not(Eq(arr2.select(0), BitVec.val(6, 256)))); await prove(Eq(arr2.store(idx, val).select(idx), constArr.store(idx, val).select(idx))); - - // TODO: add in Quantifiers and better typing of arrays - // await prove( - // ForAll([idx], idx.add(1).ugt(idx).and(arr.select(idx.add(1)).ugt(arr.select(idx)))).implies( - // arr.select(0).ult(arr.select(1000)) - // ) - // ); }); it('Finds arrays that differ but that sum to the same', async () => { @@ -465,18 +458,16 @@ describe('high-level', () => { const arr1 = Array.const('arr', BitVec.sort(2), BitVec.sort(32)); const arr2 = Array.const('arr2', BitVec.sort(2), BitVec.sort(32)); - const same_sum = arr1.select(0) + const same_sum = arr1 + .select(0) .add(arr1.select(1)) .add(arr1.select(2)) .add(arr1.select(3)) - .eq( - arr2.select(0) - .add(arr2.select(1)) - .add(arr2.select(2)) - .add(arr2.select(3)) - ); + .eq(arr2.select(0).add(arr2.select(1)).add(arr2.select(2)).add(arr2.select(3))); - const different = arr1.select(0).neq(arr2.select(0)) + const different = arr1 + .select(0) + .neq(arr2.select(0)) .or(arr1.select(1).neq(arr2.select(1))) .or(arr1.select(2).neq(arr2.select(2))) .or(arr1.select(3).neq(arr2.select(3))); @@ -485,11 +476,105 @@ describe('high-level', () => { const arr1Vals = [0, 1, 2, 3].map(i => model.eval(arr1.select(i)).value()); const arr2Vals = [0, 1, 2, 3].map(i => model.eval(arr2.select(i)).value()); - expect((arr1Vals.reduce((a, b) => a + b, 0n) % mod) === arr2Vals.reduce((a, b) => a + b, 0n) % mod); + expect(arr1Vals.reduce((a, b) => a + b, 0n) % mod === arr2Vals.reduce((a, b) => a + b, 0n) % mod); for (let i = 0; i < 4; i++) { expect(arr1Vals[i] !== arr2Vals[i]); } }); + + it('Array type inference', async () => { + const z3 = api.Context('main'); + + const Z3_ADDR = z3.BitVec.const('Vault_addr', 160); + const Z3_GLOBAL_STORAGE = z3.Array.const( + 'global_storage', + z3.BitVec.sort(160), + z3.Array.sort(z3.BitVec.sort(160), z3.BitVec.sort(256)), + ); + const Z3_STORAGE = Z3_GLOBAL_STORAGE.select(Z3_ADDR); + + // We are so far unable to properly infer the type of Z3_STORAGE + // expectType< + // SMTArray<'main', [BitVecSort<160>], BitVecSort<256>> + // >(Z3_STORAGE); + }); + }); + + describe('quantifiers', () => { + it('Basic Universal', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y'); + + const conjecture = Z3.ForAll([x, y], x.neq(y).implies(x.lt(y).or(x.gt(y)))); + expect(Z3.isBool(conjecture)).toBeTruthy(); + expect(conjecture.var_name(0)).toBe('x'); + expect(conjecture.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(conjecture.var_name(1)).toBe('y'); + expect(conjecture.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + await prove(conjecture); + }); + + it('Basic Existential', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + expect(quantifier.var_name(0)).toBe('z'); + expect(quantifier.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = Z3.Not(x.gt(y)).implies(quantifier); // Can be trivially discovered with z = x or x = y + await prove(conjecture); + }); + + it('Basic Lambda', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y z'); + const L = Z3.Lambda([x, y], x.add(y)); + expect(Z3.isArraySort(L.sort)).toBeTruthy(); + expect(Z3.isArray(L)).toBeFalsy(); + expect(L.var_name(0)).toBe('x'); + expect(L.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(L.var_name(1)).toBe('y'); + expect(L.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = L.select(Z3.Int.val(2), Z3.Int.val(5)).eq(Z3.Int.val(7)); + await prove(conjecture); + }); + + it('Loading Quantifier Preserves Type', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + + const solver = new Z3.Solver(); + solver.add(quantifier); + + const dumped_str = solver.toString(); + + const solver2 = new Z3.Solver(); + solver2.fromString(dumped_str); + const quantifier2 = solver2.assertions().get(0) as unknown as Quantifier; + expect(Z3.isBool(quantifier2)).toBeTruthy(); + expect(quantifier2.var_name(0)).toBe('z'); + }); + }); + + describe('uninterpreted functions', () => { + it('Type Inference', async () => { + const Z3 = api.Context('main'); + + const f = Z3.Function.declare('f', Z3.Int.sort(), Z3.Bool.sort()); + const input = Z3.Int.val(6); + const output = f.call(input); + expectType(output); + expect(output.sort.eqIdentity(Z3.Bool.sort())).toBeTruthy(); + }); }); describe('Solver', () => { @@ -543,10 +628,11 @@ describe('high-level', () => { describe('AstVector', () => { it('can use basic methods', async () => { - const { Solver, AstVector, Int } = api.Context('main'); + const Z3 = api.Context('main'); + const { Solver, Int } = Z3; const solver = new Solver(); - const vector = new AstVector(); + const vector = new Z3.AstVector(); for (let i = 0; i < 5; i++) { vector.push(Int.const(`int__${i}`)); } @@ -559,4 +645,57 @@ describe('high-level', () => { expect(await solver.check()).toStrictEqual('sat'); }); }); + + describe('Substitution', () => { + it('basic variable substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y); + const subst = substitute(expr, [x, z]); + expect(subst.eqIdentity(z.add(y))).toBeTruthy(); + }); + + it('term substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y).mul(Int.val(1).sub(x.add(y))); + const subst = substitute(expr, [x.add(y), z]); + expect(subst.eqIdentity(z.mul(Int.val(1).sub(z)))).toBeTruthy(); + }); + }); + + describe('Model', () => { + it('Assigning constants', async () => { + const { Int, Model } = api.Context('main'); + const m = new Model(); + + const [x, y] = Int.consts('x y'); + + m.updateValue(x, Int.val(6)); + m.updateValue(y, Int.val(12)); + + expect(m.eval(x.add(y)).eqIdentity(Int.val(18))).toBeTruthy(); + }); + + it('Creating Func Interpretations', async () => { + const { Int, Function, Model } = api.Context('main'); + const m = new Model(); + + const f = Function.declare('f', Int.sort(), Int.sort(), Int.sort()); + + const f_interp = m.addFuncInterp(f, 0); + f_interp.addEntry([Int.val(1), Int.val(2)], Int.val(3)); + f_interp.addEntry([Int.val(4), Int.val(5)], Int.val(6)); + + expect(m.eval(f.call(1, 2)).eqIdentity(Int.val(3))).toBeTruthy(); + expect(m.eval(f.call(4, 5)).eqIdentity(Int.val(6))).toBeTruthy(); + expect(m.eval(f.call(0, 0)).eqIdentity(Int.val(0))).toBeTruthy(); + }); + }); }); diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index 59a0b1e36..80f89b104 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -31,13 +31,19 @@ import { Z3_symbol, Z3_symbol_kind, Z3_tactic, + Z3_pattern, + Z3_app, + Z3_params, + Z3_func_entry, } from '../low-level'; import { AnyAst, AnyExpr, AnySort, Arith, - ArithSort, ArrayIndexType, + ArithSort, + ArrayIndexType, + CoercibleToArrayIndexType, Ast, AstMap, AstMapCtor, @@ -48,11 +54,12 @@ import { BitVecSort, Bool, BoolSort, - CheckSatResult, CoercibleFromMap, + CheckSatResult, + CoercibleToMap, CoercibleRational, CoercibleToBitVec, CoercibleToExpr, - CoercibleToExprMap, + CoercibleFromMap, Context, ContextCtor, Expr, @@ -61,7 +68,10 @@ import { FuncInterp, IntNum, Model, + Pattern, Probe, + Quantifier, + BodyT, RatNum, SMTArray, SMTArraySort, @@ -71,6 +81,9 @@ import { Tactic, Z3Error, Z3HighLevel, + CoercibleToArith, + NonEmptySortArray, + FuncEntry, } from './types'; import { allSatisfy, assert, assertExhaustive } from './utils'; @@ -81,18 +94,19 @@ const asyncMutex = new Mutex(); function isCoercibleRational(obj: any): obj is CoercibleRational { // prettier-ignore const r = ( - (obj !== null && - (typeof obj === 'object' || typeof obj === 'function')) && - (obj.numerator !== null && - (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && - (obj.denominator !== null && - (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) - ); - r && assert( - (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && - (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), - 'Fraction numerator and denominator must be integers', - ); + (obj !== null && + (typeof obj === 'object' || typeof obj === 'function')) && + (obj.numerator !== null && + (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && + (obj.denominator !== null && + (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) + ); + r && + assert( + (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && + (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), + 'Fraction numerator and denominator must be integers', + ); return r; } @@ -152,9 +166,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function createContext(name: Name, options?: Record): Context { const cfg = Z3.mk_config(); if (options != null) { - Object.entries(options).forEach( - ([key, value]) => check(Z3.set_param_value(cfg, key, value.toString())) - ); + Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))); } const contextPtr = Z3.mk_context_rc(cfg); Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); @@ -171,7 +183,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - function check(val: T) { + function check(val: T): T { throwIfError(); return val; } @@ -199,6 +211,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function _toParams(key: string, value: any): Z3_params { + const params = Z3.mk_params(contextPtr); + Z3.params_inc_ref(contextPtr, params); + // If value is a boolean + if (typeof value === 'boolean') { + Z3.params_set_bool(contextPtr, params, _toSymbol(key), value); + } else if (typeof value === 'number') { + // If value is a uint + if (Number.isInteger(value)) { + check(Z3.params_set_uint(contextPtr, params, _toSymbol(key), value)); + } else { + // If value is a double + check(Z3.params_set_double(contextPtr, params, _toSymbol(key), value)); + } + } else if (typeof value === 'string') { + check(Z3.params_set_symbol(contextPtr, params, _toSymbol(key), _toSymbol(value))); + } + return params; + } + function _toAst(ast: Z3_ast): AnyAst { switch (check(Z3.get_ast_kind(contextPtr, ast))) { case Z3_ast_kind.Z3_SORT_AST: @@ -229,13 +261,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function _toExpr(ast: Z3_ast): AnyExpr { const kind = check(Z3.get_ast_kind(contextPtr, ast)); if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) { - if (Z3.is_quantifier_forall(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_quantifier_exists(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_lambda(contextPtr, ast)) - return new ExprImpl(ast); - assert(false); + if (Z3.is_lambda(contextPtr, ast)) { + return new LambdaImpl(ast); + } + return new NonLambdaQuantifierImpl(ast); } const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast))); switch (sortKind) { @@ -325,6 +354,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return r; } + function isFuncInterp(obj: unknown): obj is FuncInterp { + const r = obj instanceof FuncInterpImpl; + r && _assertContext(obj); + return r; + } + function isApp(obj: unknown): boolean { if (!isExpr(obj)) { return false; @@ -352,7 +387,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } function isBool(obj: unknown): obj is Bool { - const r = obj instanceof BoolImpl; + const r = obj instanceof ExprImpl && obj.sort.kind() === Z3_sort_kind.Z3_BOOL_SORT; r && _assertContext(obj); return r; } @@ -389,6 +424,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return isAppOf(obj, Z3_decl_kind.Z3_OP_DISTINCT); } + function isQuantifier(obj: unknown): obj is Quantifier { + const r = obj instanceof QuantifierImpl; + r && _assertContext(obj); + return r; + } + function isArith(obj: unknown): obj is Arith { const r = obj instanceof ArithImpl; r && _assertContext(obj); @@ -531,8 +572,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // expression simplification // /////////////////////////////// - async function simplify(e: Expr) { - const result = await Z3.simplify(contextPtr, e.ast) + async function simplify(e: Expr): Promise> { + const result = await Z3.simplify(contextPtr, e.ast); return _toExpr(check(result)); } @@ -543,7 +584,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { declare: (name: string) => new SortImpl(Z3.mk_uninterpreted_sort(contextPtr, _toSymbol(name))), }; const Function = { - declare: (name: string, ...signature: FuncDeclSignature) => { + declare: [], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -552,9 +596,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); }, - fresh: (...signature: FuncDeclSignature) => { + fresh: [], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -563,7 +609,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); }, }; const RecFunc = { @@ -701,7 +747,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, }; const Array = { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends AnySort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort { const arity = sig.length - 1; @@ -711,16 +757,23 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new ArraySortImpl(Z3.mk_array_sort(contextPtr, d.ptr, r.ptr)); } const dom = sig.slice(0, arity); - return new ArraySortImpl(Z3.mk_array_sort_n(contextPtr, dom.map(s => s.ptr), r.ptr)); - }, - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] - ): SMTArray { - return new ArrayImpl( - check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)) + return new ArraySortImpl( + Z3.mk_array_sort_n( + contextPtr, + dom.map(s => s.ptr), + r.ptr, + ), ); }, - consts, ...AnySort[]], RangeSort extends AnySort>( + const, RangeSort extends AnySort>( + name: string, + ...sig: [...DomainSort, RangeSort] + ): SMTArray { + return new ArrayImpl( + check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)), + ); + }, + consts, RangeSort extends AnySort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[] { @@ -731,13 +784,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray { - return new ArrayImpl<[DomainSort], RangeSort>( - check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr)) - ); - } - } + return new ArrayImpl<[DomainSort], RangeSort>(check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr))); + }, + }; //////////////// // Operations // @@ -747,7 +798,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; function If( condition: Bool | Probe | boolean, onTrue: CoercibleToExpr | Tactic, @@ -812,6 +863,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BoolImpl(check(Z3.mk_implies(contextPtr, a.ptr, b.ptr))); } + function Iff(a: Bool | boolean, b: Bool | boolean): Bool { + a = from(a) as Bool; + b = from(b) as Bool; + _assertContext(a, b); + return new BoolImpl(check(Z3.mk_iff(contextPtr, a.ptr, b.ptr))); + } + function Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool { a = from(a); b = from(b); @@ -897,6 +955,84 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + true, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + false, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): LambdaImpl { + // TODO(walden): For some reason LambdaImpl leads to type issues + // and Typescript won't build. I'm not sure why since the types seem to all match + // up. For now, we just use any for the domain sort + + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new LambdaImpl( + check( + Z3.mk_lambda_const( + contextPtr, + quantifiers.map(q => q.ptr as unknown as Z3_app), + expr.ptr, + ), + ), + ); + } + function ToReal(expr: Arith | bigint): Arith { expr = from(expr) as Arith; _assertContext(expr); @@ -961,6 +1097,101 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new TacticImpl(check(Z3.tactic_cond(contextPtr, probe.ptr, onTrue.ptr, onFalse.ptr))); } + function LT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_lt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_gt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function LE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_le(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_ge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvult(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvugt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvule(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvuge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvslt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsle(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function Extract(hi: number, lo: number, val: BitVec): BitVec { + return new BitVecImpl(check(Z3.mk_extract(contextPtr, hi, lo, val.ast))); + } + + function Select, RangeSort extends Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap { + const args = indices.map((arg, i) => array.domain_n(i).cast(arg as any)); + if (args.length === 1) { + return _toExpr(check(Z3.mk_select(contextPtr, array.ast, args[0].ast))) as SortToExprMap; + } + const _args = args.map(arg => arg.ast); + return _toExpr(check(Z3.mk_select_n(contextPtr, array.ast, _args))) as SortToExprMap; + } + + function Store, RangeSort extends Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + const args = indicesAndValue.map((arg, i) => { + if (i === indicesAndValue.length - 1) { + return array.range().cast(arg as any) as SortToExprMap; + } + return array.domain_n(i).cast(arg as any); + }); + if (args.length <= 1) { + throw new Error('Array store requires both index and value arguments'); + } + if (args.length === 2) { + return _toExpr(check(Z3.mk_store(contextPtr, array.ast, args[0].ast, args[1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); + return _toExpr(check(Z3.mk_store_n(contextPtr, array.ast, _idxs, args[args.length - 1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + class AstImpl implements Ast { declare readonly __typename: Ast['__typename']; readonly ctx: Context; @@ -1023,6 +1254,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr)); } + set(key: string, value: any): void { + Z3.solver_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + push() { Z3.solver_push(contextPtr, this.ptr); } @@ -1110,20 +1345,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, FuncDecl]> { + *entries(): IterableIterator<[number, FuncDecl]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (const [key] of this.entries()) { yield key; } } - * values(): IterableIterator> { + *values(): IterableIterator> { for (const [, value] of this.entries()) { yield value; } @@ -1160,7 +1395,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { get(sort: Sort): AstVector>; get( i: number | FuncDecl | Expr | Sort, - to?: number + to?: number, ): FuncDecl | FuncInterp | Expr | AstVector> | FuncDecl[] { assert(to === undefined || typeof i === 'number'); if (typeof i === 'number') { @@ -1200,6 +1435,41 @@ export function createApi(Z3: Z3Core): Z3HighLevel { assert(false, 'Number, declaration or constant expected'); } + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void { + _assertContext(decl); + _assertContext(a); + if (isExpr(decl)) { + decl = decl.decl(); + } + if (isFuncDecl(decl) && decl.arity() !== 0 && isFuncInterp(a)) { + const funcInterp = this.addFuncInterp(decl, a.elseValue() as Expr); + for (let i = 0; i < a.numEntries(); i++) { + const e = a.entry(i); + const n = e.numArgs(); + const args = global.Array(n).map((_, i) => e.argValue(i)); + funcInterp.addEntry(args, e.value()); + } + return; + } + if (!isFuncDecl(decl) || decl.arity() !== 0) { + throw new Z3Error('Expecting 0-ary function or constant expression'); + } + if (!isAst(a)) { + throw new Z3Error('Only func declarations can be assigned to func interpretations'); + } + check(Z3.add_const_interp(contextPtr, this.ptr, decl.ptr, a.ast)); + } + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp { + const fi = check( + Z3.add_func_interp(contextPtr, this.ptr, decl.ptr, decl.range().cast(defaultValue).ptr as Z3_ast), + ); + return new FuncInterpImpl(fi); + } + private getInterp(expr: FuncDecl | Expr): Expr | FuncInterp | null { assert(isFuncDecl(expr) || isConst(expr), 'Declaration expected'); if (isConst(expr)) { @@ -1228,6 +1498,30 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class FuncEntryImpl implements FuncEntry { + declare readonly __typename: FuncEntry['__typename']; + + readonly ctx: Context; + + constructor(readonly ptr: Z3_func_entry) { + this.ctx = ctx; + Z3.func_entry_inc_ref(contextPtr, ptr); + cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr)); + } + + numArgs() { + return check(Z3.func_entry_get_num_args(contextPtr, this.ptr)); + } + + argValue(i: number): Expr { + return _toExpr(check(Z3.func_entry_get_arg(contextPtr, this.ptr, i))); + } + + value(): Expr { + return _toExpr(check(Z3.func_entry_get_value(contextPtr, this.ptr))); + } + } + class FuncInterpImpl implements FuncInterp { declare readonly __typename: FuncInterp['__typename']; readonly ctx: Context; @@ -1237,6 +1531,33 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Z3.func_interp_inc_ref(contextPtr, ptr); cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr)); } + + elseValue(): Expr { + return _toExpr(check(Z3.func_interp_get_else(contextPtr, this.ptr))); + } + + numEntries(): number { + return check(Z3.func_interp_get_num_entries(contextPtr, this.ptr)); + } + + arity(): number { + return check(Z3.func_interp_get_arity(contextPtr, this.ptr)); + } + + entry(i: number): FuncEntry { + return new FuncEntryImpl(check(Z3.func_interp_get_entry(contextPtr, this.ptr, i))); + } + + addEntry(args: Expr[], value: Expr): void { + const argsVec = new AstVectorImpl(); + for (const arg of args) { + argsVec.push(arg); + } + _assertContext(argsVec); + _assertContext(value); + assert(this.arity() === argsVec.length(), "Number of arguments in entry doesn't match function arity"); + check(Z3.func_interp_add_entry(contextPtr, this.ptr, argsVec.ptr, value.ptr as Z3_ast)); + } } class SortImpl extends AstImpl implements Sort { @@ -1275,10 +1596,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class FuncDeclImpl extends AstImpl implements FuncDecl { + class FuncDeclImpl[], RangeSort extends Sort> + extends AstImpl + implements FuncDecl + { declare readonly __typename: FuncDecl['__typename']; - get ast() { + get ast(): Z3_ast { return Z3.func_decl_to_ast(contextPtr, this.ptr); } @@ -1290,20 +1614,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.get_arity(contextPtr, this.ptr); } - domain(i: number) { + domain(i: T): DomainSort[T] { assert(i < this.arity(), 'Index out of bounds'); return _toSort(Z3.get_domain(contextPtr, this.ptr, i)); } - range() { - return _toSort(Z3.get_range(contextPtr, this.ptr)); + range(): RangeSort { + return _toSort(Z3.get_range(contextPtr, this.ptr)) as RangeSort; } kind() { return Z3.get_decl_kind(contextPtr, this.ptr); } - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[] { + params(): (number | string | Sort | Expr | FuncDecl)[] { const n = Z3.get_decl_num_parameters(contextPtr, this.ptr); const result = []; for (let i = 0; i < n; i++) { @@ -1319,7 +1643,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { result.push(check(Z3.get_decl_rational_parameter(contextPtr, this.ptr, i))); break; case Z3_parameter_kind.Z3_PARAMETER_SYMBOL: - result.push(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i))); + result.push(_fromSymbol(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i)))); break; case Z3_parameter_kind.Z3_PARAMETER_SORT: result.push(new SortImpl(check(Z3.get_decl_sort_parameter(contextPtr, this.ptr, i)))); @@ -1337,21 +1661,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return result; } - call(...args: CoercibleToExpr[]) { + call(...args: CoercibleToArrayIndexType): SortToExprMap { assert(args.length === this.arity(), `Incorrect number of arguments to ${this}`); return _toExpr( - check(Z3.mk_app( - contextPtr, - this.ptr, - args.map((arg, i) => { - return this.domain(i).cast(arg).ast; - }), - )), - ); + check( + Z3.mk_app( + contextPtr, + this.ptr, + args.map((arg, i) => { + return this.domain(i).cast(arg as any).ast; + }), + ), + ), + ) as SortToExprMap; } } - class ExprImpl = AnySort> extends AstImpl implements Expr { + class ExprImpl = AnySort> + extends AstImpl + implements Expr + { declare readonly __typename: Expr['__typename']; get sort(): S { @@ -1364,13 +1693,19 @@ export function createApi(Z3: Z3Core): Z3HighLevel { neq(other: CoercibleToExpr): Bool { return new BoolImpl( - check(Z3.mk_distinct( - contextPtr, - [this, other].map(expr => from(expr).ast), - )), + check( + Z3.mk_distinct( + contextPtr, + [this, other].map(expr => from(expr).ast), + ), + ), ); } + name() { + return this.decl().name(); + } + params() { return this.decl().params(); } @@ -1404,6 +1739,16 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class PatternImpl implements Pattern { + declare readonly __typename: Pattern['__typename']; + readonly ctx: Context; + + constructor(readonly ptr: Z3_pattern) { + this.ctx = ctx; + // TODO: implement rest of Pattern + } + } + class BoolSortImpl extends SortImpl implements BoolSort { declare readonly __typename: BoolSort['__typename']; @@ -1425,7 +1770,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } class BoolImpl extends ExprImpl> implements Bool { - declare readonly __typename: Bool['__typename']; + declare readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool { return Not(this); @@ -1446,6 +1791,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { implies(other: Bool | boolean): Bool { return Implies(this, other); } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } } class ProbeImpl implements Probe { @@ -1482,12 +1831,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { class ArithSortImpl extends SortImpl implements ArithSort { declare readonly __typename: ArithSort['__typename']; - cast(other: bigint | number): IntNum | RatNum; + cast(other: bigint | number | string): IntNum | RatNum; cast(other: CoercibleRational | RatNum): RatNum; cast(other: IntNum): IntNum; cast(other: Bool | Arith): Arith; cast(other: CoercibleToExpr): never; - cast(other: CoercibleToExpr): Arith | RatNum | IntNum { + cast(other: CoercibleToExpr | string): Arith | RatNum | IntNum { const sortTypeStr = isIntSort(this) ? 'IntSort' : 'RealSort'; if (isExpr(other)) { const otherS = other.sort; @@ -1519,51 +1868,164 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sum( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sum>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec add only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvadd(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_add(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sub( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sub>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec sub only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvsub(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_sub(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Product( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Product>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec mul only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvmul(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_mul(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Div(arg0: Arith, arg1: CoercibleToArith): Arith; + function Div( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec; + function Div>(arg0: T, arg1: CoercibleToMap): T { + if (arg0 instanceof BitVecImpl) { + return new BitVecImpl( + check(Z3.mk_bvsdiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_div(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast))) as unknown as T; + } + } + + function BUDiv( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec { + return new BitVecImpl( + check(Z3.mk_bvudiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as BitVec; + } + + function Neg(a: Arith): Arith; + function Neg(a: BitVec): BitVec; + function Neg>(a: T): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, a.ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, a.ast))) as unknown as T; + } + } + + function Mod(a: Arith, b: CoercibleToArith): Arith; + function Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + function Mod>(a: T, b: CoercibleToMap): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvsrem(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_mod(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } + } + class ArithImpl extends ExprImpl> implements Arith { declare readonly __typename: Arith['__typename']; - add(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_add(contextPtr, [this.ast, this.sort.cast(other).ast]))); + add(other: CoercibleToArith) { + return Sum(this, other); } - mul(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mul(contextPtr, [this.ast, this.sort.cast(other).ast]))); + mul(other: CoercibleToArith) { + return Product(this, other); } - sub(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_sub(contextPtr, [this.ast, this.sort.cast(other).ast]))); + sub(other: CoercibleToArith) { + return Sub(this, other); } - pow(exponent: Arith | number | bigint | string | CoercibleRational) { + pow(exponent: CoercibleToArith) { return new ArithImpl(check(Z3.mk_power(contextPtr, this.ast, this.sort.cast(exponent).ast))); } - div(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_div(contextPtr, this.ast, this.sort.cast(other).ast))); + div(other: CoercibleToArith) { + return Div(this, other); } - mod(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mod(contextPtr, this.ast, this.sort.cast(other).ast))); + mod(other: CoercibleToArith) { + return Mod(this, other); } neg() { - return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, this.ast))); + return Neg(this); } - le(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_le(contextPtr, this.ast, this.sort.cast(other).ast))); + le(other: CoercibleToArith) { + return LE(this, other); } - lt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_lt(contextPtr, this.ast, this.sort.cast(other).ast))); + lt(other: CoercibleToArith) { + return LT(this, other); } - gt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_gt(contextPtr, this.ast, this.sort.cast(other).ast))); + gt(other: CoercibleToArith) { + return GT(this, other); } - ge(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_ge(contextPtr, this.ast, this.sort.cast(other).ast))); + ge(other: CoercibleToArith) { + return GE(this, other); } } @@ -1644,27 +2106,27 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } add(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sum(this, other); } mul(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast))); + return Product(this, other); } sub(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sub(this, other); } sdiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return Div(this, other); } udiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return BUDiv(this, other); } smod(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast))); + return Mod(this, other); } urem(other: CoercibleToBitVec): BitVec { @@ -1676,7 +2138,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } neg(): BitVec { - return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, this.ast))); + return Neg(this); } or(other: CoercibleToBitVec): BitVec { @@ -1723,8 +2185,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BitVecImpl(check(Z3.mk_bvnot(contextPtr, this.ast))); } - extract(high: number, low: number): BitVec { - return new BitVecImpl(check(Z3.mk_extract(contextPtr, high, low, this.ast))); + extract(high: number, low: number): BitVec { + return Extract(high, low, this); } signExt(count: number): BitVec { @@ -1740,35 +2202,35 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } sle(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLE(this, other); } ule(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULE(this, other); } slt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLT(this, other); } ult(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULT(this, other); } sge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGE(this, other); } uge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGE(this, other); } sgt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGT(this, other); } ugt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGT(this, other); } redAnd(): BitVec { @@ -1840,10 +2302,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class ArraySortImpl, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort> + class ArraySortImpl, RangeSort extends Sort> extends SortImpl - implements SMTArraySort { + implements SMTArraySort + { declare readonly __typename: SMTArraySort['__typename']; domain(): DomainSort[0] { @@ -1857,15 +2319,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { range(): RangeSort { return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr))) as RangeSort; } - } - class ArrayImpl< - DomainSort extends [AnySort, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort - > extends ExprImpl> - implements SMTArray { - declare readonly __typename: SMTArray['__typename']; + class ArrayImpl, RangeSort extends Sort> + extends ExprImpl> + implements SMTArray + { + declare readonly __typename: 'Array' | 'Lambda'; domain(): DomainSort[0] { return this.sort.domain(); @@ -1879,35 +2339,145 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.sort.range(); } - select(...indices: ArrayIndexType): SortToExprMap { - const args = indices.map((arg, i) => this.domain_n(i).cast(arg as any)); - if (args.length === 1) { - return _toExpr(check(Z3.mk_select(contextPtr, this.ast, args[0].ast))) as SortToExprMap; - } - const _args = args.map(arg => arg.ast); - return _toExpr(check(Z3.mk_select_n(contextPtr, this.ast, _args))) as SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices) as SortToExprMap; } store( ...indicesAndValue: [ - ...ArrayIndexType, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray { - const args = indicesAndValue.map((arg, i) => { - if (i === indicesAndValue.length - 1) { - return this.range().cast(arg as CoercibleFromMap, Name>); - } - return this.domain_n(i).cast(arg as any); - }); - if (args.length <= 1) { - throw new Z3Error("Array store requires both index and value arguments"); - } - if (args.length === 2) { - return _toExpr(check(Z3.mk_store(contextPtr, this.ast, args[0].ast, args[1].ast))) as SMTArray; - } - const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); - return _toExpr(check(Z3.mk_store_n(contextPtr, this.ast, _idxs, args[args.length - 1].ast))) as SMTArray; + return Store(this, ...indicesAndValue); + } + } + + class QuantifierImpl< + QVarSorts extends NonEmptySortArray, + QSort extends BoolSort | SMTArraySort, + > + extends ExprImpl + implements Quantifier + { + declare readonly __typename: Quantifier['__typename']; + + is_forall(): boolean { + return Z3.is_quantifier_forall(contextPtr, this.ast); + } + + is_exists(): boolean { + return Z3.is_quantifier_exists(contextPtr, this.ast); + } + + is_lambda(): boolean { + return Z3.is_lambda(contextPtr, this.ast); + } + + weight(): number { + return Z3.get_quantifier_weight(contextPtr, this.ast); + } + + num_patterns(): number { + return Z3.get_quantifier_num_patterns(contextPtr, this.ast); + } + + pattern(i: number): Pattern { + return new PatternImpl(check(Z3.get_quantifier_pattern_ast(contextPtr, this.ast, i))); + } + + num_no_patterns(): number { + return Z3.get_quantifier_num_no_patterns(contextPtr, this.ast); + } + + no_pattern(i: number): Expr { + return _toExpr(check(Z3.get_quantifier_no_pattern_ast(contextPtr, this.ast, i))); + } + + body(): BodyT { + return _toExpr(check(Z3.get_quantifier_body(contextPtr, this.ast))) as any; + } + + num_vars(): number { + return Z3.get_quantifier_num_bound(contextPtr, this.ast); + } + + var_name(i: number): string | number { + return _fromSymbol(Z3.get_quantifier_bound_name(contextPtr, this.ast, i)); + } + + var_sort(i: T): QVarSorts[T] { + return _toSort(check(Z3.get_quantifier_bound_sort(contextPtr, this.ast, i))); + } + + children(): [BodyT] { + return [this.body()]; + } + } + + class NonLambdaQuantifierImpl> + extends QuantifierImpl> + implements Quantifier>, Bool + { + declare readonly __typename: 'NonLambdaQuantifier'; + + not(): Bool { + return Not(this); + } + + and(other: Bool | boolean): Bool { + return And(this, other); + } + + or(other: Bool | boolean): Bool { + return Or(this, other); + } + + xor(other: Bool | boolean): Bool { + return Xor(this, other); + } + + implies(other: Bool | boolean): Bool { + return Implies(this, other); + } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } + } + + // isBool will return false which is unlike the python API (but like the C API) + class LambdaImpl, RangeSort extends Sort> + extends QuantifierImpl> + implements + Quantifier>, + SMTArray + { + declare readonly __typename: 'Lambda'; + + domain(): DomainSort[0] { + return this.sort.domain(); + } + + domain_n(i: T): DomainSort[T] { + return this.sort.domain_n(i); + } + + range(): RangeSort { + return this.sort.range(); + } + + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices); + } + + store( + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + return Store(this, ...indicesAndValue); } } @@ -1929,20 +2499,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, Item]> { + *entries(): IterableIterator<[number, Item]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (let [key] of this.entries()) { yield key; } } - * values(): IterableIterator { + *values(): IterableIterator { for (let [, value] of this.entries()) { yield value; } @@ -2027,7 +2597,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.ast_map_size(contextPtr, this.ptr); } - * entries(): IterableIterator<[Key, Value]> { + *entries(): IterableIterator<[Key, Value]> { for (const key of this.keys()) { yield [key, this.get(key)]; } @@ -2037,7 +2607,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr)); } - * values(): IterableIterator { + *values(): IterableIterator { for (const [_, value] of this.entries()) { yield value; } @@ -2068,6 +2638,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr { + _assertContext(t); + const from: Z3_ast[] = []; + const to: Z3_ast[] = []; + for (const [f, t] of substitutions) { + _assertContext(f); + _assertContext(t); + from.push(f.ast); + to.push(t.ast); + } + return _toExpr(check(Z3.substitute(contextPtr, t.ast, from, to))); + } + + function ast_from_string(s: string): Ast { + const sort_names: Z3_symbol[] = []; + const sorts: Z3_sort[] = []; + const decl_names: Z3_symbol[] = []; + const decls: Z3_func_decl[] = []; + const v = new AstVectorImpl(check(Z3.parse_smtlib2_string(contextPtr, s, sort_names, sorts, decl_names, decls))); + if (v.length() !== 1) { + throw new Error('Expected exactly one AST. Instead got ' + v.length() + ': ' + v.sexpr()); + } + return v.get(0); + } + const ctx: Context = { ptr: contextPtr, name, @@ -2089,6 +2684,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isAst, isSort, isFuncDecl, + isFuncInterp, isApp, isConst, isExpr, @@ -2103,6 +2699,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isNot, isEq, isDistinct, + isQuantifier, isArith, isArithSort, isInt, @@ -2147,11 +2744,15 @@ export function createApi(Z3: Z3Core): Z3HighLevel { FreshConst, Var, Implies, + Iff, Eq, Xor, Not, And, Or, + ForAll, + Exists, + Lambda, ToReal, ToInt, IsInt, @@ -2161,6 +2762,36 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Int2BV, Concat, Cond, + LT, + GT, + LE, + GE, + ULT, + UGT, + ULE, + UGE, + SLT, + SGT, + SLE, + SGE, + Sum, + Sub, + Product, + Div, + BUDiv, + Neg, + Mod, + Select, + Store, + Extract, + + substitute, + simplify, + + ///////////// + // Loading // + ///////////// + ast_from_string, }; cleanup.register(ctx, () => Z3.del_context(contextPtr)); return ctx; diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index a6cb01e79..c4b560442 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -5,13 +5,13 @@ import { Z3_context, Z3_decl_kind, Z3_func_decl, + Z3_func_entry, Z3_func_interp, Z3_model, Z3_probe, Z3_solver, Z3_sort, Z3_sort_kind, - Z3_symbol, Z3_tactic, } from '../low-level'; @@ -21,7 +21,7 @@ export type AnySort = | BoolSort | ArithSort | BitVecSort - | SMTArraySort, ...AnySort[]], AnySort>; + | SMTArraySort; /** @hidden */ export type AnyExpr = | Expr @@ -31,53 +31,64 @@ export type AnyExpr = | RatNum | BitVec | BitVecNum - | SMTArray, ...AnySort[]], AnySort>; + | SMTArray; /** @hidden */ export type AnyAst = AnyExpr | AnySort | FuncDecl; /** @hidden */ -export type SortToExprMap, Name extends string = 'main'> = - S extends BoolSort - ? Bool - : S extends ArithSort - ? Arith - : S extends BitVecSort - ? BitVec - : S extends SMTArraySort - ? SMTArray - : S extends Sort - ? Expr - : never; +export type SortToExprMap, Name extends string = 'main'> = S extends BoolSort + ? Bool + : S extends ArithSort + ? Arith + : S extends BitVecSort + ? BitVec + : S extends SMTArraySort + ? SMTArray + : S extends Sort + ? Expr + : never; /** @hidden */ -export type CoercibleToExprMap, Name extends string = 'main'> = - S extends bigint - ? ArithSort - : S extends number | CoercibleRational - ? RatNum - : S extends boolean - ? Bool - : S extends Expr - ? S - : never; +export type CoercibleFromMap, Name extends string = 'main'> = S extends bigint + ? Arith + : S extends number | CoercibleRational + ? RatNum + : S extends boolean + ? Bool + : S extends Expr + ? S + : never; /** @hidden */ -export type CoercibleFromMap, Name extends string = 'main'> = - S extends Bool - ? (boolean | Bool) - : S extends IntNum - ? (bigint | number | IntNum) - : S extends RatNum - ? (bigint | number | CoercibleRational | RatNum) - : S extends Arith - ? (bigint | number | CoercibleRational | Arith) - : S extends BitVec - ? (number | BitVec) - : S extends SMTArray - ? SMTArray - : S extends Expr - ? Expr - : never; +export type CoercibleToBitVec = + | bigint + | number + | BitVec; + +export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; + +/** @hidden */ +export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; + +/** @hidden */ +export type CoercibleToArith = number | string | bigint | CoercibleRational | Arith; + +/** @hidden */ +export type CoercibleToMap, Name extends string = 'main'> = T extends Bool + ? boolean | Bool + : T extends IntNum + ? bigint | number | IntNum + : T extends RatNum + ? bigint | number | CoercibleRational | RatNum + : T extends Arith + ? CoercibleToArith + : T extends BitVec + ? CoercibleToBitVec + : T extends SMTArray + ? SMTArray + : T extends Expr + ? Expr + : never; /** * Used to create a Real constant @@ -97,16 +108,10 @@ export type CoercibleFromMap, Name extends string = 'mai * @see {@link Context.from} * @category Global */ -export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; -/** @hidden */ -export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; +export class Z3Error extends Error {} -export class Z3Error extends Error { -} - -export class Z3AssertionError extends Z3Error { -} +export class Z3AssertionError extends Z3Error {} /** @category Global */ export type CheckSatResult = 'sat' | 'unsat' | 'unknown'; @@ -149,6 +154,9 @@ export interface Context { /** @category Functions */ isFuncDecl(obj: unknown): obj is FuncDecl; + /** @category Functions */ + isFuncInterp(obj: unknown): obj is FuncInterp; + /** @category Functions */ isApp(obj: unknown): boolean; @@ -191,6 +199,9 @@ export interface Context { /** @category Functions */ isDistinct(obj: unknown): boolean; + /** @category Functions */ + isQuantifier(obj: unknown): obj is Quantifier; + /** @category Functions */ isArith(obj: unknown): obj is Arith; @@ -225,10 +236,10 @@ export interface Context { isBitVecVal(obj: unknown): obj is BitVecNum; /** @category Functions */ - isArraySort(obj: unknown): obj is SMTArraySort, ...AnySort[]], AnySort>; + isArraySort(obj: unknown): obj is SMTArraySort; /** @category Functions */ - isArray(obj: unknown): obj is SMTArray, ...AnySort[]], AnySort>; + isArray(obj: unknown): obj is SMTArray; /** @category Functions */ isConstArray(obj: unknown): boolean; @@ -315,7 +326,11 @@ export interface Context { /** @category Classes */ readonly AstVector: new = AnyAst>() => AstVector; /** @category Classes */ - readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap; + readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap< + Name, + Key, + Value + >; /** @category Classes */ readonly Tactic: new (name: string) => Tactic; @@ -363,7 +378,7 @@ export interface Context { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; /** @category Operations */ Distinct(...args: CoercibleToExpr[]): Bool; @@ -371,6 +386,9 @@ export interface Context { /** @category Operations */ Implies(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ + Iff(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool; @@ -407,6 +425,28 @@ export interface Context { /** @category Operations */ Or(...args: Probe[]): Probe; + // Quantifiers + + /** @category Operations */ + ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): Quantifier> & SMTArray; + // Arithmetic /** @category Operations */ ToReal(expr: Arith | bigint): Arith; @@ -437,7 +477,7 @@ export interface Context { * // a**(1/2) * ``` * @category Operations */ - Sqrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Sqrt(a: CoercibleToArith): Arith; /** * Returns a Z3 expression representing cubic root of a @@ -449,7 +489,7 @@ export interface Context { * // a**(1/3) * ``` * @category Operations */ - Cbrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Cbrt(a: CoercibleToArith): Arith; // Bit Vectors /** @category Operations */ @@ -462,7 +502,102 @@ export interface Context { Concat(...bitvecs: BitVec[]): BitVec; /** @category Operations */ - Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic + Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic; + + // Arith + + /** @category Operations */ + LT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + LE(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GE(a: Arith, b: CoercibleToArith): Bool; + + // Bit Vectors + + /** @category Operations */ + ULT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + ULE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sum(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sub(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Product(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Div(arg0: Arith, arg1: CoercibleToArith): Arith; + + Div(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + BUDiv(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + Neg(a: Arith): Arith; + + Neg(a: BitVec): BitVec; + + Mod(a: Arith, b: CoercibleToArith): Arith; + + Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + + // Arrays + + /** @category Operations */ + Select, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap; + + /** @category Operations */ + Store, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray; + + /** @category Operations */ + Extract(hi: number, lo: number, val: BitVec): BitVec; + + /** @category Operations */ + ast_from_string(s: string): Ast; + + /** @category Operations */ + substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr; + + simplify(expr: Expr): Promise>; } export interface Ast { @@ -490,7 +625,7 @@ export interface Ast { /** @hidden */ export interface SolverCtor { - new(): Solver; + new (): Solver; } export interface Solver { @@ -500,10 +635,11 @@ export interface Solver { readonly ctx: Context; readonly ptr: Z3_solver; - /* TODO(ritave): Decide on how to discern between integer and float parameters set(key: string, value: any): void; - set(params: Record): void; - */ + + /* TODO(ritave): Decide on how to discern between integer and float parameters + set(params: Record): void; + */ push(): void; pop(num?: number): void; @@ -527,7 +663,7 @@ export interface Solver { /** @hidden */ export interface ModelCtor { - new(): Model; + new (): Model; } export interface Model extends Iterable> { @@ -566,6 +702,13 @@ export interface Model extends Iterable): Expr; get(sort: Sort): AstVector>; + + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void; + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp; } /** @@ -608,6 +751,23 @@ export interface Sort extends Ast { name(): string | number; } +/** + * @category Functions + */ +export interface FuncEntry { + /** @hidden */ + readonly __typename: 'FuncEntry'; + + readonly ctx: Context; + readonly ptr: Z3_func_entry; + + numArgs(): number; + + argValue(i: number): Expr; + + value(): Expr; +} + /** * @category Functions */ @@ -617,6 +777,16 @@ export interface FuncInterp { readonly ctx: Context; readonly ptr: Z3_func_interp; + + elseValue(): Expr; + + numEntries(): number; + + arity(): number; + + entry(i: number): FuncEntry; + + addEntry(args: Expr[], value: Expr): void; } /** @hidden */ @@ -639,9 +809,14 @@ export interface FuncDeclCreation { * @param name Name of the function * @param signature The domains, and last parameter - the range of the function */ - declare(name: string, ...signature: FuncDeclSignature): FuncDecl; + declare[], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; - fresh(...signature: FuncDeclSignature): FuncDecl; + fresh[], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; } /** @@ -656,7 +831,11 @@ export interface RecFuncCreation { /** * @category Functions */ -export interface FuncDecl extends Ast { +export interface FuncDecl< + Name extends string = 'main', + DomainSort extends Sort[] = Sort[], + RangeSort extends Sort = Sort, +> extends Ast { /** @hidden */ readonly __typename: 'FuncDecl'; @@ -664,21 +843,26 @@ export interface FuncDecl extends Ast; + domain(i: T): DomainSort[T]; - range(): Sort; + range(): RangeSort; kind(): Z3_decl_kind; - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[]; + params(): (number | string | Sort | Expr | FuncDecl)[]; - call(...args: CoercibleToExpr[]): AnyExpr; + call(...args: CoercibleToArrayIndexType): SortToExprMap; } export interface Expr = AnySort, Ptr = unknown> extends Ast { /** @hidden */ - readonly __typename: 'Expr' | Bool['__typename'] | Arith['__typename'] | BitVec['__typename'] | SMTArray['__typename']; + readonly __typename: + | 'Expr' + | Bool['__typename'] + | Arith['__typename'] + | BitVec['__typename'] + | SMTArray['__typename']; get sort(): S; @@ -688,6 +872,8 @@ export interface Expr = AnySo params(): ReturnType['params']>; + name(): ReturnType['name']>; + decl(): FuncDecl; numArgs(): number; @@ -725,7 +911,7 @@ export interface BoolCreation { /** @category Booleans */ export interface Bool extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Bool'; + readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool; @@ -738,6 +924,13 @@ export interface Bool extends Expr | boolean): Bool; } +// TODO: properly implement pattern +/** @category Quantifiers */ +export interface Pattern { + /** @hidden */ + readonly __typename: 'Pattern'; +} + /** * A Sort that represents Integers or Real numbers * @category Arithmetic @@ -798,17 +991,17 @@ export interface Arith extends Expr | number | bigint | string): Arith; + add(other: CoercibleToArith): Arith; /** * Multiplies two numbers together */ - mul(other: Arith | number | bigint | string): Arith; + mul(other: CoercibleToArith): Arith; /** * Subtract second number from the first one */ - sub(other: Arith | number | bigint | string): Arith; + sub(other: CoercibleToArith): Arith; /** * Applies power to the number @@ -820,12 +1013,12 @@ export interface Arith extends Expr | number | bigint | string): Arith; + pow(exponent: CoercibleToArith): Arith; /** * Divides the number by the second one */ - div(other: Arith | number | bigint | string): Arith; + div(other: CoercibleToArith): Arith; /** * Returns a number modulo second one @@ -837,7 +1030,7 @@ export interface Arith extends Expr | number | bigint | string): Arith; + mod(other: CoercibleToArith): Arith; /** * Returns a negation of the number @@ -847,22 +1040,22 @@ export interface Arith extends Expr | number | bigint | string): Bool; + le(other: CoercibleToArith): Bool; /** * Returns whether the number is less than the second one (`<`) */ - lt(other: Arith | number | bigint | string): Bool; + lt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater than the second one (`>`) */ - gt(other: Arith | number | bigint | string): Bool; + gt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater or equal than the second one (`>=`) */ - ge(other: Arith | number | bigint | string): Bool; + ge(other: CoercibleToArith): Bool; } /** @@ -939,12 +1132,6 @@ export interface BitVecSort): Expr; } -/** @hidden */ -export type CoercibleToBitVec = - | bigint - | number - | BitVec; - /** @category Bit Vectors */ export interface BitVecCreation { sort(bits: Bits): BitVecSort; @@ -1213,10 +1400,11 @@ export interface BitVecNum, ...AnySort[]] = [Sort, ...Sort[]], +export interface SMTArraySort< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], RangeSort extends AnySort = AnySort, - > extends Sort { +> extends Sort { /** @hidden */ readonly __typename: 'ArraySort'; @@ -1236,36 +1424,47 @@ export interface SMTArraySort { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends Sort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort; - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] + const, RangeSort extends Sort>( + name: string, + ...sig: [...DomainSort, RangeSort] ): SMTArray; - consts, ...AnySort[]], RangeSort extends AnySort>( + consts, RangeSort extends Sort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[]; K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray; } -export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]]> = [...{ - [Index in keyof DomainSort]: DomainSort[Index] extends AnySort ? - CoercibleFromMap, Name> : - DomainSort[Index]; -}] +export type NonEmptySortArray = [Sort, ...Array>]; + +export type ArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? SortToExprMap + : DomainSort[Key]; + }, +]; + +export type CoercibleToArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? CoercibleToMap, Name> + : DomainSort[Key]; + }, +]; /** * Represents Array expression @@ -1274,13 +1473,13 @@ export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = AnySort> - extends Expr, Z3_ast> { - +export interface SMTArray< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], + RangeSort extends Sort = Sort, +> extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Array'; + readonly __typename: 'Array' | 'Lambda'; domain(): DomainSort[0]; @@ -1288,7 +1487,7 @@ export interface SMTArray): SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap; /** * value should be coercible to RangeSort @@ -1297,11 +1496,60 @@ export interface SMTArray, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray; +} +/** + * Defines the expression type of the body of a quantifier expression + * + * @category Quantifiers + */ +export type BodyT< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> = QSort extends BoolSort + ? Bool + : QSort extends SMTArray + ? SortToExprMap + : never; + +/** @category Quantifiers */ +export interface Quantifier< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> extends Expr { + readonly __typename: 'NonLambdaQuantifier' | 'Lambda'; + + is_forall(): boolean; + + is_exists(): boolean; + + is_lambda(): boolean; + + weight(): number; + + num_patterns(): number; + + pattern(i: number): Pattern; + + num_no_patterns(): number; + + no_pattern(i: number): Expr; + + body(): BodyT; + + num_vars(): number; + + var_name(i: number): string | number; + + var_sort(i: T): QVarSorts[T]; + + children(): [BodyT]; } export interface Probe { @@ -1314,7 +1562,7 @@ export interface Probe { /** @hidden */ export interface TacticCtor { - new(name: string): Tactic; + new (name: string): Tactic; } export interface Tactic { @@ -1327,7 +1575,7 @@ export interface Tactic { /** @hidden */ export interface AstVectorCtor { - new = AnyAst>(): AstVector; + new = AnyAst>(): AstVector; } /** @@ -1378,7 +1626,7 @@ export interface AstVector /** @hidden */ export interface AstMapCtor { - new = AnyAst, Value extends Ast = AnyAst>(): AstMap; + new = AnyAst, Value extends Ast = AnyAst>(): AstMap; } /** @@ -1402,8 +1650,11 @@ export interface AstMapCtor { * // 0 * ``` */ -export interface AstMap = AnyAst, Value extends Ast = AnyAst> - extends Iterable<[Key, Value]> { +export interface AstMap< + Name extends string = 'main', + Key extends Ast = AnyAst, + Value extends Ast = AnyAst, +> extends Iterable<[Key, Value]> { /** @hidden */ readonly __typename: 'AstMap'; From cac5052685eedb9dca304b623616fc9b8659bf58 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 13:43:44 -0800 Subject: [PATCH 021/220] fixes related to #6577 - enforce elim-and in bool-rewriter when invoking hoisting. - make cnf tactic more resilient to non-normalized input. - enable eliminate predicates on ground formulas --- src/ast/rewriter/bool_rewriter.h | 5 ++- src/ast/rewriter/hoist_rewriter.cpp | 22 +++++++++-- src/ast/rewriter/hoist_rewriter.h | 7 ++++ src/ast/simplifiers/eliminate_predicates.cpp | 2 - src/tactic/core/tseitin_cnf_tactic.cpp | 41 +++++++++++++++++++- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 8f2221a8c..488949084 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -81,7 +81,10 @@ class bool_rewriter { void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits); public: - bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { updt_params(p); } + bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { + updt_params(p); + m_hoist.set(*this); + } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } bool is_eq(expr * t) const { return m().is_eq(t); } diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 40ad4604a..b86f1eb47 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -17,16 +17,30 @@ Author: #include "ast/rewriter/hoist_rewriter.h" +#include "ast/rewriter/bool_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" - hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): m(m), m_args1(m), m_args2(m), m_subst(m) { updt_params(p); } +expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { + if (m_rewriter) + return m_rewriter->mk_and(args); + else + return ::mk_and(args); +} + +expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { + if (m_rewriter) + return m_rewriter->mk_or(args); + else + return ::mk_or(args); +} + br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { if (num_args < 2) return BR_FAILED; @@ -152,13 +166,13 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi for (expr* e : m_args1) if (!preds.contains(e)) fmls.push_back(e); - args.push_back(::mk_and(fmls)); + args.push_back(mk_and(fmls)); } fmls.reset(); - fmls.push_back(::mk_or(args)); + fmls.push_back(mk_or(args)); for (auto* p : preds) fmls.push_back(p); - result = ::mk_and(fmls); + result = mk_and(fmls); return result; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index cc83bfa56..72d44d0bc 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -25,8 +25,11 @@ Notes: #include "util/union_find.h" #include "util/obj_hashtable.h" +class bool_rewriter; + class hoist_rewriter { ast_manager & m; + bool_rewriter* m_rewriter = nullptr; expr_ref_vector m_args1, m_args2; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; @@ -39,6 +42,8 @@ class hoist_rewriter { expr_mark m_mark; bool is_and(expr* e, expr_ref_vector* args); + expr_ref mk_and(expr_ref_vector const& args); + expr_ref mk_or(expr_ref_vector const& args); bool is_var(expr* e) { return m_expr2var.contains(e); } expr* mk_expr(unsigned v) { return m_var2expr[v]; } @@ -48,6 +53,7 @@ class hoist_rewriter { expr_ref hoist_predicates(obj_hashtable const& p, unsigned num_args, expr* const* args); + public: hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); family_id get_fid() const { return m.get_basic_family_id(); } @@ -56,6 +62,7 @@ public: static void get_param_descrs(param_descrs & r) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); + void set(bool_rewriter& r) { m_rewriter = &r; } }; struct hoist_rewriter_cfg : public default_rewriter_cfg { diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 6c1eccea0..05f347f25 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -1008,8 +1008,6 @@ void eliminate_predicates::reset() { void eliminate_predicates::reduce() { - if (!m_fmls.has_quantifiers()) - return; reset(); init_clauses(); find_definitions(); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index bd2f58b44..ec03849bc 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -141,6 +141,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: + case OP_AND: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -187,6 +188,7 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: + case OP_AND: visited = false; push_frame(to_app(n)); return; @@ -197,7 +199,6 @@ class tseitin_cnf_tactic : public tactic { push_frame(to_app(n)); } return; - case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: @@ -617,6 +618,43 @@ class tseitin_cnf_tactic : public tactic { } return DONE; } + + mres match_and(app * t, bool first, bool root) { + if (!m.is_and(t)) + return NO; + if (first) { + bool visited = true; + for (expr* a : *t) + visit(a, visited); + if (!visited) + return CONT; + } + expr_ref_buffer lits(m); + expr_ref l(m), nl(m); + app_ref k(m), nk(m); + if (root) { + for (expr* arg : *t) { + get_lit(arg, false, l); + expr* lits[1] = { l }; + mk_clause(1, lits); + } + } + else { + k = mk_fresh(); + nk = m.mk_not(k); + cache_result(t, k); + + for (expr* arg : *t) { + get_lit(arg, false, l); + mk_clause(nk, l); + inv(l, nl); + lits.push_back(nl); + } + lits.push_back(k); + mk_clause(lits.size(), lits.data()); + } + return DONE; + } mres match_or(app * t, bool first, bool root) { if (!m.is_or(t)) @@ -778,6 +816,7 @@ class tseitin_cnf_tactic : public tactic { fr.m_first = false; TRY(match_or_3and); TRY(match_or); + TRY(match_and); TRY(match_iff3); // TRY(match_iff_or); TRY(match_iff); From 102eee77dcd955a0b938d198444312d46c3e0a34 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:12:01 -0800 Subject: [PATCH 022/220] patch regressions Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/hoist_rewriter.cpp | 2 +- src/sat/smt/arith_sls.cpp | 127 ++++++++++++------------- src/sat/smt/arith_sls.h | 42 ++++---- src/tactic/core/tseitin_cnf_tactic.cpp | 5 +- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index b86f1eb47..2329fd527 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -35,7 +35,7 @@ expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { } expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { - if (m_rewriter) + if (false && m_rewriter) return m_rewriter->mk_or(args); else return ::mk_or(args); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 79e6aec54..d7a85cdb1 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -62,12 +62,12 @@ namespace arith { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { - rational val; + int64_t val; lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - val += arg.coeff() * value(w); + val += to_numeral(arg.coeff()) * value(w); } update(v, val); } @@ -77,15 +77,18 @@ namespace arith { continue; if (!s.lp().external_is_used(v)) continue; - rational old_value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); - rational new_value = value(v); + int64_t old_value = 0; + if (s.is_registered_var(v)) + old_value = to_numeral(s.get_ivalue(v).x); + int64_t new_value = value(v); if (old_value == new_value) continue; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); SASSERT(!vj.is_null()); if (!s.lp().is_base(vj.index())) { - lp::impq val(new_value); + rational new_value_(new_value, rational::i64()); + lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); } } @@ -120,31 +123,31 @@ namespace arith { } // distance to true - rational sls::dtt(rational const& args, ineq const& ineq) const { + int64_t sls::dtt(int64_t args, ineq const& ineq) const { switch (ineq.m_op) { case ineq_kind::LE: if (args <= ineq.m_bound) - return rational::zero(); + return 0; return args - ineq.m_bound; case ineq_kind::EQ: if (args == ineq.m_bound) - return rational::zero(); - return rational::one(); + return 0; + return 1; case ineq_kind::NE: if (args == ineq.m_bound) - return rational::one(); - return rational::zero(); + return 1; + return 0; case ineq_kind::LT: if (args < ineq.m_bound) - return rational::zero(); + return 0; return args - ineq.m_bound + 1; default: UNREACHABLE(); - return rational::zero(); + return 0; } } - rational sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const { + int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { auto new_args_value = ineq.m_args_value; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { @@ -156,18 +159,18 @@ namespace arith { } // critical move - bool sls::cm(ineq const& ineq, var_t v, rational& new_value) { + bool sls::cm(ineq const& ineq, var_t v, int64_t& new_value) { SASSERT(!ineq.is_true()); - auto delta = ineq.m_args_value - ineq.m_bound; + int64_t delta = ineq.m_args_value - ineq.m_bound; if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) delta--; for (auto const& [coeff, w] : ineq.m_args) { if (w == v) { if (coeff > 0) - new_value = value(v) - abs(ceil(delta / coeff)); + new_value = value(v) - abs((delta + coeff - 1)/ coeff); else - new_value = value(v) + abs(floor(delta / coeff)); + new_value = value(v) + abs(delta) / -coeff; switch (ineq.m_op) { case ineq_kind::LE: @@ -199,7 +202,7 @@ namespace arith { bool sls::flip(unsigned cl) { auto const& clause = get_clause(cl); - rational new_value; + int64_t new_value; for (literal lit : clause) { if (is_true(lit)) continue; @@ -246,7 +249,7 @@ namespace arith { bool sls::flip_dscore(unsigned cl) { auto const& clause = get_clause(cl); - rational new_value, min_value, min_score(-1); + int64_t new_value, min_value, min_score(-1); var_t min_var = UINT_MAX; for (auto lit : clause) { auto const* ineq = atom(lit); @@ -254,7 +257,7 @@ namespace arith { continue; for (auto const& [coeff, v] : ineq->m_args) { if (cm(*ineq, v, new_value)) { - rational score = dscore(v, new_value); + int64_t score = dscore(v, new_value); if (UINT_MAX == min_var || score < min_score) { min_var = v; min_value = new_value; @@ -290,22 +293,22 @@ namespace arith { // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - rational sls::dscore(var_t v, rational const& new_value) const { + int64_t sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; - rational score(0); + int64_t score(0); for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * int64_t(get_weight(cl)); return score; } - int sls::cm_score(var_t v, rational const& new_value) { + int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; for (auto const& [coeff, lit] : vi.m_literals) { auto const& ineq = *atom(lit); - rational dtt_old = dtt(ineq); - rational dtt_new = dtt(ineq, v, new_value); + int64_t dtt_old = dtt(ineq); + int64_t dtt_new = dtt(ineq, v, new_value); for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { @@ -322,8 +325,8 @@ namespace arith { return score; } - rational sls::compute_dts(unsigned cl) const { - rational d(1), d2; + int64_t sls::compute_dts(unsigned cl) const { + int64_t d(1), d2; bool first = true; for (auto a : get_clause(cl)) { auto const* ineq = atom(a); @@ -340,8 +343,8 @@ namespace arith { return d; } - rational sls::dts(unsigned cl, var_t v, rational const& new_value) const { - rational d(1), d2; + int64_t sls::dts(unsigned cl, var_t v, int64_t new_value) const { + int64_t d(1), d2; bool first = true; for (auto lit : get_clause(cl)) { auto const* ineq = atom(lit); @@ -358,28 +361,17 @@ namespace arith { return d; } - void sls::update(var_t v, rational const& new_value) { + void sls::update(var_t v, int64_t new_value) { auto& vi = m_vars[v]; auto const& old_value = vi.m_value; for (auto const& [coeff, lit] : vi.m_literals) { auto& ineq = *atom(lit); - rational dtt_old = dtt(ineq); ineq.m_args_value += coeff * (new_value - old_value); - rational dtt_new = dtt(ineq); - if ((dtt_new == 0) == is_true(lit)) { - dtt(ineq) = dtt_new; - continue; - } - VERIFY((dtt_old == 0) == is_true(lit)); - VERIFY(!(dtt_new == 0 && dtt_new < dtt_old) || !is_true(lit)); - VERIFY(!(dtt_old == 0 && dtt_new > dtt_old) || is_true(lit)); - if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true - m_bool_search->flip(lit.var()); - else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false - m_bool_search->flip(lit.var()); - dtt(ineq) = dtt_new; - - VERIFY((dtt_new == 0) == is_true(lit)); + int64_t dtt_new = dtt(ineq); + if ((dtt_new == 0) != is_true(lit)) + m_bool_search->flip(lit.var()); + + SASSERT((dtt_new == 0) == is_true(lit)); } vi.m_value = new_value; } @@ -387,21 +379,20 @@ namespace arith { void sls::add_vars() { SASSERT(m_vars.empty()); for (unsigned v = 0; v < s.get_num_vars(); ++v) { - rational value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero(); - value = s.is_int(v) ? ceil(value) : value; + int64_t value = s.is_registered_var(v) ? to_numeral(s.get_ivalue(v).x) : 0; auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL; m_vars.push_back({ value, value, k, {} }); } } - sls::ineq& sls::new_ineq(ineq_kind op, rational const& bound) { + sls::ineq& sls::new_ineq(ineq_kind op, int64_t const& bound) { auto* i = alloc(ineq); i->m_bound = bound; i->m_op = op; return *i; } - void sls::add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v) { + void sls::add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v) { ineq.m_args.push_back({ c, v }); ineq.m_args_value += c * value(v); m_vars[v].m_literals.push_back({ c, lit }); @@ -426,36 +417,42 @@ namespace arith { bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); if (has_lo && has_hi && lo == hi) { - auto& ineq = new_ineq(sls::ineq_kind::EQ, lo); + auto& ineq = new_ineq(sls::ineq_kind::EQ, to_numeral(lo)); sat::literal lit(bvars++); - add_arg(lit, ineq, rational::one(), v); + add_arg(lit, ineq, 1, v); add_ineq(lit, ineq); continue; } if (has_lo) { - auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, -lo); + auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(-lo)); sat::literal lit(bvars++); - add_arg(lit, ineq, -rational::one(), v); + add_arg(lit, ineq, -1, v); add_ineq(lit, ineq); } if (has_hi) { - auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, hi); + auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(hi)); sat::literal lit(bvars++); - add_arg(lit, ineq, rational::one(), v); + add_arg(lit, ineq, 1, v); add_ineq(lit, ineq); } } } + int64_t sls::to_numeral(rational const& r) { + if (r.is_int64()) + return r.get_int64(); + return 0; + } - void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, rational sign) { + + void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - add_arg(lit, ineq, sign * arg.coeff(), w); + add_arg(lit, ineq, sign * to_numeral(arg.coeff()), w); } } else @@ -489,9 +486,9 @@ namespace arith { } if (should_minus) bound.neg(); - auto& ineq = new_ineq(op, bound); + auto& ineq = new_ineq(op, to_numeral(bound)); - add_args(lit, ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one()); + add_args(lit, ineq, t, b->get_var(), should_minus ? -1 : 1); m_literals.set(lit.index(), &ineq); return; } @@ -503,9 +500,9 @@ namespace arith { theory_var v = s.get_th_var(r); lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); - auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero()); - add_args(lit, ineq, tu, u, rational::one()); - add_args(lit, ineq, tv, v, -rational::one()); + auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, 0); + add_args(lit, ineq, tu, u, 1); + add_args(lit, ineq, tv, v, -1); m_literals.set(lit.index(), &ineq); return; } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 9a4cfcd81..9682376be 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -58,10 +58,10 @@ namespace arith { public: // encode args <= bound, args = bound, args < bound struct ineq { - vector> m_args; + vector> m_args; ineq_kind m_op = ineq_kind::LE; - rational m_bound; - rational m_args_value; + int64_t m_bound; + int64_t m_args_value; bool is_true() const { switch (m_op) { @@ -94,15 +94,15 @@ namespace arith { private: struct var_info { - rational m_value; - rational m_best_value; + int64_t m_value; + int64_t m_best_value; var_kind m_kind = var_kind::INT; - vector> m_literals; + vector> m_literals; }; struct clause { unsigned m_weight = 1; - rational m_dts = rational::one(); + int64_t m_dts = 1; }; solver& s; @@ -137,27 +137,29 @@ namespace arith { bool flip_dscore(); bool flip_dscore(unsigned cl); bool flip(unsigned cl); - rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - rational dtt(rational const& args, ineq const& ineq) const; - rational dtt(ineq const& ineq, var_t v, rational const& new_value) const; - rational dts(unsigned cl, var_t v, rational const& new_value) const; - rational compute_dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, rational& new_value); - int cm_score(var_t v, rational const& new_value); - void update(var_t v, rational const& new_value); + int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } + int64_t dtt(int64_t args, ineq const& ineq) const; + int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; + int64_t dts(unsigned cl, var_t v, int64_t new_value) const; + int64_t compute_dts(unsigned cl) const; + bool cm(ineq const& ineq, var_t v, int64_t& new_value); + int cm_score(var_t v, int64_t new_value); + void update(var_t v, int64_t new_value); void paws(); - rational dscore(var_t v, rational const& new_value) const; + int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); void add_vars(); - sls::ineq& new_ineq(ineq_kind op, rational const& bound); - void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v); + sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); + void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); void add_bounds(sat::literal_vector& bounds); - void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, rational sign); + void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); void init_literal_assignment(sat::literal lit); - rational value(var_t v) const { return m_vars[v].m_value; } + int64_t value(var_t v) const { return m_vars[v].m_value; } + int64_t to_numeral(rational const& r); + public: sls(solver& s); lbool operator ()(bool_vector& phase); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index ec03849bc..411b8aa6e 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -141,7 +141,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: - case OP_AND: + // case OP_AND: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -188,7 +188,7 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: - case OP_AND: + // case OP_AND: visited = false; push_frame(to_app(n)); return; @@ -202,6 +202,7 @@ class tseitin_cnf_tactic : public tactic { case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: + case OP_AND: throw_op_not_handled(); default: return; From bb81bc5452f9c93a1044544b8b4d6034b22177ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:21:53 -0800 Subject: [PATCH 023/220] fix #6580 Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_sls.cpp | 2 +- src/smt/theory_lra.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index d7a85cdb1..7afabe247 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -62,7 +62,7 @@ namespace arith { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { - int64_t val; + int64_t val = 0; lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c5b706fde..d26ea516a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1596,8 +1596,12 @@ public: final_check_status eval_power(expr* e) { expr* x, * y; + rational r; VERIFY(a.is_power(e, x, y)); - + if (a.is_numeral(x, r) && r == 0 && a.is_numeral(y, r) && r == 0) + return FC_DONE; + if (!m_nla) + return FC_GIVEUP; switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { case l_true: return FC_DONE; From 7956cf120147b4b8cdfb74851196d1026c8c57ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Feb 2023 20:55:44 -0800 Subject: [PATCH 024/220] annotate arith_sls --- src/sat/sat_ddfw.h | 1 + src/sat/smt/arith_sls.cpp | 13 +++++++++++++ src/sat/smt/euf_local_search.cpp | 7 ++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 9971033e3..ee1c73b6e 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -52,6 +52,7 @@ namespace sat { p(p), i(lit.index()) {} unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; } unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; } + unsigned size() const { return p.m_use_list_index[i + 1] - p.m_use_list_index[i]; } }; protected: diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 7afabe247..809295cca 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -90,6 +90,7 @@ namespace arith { rational new_value_(new_value, rational::i64()); lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); + // TODO - figure out why this leads to unsound (unsat). } } } @@ -147,6 +148,11 @@ namespace arith { } } + // + // dtt is high overhead. It walks ineq.m_args + // m_vars[w].m_value can be computed outside and shared among calls + // different data-structures for storing coefficients + // int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { auto new_args_value = ineq.m_args_value; for (auto const& [coeff, w] : ineq.m_args) { @@ -302,6 +308,12 @@ namespace arith { return score; } + // + // cm_score is costly. It involves several cache misses. + // Note that + // - m_bool_search->get_use_list(lit).size() is "often" 1 or 2 + // - dtt_old can be saved + // int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; @@ -309,6 +321,7 @@ namespace arith { auto const& ineq = *atom(lit); int64_t dtt_old = dtt(ineq); int64_t dtt_new = dtt(ineq, v, new_value); + for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); if (!clause.is_true()) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index fab3b7815..5357c0837 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -81,11 +81,8 @@ namespace euf { return; } euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); - for (auto const& thv : enode_th_vars(n)) { - auto* th = m_id2solver.get(thv.get_id(), nullptr); - if (th) - th->set_bounds(n); - } + for (auto* s : m_solvers) + s->set_bounds(n); }; for (auto cl : bool_search.unsat_set()) { From 52804b5c8f1c67bf6b699d17bb33b2424e2832d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 08:29:32 -0800 Subject: [PATCH 025/220] save on dtt Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_sls.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 809295cca..34d4ac84e 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -317,10 +317,12 @@ namespace arith { int sls::cm_score(var_t v, int64_t new_value) { int score = 0; auto& vi = m_vars[v]; + int64_t old_value = vi.m_value; for (auto const& [coeff, lit] : vi.m_literals) { auto const& ineq = *atom(lit); int64_t dtt_old = dtt(ineq); - int64_t dtt_new = dtt(ineq, v, new_value); + int64_t delta = coeff * (new_value - old_value); + int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); @@ -331,7 +333,7 @@ namespace arith { } else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; - else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO + else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || lit == lit2 || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO --score; } } From 2b7701299371a72c66aa2bab739dca45db9a19bb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 08:36:12 -0800 Subject: [PATCH 026/220] fix build --- src/sat/smt/arith_sls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 34d4ac84e..8183fe1ba 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -333,7 +333,7 @@ namespace arith { } else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals continue; - else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || lit == lit2 || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO + else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO --score; } } From 3dc91de531dd835b723b5d572f5024a491a966cf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Feb 2023 13:20:55 -0800 Subject: [PATCH 027/220] fix #6582 --- src/math/lp/nla_core.cpp | 7 +++++++ src/math/lp/nla_core.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 386ad296d..4d1cc6edb 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1745,6 +1745,13 @@ bool core::influences_nl_var(lpvar j) const { return false; } +void core::set_use_nra_model(bool m) { + if (m != m_use_nra_model) { + trail().push(value_trail(m_use_nra_model)); + m_use_nra_model = m; + } +} + void core::collect_statistics(::statistics & st) { st.update("arith-nla-explanations", m_stats.m_nla_explanations); st.update("arith-nla-lemmas", m_stats.m_nla_lemmas); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 016ff6e9d..938bcbe83 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -418,7 +418,7 @@ public: bool var_is_big(lpvar) const; bool has_real(const factorization&) const; bool has_real(const monic& m) const; - void set_use_nra_model(bool m) { m_use_nra_model = m; } + void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); private: From 9ce5fe707d5d6444843d1647c969f18ccb614d74 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 11:09:11 -0800 Subject: [PATCH 028/220] track assumptions when parsing into a solver. This enables solver.from_file/solver.from_string to support assumptions/cores #6587 Signed-off-by: Nikolaj Bjorner --- src/api/api_parsers.cpp | 10 +++++++--- src/api/api_solver.cpp | 7 +++++-- src/cmd_context/cmd_context.cpp | 11 ++++------- src/cmd_context/cmd_context.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 899750ef7..94a955d4b 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -128,7 +128,8 @@ extern "C" { static Z3_ast_vector Z3_parser_context_parse_stream(Z3_context c, scoped_ptr& ctx, bool owned, std::istream& is) { Z3_TRY; - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + ast_manager& m = mk_c(c)->m(); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); std::stringstream errstrm; ctx->set_regular_stream(errstrm); @@ -147,8 +148,11 @@ extern "C" { SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str()); return of_ast_vector(v); } - for (expr* e : ctx->tracked_assertions()) - v->m_ast_vector.push_back(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + v->m_ast_vector.push_back(m.mk_implies(an, asr)); + else + v->m_ast_vector.push_back(asr); ctx->reset_tracked_assertions(); return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ca39329bb..2c19d0d9e 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -314,8 +314,11 @@ extern "C" { bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); - for (expr* e : ctx->tracked_assertions()) - to_solver(s)->assert_expr(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + to_solver(s)->assert_expr(asr, an); + else + to_solver(s)->assert_expr(asr); ctx->reset_tracked_assertions(); to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); auto* ctx_s = ctx->get_solver(); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 89733a7ea..1d3476b3d 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -2201,21 +2201,18 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { } -expr_ref_vector cmd_context::tracked_assertions() { - expr_ref_vector result(m()); +vector> cmd_context::tracked_assertions() { + vector> result; if (assertion_names().size() == assertions().size()) { for (unsigned i = 0; i < assertions().size(); ++i) { expr* an = assertion_names()[i]; expr* asr = assertions()[i]; - if (an) - result.push_back(m().mk_implies(an, asr)); - else - result.push_back(asr); + result.push_back({ asr, an }); } } else { for (expr * e : assertions()) - result.push_back(e); + result.push_back({ e, nullptr}); } return result; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index f5a247794..b034a9ffc 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -523,7 +523,7 @@ public: ptr_vector const& assertions() const { return m_assertions; } ptr_vector const& assertion_names() const { return m_assertion_names; } - expr_ref_vector tracked_assertions(); + vector> tracked_assertions(); void reset_tracked_assertions(); /** From 44fcf60a72342e0580e92ed70a4d2f528a50b520 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 15:06:21 -0800 Subject: [PATCH 029/220] wip experiments with sls --- src/sat/smt/arith_sls.cpp | 19 +++++++++++++++---- src/sat/smt/arith_sls.h | 1 + src/sat/smt/euf_local_search.cpp | 14 +++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 8183fe1ba..6cecb4dbb 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -35,9 +35,8 @@ namespace arith { unsigned num_steps = 0; for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - m_best_min_unsat = unsat().size(); verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; - //m_max_arith_steps = 10000; + m_max_arith_steps = std::max(1000u, m_max_arith_steps); while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { if (!flip()) break; @@ -50,6 +49,7 @@ namespace arith { save_best_values(); } } + store_best_values(); log(); return unsat().empty() ? l_true : l_undef; } @@ -59,6 +59,11 @@ namespace arith { } void sls::save_best_values() { + for (unsigned v = 0; v < s.get_num_vars(); ++v) + m_vars[v].m_best_value = m_vars[v].m_value; + } + + void sls::store_best_values() { // first compute assignment to terms // then update non-basic variables in tableau. for (auto const& [t, v] : m_terms) { @@ -80,7 +85,7 @@ namespace arith { int64_t old_value = 0; if (s.is_registered_var(v)) old_value = to_numeral(s.get_ivalue(v).x); - int64_t new_value = value(v); + int64_t new_value = m_vars[v].m_best_value; if (old_value == new_value) continue; s.ensure_column(v); @@ -104,6 +109,9 @@ namespace arith { for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) init_literal(lit); + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + m_best_min_unsat = std::numeric_limits::max(); } void sls::set_bounds_begin() { @@ -115,7 +123,7 @@ namespace arith { } void sls::set_bounds_end(unsigned num_literals) { - m_max_arith_steps = (m_config.L * m_max_arith_steps) / num_literals; + m_max_arith_steps = (m_config.L * m_max_arith_steps); // / num_literals; } bool sls::flip() { @@ -323,6 +331,9 @@ namespace arith { int64_t dtt_old = dtt(ineq); int64_t delta = coeff * (new_value - old_value); int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); + + if (dtt_old == dtt_new) + continue; for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 9682376be..706fff1dd 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -148,6 +148,7 @@ namespace arith { void paws(); int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); + void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 5357c0837..0572afc39 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -40,9 +40,11 @@ namespace euf { // Non-boolean literals are assumptions to Boolean search literal_vector assumptions; +#if 0 for (unsigned v = 0; v < phase.size(); ++v) if (!is_propositional(literal(v))) assumptions.push_back(literal(v, !bool_search.get_value(v))); +#endif verbose_stream() << "assumptions " << assumptions.size() << "\n"; @@ -51,6 +53,14 @@ namespace euf { lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); bool_search.rlimit().pop(); +#if 0 + // restore state to optimal model + auto const& mdl = bool_search.get_model(); + for (unsigned i = 0; i < mdl.size(); ++i) + if ((mdl[i] == l_true) != bool_search.get_value(i)) + bool_search.flip(i); +#endif + for (auto* th : m_solvers) th->local_search(phase); @@ -92,7 +102,9 @@ namespace euf { count_literal(l); } - m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals; + m_max_bool_steps = (m_ls_config.L * num_bool); // / num_literals; + m_max_bool_steps = std::max(10000u, m_max_bool_steps); + verbose_stream() << "num literals " << num_literals << " num bool " << num_bool << " max bool steps " << m_max_bool_steps << "\n"; for (auto* th : m_solvers) th->set_bounds_end(num_literals); From a976b781a04e26abf763b2f8029c98a7b0a294b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 15:33:17 -0800 Subject: [PATCH 030/220] fix #6585 --- src/math/lp/emonics.cpp | 2 +- src/math/lp/emonics.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index 21bfe6792..bcdb81dd8 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -68,8 +68,8 @@ void emonics::pop(unsigned n) { TRACE("nla_solver_mons", tout << "pop: " << n << "\n";); SASSERT(invariant()); for (unsigned i = 0; i < n; ++i) { - m_u_f_stack.pop_scope(1); m_ve.pop(1); + m_u_f_stack.pop_scope(1); } SASSERT(invariant()); SASSERT(monics_are_canonized()); diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index d26d96ad6..e4f4f4848 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -81,8 +81,8 @@ class emonics { } }; - union_find m_u_f; trail_stack m_u_f_stack; + union_find m_u_f; mutable svector m_find_key; // the key used when looking for a monic with the specific variables var_eqs& m_ve; mutable vector m_monics; // set of monics @@ -125,8 +125,8 @@ public: other calls to push/pop to the var_eqs should take place. */ emonics(var_eqs& ve): - m_u_f(*this), m_u_f_stack(), + m_u_f(*this), m_ve(ve), m_visited(0), m_cg_hash(*this), From c2fe76569fdb118101e8131f982cc5bfd7cc4438 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Feb 2023 17:48:02 -0800 Subject: [PATCH 031/220] remove dependency on bool-rewriter in hoist rewriter deal with regression reported in https://github.com/Z3Prover/z3/commit/cac5052685eedb9dca304b623616fc9b8659bf58#commitcomment-100606067 and unit tests doc.cpp --- src/ast/rewriter/bool_rewriter.cpp | 1 + src/ast/rewriter/bool_rewriter.h | 1 - src/ast/rewriter/hoist_rewriter.cpp | 25 ++++++++++++++++--------- src/ast/rewriter/hoist_rewriter.h | 4 ++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 392b2e681..378c794cd 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -32,6 +32,7 @@ void bool_rewriter::updt_params(params_ref const & _p) { m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); + m_hoist.set_elim_and(m_elim_and); } void bool_rewriter::get_param_descrs(param_descrs & r) { diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 488949084..e02bf86d3 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -83,7 +83,6 @@ class bool_rewriter { public: bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { updt_params(p); - m_hoist.set(*this); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 2329fd527..d3f5af2b5 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -28,17 +28,23 @@ hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): } expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { - if (m_rewriter) - return m_rewriter->mk_and(args); + if (m_elim_and) { + expr_ref_vector negs(m); + for (expr* a : args) + if (m.is_false(a)) + return expr_ref(m.mk_false(), m); + else if (m.is_true(a)) + continue; + else + negs.push_back(::mk_not(m, a)); + return expr_ref(::mk_not(m, mk_or(negs)), m); + } else return ::mk_and(args); } expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { - if (false && m_rewriter) - return m_rewriter->mk_or(args); - else - return ::mk_or(args); + return ::mk_or(args); } br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { @@ -159,11 +165,11 @@ unsigned hoist_rewriter::mk_var(expr* e) { expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsigned num_args, expr* const* es) { expr_ref result(m); - expr_ref_vector args(m), fmls(m); + expr_ref_vector args(m), args1(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { - VERIFY(is_and(es[i], &m_args1)); + VERIFY(is_and(es[i], &args1)); fmls.reset(); - for (expr* e : m_args1) + for (expr* e : args1) if (!preds.contains(e)) fmls.push_back(e); args.push_back(mk_and(fmls)); @@ -199,6 +205,7 @@ bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { args->reset(); for (expr* arg : *to_app(e)) args->push_back(::mk_not(m, arg)); + TRACE("hoist", tout << args << " " << * args << "\n"); } return true; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index 72d44d0bc..ae7dff3bc 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -29,7 +29,6 @@ class bool_rewriter; class hoist_rewriter { ast_manager & m; - bool_rewriter* m_rewriter = nullptr; expr_ref_vector m_args1, m_args2; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; @@ -40,6 +39,7 @@ class hoist_rewriter { obj_map m_expr2var; ptr_vector m_var2expr; expr_mark m_mark; + bool m_elim_and = false; bool is_and(expr* e, expr_ref_vector* args); expr_ref mk_and(expr_ref_vector const& args); @@ -62,7 +62,7 @@ public: static void get_param_descrs(param_descrs & r) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); - void set(bool_rewriter& r) { m_rewriter = &r; } + void set_elim_and(bool b) { m_elim_and = b; } }; struct hoist_rewriter_cfg : public default_rewriter_cfg { From 8ce0c56ff5580e199c7c940b49419e792582786b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:36:01 -0800 Subject: [PATCH 032/220] fix #6590 --- src/math/lp/nla_powers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 3af31a488..96284f26a 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -81,6 +81,8 @@ namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) + return l_undef; core& c = m_core; if (c.use_nra_model()) From 4f20b8e2ba92de6d78fcbe0806f70a07dd63fdaf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:36:10 -0800 Subject: [PATCH 033/220] wip - local search --- src/sat/sat_ddfw.cpp | 105 +++++++++++++++++++++++++++++-------------- src/sat/sat_ddfw.h | 49 +++++++++++++++++--- 2 files changed, 114 insertions(+), 40 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 747ea4940..478e793e3 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -9,7 +9,7 @@ DDFW Local search module for clauses - Author: + Author: Nikolaj Bjorner, Marijn Heule 2019-4-23 @@ -33,27 +33,45 @@ namespace sat { ddfw::~ddfw() { - for (auto& ci : m_clauses) { - m_alloc.del_clause(ci.m_clause); - } + for (auto& ci : m_clauses) + m_alloc.del_clause(ci.m_clause); } - lbool ddfw::check(unsigned sz, literal const* assumptions, parallel* p) { init(sz, assumptions); flet _p(m_par, p); - while (m_limit.inc() && m_min_sz > 0) { - if (should_reinit_weights()) do_reinit_weights(); - else if (do_flip()) ; - else if (should_restart()) do_restart(); - else if (should_parallel_sync()) do_parallel_sync(); - else shift_weights(); - } + if (m_plugin) + check_with_plugin(); + else + check_without_plugin(); remove_assumptions(); log(); return m_min_sz == 0 ? l_true : l_undef; } + void ddfw::check_without_plugin() { + while (m_limit.inc() && m_min_sz > 0) { + if (should_reinit_weights()) do_reinit_weights(); + else if (do_flip()); + else if (should_restart()) do_restart(); + else if (should_parallel_sync()) do_parallel_sync(); + else shift_weights(); + } + } + + void ddfw::check_with_plugin() { + m_plugin->init_search(); + while (m_limit.inc() && m_min_sz > 0) { + if (should_reinit_weights()) do_reinit_weights(); + else if (do_flip()); + else if (do_literal_flip()); + else if (should_restart()) do_restart(), m_plugin->on_restart(); + else if (should_parallel_sync()) do_parallel_sync(); + else shift_weights(), m_plugin->on_rescale(); + } + m_plugin->finish_search(); + } + void ddfw::log() { double sec = m_stopwatch.get_current_seconds(); double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec); @@ -77,55 +95,72 @@ namespace sat { m_last_flips = m_flips; } + template bool ddfw::do_flip() { - bool_var v = pick_var(); + bool_var v = pick_var(); + return apply_flip(v); + } + + template + bool ddfw::apply_flip(bool_var v) { + if (v == null_bool_var) + return false; if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { - flip(v); - if (m_unsat.size() <= m_min_sz) save_best_values(); + if (uses_plugin) + m_plugin->flip(v); + else + flip(v); + if (m_unsat.size() <= m_min_sz) + save_best_values(); return true; } return false; } + template bool_var ddfw::pick_var() { double sum_pos = 0; unsigned n = 1; + double r; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { - double r = reward(v); - if (r > 0.0) { - sum_pos += score(r); - } - else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { - v0 = v; - } + r = uses_plugin ? plugin_reward(v) : reward(v); + if (r > 0.0) + sum_pos += score(r); + else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) + v0 = v; } if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { - double r = reward(v); + r = uses_plugin ? plugin_reward(v) : reward(v); if (r > 0) { lim_pos -= score(r); - if (lim_pos <= 0) { - return v; - } + if (lim_pos <= 0) + return v; } } } - if (v0 != null_bool_var) { + if (v0 != null_bool_var) return v0; - } + if (m_unsat_vars.empty()) + return 0; return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size())); } - /** - * TBD: map reward value to a score, possibly through an exponential function, such as - * exp(-tau/r), where tau > 0 - */ - double ddfw::mk_score(double r) { - return r; + template + bool ddfw::do_literal_flip() { + return apply_flip(pick_literal_var()); } + /* + * Pick a random false literal from a satisfied clause such that + * the literal has zero break count and positive reward. + */ + template + bool_var ddfw::pick_literal_var() { + return null_bool_var; + } void ddfw::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); @@ -409,6 +444,8 @@ namespace sat { for (unsigned i = 0; i < num_vars(); ++i) m_model[i] = to_lbool(value(i)); save_priorities(); + if (m_plugin) + m_plugin->on_save_model(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ee1c73b6e..5d56c3adc 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -31,6 +31,17 @@ namespace sat { class solver; class parallel; + class local_search_plugin { + public: + virtual void init_search() = 0; + virtual void finish_search() = 0; + virtual void flip(bool_var v) = 0; + virtual double reward(bool_var v) = 0; + virtual void on_rescale() = 0; + virtual void on_save_model() = 0; + virtual void on_restart() = 0; + }; + class ddfw : public i_local_search { public: struct clause_info { @@ -83,6 +94,7 @@ namespace sat { double m_reward = 0; unsigned m_make_count = 0; int m_bias = 0; + bool m_external = false; ema m_reward_avg = 1e-5; }; @@ -113,14 +125,15 @@ namespace sat { stopwatch m_stopwatch; parallel* m_par; - - + scoped_ptr< local_search_plugin> m_plugin; void flatten_use_list(); - double mk_score(double r); - - inline double score(double r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } + /** + * TBD: map reward value to a score, possibly through an exponential function, such as + * exp(-tau/r), where tau > 0 + */ + inline double score(double r) { return r; } inline unsigned num_vars() const { return m_vars.size(); } @@ -134,6 +147,12 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } + inline double plugin_reward(bool_var v) const { return m_plugin->reward(v); } + + void set_external(bool_var v) { m_vars[v].m_external = true; } + + inline bool is_external(bool_var v) const { return m_vars[v].m_external; } + inline int& bias(bool_var v) { return m_vars[v].m_bias; } unsigned value_hash() const; @@ -164,9 +183,25 @@ namespace sat { inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; } + void check_with_plugin(); + void check_without_plugin(); + // flip activity + template bool do_flip(); - bool_var pick_var(); + + template + bool_var pick_var(); + + template + bool apply_flip(bool_var v); + + template + bool do_literal_flip(); + + template + bool_var pick_literal_var(); + void save_best_values(); void save_model(); void save_priorities(); @@ -215,6 +250,8 @@ namespace sat { ~ddfw() override; + void set(local_search_plugin* p) { m_plugin = p; } + lbool check(unsigned sz, literal const* assumptions, parallel* p) override; void updt_params(params_ref const& p) override; From a1f73d3805dfa2f32a8d30b36662ab02bb0ebfc8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 08:48:37 -0800 Subject: [PATCH 034/220] wip - local search - fix build --- src/sat/sat_ddfw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 5d56c3adc..a681d75f5 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -33,6 +33,7 @@ namespace sat { class local_search_plugin { public: + virtual ~local_search_plugin() {} virtual void init_search() = 0; virtual void finish_search() = 0; virtual void flip(bool_var v) = 0; From c1ecc49021c53072d65aba54c2f9cc9ca8d8a284 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 13:32:30 -0800 Subject: [PATCH 035/220] wip - local search - move to plugin model --- src/sat/sat_ddfw.h | 9 +- src/sat/sat_solver/sat_smt_setup.h | 90 ++++++++++++++++++++ src/sat/smt/arith_sls.cpp | 127 ++++++++++++++++++++++++----- src/sat/smt/arith_sls.h | 21 ++++- src/sat/smt/euf_local_search.cpp | 37 +-------- 5 files changed, 225 insertions(+), 59 deletions(-) create mode 100644 src/sat/sat_solver/sat_smt_setup.h diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index a681d75f5..fc03f8ba2 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -27,6 +27,10 @@ #include "sat/sat_clause.h" #include "sat/sat_types.h" +namespace arith { + class sls; +} + namespace sat { class solver; class parallel; @@ -44,6 +48,7 @@ namespace sat { }; class ddfw : public i_local_search { + friend class arith::sls; public: struct clause_info { clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {} @@ -126,7 +131,7 @@ namespace sat { stopwatch m_stopwatch; parallel* m_par; - scoped_ptr< local_search_plugin> m_plugin; + local_search_plugin* m_plugin = nullptr; void flatten_use_list(); @@ -148,7 +153,7 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } - inline double plugin_reward(bool_var v) const { return m_plugin->reward(v); } + inline double plugin_reward(bool_var v) const { return is_external(v) ? m_plugin->reward(v) : reward(v); } void set_external(bool_var v) { m_vars[v].m_external = true; } diff --git a/src/sat/sat_solver/sat_smt_setup.h b/src/sat/sat_solver/sat_smt_setup.h new file mode 100644 index 000000000..c78eb72c8 --- /dev/null +++ b/src/sat/sat_solver/sat_smt_setup.h @@ -0,0 +1,90 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + sat_smt_setup.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-17 + +--*/ +#pragma once + +#include "ast/ast.h" +#include "smt/params/smt_params.h" +#include "sat/sat_config.h" +#include "ast/simplifiers/dependent_expr_state.h" + +struct static_features; + +namespace sat_smt { + + void setup_sat_config(smt_params const& p, sat::config& config); + + class setup { + ast_manager& m; + dependent_expr_state& m_st; + smt_params& m_params; + symbol m_logic; + bool m_already_configured = false; + + void setup_auto_config(); + void setup_default(); + // + // setup_() methods do not depend on static features of the formula. So, they are safe to use + // even in an incremental setting. + // + // setup_(static_features & st) can only be used if the logical context will perform a single + // check. + // + void setup_QF_DT(); + void setup_QF_UF(); + void setup_QF_UF(static_features const & st); + void setup_QF_RDL(); + void setup_QF_RDL(static_features & st); + void setup_QF_IDL(); + void setup_QF_IDL(static_features & st); + void setup_QF_UFIDL(); + void setup_QF_UFIDL(static_features & st); + void setup_QF_LRA(); + void setup_QF_LRA(static_features const & st); + void setup_QF_LIA(); + void setup_QF_LIRA(static_features const& st); + void setup_QF_LIA(static_features const & st); + void setup_QF_UFLIA(); + void setup_QF_UFLIA(static_features & st); + void setup_QF_UFLRA(); + void setup_QF_BV(); + void setup_QF_AUFBV(); + void setup_QF_AX(); + void setup_QF_AX(static_features const & st); + void setup_QF_AUFLIA(); + void setup_QF_AUFLIA(static_features const & st); + void setup_QF_FP(); + void setup_QF_FPBV(); + void setup_QF_S(); + void setup_LRA(); + void setup_CSP(); + void setup_AUFLIA(bool simple_array = true); + void setup_AUFLIA(static_features const & st); + void setup_AUFLIRA(bool simple_array = true); + void setup_UFNIA(); + void setup_UFLRA(); + void setup_AUFLIAp(); + void setup_AUFNIRA(); + void setup_QF_BVRE(); + void setup_unknown(); + void setup_unknown(static_features & st); + + public: + setup(ast_manager& m, dependent_expr_state& st, smt_params & params); + void setk_already_configured() { m_already_configured = true; } + bool already_configured() const { return m_already_configured; } + symbol const & get_logic() const { return m_logic; } + void operator()(); + }; +}; + + diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 6cecb4dbb..3d4522e76 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2020 Microsoft Corporation +Copyright (c) 2023 Microsoft Corporation Module Name: @@ -112,6 +112,8 @@ namespace arith { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); m_best_min_unsat = std::numeric_limits::max(); + + d->set(this); } void sls::set_bounds_begin() { @@ -209,14 +211,13 @@ namespace arith { unsigned start = s.random(); unsigned sz = unsat().size(); for (unsigned i = sz; i-- > 0; ) - if (flip(unsat().elem_at((i + start) % sz))) + if (flip_clause(unsat().elem_at((i + start) % sz))) return true; return false; } - bool sls::flip(unsigned cl) { + bool sls::flip_clause(unsigned cl) { auto const& clause = get_clause(cl); - int64_t new_value; for (literal lit : clause) { if (is_true(lit)) continue; @@ -224,20 +225,32 @@ namespace arith { if (!ineq) continue; SASSERT(!ineq->is_true()); - for (auto const& [coeff, v] : ineq->m_args) { - if (!cm(*ineq, v, new_value)) - continue; - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(2, - verbose_stream() << "v" << v << " score " << score << " " - << num_unsat << " -> " << unsat().size() << "\n"); - SASSERT(num_unsat > unsat().size()); + if (flip(*ineq)) return true; - } + + } + return false; + } + + // flip on the first positive score + // it could be changed to flip on maximal positive score + // or flip on maximal non-negative score + // or flip on first non-negative score + bool sls::flip(ineq const& ineq) { + int64_t new_value; + for (auto const& [coeff, v] : ineq.m_args) { + if (!cm(ineq, v, new_value)) + continue; + int score = cm_score(v, new_value); + if (score <= 0) + continue; + unsigned num_unsat = unsat().size(); + update(v, new_value); + IF_VERBOSE(2, + verbose_stream() << "v" << v << " score " << score << " " + << num_unsat << " -> " << unsat().size() << "\n"); + SASSERT(num_unsat > unsat().size()); + return true; } return false; } @@ -246,7 +259,7 @@ namespace arith { unsigned start = s.random(); unsigned sz = m_bool_search->num_clauses(); for (unsigned i = sz; i-- > 0; ) - if (flip((i + start) % sz)) + if (flip_clause((i + start) % sz)) return true; return false; } @@ -541,9 +554,85 @@ namespace arith { void sls::init_literal_assignment(sat::literal lit) { auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && is_true(lit) != (dtt(*ineq) == 0)) m_bool_search->flip(lit.var()); } + + void sls::init_search() { + on_restart(); + } + + void sls::finish_search() { + store_best_values(); + } + + void sls::flip(sat::bool_var v) { + sat::literal lit(v, m_bool_search->get_value(v)); + SASSERT(!is_true(lit)); + auto const* ineq = atom(lit); + if (!ineq) + IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n"); + if (!ineq) + return; + IF_VERBOSE(1, verbose_stream() << "flip " << lit << "\n"); + SASSERT(!ineq->is_true()); + flip(*ineq); + } + + double sls::reward(sat::bool_var v) { + if (m_dscore_mode) + return dscore_reward(v); + else + return dtt_reward(v); + } + + double sls::dtt_reward(sat::bool_var v) { + sat::literal litv(v, m_bool_search->get_value(v)); + auto const* ineq = atom(litv); + if (!ineq) + return 0; + int64_t new_value; + double result = 0; + for (auto const & [coeff, x] : ineq->m_args) { + if (!cm(*ineq, x, new_value)) + continue; + for (auto const [coeff, lit] : m_vars[x].m_literals) { + auto dtt_old = dtt(*atom(lit)); + auto dtt_new = dtt(*atom(lit), x, new_value); + if ((dtt_new == 0) != (dtt_old == 0)) + result += m_bool_search->reward(lit.var()); + } + } + return result; + } + + double sls::dscore_reward(sat::bool_var x) { + m_dscore_mode = false; + sat::literal litv(x, m_bool_search->get_value(x)); + auto const* ineq = atom(litv); + if (!ineq) + return 0; + SASSERT(!ineq->is_true()); + int64_t new_value; + double result = 0; + for (auto const& [coeff, v] : ineq->m_args) + if (cm(*ineq, v, new_value)) + result += dscore(v, new_value); + return result; + } + + // switch to dscore mode + void sls::on_rescale() { + m_dscore_mode = true; + } + + void sls::on_save_model() { + save_best_values(); + } + + void sls::on_restart() { + for (unsigned v = 0; v < s.s().num_vars(); ++v) + init_bool_var_assignment(v); + } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 706fff1dd..e781e9666 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -37,7 +37,7 @@ namespace arith { class solver; // local search portion for arithmetic - class sls { + class sls : public sat::local_search_plugin { enum class ineq_kind { EQ, LE, LT, NE }; enum class var_kind { INT, REAL }; typedef unsigned var_t; @@ -78,7 +78,7 @@ namespace arith { std::ostream& display(std::ostream& out) const { bool first = true; for (auto const& [c, v] : m_args) - out << (first? "": " + ") << c << " * v" << v, first = false; + out << (first ? "" : " + ") << c << " * v" << v, first = false; switch (m_op) { case ineq_kind::LE: return out << " <= " << m_bound << "(" << m_args_value << ")"; @@ -97,7 +97,7 @@ namespace arith { int64_t m_value; int64_t m_best_value; var_kind m_kind = var_kind::INT; - vector> m_literals; + svector> m_literals; }; struct clause { @@ -116,6 +116,7 @@ namespace arith { vector m_vars; vector m_clauses; svector> m_terms; + bool m_dscore_mode = false; indexed_uint_set& unsat() { return m_bool_search->unsat_set(); } @@ -136,7 +137,8 @@ namespace arith { bool flip_clauses(); bool flip_dscore(); bool flip_dscore(unsigned cl); - bool flip(unsigned cl); + bool flip_clause(unsigned cl); + bool flip(ineq const& ineq); int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } int64_t dtt(int64_t args, ineq const& ineq) const; int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; @@ -145,6 +147,8 @@ namespace arith { bool cm(ineq const& ineq, var_t v, int64_t& new_value); int cm_score(var_t v, int64_t new_value); void update(var_t v, int64_t new_value); + double dscore_reward(sat::bool_var v); + double dtt_reward(sat::bool_var v); void paws(); int64_t dscore(var_t v, int64_t new_value) const; void save_best_values(); @@ -163,11 +167,20 @@ namespace arith { public: sls(solver& s); + ~sls() override {} lbool operator ()(bool_vector& phase); void set_bounds_begin(); void set_bounds_end(unsigned num_literals); void set_bounds(euf::enode* n); void set(sat::ddfw* d); + + void init_search() override; + void finish_search() override; + void flip(sat::bool_var v) override; + double reward(sat::bool_var v) override; + void on_rescale() override; + void on_save_model() override; + void on_restart() override; }; inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 0572afc39..801a92150 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -29,44 +29,13 @@ namespace euf { bool_search.set_seed(rand()); scoped_rl.push_child(&(bool_search.rlimit())); - unsigned max_rounds = 30; - for (auto* th : m_solvers) th->set_bool_search(&bool_search); - for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) { + bool_search.rlimit().push(m_max_bool_steps); + lbool r = bool_search.check(0, nullptr, nullptr); + bool_search.rlimit().pop(); - setup_bounds(bool_search, phase); - - // Non-boolean literals are assumptions to Boolean search - literal_vector assumptions; -#if 0 - for (unsigned v = 0; v < phase.size(); ++v) - if (!is_propositional(literal(v))) - assumptions.push_back(literal(v, !bool_search.get_value(v))); -#endif - - verbose_stream() << "assumptions " << assumptions.size() << "\n"; - - bool_search.rlimit().push(m_max_bool_steps); - - lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr); - bool_search.rlimit().pop(); - -#if 0 - // restore state to optimal model - auto const& mdl = bool_search.get_model(); - for (unsigned i = 0; i < mdl.size(); ++i) - if ((mdl[i] == l_true) != bool_search.get_value(i)) - bool_search.flip(i); -#endif - - for (auto* th : m_solvers) - th->local_search(phase); - - if (bool_search.unsat_set().empty()) - break; - } auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) phase[i] = mdl[i] == l_true; From 7c08e53e944f806af7db74cc512a197c4d76c7f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Feb 2023 15:11:44 -0800 Subject: [PATCH 036/220] fixes for #6590 --- src/math/lp/nla_powers.cpp | 18 +++++++++++++----- src/smt/theory_lra.cpp | 8 ++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 96284f26a..f389aad93 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -79,6 +79,7 @@ am().set(rval, am_value(r)); namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { + TRACE("nla", tout << r << " == " << x << "^" << y << "\n"); if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) @@ -145,17 +146,24 @@ namespace nla { auto r2val = power(xval, yval.get_unsigned()); if (rval == r2val) return l_true; - if (xval > 0 && r2val < rval) { - SASSERT(yval > 0); + if (c.random() % 2 == 0) { + new_lemma lemma(c, "x == x0, y == y0 => r = x0^y0"); + lemma |= ineq(x, llc::NE, xval); + lemma |= ineq(y, llc::NE, yval); + lemma |= ineq(r, llc::EQ, r2val); + return l_false; + } + if (yval > 0 && r2val > rval) { new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); lemma |= ineq(x, llc::LT, xval); lemma |= ineq(y, llc::LT, yval); lemma |= ineq(r, llc::GE, r2val); return l_false; } - if (xval > 0 && r2val < rval) { - new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); - lemma |= ineq(x, llc::LT, xval); + if (r2val < rval) { + new_lemma lemma(c, "0 < x <= x0, y <= y0 => r <= x0^y0"); + lemma |= ineq(x, llc::LE, rational::zero()); + lemma |= ineq(x, llc::GT, xval); lemma |= ineq(y, llc::GT, yval); lemma |= ineq(r, llc::LE, r2val); return l_false; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d26ea516a..d61910ff2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -814,12 +814,13 @@ class theory_lra::imp { } lpvar get_lpvar(expr* e) { - return get_lpvar(get_enode(e)); + theory_var v = mk_var(e); + m_solver->register_existing_terms(); + return register_theory_var_in_lar_solver(v); } lpvar get_lpvar(enode* n) { - ensure_column(n); - return n ? get_lpvar(n->get_th_var(get_id())) : lp::null_lpvar; + return get_lpvar(n->get_expr()); } lpvar get_lpvar(theory_var v) const { @@ -1674,7 +1675,6 @@ public: for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; - st = FC_DONE; switch (eval_unsupported(e)) { case FC_CONTINUE: st = FC_CONTINUE; From 554a9e8efe71f9bc0aa19b61d8e2cefda7b03a37 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 08:53:08 -0800 Subject: [PATCH 037/220] fix #6346 --- src/util/zstring.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 570510458..7d2b4296a 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -214,7 +214,8 @@ int zstring::indexofu(zstring const& other, unsigned offset) const { } int zstring::last_indexof(zstring const& other) const { - if (other.length() == 0) return length(); + if (length() == 0 && other.length() == 0) return 0; + if (other.length() == 0) return -1; if (other.length() > length()) return -1; for (unsigned last = length() - other.length() + 1; last-- > 0; ) { bool suffix = true; From ac068888e79c61c037bfdfc58f23b14d853ad55b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 08:59:55 -0800 Subject: [PATCH 038/220] add trichotomy for sequence comparison. #6586 --- src/ast/rewriter/seq_axioms.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index c7dde763f..14f34f1f6 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -928,7 +928,6 @@ namespace seq { e1 < e2 => prefix(e1, e2) or e1 = xcy e1 < e2 => prefix(e1, e2) or c < d e1 < e2 => prefix(e1, e2) or e2 = xdz - e1 < e2 => e1 != e2 !(e1 < e2) => prefix(e2, e1) or e2 = xdz !(e1 < e2) => prefix(e2, e1) or d < c !(e1 < e2) => prefix(e2, e1) or e1 = xcy @@ -938,6 +937,7 @@ namespace seq { e1 < e2 or e1 = e2 or e2 < e1 !(e1 = e2) or !(e2 < e1) !(e1 < e2) or !(e2 < e1) + */ void axioms::lt_axiom(expr* n) { expr* _e1 = nullptr, *_e2 = nullptr; @@ -948,6 +948,7 @@ namespace seq { sort* char_sort = nullptr; VERIFY(seq.is_seq(s, char_sort)); expr_ref lt = expr_ref(n, m); + expr_ref gt = expr_ref(seq.str.mk_lex_lt(e2, e1), m); expr_ref x = m_sk.mk("str.<.x", e1, e2); expr_ref y = m_sk.mk("str.<.y", e1, e2); expr_ref z = m_sk.mk("str.<.z", e1, e2); @@ -969,6 +970,7 @@ namespace seq { add_clause(lt, pref21, ltdc); add_clause(lt, pref21, e2xdz); add_clause(~eq, ~lt); + add_clause(eq, lt, gt); } /** From bd10ddf6aea3808968bbea9f30ad532844d410bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Feb 2023 09:17:11 -0800 Subject: [PATCH 039/220] wip - local search - use dispatch model from bool local search instead of separate phases. --- src/sat/sat_ddfw.cpp | 24 ++++- src/sat/sat_ddfw.h | 2 +- src/sat/sat_solver.cpp | 3 - src/sat/smt/arith_sls.cpp | 174 +------------------------------ src/sat/smt/arith_sls.h | 25 +---- src/sat/smt/arith_solver.h | 4 - src/sat/smt/euf_local_search.cpp | 40 +------ src/sat/smt/euf_solver.h | 7 +- 8 files changed, 31 insertions(+), 248 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 478e793e3..64589dc6e 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -61,7 +61,8 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); - while (m_limit.inc() && m_min_sz > 0) { + m_steps_since_progress = 0; + while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { if (should_reinit_weights()) do_reinit_weights(); else if (do_flip()); else if (do_literal_flip()); @@ -106,7 +107,7 @@ namespace sat { if (v == null_bool_var) return false; if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { - if (uses_plugin) + if (uses_plugin && is_external(v)) m_plugin->flip(v); else flip(v); @@ -159,6 +160,24 @@ namespace sat { */ template bool_var ddfw::pick_literal_var() { +#if false + unsigned sz = m_clauses.size(); + unsigned start = rand(); + for (unsigned i = 0; i < 100; ++i) { + unsigned cl = (i + start) % sz; + if (m_unsat.contains(cl)) + continue; + for (auto lit : *m_clauses[cl].m_clause) { + if (is_true(lit)) + continue; + double r = uses_plugin ? plugin_reward(lit.var()) : reward(lit.var()); + if (r < 0) + continue; + //verbose_stream() << "false " << r << " " << lit << "\n"; + return lit.var(); + } + } +#endif return null_bool_var; } @@ -453,6 +472,7 @@ namespace sat { if (m_unsat.empty()) save_model(); else if (m_unsat.size() < m_min_sz) { + m_steps_since_progress = 0; if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) save_model(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index fc03f8ba2..654910111 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -126,7 +126,7 @@ namespace sat { unsigned m_restart_count = 0, m_reinit_count = 0, m_parsync_count = 0; uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0; uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0; - unsigned m_min_sz = 0; + unsigned m_min_sz = 0, m_steps_since_progress = 0; hashtable> m_models; stopwatch m_stopwatch; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 898c3a2a4..c570843b5 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,9 +1302,6 @@ namespace sat { return l_undef; } - // uncomment this to test bounded local search: - // bounded_local_search(); - log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 3d4522e76..54141e635 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -26,34 +26,9 @@ namespace arith { void sls::reset() { m_literals.reset(); m_vars.reset(); - m_clauses.reset(); m_terms.reset(); } - lbool sls::operator()(bool_vector& phase) { - - unsigned num_steps = 0; - for (unsigned v = 0; v < s.s().num_vars(); ++v) - init_bool_var_assignment(v); - verbose_stream() << "max arith steps " << m_max_arith_steps << "\n"; - m_max_arith_steps = std::max(1000u, m_max_arith_steps); - while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) { - if (!flip()) - break; - ++m_stats.m_num_flips; - ++num_steps; - unsigned num_unsat = unsat().size(); - if (num_unsat < m_best_min_unsat) { - m_best_min_unsat = num_unsat; - num_steps = 0; - save_best_values(); - } - } - store_best_values(); - log(); - return unsat().empty() ? l_true : l_undef; - } - void sls::log() { IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); } @@ -105,7 +80,6 @@ namespace arith { reset(); m_literals.reserve(s.s().num_vars() * 2); add_vars(); - m_clauses.resize(d->num_clauses()); for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) init_literal(lit); @@ -116,22 +90,6 @@ namespace arith { d->set(this); } - void sls::set_bounds_begin() { - m_max_arith_steps = 0; - } - - void sls::set_bounds(enode* n) { - ++m_max_arith_steps; - } - - void sls::set_bounds_end(unsigned num_literals) { - m_max_arith_steps = (m_config.L * m_max_arith_steps); // / num_literals; - } - - bool sls::flip() { - log(); - return flip_unsat() || flip_clauses() || flip_dscore(); - } // distance to true int64_t sls::dtt(int64_t args, ineq const& ineq) const { @@ -207,31 +165,6 @@ namespace arith { return false; } - bool sls::flip_unsat() { - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) - if (flip_clause(unsat().elem_at((i + start) % sz))) - return true; - return false; - } - - bool sls::flip_clause(unsigned cl) { - auto const& clause = get_clause(cl); - for (literal lit : clause) { - if (is_true(lit)) - continue; - auto const* ineq = atom(lit); - if (!ineq) - continue; - SASSERT(!ineq->is_true()); - if (flip(*ineq)) - return true; - - } - return false; - } - // flip on the first positive score // it could be changed to flip on maximal positive score // or flip on maximal non-negative score @@ -255,77 +188,17 @@ namespace arith { return false; } - bool sls::flip_clauses() { - unsigned start = s.random(); - unsigned sz = m_bool_search->num_clauses(); - for (unsigned i = sz; i-- > 0; ) - if (flip_clause((i + start) % sz)) - return true; - return false; - } - - bool sls::flip_dscore() { - paws(); - unsigned start = s.random(); - unsigned sz = unsat().size(); - for (unsigned i = sz; i-- > 0; ) - if (flip_dscore(unsat().elem_at((i + start) % sz))) - return true; - return false; - } - - bool sls::flip_dscore(unsigned cl) { - auto const& clause = get_clause(cl); - int64_t new_value, min_value, min_score(-1); - var_t min_var = UINT_MAX; - for (auto lit : clause) { - auto const* ineq = atom(lit); - if (!ineq || ineq->is_true()) - continue; - for (auto const& [coeff, v] : ineq->m_args) { - if (cm(*ineq, v, new_value)) { - int64_t score = dscore(v, new_value); - if (UINT_MAX == min_var || score < min_score) { - min_var = v; - min_value = new_value; - min_score = score; - } - } - } - } - if (min_var != UINT_MAX) { - update(min_var, min_value); - return true; - } - return false; - } - - /** - * redistribute weights of clauses. - * TODO - re-use ddfw weights instead. - */ - void sls::paws() { - for (unsigned cl = num_clauses(); cl-- > 0; ) { - auto& clause = get_clause_info(cl); - bool above = 10000 * m_config.sp <= (s.random() % 10000); - if (!above && clause.is_true() && get_weight(cl) > 1) - get_weight(cl) -= 1; - if (above && !clause.is_true()) - get_weight(cl) += 1; - } - } - // // dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c) // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - int64_t sls::dscore(var_t v, int64_t new_value) const { + double sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; - int64_t score(0); + double score = 0; for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * int64_t(get_weight(cl)); + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); return score; } @@ -437,46 +310,6 @@ namespace arith { m_vars[v].m_literals.push_back({ c, lit }); } - void sls::add_bounds(sat::literal_vector& bounds) { - unsigned bvars = s.s().num_vars(); - auto add_ineq = [&](sat::literal lit, ineq& i) { - m_literals.set(lit.index(), &i); - bounds.push_back(lit); - }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) { - rational lo, hi; - bool is_strict_lo = false, is_strict_hi = false; - lp::constraint_index ci; - if (!s.is_registered_var(v)) - continue; - lp::column_index vi = s.lp().to_column_index(v); - if (vi.is_null()) - continue; - bool has_lo = s.lp().has_lower_bound(vi.index(), ci, lo, is_strict_lo); - bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi); - - if (has_lo && has_hi && lo == hi) { - auto& ineq = new_ineq(sls::ineq_kind::EQ, to_numeral(lo)); - sat::literal lit(bvars++); - add_arg(lit, ineq, 1, v); - add_ineq(lit, ineq); - continue; - } - if (has_lo) { - auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(-lo)); - sat::literal lit(bvars++); - add_arg(lit, ineq, -1, v); - add_ineq(lit, ineq); - } - if (has_hi) { - auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, to_numeral(hi)); - sat::literal lit(bvars++); - add_arg(lit, ineq, 1, v); - add_ineq(lit, ineq); - } - } - } - int64_t sls::to_numeral(rational const& r) { if (r.is_int64()) return r.get_int64(); @@ -635,4 +468,3 @@ namespace arith { init_bool_var_assignment(v); } } - diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index e781e9666..5496be637 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -100,11 +100,6 @@ namespace arith { svector> m_literals; }; - struct clause { - unsigned m_weight = 1; - int64_t m_dts = 1; - }; - solver& s; ast_manager& m; sat::ddfw* m_bool_search = nullptr; @@ -114,7 +109,6 @@ namespace arith { config m_config; scoped_ptr_vector m_literals; vector m_vars; - vector m_clauses; svector> m_terms; bool m_dscore_mode = false; @@ -129,15 +123,9 @@ namespace arith { void reset(); ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } - unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; } - unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } - bool flip(); + void log(); - bool flip_unsat(); - bool flip_clauses(); - bool flip_dscore(); - bool flip_dscore(unsigned cl); - bool flip_clause(unsigned cl); + bool flip(ineq const& ineq); int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } int64_t dtt(int64_t args, ineq const& ineq) const; @@ -149,14 +137,12 @@ namespace arith { void update(var_t v, int64_t new_value); double dscore_reward(sat::bool_var v); double dtt_reward(sat::bool_var v); - void paws(); - int64_t dscore(var_t v, int64_t new_value) const; + double dscore(var_t v, int64_t new_value) const; void save_best_values(); void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); - void add_bounds(sat::literal_vector& bounds); void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); void init_literal(sat::literal lit); void init_bool_var_assignment(sat::bool_var v); @@ -168,12 +154,7 @@ namespace arith { public: sls(solver& s); ~sls() override {} - lbool operator ()(bool_vector& phase); - void set_bounds_begin(); - void set_bounds_end(unsigned num_literals); - void set_bounds(euf::enode* n); void set(sat::ddfw* d); - void init_search() override; void finish_search() override; void flip(sat::bool_var v) override; diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a31ca844a..9ae671c16 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -512,10 +512,6 @@ namespace arith { bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); } bool has_unhandled() const override { return m_not_handled != nullptr; } - void set_bounds_begin() override { m_local_search.set_bounds_begin(); } - void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); } - void set_bounds(enode* n) override { m_local_search.set_bounds(n); } - lbool local_search(bool_vector& phase) override { return m_local_search(phase); } void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); } // bounds and equality propagation callbacks diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 801a92150..d22a65fb8 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -32,9 +32,7 @@ namespace euf { for (auto* th : m_solvers) th->set_bool_search(&bool_search); - bool_search.rlimit().push(m_max_bool_steps); - lbool r = bool_search.check(0, nullptr, nullptr); - bool_search.rlimit().pop(); + lbool r = bool_search.check(0, nullptr, nullptr); auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) @@ -42,40 +40,4 @@ namespace euf { return bool_search.unsat_set().empty() ? l_true : l_undef; } - - bool solver::is_propositional(sat::literal lit) { - expr* e = m_bool_var2expr.get(lit.var(), nullptr); - return !e || is_uninterp_const(e) || !m_egraph.find(e); - } - - void solver::setup_bounds(sat::ddfw& bool_search, bool_vector const& phase) { - unsigned num_literals = 0; - unsigned num_bool = 0; - for (auto* th : m_solvers) - th->set_bounds_begin(); - - auto count_literal = [&](sat::literal l) { - if (is_propositional(l)) { - ++num_bool; - return; - } - euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr)); - for (auto* s : m_solvers) - s->set_bounds(n); - }; - - for (auto cl : bool_search.unsat_set()) { - auto& c = *bool_search.get_clause_info(cl).m_clause; - num_literals += c.size(); - for (auto l : c) - count_literal(l); - } - - m_max_bool_steps = (m_ls_config.L * num_bool); // / num_literals; - m_max_bool_steps = std::max(10000u, m_max_bool_steps); - verbose_stream() << "num literals " << num_literals << " num bool " << num_bool << " max bool steps " << m_max_bool_steps << "\n"; - - for (auto* th : m_solvers) - th->set_bounds_end(num_literals); - } } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index d62390329..e51b68b09 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -260,12 +260,7 @@ namespace euf { constraint& mk_constraint(constraint*& c, constraint::kind_t k); constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } - constraint& lit_constraint(enode* n); - - // local search - unsigned m_max_bool_steps = 10; - bool is_propositional(sat::literal lit); - void setup_bounds(sat::ddfw& bool_search, bool_vector const& mdl); + constraint& lit_constraint(enode* n); // user propagator void check_for_user_propagator() { From 828ff98c77eb5437178b7b8aff902394e29e0445 Mon Sep 17 00:00:00 2001 From: Ding Fei Date: Sat, 18 Feb 2023 01:26:45 +0800 Subject: [PATCH 040/220] fix tpl instantiation issue for mingw (#6597) --- src/ast/rewriter/rewriter.h | 2 +- src/ast/rewriter/rewriter_def.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index a04c96f4f..cd89bb2f9 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -347,7 +347,7 @@ public: Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } - ~rewriter_tpl() override; + ~rewriter_tpl() override {}; void reset(); void cleanup(); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index e9b9c316a..44bed09c6 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -640,10 +640,6 @@ rewriter_tpl::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg m_pr2(m) { } -template -rewriter_tpl::~rewriter_tpl() { -} - template void rewriter_tpl::reset() { m_cfg.reset(); From f66a082de9e51205b42a910a0640a73172aa5eeb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:10:58 -0800 Subject: [PATCH 041/220] fix #6595 --- .gitignore | 1 + src/ast/rewriter/bv_bounds_base.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 936f977aa..b0c19a432 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ ocamlz3 # Directories with generated code and documentation release/* build/* +trace/* build-dist/* dist/* src/out/* diff --git a/src/ast/rewriter/bv_bounds_base.h b/src/ast/rewriter/bv_bounds_base.h index 840cf6e3e..8542e5921 100644 --- a/src/ast/rewriter/bv_bounds_base.h +++ b/src/ast/rewriter/bv_bounds_base.h @@ -185,7 +185,7 @@ namespace bv { m_args.push_back(arg); continue; } - if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { + if (!m_bv.is_extract(arg) && m_bound.find(arg, b) && b.lo() <= b.hi()) { unsigned num_bits = b.hi().get_num_bits(); unsigned bv_size = m_bv.get_bv_size(arg); if (0 < num_bits && num_bits < bv_size) { @@ -202,6 +202,7 @@ namespace bv { if (simplified) { result = m.mk_app(to_app(t)->get_decl(), m_args); + TRACE("bv", tout << mk_pp(t, m) << " -> " << result << "\n"); return true; } From c5e33b79b580ddf228c508549fc93bdbd855a0d6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:11:42 -0800 Subject: [PATCH 042/220] wip - arith sls overhaul to tier inequalities with Boolean variables instead of literals --- src/sat/sat_ddfw.cpp | 23 +- src/sat/sat_ddfw.h | 4 +- src/sat/sat_solver.cpp | 18 +- src/sat/smt/arith_sls.cpp | 450 ++++++++++++++++++++----------- src/sat/smt/arith_sls.h | 32 +-- src/sat/smt/arith_solver.cpp | 8 +- src/sat/smt/euf_local_search.cpp | 7 + 7 files changed, 352 insertions(+), 190 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 64589dc6e..329d783f4 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -98,15 +98,17 @@ namespace sat { template bool ddfw::do_flip() { - bool_var v = pick_var(); - return apply_flip(v); + double reward = 0; + bool_var v = pick_var(reward); + return apply_flip(v, reward); } template - bool ddfw::apply_flip(bool_var v) { - if (v == null_bool_var) + bool ddfw::apply_flip(bool_var v, double reward) { + if (v == null_bool_var) return false; - if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { + + if (reward > 0 || (reward == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { if (uses_plugin && is_external(v)) m_plugin->flip(v); else @@ -119,10 +121,9 @@ namespace sat { } template - bool_var ddfw::pick_var() { + bool_var ddfw::pick_var(double& r) { double sum_pos = 0; unsigned n = 1; - double r; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { r = uses_plugin ? plugin_reward(v) : reward(v); @@ -142,16 +143,18 @@ namespace sat { } } } + r = 0; if (v0 != null_bool_var) return v0; if (m_unsat_vars.empty()) - return 0; + return null_bool_var; return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size())); } template bool ddfw::do_literal_flip() { - return apply_flip(pick_literal_var()); + double reward = 1; + return apply_flip(pick_literal_var(), reward); } /* @@ -414,7 +417,7 @@ namespace sat { bool ddfw::should_restart() { return m_flips >= m_restart_next; } - + void ddfw::do_restart() { reinit_values(); init_clause_data(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 654910111..8c4f9287f 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -197,10 +197,10 @@ namespace sat { bool do_flip(); template - bool_var pick_var(); + bool_var pick_var(double& reward); template - bool apply_flip(bool_var v); + bool apply_flip(bool_var v, double reward); template bool do_literal_flip(); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index c570843b5..5ac0fbcf3 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1363,7 +1363,13 @@ namespace sat { if (m_ext) { verbose_stream() << "bounded local search\n"; do_restart(true); - m_ext->local_search(m_best_phase); + lbool r = m_ext->local_search(m_best_phase); + verbose_stream() << r << "\n"; + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } return; } literal_vector _lits; @@ -1728,6 +1734,8 @@ namespace sat { push(); m_stats.m_decision++; + CTRACE("sat", m_best_phase[next] != guess(next), tout << "phase " << phase << " " << m_best_phase[next] << " " << guess(next) << "\n"); + if (phase == l_undef) phase = guess(next) ? l_true: l_false; @@ -1738,12 +1746,12 @@ namespace sat { m_case_split_queue.unassign_var_eh(next); next_lit = literal(next, false); } - + if (phase == l_undef) is_pos = guess(next); else is_pos = phase == l_true; - + if (!is_pos) next_lit.neg(); @@ -2966,7 +2974,7 @@ namespace sat { } bool solver::should_rephase() { - return m_conflicts_since_init > m_rephase_lim; + return m_conflicts_since_init > 5 && m_conflicts_since_init > m_rephase_lim; } void solver::do_rephase() { @@ -3015,7 +3023,7 @@ namespace sat { UNREACHABLE(); break; } - m_rephase_inc += m_config.m_rephase_base; + m_rephase_inc = m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; } diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 54141e635..aeddd319b 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -24,7 +24,7 @@ namespace arith { s(s), m(s.m) {} void sls::reset() { - m_literals.reset(); + m_bool_vars.reset(); m_vars.reset(); m_terms.reset(); } @@ -36,6 +36,21 @@ namespace arith { void sls::save_best_values() { for (unsigned v = 0; v < s.get_num_vars(); ++v) m_vars[v].m_best_value = m_vars[v].m_value; + + auto check_bool_var = [&](sat::bool_var bv) { + auto const* ineq = atom(bv); + if (!ineq) + return; + sat::literal lit(bv, !m_bool_search->get_value(bv)); + int64_t d = dtt(lit.sign(), *ineq); + // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + if (is_true(lit) != (d == 0)) { + verbose_stream() << lit << " " << *ineq << "\n"; + } + VERIFY(is_true(lit) == (d == 0)); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) + check_bool_var(v); } void sls::store_best_values() { @@ -47,7 +62,7 @@ namespace arith { for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - val += to_numeral(arg.coeff()) * value(w); + val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; } update(v, val); } @@ -55,14 +70,12 @@ namespace arith { for (unsigned v = 0; v < s.get_num_vars(); ++v) { if (s.is_bool(v)) continue; - if (!s.lp().external_is_used(v)) - continue; + if (!s.lp().external_is_used(v)) + continue; int64_t old_value = 0; if (s.is_registered_var(v)) old_value = to_numeral(s.get_ivalue(v).x); int64_t new_value = m_vars[v].m_best_value; - if (old_value == new_value) - continue; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); SASSERT(!vj.is_null()); @@ -73,40 +86,98 @@ namespace arith { // TODO - figure out why this leads to unsound (unsat). } } + + lbool r = s.make_feasible(); + VERIFY (!unsat().empty() || r == l_true); + if (unsat().empty()) { + s.m_num_conflicts = s.get_config().m_arith_propagation_threshold; + } + verbose_stream() << "has changed " << s.m_solver->has_changed_columns() << "\n"; + + auto check_bool_var = [&](sat::bool_var bv) { + auto* ineq = m_bool_vars.get(bv, nullptr); + if (!ineq) + return; + api_bound* b = nullptr; + s.m_bool_var2bound.find(bv, b); + if (!b) + return; + auto bound = b->get_value(); + theory_var v = b->get_var(); + if (s.get_phase(bv) == m_bool_search->get_model()[bv]) + return; + switch (b->get_bound_kind()) { + case lp_api::lower_t: + verbose_stream() << bv << " " << bound << " <= " << s.get_value(v) << "\n"; + break; + case lp_api::upper_t: + verbose_stream() << bv << " " << bound << " >= " << s.get_value(v) << "\n"; + break; + } + int64_t value = 0; + for (auto const& [coeff, v] : ineq->m_args) { + value += coeff * m_vars[v].m_best_value; + } + ineq->m_args_value = value; + verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n"; + }; + + if (unsat().empty()) { + for (bool_var v = 0; v < s.s().num_vars(); ++v) + check_bool_var(v); + } } void sls::set(sat::ddfw* d) { m_bool_search = d; reset(); - m_literals.reserve(s.s().num_vars() * 2); + m_bool_vars.reserve(s.s().num_vars()); add_vars(); for (unsigned i = 0; i < d->num_clauses(); ++i) for (sat::literal lit : *d->get_clause_info(i).m_clause) - init_literal(lit); + init_bool_var(lit.var()); for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - m_best_min_unsat = std::numeric_limits::max(); d->set(this); } - // distance to true - int64_t sls::dtt(int64_t args, ineq const& ineq) const { + int64_t sls::dtt(bool sign, int64_t args, ineq const& ineq) const { switch (ineq.m_op) { case ineq_kind::LE: + if (sign) { + if (args <= ineq.m_bound) + return ineq.m_bound - args + 1; + return 0; + } if (args <= ineq.m_bound) return 0; return args - ineq.m_bound; case ineq_kind::EQ: + if (sign) { + if (args == ineq.m_bound) + return 1; + return 0; + } if (args == ineq.m_bound) return 0; return 1; case ineq_kind::NE: + if (sign) { + if (args == ineq.m_bound) + return 0; + return 1; + } if (args == ineq.m_bound) return 1; return 0; case ineq_kind::LT: + if (sign) { + if (args < ineq.m_bound) + return ineq.m_bound - args; + return 0; + } if (args < ineq.m_bound) return 0; return args - ineq.m_bound + 1; @@ -121,45 +192,95 @@ namespace arith { // m_vars[w].m_value can be computed outside and shared among calls // different data-structures for storing coefficients // - int64_t sls::dtt(ineq const& ineq, var_t v, int64_t new_value) const { - auto new_args_value = ineq.m_args_value; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - new_args_value += coeff * (new_value - m_vars[w].m_value); + int64_t sls::dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const { + for (auto const& [coeff, w] : ineq.m_args) + if (w == v) + return dtt(sign, ineq.m_args_value + coeff * (new_value - m_vars[v].m_value), ineq); + return 1; + } + + int64_t sls::dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const { + return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq); + } + + bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value) { + for (auto const& [coeff, w] : ineq.m_args) + if (w == v) + return cm(sign, ineq, v, coeff, new_value); + return false; + } + + bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + VERIFY(ineq.is_true() == sign); + verbose_stream() << "cm " << ineq << " for " << v << " sign " << sign << "\n"; + auto bound = ineq.m_bound; + auto argsv = ineq.m_args_value; + bool solved = false; + int64_t delta = argsv - bound; + if (sign) { + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(argsv <= bound); + SASSERT(delta <= 0); + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) > bound); + return true; + case ineq_kind::LT: + SASSERT(argsv <= ineq.m_bound); + SASSERT(delta <= 0); + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) >= bound); + return true; + case ineq_kind::EQ: + if (delta >= 0) + delta++; + else + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + case ineq_kind::NE: + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved) verbose_stream() << "did not solve disequality " << ineq << " for " << v << "\n"; + return solved; + default: + UNREACHABLE(); break; } } - return dtt(new_args_value, ineq); - } - - // critical move - bool sls::cm(ineq const& ineq, var_t v, int64_t& new_value) { - SASSERT(!ineq.is_true()); - int64_t delta = ineq.m_args_value - ineq.m_bound; - if (ineq.m_op == ineq_kind::NE || ineq.m_op == ineq_kind::LT) - delta--; - for (auto const& [coeff, w] : ineq.m_args) { - if (w == v) { - - if (coeff > 0) - new_value = value(v) - abs((delta + coeff - 1)/ coeff); + else { + switch (ineq.m_op) { + case ineq_kind::LE: + SASSERT(argsv > ineq.m_bound); + SASSERT(delta > 0); + new_value = value(v) - (delta + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) <= bound); + return true; + case ineq_kind::LT: + SASSERT(argsv >= ineq.m_bound); + SASSERT(delta >= 0); + ++delta; + new_value = value(v) - (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) < bound); + return true; + case ineq_kind::NE: + if (delta >= 0) + delta++; else - new_value = value(v) + abs(delta) / -coeff; - - switch (ineq.m_op) { - case ineq_kind::LE: - SASSERT(delta + coeff * (new_value - value(v)) <= 0); - return true; - case ineq_kind::EQ: - return delta + coeff * (new_value - value(v)) == 0; - case ineq_kind::NE: - return delta + coeff * (new_value - value(v)) != 0; - case ineq_kind::LT: - return delta + coeff * (new_value - value(v)) < 0; - default: - UNREACHABLE(); - break; - } + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + case ineq_kind::EQ: + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved) verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; + return solved; + default: + UNREACHABLE(); + break; } } return false; @@ -169,23 +290,19 @@ namespace arith { // it could be changed to flip on maximal positive score // or flip on maximal non-negative score // or flip on first non-negative score - bool sls::flip(ineq const& ineq) { + bool sls::flip(bool sign, ineq const& ineq) { int64_t new_value; - for (auto const& [coeff, v] : ineq.m_args) { - if (!cm(ineq, v, new_value)) - continue; - int score = cm_score(v, new_value); - if (score <= 0) - continue; - unsigned num_unsat = unsat().size(); - update(v, new_value); - IF_VERBOSE(2, - verbose_stream() << "v" << v << " score " << score << " " - << num_unsat << " -> " << unsat().size() << "\n"); - SASSERT(num_unsat > unsat().size()); - return true; + auto v = ineq.m_var_to_flip; + if (v == UINT_MAX) { + verbose_stream() << "no var to flip\n"; + return false; } - return false; + if (!cm(sign, ineq, v, new_value)) { + verbose_stream() << "no critical move for " << v << "\n"; + return false; + } + update(v, new_value); + return true; } // @@ -195,10 +312,13 @@ namespace arith { // double sls::dscore(var_t v, int64_t new_value) const { auto const& vi = m_vars[v]; + verbose_stream() << "dscore " << v << "\n"; double score = 0; +#if 0 for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); +#endif return score; } @@ -212,25 +332,28 @@ namespace arith { int score = 0; auto& vi = m_vars[v]; int64_t old_value = vi.m_value; - for (auto const& [coeff, lit] : vi.m_literals) { - auto const& ineq = *atom(lit); - int64_t dtt_old = dtt(ineq); - int64_t delta = coeff * (new_value - old_value); - int64_t dtt_new = dtt(ineq.m_args_value + delta, ineq); - - if (dtt_old == dtt_new) + for (auto const& [coeff, bv] : vi.m_bool_vars) { + auto const& ineq = *atom(bv); + bool sign = !m_bool_search->value(bv); + int64_t dtt_old = dtt(sign, ineq); + int64_t dtt_new = dtt(sign, ineq, coeff, old_value, new_value); + if ((dtt_old == 0) == (dtt_new == 0)) continue; - + sat::literal lit(bv, sign); + if (dtt_old == 0) + // flip from true to false + lit.neg(); + + // lit flips form false to true: for (auto cl : m_bool_search->get_use_list(lit)) { auto const& clause = get_clause_info(cl); - if (!clause.is_true()) { - VERIFY(dtt_old != 0); - if (dtt_new == 0) - ++score; // false -> true - } - else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 1) // true -> true not really, same variable can be in multiple literals - continue; - else if (all_of(*clause.m_clause, [&](auto lit2) { return !atom(lit2) || dtt(*atom(lit2), v, new_value) > 0; })) // ?? TODO + if (!clause.is_true()) + ++score; + } + // ignore the situation where clause contains multiple literals using v + for (auto cl : m_bool_search->get_use_list(~lit)) { + auto const& clause = get_clause_info(cl); + if (clause.m_num_trues == 1) --score; } } @@ -241,10 +364,10 @@ namespace arith { int64_t d(1), d2; bool first = true; for (auto a : get_clause(cl)) { - auto const* ineq = atom(a); + auto const* ineq = atom(a.var()); if (!ineq) continue; - d2 = dtt(*ineq); + d2 = dtt(a.sign(), *ineq); if (first) d = d2, first = false; else @@ -259,10 +382,10 @@ namespace arith { int64_t d(1), d2; bool first = true; for (auto lit : get_clause(cl)) { - auto const* ineq = atom(lit); + auto const* ineq = atom(lit.var()); if (!ineq) continue; - d2 = dtt(*ineq, v, new_value); + d2 = dtt(lit.sign(), *ineq, v, new_value); if (first) d = d2, first = false; else @@ -275,15 +398,17 @@ namespace arith { void sls::update(var_t v, int64_t new_value) { auto& vi = m_vars[v]; - auto const& old_value = vi.m_value; - for (auto const& [coeff, lit] : vi.m_literals) { - auto& ineq = *atom(lit); + auto old_value = vi.m_value; + for (auto const& [coeff, bv] : vi.m_bool_vars) { + auto& ineq = *atom(bv); + bool sign = !m_bool_search->value(bv); + sat::literal lit(bv, sign); + SASSERT(is_true(lit)); ineq.m_args_value += coeff * (new_value - old_value); - int64_t dtt_new = dtt(ineq); - if ((dtt_new == 0) != is_true(lit)) - m_bool_search->flip(lit.var()); - - SASSERT((dtt_new == 0) == is_true(lit)); + int64_t dtt_new = dtt(sign, ineq); + if (dtt_new != 0) + m_bool_search->flip(bv); + SASSERT(dtt(!m_bool_search->value(bv), ineq) == 0); } vi.m_value = new_value; } @@ -304,10 +429,10 @@ namespace arith { return *i; } - void sls::add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v) { + void sls::add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v) { ineq.m_args.push_back({ c, v }); ineq.m_args_value += c * value(v); - m_vars[v].m_literals.push_back({ c, lit }); + m_vars[v].m_bool_vars.push_back({ c, bv}); } int64_t sls::to_numeral(rational const& r) { @@ -316,79 +441,63 @@ namespace arith { return 0; } - - void sls::add_args(sat::literal lit, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { + void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); - add_arg(lit, ineq, sign * to_numeral(arg.coeff()), w); + add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w); } } else - add_arg(lit, ineq, sign, s.lp().local_to_external(t.id())); + add_arg(bv, ineq, sign, s.lp().local_to_external(t.id())); } - - void sls::init_literal(sat::literal lit) { - if (m_literals.get(lit.index(), nullptr)) + void sls::init_bool_var(sat::bool_var bv) { + if (m_bool_vars.get(bv, nullptr)) return; api_bound* b = nullptr; - s.m_bool_var2bound.find(lit.var(), b); + s.m_bool_var2bound.find(bv, b); if (b) { auto t = b->tv(); rational bound = b->get_value(); bool should_minus = false; sls::ineq_kind op; - if (!lit.sign()) { - should_minus = b->get_bound_kind() == lp_api::bound_kind::upper_t; - op = sls::ineq_kind::LE; - } - else { - should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; - if (s.is_int(b->get_var())) { - bound -= 1; - op = sls::ineq_kind::LE; - } - else - op = sls::ineq_kind::LT; - - } + should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t; + op = sls::ineq_kind::LE; if (should_minus) bound.neg(); + auto& ineq = new_ineq(op, to_numeral(bound)); - add_args(lit, ineq, t, b->get_var(), should_minus ? -1 : 1); - m_literals.set(lit.index(), &ineq); + add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1); + m_bool_vars.set(bv, &ineq); + m_bool_search->set_external(bv); return; } - expr* e = s.bool_var2expr(lit.var()); + expr* e = s.bool_var2expr(bv); expr* l = nullptr, * r = nullptr; if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) { theory_var u = s.get_th_var(l); theory_var v = s.get_th_var(r); lp::tv tu = s.get_tv(u); lp::tv tv = s.get_tv(v); - auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, 0); - add_args(lit, ineq, tu, u, 1); - add_args(lit, ineq, tv, v, -1); - m_literals.set(lit.index(), &ineq); + auto& ineq = new_ineq(sls::ineq_kind::EQ, 0); + add_args(bv, ineq, tu, u, 1); + add_args(bv, ineq, tv, v, -1); + m_bool_vars.set(bv, &ineq); + m_bool_search->set_external(bv); return; } } void sls::init_bool_var_assignment(sat::bool_var v) { - init_literal_assignment(literal(v, false)); - init_literal_assignment(literal(v, true)); - } - - void sls::init_literal_assignment(sat::literal lit) { - auto* ineq = m_literals.get(lit.index(), nullptr); - if (ineq && is_true(lit) != (dtt(*ineq) == 0)) - m_bool_search->flip(lit.var()); + auto* ineq = m_bool_vars.get(v, nullptr); + if (ineq && is_true(sat::literal(v, false)) != (dtt(false, *ineq) == 0)) + m_bool_search->flip(v); } void sls::init_search() { @@ -402,14 +511,13 @@ namespace arith { void sls::flip(sat::bool_var v) { sat::literal lit(v, m_bool_search->get_value(v)); SASSERT(!is_true(lit)); - auto const* ineq = atom(lit); + auto const* ineq = atom(v); if (!ineq) IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n"); if (!ineq) return; - IF_VERBOSE(1, verbose_stream() << "flip " << lit << "\n"); - SASSERT(!ineq->is_true()); - flip(*ineq); + SASSERT(ineq->is_true() == lit.sign()); + flip(!lit.sign(), *ineq); } double sls::reward(sat::bool_var v) { @@ -419,39 +527,56 @@ namespace arith { return dtt_reward(v); } - double sls::dtt_reward(sat::bool_var v) { - sat::literal litv(v, m_bool_search->get_value(v)); - auto const* ineq = atom(litv); + double sls::dtt_reward(sat::bool_var bv0) { + bool sign0 = !m_bool_search->get_value(bv0); + auto* ineq = atom(bv0); if (!ineq) - return 0; - int64_t new_value; + return -1; + int64_t new_value; double result = 0; + double max_result = -1; + theory_var max_var = 0; for (auto const & [coeff, x] : ineq->m_args) { - if (!cm(*ineq, x, new_value)) + if (!cm(!sign0, *ineq, x, coeff, new_value)) continue; - for (auto const [coeff, lit] : m_vars[x].m_literals) { - auto dtt_old = dtt(*atom(lit)); - auto dtt_new = dtt(*atom(lit), x, new_value); + double result = 0; + auto old_value = m_vars[x].m_value; + for (auto const [coeff, bv] : m_vars[x].m_bool_vars) { + bool sign = !m_bool_search->value(bv); + auto dtt_old = dtt(sign, *atom(bv)); + auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); if ((dtt_new == 0) != (dtt_old == 0)) - result += m_bool_search->reward(lit.var()); + result += m_bool_search->reward(bv); + } + if (result > max_result) { + max_result = result; + ineq->m_var_to_flip = x; } } - return result; + return max_result; } - double sls::dscore_reward(sat::bool_var x) { + double sls::dscore_reward(sat::bool_var bv) { m_dscore_mode = false; - sat::literal litv(x, m_bool_search->get_value(x)); - auto const* ineq = atom(litv); + bool sign = !m_bool_search->get_value(bv); + sat::literal litv(bv, sign); + auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(!ineq->is_true()); + SASSERT(ineq->is_true() == sign); int64_t new_value; - double result = 0; - for (auto const& [coeff, v] : ineq->m_args) - if (cm(*ineq, v, new_value)) - result += dscore(v, new_value); - return result; + + for (auto const& [coeff, v] : ineq->m_args) { + double result = 0; + if (cm(sign, *ineq, v, coeff, new_value)) + result = dscore(v, new_value); + // just pick first positive, or pick a max? + if (result > 0) { + ineq->m_var_to_flip = v; + return result; + } + } + return 0; } // switch to dscore mode @@ -466,5 +591,24 @@ namespace arith { void sls::on_restart() { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); + + verbose_stream() << "on-restart\n"; + auto check_bool_var = [&](sat::bool_var bv) { + auto const* ineq = atom(bv); + if (!ineq) + return; + bool sign = !m_bool_search->get_value(bv); + int64_t d = dtt(sign, *ineq); + sat::literal lit(bv, sign); + // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + if (is_true(lit) != (d == 0)) { + verbose_stream() << "restart " << bv << " " << *ineq << "\n"; + } + VERIFY(is_true(lit) == (d == 0)); + }; + for (unsigned v = 0; v < s.get_num_vars(); ++v) + check_bool_var(v); + + verbose_stream() << "on-restart-done\n"; } } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 5496be637..3c9daaa51 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -60,8 +60,9 @@ namespace arith { struct ineq { vector> m_args; ineq_kind m_op = ineq_kind::LE; - int64_t m_bound; - int64_t m_args_value; + int64_t m_bound; + int64_t m_args_value; + unsigned m_var_to_flip = UINT_MAX; bool is_true() const { switch (m_op) { @@ -97,17 +98,15 @@ namespace arith { int64_t m_value; int64_t m_best_value; var_kind m_kind = var_kind::INT; - svector> m_literals; + svector> m_bool_vars; }; solver& s; ast_manager& m; sat::ddfw* m_bool_search = nullptr; - unsigned m_max_arith_steps = 0; - unsigned m_best_min_unsat = UINT_MAX; stats m_stats; config m_config; - scoped_ptr_vector m_literals; + scoped_ptr_vector m_bool_vars; vector m_vars; svector> m_terms; bool m_dscore_mode = false; @@ -122,17 +121,19 @@ namespace arith { bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } void reset(); - ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; } + ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; } void log(); - bool flip(ineq const& ineq); - int64_t dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); } - int64_t dtt(int64_t args, ineq const& ineq) const; - int64_t dtt(ineq const& ineq, var_t v, int64_t new_value) const; + bool flip(bool sign, ineq const& ineq); + int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); } + int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const; + int64_t dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const; + int64_t dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const; int64_t dts(unsigned cl, var_t v, int64_t new_value) const; int64_t compute_dts(unsigned cl) const; - bool cm(ineq const& ineq, var_t v, int64_t& new_value); + bool cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value); + bool cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value); int cm_score(var_t v, int64_t new_value); void update(var_t v, int64_t new_value); double dscore_reward(sat::bool_var v); @@ -142,11 +143,10 @@ namespace arith { void store_best_values(); void add_vars(); sls::ineq& new_ineq(ineq_kind op, int64_t const& bound); - void add_arg(sat::literal lit, ineq& ineq, int64_t const& c, var_t v); - void add_args(sat::literal lit, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); - void init_literal(sat::literal lit); + void add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v); + void add_args(sat::bool_var bv, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign); + void init_bool_var(sat::bool_var v); void init_bool_var_assignment(sat::bool_var v); - void init_literal_assignment(sat::literal lit); int64_t value(var_t v) const { return m_vars[v].m_value; } int64_t to_numeral(rational const& r); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index fd507ed15..bd5dd315f 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -101,8 +101,7 @@ namespace arith { return false; switch (lbl) { - case l_false: - TRACE("arith", tout << "propagation conflict\n";); + case l_false: get_infeasibility_explanation_and_set_conflict(); break; case l_true: @@ -382,9 +381,9 @@ namespace arith { void solver::assert_bound(bool is_true, api_bound& b) { - TRACE("arith", tout << b << "\n";); lp::constraint_index ci = b.get_constraint(is_true); lp().activate(ci); + TRACE("arith", tout << b << " " << is_infeasible() << "\n";); if (is_infeasible()) return; lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true); @@ -1066,6 +1065,7 @@ namespace arith { TRACE("pcs", tout << lp().constraints();); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); + TRACE("arith", tout << status << "\n"); switch (status) { case lp::lp_status::INFEASIBLE: return l_false; @@ -1202,7 +1202,7 @@ namespace arith { TRACE("arith", tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n"; - for (literal c : m_core) tout << literal2expr(c) << "\n"; + for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n"; for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";); if (is_conflict) { diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index d22a65fb8..1c83b3a69 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -38,6 +38,13 @@ namespace euf { for (unsigned i = 0; i < mdl.size(); ++i) phase[i] = mdl[i] == l_true; + if (bool_search.unsat_set().empty()) { + enable_trace("arith"); + enable_trace("sat"); + enable_trace("euf"); + TRACE("sat", s().display(tout)); + } + return bool_search.unsat_set().empty() ? l_true : l_undef; } } From daeaed1e82748c5b404d514bfc418a6ce69c21ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:13:40 -0800 Subject: [PATCH 043/220] revert debug only changes to sat-solver --- src/sat/sat_solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 5ac0fbcf3..56330d68d 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -2974,7 +2974,7 @@ namespace sat { } bool solver::should_rephase() { - return m_conflicts_since_init > 5 && m_conflicts_since_init > m_rephase_lim; + return m_conflicts_since_init > m_rephase_lim; } void solver::do_rephase() { @@ -3023,7 +3023,7 @@ namespace sat { UNREACHABLE(); break; } - m_rephase_inc = m_config.m_rephase_base; + m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; } From 6092bf534c38ffc9573e4f8475f418f31faacad9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 14:18:02 -0800 Subject: [PATCH 044/220] fix #6599 --- src/sat/sat_prob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index d8d58d091..691758154 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -140,7 +140,7 @@ namespace sat { model const& get_model() const override { return m_model; } - double get_priority(bool_var v) const { return 0; } + double get_priority(bool_var v) const override { return 0; } std::ostream& display(std::ostream& out) const; From c0f80f92ba5f84671dfb56334959202ccc761f78 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 17:53:37 -0800 Subject: [PATCH 045/220] deal with compiler warnings (unused variables etc) --- src/ast/rewriter/bv_rewriter.cpp | 2 +- src/sat/sat_ddfw.cpp | 3 +-- src/sat/smt/arith_sls.cpp | 6 +----- src/sat/smt/euf_local_search.cpp | 2 +- src/smt/theory_pb.cpp | 2 +- src/test/api.cpp | 1 + src/test/bit_blaster.cpp | 10 +++------- src/test/interval.cpp | 11 +++++------ 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 1c374dcb8..5f76d3fdd 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1554,7 +1554,7 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re if (eq_args) { if (m.is_ite(new_args.back(), x, y, z)) { ptr_buffer args1, args2; - for (expr* arg : new_args) + for (unsigned i = 0; i < new_args.size(); ++i) args1.push_back(y), args2.push_back(z); result = m.mk_ite(x, m_util.mk_concat(args1), m_util.mk_concat(args2)); return BR_REWRITE2; diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 329d783f4..7ab05804a 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -583,8 +583,7 @@ namespace sat { void ddfw::shift_weights() { ++m_shifts; for (unsigned to_idx : m_unsat) { - auto& cf = m_clauses[to_idx]; - SASSERT(!cf.is_true()); + SASSERT(!m_clauses[to_idx].is_true()); unsigned from_idx = select_max_same_sign(to_idx); if (from_idx == UINT_MAX || disregard_neighbor()) from_idx = select_random_true_clause(); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index aeddd319b..26358dc17 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -72,9 +72,6 @@ namespace arith { continue; if (!s.lp().external_is_used(v)) continue; - int64_t old_value = 0; - if (s.is_registered_var(v)) - old_value = to_numeral(s.get_ivalue(v).x); int64_t new_value = m_vars[v].m_best_value; s.ensure_column(v); lp::column_index vj = s.lp().to_column_index(v); @@ -535,13 +532,12 @@ namespace arith { int64_t new_value; double result = 0; double max_result = -1; - theory_var max_var = 0; for (auto const & [coeff, x] : ineq->m_args) { if (!cm(!sign0, *ineq, x, coeff, new_value)) continue; double result = 0; auto old_value = m_vars[x].m_value; - for (auto const [coeff, bv] : m_vars[x].m_bool_vars) { + for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) { bool sign = !m_bool_search->value(bv); auto dtt_old = dtt(sign, *atom(bv)); auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp index 1c83b3a69..ca450e513 100644 --- a/src/sat/smt/euf_local_search.cpp +++ b/src/sat/smt/euf_local_search.cpp @@ -32,7 +32,7 @@ namespace euf { for (auto* th : m_solvers) th->set_bool_search(&bool_search); - lbool r = bool_search.check(0, nullptr, nullptr); + bool_search.check(0, nullptr, nullptr); auto const& mdl = bool_search.get_model(); for (unsigned i = 0; i < mdl.size(); ++i) diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index db076ef8c..718d5c65a 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1604,7 +1604,7 @@ namespace smt { std::cout << B << "\n"; } #endif - SASSERT(is_sat != l_true); + VERIFY(is_sat != l_true); return true; } diff --git a/src/test/api.cpp b/src/test/api.cpp index ccbbef6ea..ea95c1264 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -100,6 +100,7 @@ static void test_mk_distinct() { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); + VERIFY(d); ENSURE(cb_called); Z3_del_config(cfg); Z3_del_context(ctx); diff --git a/src/test/bit_blaster.cpp b/src/test/bit_blaster.cpp index 8c4cd0903..fa623f767 100644 --- a/src/test/bit_blaster.cpp +++ b/src/test/bit_blaster.cpp @@ -28,13 +28,9 @@ void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector sort_ref b(m); b = m.mk_bool_sort(); for (unsigned i = 0; i < sz; ++i) { - char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i); -#else - sprintf(buffer, "%s%d.smt", prefix, i); -#endif - r.push_back(m.mk_const(symbol(buffer), b)); + std::stringstream ous; + ous << prefix << i << ".smt2"; + r.push_back(m.mk_const(symbol(ous.str()), b)); } } diff --git a/src/test/interval.cpp b/src/test/interval.cpp index f289871de..64f6cb88c 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -24,6 +24,7 @@ Revision History: #include "util/debug.h" #include "util/rlimit.h" #include +#include template class interval_manager; typedef im_default_config::interval interval; @@ -200,15 +201,13 @@ static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; +static std::stringstream ous; char const * get_next_file_name() { -#ifdef _WINDOWS - sprintf_s(g_buffer, BUFFER_SZ, "interval_lemma_%d.smt2", g_problem_id); -#else - sprintf(g_buffer, "interval_lemma_%d.smt2", g_problem_id); -#endif + ous.clear(); + ous << "interval_lemma_" << g_problem_id << ".smt2"; g_problem_id++; - return g_buffer; + return ous.str().c_str(); } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, From cb814732603b584511b49a759fe5c99d3627bdb7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 17:54:26 -0800 Subject: [PATCH 046/220] add destructive equality resolution to the main simplifier. --- src/ast/rewriter/der.cpp | 5 +++-- src/ast/rewriter/th_rewriter.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index fd79529f9..0e28cf6f6 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -197,9 +197,10 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { m_pos2var.reserve(num_args, -1); - // Find all disequalities + // Find all equalities/disequalities for (unsigned i = 0; i < num_args; i++) { - is_eq = is_forall(q) ? is_var_diseq(literals.get(i), num_decls, v, t) : is_var_eq(literals.get(i), num_decls, v, t); + expr* arg = literals.get(i); + is_eq = is_forall(q) ? is_var_diseq(arg, num_decls, v, t) : is_var_eq(arg, num_decls, v, t); if (is_eq) { unsigned idx = v->get_idx(); if (m_map.get(idx, nullptr) == nullptr) { diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index c6ab22636..c57d3aabc 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -31,6 +31,7 @@ Notes: #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/der.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -54,6 +55,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { recfun_rewriter m_rec_rw; arith_util m_a_util; bv_util m_bv_util; + der m_der; expr_safe_replace m_rep; expr_ref_vector m_pinned; // substitution support @@ -819,6 +821,15 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result_pr = m().mk_transitivity(p1, p2); } + if (is_quantifier(result)) { + proof_ref p2(m()); + expr_ref r(m()); + m_der(to_quantifier(result), r, p2); + if (m().proofs_enabled() && result.get() != r.get()) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); SASSERT(old_q->get_sort() == result->get_sort()); @@ -839,6 +850,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_rec_rw(m), m_a_util(m), m_bv_util(m), + m_der(m), m_rep(m), m_pinned(m), m_used_dependencies(m) { From a6eed9f00c1a070e7c97871d2d2f9319b6e82480 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Feb 2023 18:43:20 -0800 Subject: [PATCH 047/220] Update api.cpp fix test --- src/test/api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/api.cpp b/src/test/api.cpp index ea95c1264..560dd1121 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -88,7 +88,7 @@ void test_bvneg() { static bool cb_called = false; static void my_cb(Z3_context, Z3_error_code) { - cb_called = true; + cb_called = true; } static void test_mk_distinct() { @@ -100,8 +100,8 @@ static void test_mk_distinct() { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); - VERIFY(d); ENSURE(cb_called); + VERIFY(!d); Z3_del_config(cfg); Z3_del_context(ctx); From 6352340478e605711348a1abc8d2fdba23e9474d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 09:59:33 -0800 Subject: [PATCH 048/220] update do logging --- src/smt/mam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 44a435041..3804b7228 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -3958,7 +3958,7 @@ namespace { void relevant_eh(enode * n, bool lazy) override { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_expr(), m) << "\n"; tout << "mam: " << this << "\n";); - TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); + TRACE("mam", tout << "relevant_eh: #" << enode_pp(n, m_context) << "\n";); if (n->has_lbl_hash()) update_lbls(n, n->get_lbl_hash()); From 9b6ac45e022d53b263d5507500ec72b76a5ccb13 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 10:03:38 -0800 Subject: [PATCH 049/220] compile warnings Signed-off-by: Nikolaj Bjorner --- src/test/interval.cpp | 8 +++----- src/test/no_overflow.cpp | 4 ++-- src/test/qe_arith.cpp | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/interval.cpp b/src/test/interval.cpp index 64f6cb88c..289265949 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -200,14 +200,12 @@ static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { #define BUFFER_SZ 256 static int g_problem_id = 0; -static char g_buffer[BUFFER_SZ]; -static std::stringstream ous; -char const * get_next_file_name() { - ous.clear(); +std::string get_next_file_name() { + std::stringstream ous; ous << "interval_lemma_" << g_problem_id << ".smt2"; g_problem_id++; - return ous.str().c_str(); + return ous.str(); } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, diff --git a/src/test/no_overflow.cpp b/src/test/no_overflow.cpp index dd826bad8..c7124a5ce 100644 --- a/src/test/no_overflow.cpp +++ b/src/test/no_overflow.cpp @@ -529,8 +529,8 @@ void test_div(unsigned bvsize) { Z3_del_context(ctx); } -typedef Z3_ast (Z3_API *NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); -typedef Z3_ast (Z3_API *ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); +typedef Z3_ast (*NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); +typedef Z3_ast (*ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); typedef enum { OVFL_FUNC, UDFL_FUNC } overflow_type; diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 859d7f2e5..2e170979a 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -377,6 +377,9 @@ static void add_random_ineq( case opt::t_mod: NOT_IMPLEMENTED_YET(); break; + default: + NOT_IMPLEMENTED_YET(); + break; } fmls.push_back(fml); mbo.add_constraint(vars, rational(coeff), rel); From bc6037464d4f63e7ff8721c36f9b3d7709cf924c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 10:08:31 -0800 Subject: [PATCH 050/220] clean up build warnings Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_convex_closure.cpp | 16 +++++++++------- src/sat/smt/arith_sls.cpp | 5 ++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/muz/spacer/spacer_convex_closure.cpp b/src/muz/spacer/spacer_convex_closure.cpp index 476821643..e313c972b 100644 --- a/src/muz/spacer/spacer_convex_closure.cpp +++ b/src/muz/spacer/spacer_convex_closure.cpp @@ -22,21 +22,23 @@ Notes: #include "ast/rewriter/th_rewriter.h" namespace { + +#ifdef Z3DEBUG bool is_int_matrix(const spacer::spacer_matrix &matrix) { - rational val; - for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) { + for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) for (unsigned j = 0, cols = matrix.num_cols(); j < cols; j++) - if (!matrix.get(i, j).is_int()) return false; - } + if (!matrix.get(i, j).is_int()) + return false; return true; } bool is_sorted(const vector &data) { - for (unsigned i = 0; i < data.size() - 1; i++) { - if (!(data[i] >= data[i + 1])) return false; - } + for (unsigned i = 0; i < data.size() - 1; i++) + if (!(data[i] >= data[i + 1])) + return false; return true; } +#endif /// Check whether all elements of \p data are congruent modulo \p m bool is_congruent_mod(const vector &data, const rational &m) { diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 26358dc17..42594f043 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -308,10 +308,10 @@ namespace arith { // cached dts has to be updated when the score of literals are updated. // double sls::dscore(var_t v, int64_t new_value) const { - auto const& vi = m_vars[v]; - verbose_stream() << "dscore " << v << "\n"; double score = 0; #if 0 + auto const& vi = m_vars[v]; + verbose_stream() << "dscore " << v << "\n"; for (auto const& [coeff, lit] : vi.m_literals) for (auto cl : m_bool_search->get_use_list(lit)) score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); @@ -530,7 +530,6 @@ namespace arith { if (!ineq) return -1; int64_t new_value; - double result = 0; double max_result = -1; for (auto const & [coeff, x] : ineq->m_args) { if (!cm(!sign0, *ineq, x, coeff, new_value)) From 6454e7fa3f541029868519c04acd16433ee2b33b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 11:03:04 -0800 Subject: [PATCH 051/220] apply rewriting if result of destructive equality resolution is simplified --- src/ast/rewriter/th_rewriter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index c57d3aabc..93b867c45 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -821,17 +821,28 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result_pr = m().mk_transitivity(p1, p2); } + TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); + + proof_ref p2(m()); + expr_ref r(m()); + + bool der_change = false; if (is_quantifier(result)) { - proof_ref p2(m()); - expr_ref r(m()); m_der(to_quantifier(result), r, p2); + der_change = result.get() != r.get(); + if (m().proofs_enabled() && der_change) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + + if (der_change) { + th_rewriter rw(m()); + rw(result, r, p2); if (m().proofs_enabled() && result.get() != r.get()) result_pr = m().mk_transitivity(result_pr, p2); result = r; } - TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); - SASSERT(old_q->get_sort() == result->get_sort()); return true; } From 0758c930868959937b09d94568ea26bc6297a196 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 11:09:52 -0800 Subject: [PATCH 052/220] fix #6591 - add check for lambdas similar to as-array in context of quantifiers. MBQI is not a decision procedure for this combination and can then incorrectly conclude satisfiabiltiy. Scenario The formula contains assertions - bv = (map or (lambda ..) t) - forall y (not (select bv (pair s y))) Since bv is extensionally equal to a term that depends on a lambda, MBQI cannot just take the current finite approximation of bv when checking the quantifier for satisfiability. --- src/smt/theory_array_full.cpp | 8 ++++++++ src/smt/theory_array_full.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 120e12418..a4876ab4d 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -252,6 +252,8 @@ namespace smt { else if (m.is_lambda_def(n->get_decl())) { instantiate_default_lambda_def_axiom(n); d->m_lambdas.push_back(n); + m_lambdas.push_back(n); + ctx.push_trail(push_back_vector(m_lambdas)); } return r; } @@ -830,6 +832,12 @@ namespace smt { return true; } } + for (enode* n : m_lambdas) + for (enode* p : n->get_parents()) + if (!ctx.is_beta_redex(p, n)) { + TRACE("array", tout << "not a beta redex " << enode_pp(p, ctx) << "\n"); + return true; + } return false; } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 210426e10..98e53627c 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -86,6 +86,7 @@ namespace smt { bool has_unitary_domain(app* array_term); std::pair mk_epsilon(sort* s); enode_vector m_as_array; + enode_vector m_lambdas; bool has_non_beta_as_array(); bool instantiate_select_const_axiom(enode* select, enode* cnst); From 755b517001cf3f6926fd448a8e568e32efde0689 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Feb 2023 14:02:37 -0800 Subject: [PATCH 053/220] fix #6600 ensure that semantics of last-indexof(t,"") = len(t) --- src/ast/rewriter/seq_axioms.cpp | 4 ++-- src/util/zstring.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 14f34f1f6..e08ead08b 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -513,8 +513,8 @@ namespace seq { !contains(t, s) => i = -1 |t| = 0 => |s| = 0 or i = -1 - |t| = 0 & |s| = 0 => i = 0 |t| != 0 & contains(t, s) => t = xsy & i = len(x) + |s| = 0 => i = len(t) |s| = 0 or s = s_head*s_tail |s| = 0 or !contains(s_tail*y, s) @@ -540,7 +540,7 @@ namespace seq { add_clause(cnt, i_eq_m1); add_clause(~t_eq_empty, s_eq_empty, i_eq_m1); - add_clause(~t_eq_empty, ~s_eq_empty, i_eq_0); + add_clause(~s_eq_empty, mk_eq(i, mk_len(t))); add_clause(t_eq_empty, ~cnt, mk_seq_eq(t, xsy)); add_clause(t_eq_empty, ~cnt, mk_eq(i, mk_len(x))); add_clause(s_eq_empty, mk_eq(s, mk_concat(s_head, s_tail))); diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 7d2b4296a..570510458 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -214,8 +214,7 @@ int zstring::indexofu(zstring const& other, unsigned offset) const { } int zstring::last_indexof(zstring const& other) const { - if (length() == 0 && other.length() == 0) return 0; - if (other.length() == 0) return -1; + if (other.length() == 0) return length(); if (other.length() > length()) return -1; for (unsigned last = length() - other.length() + 1; last-- > 0; ) { bool suffix = true; From 4aa05b2b5759a1049464e9ff453c9b448d82b835 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Feb 2023 12:16:43 -0800 Subject: [PATCH 054/220] remove limiting error mode #6600 --- src/parsers/smt2/smt2parser.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 9f61d48ca..7a1fd640a 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2640,8 +2640,6 @@ namespace smt2 { check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { parse_expr(); - if (!is_ground(expr_stack().back())) - throw cmd_exception("invalid get-value term, term must be ground and must not contain quantifiers"); m_cached_strings.push_back(m_scanner.cached_str(cache_it, m_cache_end)); cache_it = m_cache_end; } @@ -2680,7 +2678,7 @@ namespace smt2 { m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; m_ctx.display(m_ctx.regular_stream(), v); - m_ctx.regular_stream() << ")"; + m_ctx.regular_stream() << ")"; } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); From 146f0eae0687c3ee5e7030deff64e5ea6c2774e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Feb 2023 12:17:14 -0800 Subject: [PATCH 055/220] wip - arith local search --- src/sat/sat_ddfw.cpp | 2 +- src/sat/sat_solver.cpp | 6 ++++ src/sat/smt/arith_sls.cpp | 74 ++++++++++++++++++++++----------------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 7ab05804a..5d80e5af4 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -62,7 +62,7 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); m_steps_since_progress = 0; - while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { + while (m_min_sz > 0 && m_steps_since_progress++ <= 150000) { if (should_reinit_weights()) do_reinit_weights(); else if (do_flip()); else if (do_literal_flip()); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 56330d68d..d8d68d262 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1302,6 +1302,12 @@ namespace sat { return l_undef; } + if (false && m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { + IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); + bounded_local_search(); + exit(0); + } + log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index 42594f043..b32358a3c 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -207,41 +207,59 @@ namespace arith { return false; } - bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { - VERIFY(ineq.is_true() == sign); - verbose_stream() << "cm " << ineq << " for " << v << " sign " << sign << "\n"; + bool sls::cm(bool new_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + SASSERT(ineq.is_true() == new_sign); + VERIFY(ineq.is_true() == new_sign); auto bound = ineq.m_bound; auto argsv = ineq.m_args_value; bool solved = false; int64_t delta = argsv - bound; - if (sign) { + auto make_eq = [&]() { + SASSERT(delta != 0); + if (delta < 0) + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + else + new_value = value(v) - (delta + abs(coeff) - 1) / coeff; + solved = argsv + coeff * (new_value - value(v)) == bound; + if (!solved && abs(coeff) == 1) { + verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; + verbose_stream() << new_value << " " << value(v) << " delta " << delta << " lhs " << (argsv + coeff * (new_value - value(v))) << " bound " << bound << "\n"; + UNREACHABLE(); + } + return solved; + }; + + auto make_diseq = [&]() { + if (delta >= 0) + delta++; + else + delta--; + new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; + VERIFY(argsv + coeff * (new_value - value(v)) != bound); + return true; + }; + + if (new_sign) { switch (ineq.m_op) { case ineq_kind::LE: + // args <= bound -> args > bound SASSERT(argsv <= bound); SASSERT(delta <= 0); - delta--; + --delta; new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; VERIFY(argsv + coeff * (new_value - value(v)) > bound); return true; case ineq_kind::LT: + // args < bound -> args >= bound SASSERT(argsv <= ineq.m_bound); SASSERT(delta <= 0); new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; VERIFY(argsv + coeff * (new_value - value(v)) >= bound); return true; case ineq_kind::EQ: - if (delta >= 0) - delta++; - else - delta--; - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - VERIFY(argsv + coeff * (new_value - value(v)) != bound); - return true; + return make_diseq(); case ineq_kind::NE: - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - solved = argsv + coeff * (new_value - value(v)) == bound; - if (!solved) verbose_stream() << "did not solve disequality " << ineq << " for " << v << "\n"; - return solved; + return make_eq(); default: UNREACHABLE(); break; @@ -263,18 +281,9 @@ namespace arith { VERIFY(argsv + coeff * (new_value - value(v)) < bound); return true; case ineq_kind::NE: - if (delta >= 0) - delta++; - else - delta--; - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - VERIFY(argsv + coeff * (new_value - value(v)) != bound); - return true; + return make_diseq(); case ineq_kind::EQ: - new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff; - solved = argsv + coeff * (new_value - value(v)) == bound; - if (!solved) verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n"; - return solved; + return make_eq(); default: UNREACHABLE(); break; @@ -291,10 +300,10 @@ namespace arith { int64_t new_value; auto v = ineq.m_var_to_flip; if (v == UINT_MAX) { - verbose_stream() << "no var to flip\n"; + // verbose_stream() << "no var to flip\n"; return false; } - if (!cm(sign, ineq, v, new_value)) { + if (!cm(!sign, ineq, v, new_value)) { verbose_stream() << "no critical move for " << v << "\n"; return false; } @@ -308,6 +317,7 @@ namespace arith { // cached dts has to be updated when the score of literals are updated. // double sls::dscore(var_t v, int64_t new_value) const { + verbose_stream() << "dscore\n"; double score = 0; #if 0 auto const& vi = m_vars[v]; @@ -558,12 +568,12 @@ namespace arith { auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(ineq->is_true() == sign); + SASSERT(ineq->is_true() != sign); int64_t new_value; for (auto const& [coeff, v] : ineq->m_args) { double result = 0; - if (cm(sign, *ineq, v, coeff, new_value)) + if (cm(!sign, *ineq, v, coeff, new_value)) result = dscore(v, new_value); // just pick first positive, or pick a max? if (result > 0) { @@ -576,7 +586,7 @@ namespace arith { // switch to dscore mode void sls::on_rescale() { - m_dscore_mode = true; + // m_dscore_mode = true; } void sls::on_save_model() { From 828fff96849f5f102e8217b4882650c20f31520b Mon Sep 17 00:00:00 2001 From: hgvk94 Date: Wed, 22 Feb 2023 18:28:33 -0500 Subject: [PATCH 056/220] fix #6543. don't assume order on bindings --- src/muz/spacer/spacer_global_generalizer.cpp | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/muz/spacer/spacer_global_generalizer.cpp b/src/muz/spacer/spacer_global_generalizer.cpp index 8c0d7aa6f..55bc4eec7 100644 --- a/src/muz/spacer/spacer_global_generalizer.cpp +++ b/src/muz/spacer/spacer_global_generalizer.cpp @@ -170,14 +170,14 @@ void lemma_global_generalizer::subsumer::mk_col_names(const lemma_cluster &lc) { m_col_names.reserve(sub.get_num_bindings()); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - // get var id (sub is in reverse order) - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); auto *sort = r.get_expr()->get_sort(); - - if (!m_col_names.get(j) || m_col_names.get(j)->get_sort() != sort) { + auto i = v.first; + SASSERT(0 <= i && i < sz); + if (!m_col_names.get(i) || m_col_names.get(i)->get_sort() != sort) { // create a fresh skolem constant for the jth variable // reuse variables if they are already here and have matching sort - m_col_names[j] = m.mk_fresh_const("mrg_cvx!!", sort); + m_col_names[i] = m.mk_fresh_const("mrg_cvx!!", sort); } } @@ -210,10 +210,13 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( is_first = false; } + unsigned i; for (unsigned j = 0; j < n_vars; j++) { - sub.get_binding(n_vars - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; + SASSERT(0 <= i && i < n_vars); if (is_numeral(r.get_expr(), num)) { - m_col_lcm[j] = lcm(m_col_lcm.get(j), abs(denominator(num))); + m_col_lcm[i] = lcm(m_col_lcm.get(i), abs(denominator(num))); } } } @@ -229,14 +232,17 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( cc.set_col_var(j, mk_rat_mul(m_col_lcm.get(j), m_col_names.get(j))); vector row; + unsigned i; for (const auto &lemma : lemmas) { row.reset(); + row.reserve(n_vars); const substitution &sub = lemma.get_sub(); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; VERIFY(is_numeral(r.get_expr(), num)); - row.push_back(m_col_lcm.get(j) * num); + row[i] = m_col_lcm.get(i) * num; } // -- add normalized row to convex closure From 6e7d80633da849da13633ca000a05e1573ad509a Mon Sep 17 00:00:00 2001 From: Julian Parsert Date: Tue, 28 Feb 2023 19:44:21 +0000 Subject: [PATCH 057/220] Documentation on how to add z3 to CMake project using FetchContent and documentation to recdef function. (#6613) * Added overloaded versions of context::recfun in the c++ api that allow for the declaration of recursive functions where the domain is given by a z3::sort_vector instead of an arity and sort* * added documentation to recdef function * added a section in the README-CMake.md that explains how z3 can be added to a CMake project as a dependency --------- Co-authored-by: Julian Parsert --- README-CMake.md | 31 +++++++++++++++++++++++++++++++ src/api/c++/z3++.h | 8 +++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/README-CMake.md b/README-CMake.md index 7b7381107..5845a52c3 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -90,6 +90,37 @@ CFLAGS="-m32" CXXFLAGS="-m32" CC=gcc CXX=g++ cmake ../ Note like with the ``CC`` and ``CXX`` flags this must be done on the very first invocation to CMake in the build directory. +### Adding Z3 as a dependency to a CMAKE Project + +CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) allows +the fetching and populating of an external project. This is useful when a certain version +of z3 is required that may not match with the system version. With the following code in the +cmake file of your project, z3 version 4.12.1 is downloaded to the build directory and the +cmake targets are added to the project: + +``` +FetchContent_Declare(z3 + GIT_REPOSITORY https://github.com/Z3Prover/z3 + GIT_TAG z3-4.12.1 +) +FetchContent_MakeAvailable(z3) +``` + +The header files can be added to the included directories as follows: + +``` +include_directories( ${z3_SOURCE_DIR}/src/api ) +``` + +Finally, the z3 library can be linked to a `yourTarget` using + +``` +target_link_libraries(yourTarget libz3) +``` +Note that this is `libz3` not `z3` (`libz3` refers to the library target from `src/CMakeLists.txt`). + + + ### Ninja [Ninja](https://ninja-build.org/) is a simple build system that is built for speed. diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f2c0f3f24..52d5e6573 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -366,7 +366,13 @@ namespace z3 { func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); - void recdef(func_decl, expr_vector const& args, expr const& body); + /** + * \brief add function definition body to declaration decl. decl needs to be declared using context::. + * @param decl + * @param args + * @param body + */ + void recdef(func_decl decl, expr_vector const& args, expr const& body); func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range); /** From 1a9990a92f5986a7ed8baf8eca3f211b1e482558 Mon Sep 17 00:00:00 2001 From: Kevin Phoenix Date: Tue, 28 Feb 2023 12:46:10 -0700 Subject: [PATCH 058/220] Use sys.getdefaultencoding() instead of sys.stdout.encoding (#6612) --- doc/mk_params_doc.py | 4 ++-- doc/mk_tactic_doc.py | 2 +- scripts/mk_util.py | 2 +- scripts/update_api.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/mk_params_doc.py b/doc/mk_params_doc.py index de5276416..021cab3c3 100644 --- a/doc/mk_params_doc.py +++ b/doc/mk_params_doc.py @@ -37,7 +37,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pm"],stdout=subprocess.PIPE).communicate()[0] modules = ["global"] if out != None: - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) module_re = re.compile(r"\[module\] (.*)\,") lines = out.split("\n") for line in lines: @@ -48,7 +48,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pmmd:%s" % module],stdout=subprocess.PIPE).communicate()[0] if out == None: continue - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) out = out.replace("\r","") ous.write(out) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index a22201e27..804df2f7e 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -28,7 +28,7 @@ def extract_params(ous, tac): out = subprocess.Popen([z3_exe, f"-tacticsmd:{tac}"], stdout=subprocess.PIPE).communicate()[0] if not out: return - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) if is_ws(out): return ous.write("### Parameters\n\n") diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 635b8cec1..805aea19d 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -122,7 +122,7 @@ FPMATH_ENABLED=getenv("FPMATH_ENABLED", "True") def check_output(cmd): out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] if out != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() if enc != None: return out.decode(enc).rstrip('\r\n') else: return out.rstrip('\r\n') else: diff --git a/scripts/update_api.py b/scripts/update_api.py index 62b961d67..a3d92a7e9 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1836,14 +1836,14 @@ if sys.version < '3': else: def _str_to_bytes(s): if isinstance(s, str): - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.encode(enc if enc != None else 'latin-1') else: return s def _to_pystr(s): if s != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.decode(enc if enc != None else 'latin-1') else: return "" From 5974a2dc58221e1037919512bc83880e86c3596e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:35:54 -0800 Subject: [PATCH 059/220] remove m_b from lar_core_solver the column vector is pure overhead for the way the lar solver uses lp. Some other solver modules use column vectors b and integrate with the lp_core_solver_base. The interaction model should be reviewed. Unused solvers should be removed to make it easier to maintain this code. --- src/math/lp/lar_core_solver.h | 7 +- src/math/lp/lar_core_solver_def.h | 30 +++----- src/math/lp/lar_solver.cpp | 10 +-- src/math/lp/lp_core_solver_base.cpp | 10 ++- src/math/lp/lp_core_solver_base.h | 11 +-- src/math/lp/lp_core_solver_base_def.h | 106 +++++++++----------------- src/math/lp/lp_dual_core_solver.h | 2 +- src/math/lp/lp_primal_core_solver.h | 4 +- 8 files changed, 67 insertions(+), 113 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index bcd33966f..8a6c64ef0 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -66,6 +66,7 @@ public: ); lp_settings & settings() { return m_r_solver.m_settings;} + const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } @@ -340,6 +341,7 @@ public: switch (m_column_types[j]) { case column_type::free_column: lp_assert(false); // unreachable + break; case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; @@ -365,7 +367,7 @@ public: } } - lp_assert(is_zero_vector(s.m_b)); + // lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } @@ -463,7 +465,8 @@ public: m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; - } else { + } + else { prepare_solver_x_with_signature_tableau(signature); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 939a05114..75fff64fd 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,14 +46,10 @@ lar_core_solver::lar_core_solver( column_names) { } - - void lar_core_solver::calculate_pivot_row(unsigned i) { m_r_solver.calculate_pivot_row(i); } - - void lar_core_solver::prefix_r() { if (!m_r_solver.m_settings.use_tableau()) { m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); @@ -67,7 +63,7 @@ void lar_core_solver::prefix_r() { init_column_row_nz_for_r_solver(); } - m_r_solver.m_b.resize(m_r_solver.m_m()); + // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); @@ -78,7 +74,7 @@ void lar_core_solver::prefix_r() { } void lar_core_solver::prefix_d() { - m_d_solver.m_b.resize(m_d_solver.m_m()); + // m_d_solver.m_b.resize(m_d_solver.m_m()); m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); m_d_solver.m_costs.resize(m_d_solver.m_n()); @@ -100,9 +96,8 @@ void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); - for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) { - m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); - } + for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) + m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); } void lar_core_solver::fill_not_improvable_zero_sum() { @@ -115,26 +110,23 @@ void lar_core_solver::fill_not_improvable_zero_sum() { m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; - if (!numeric_traits::is_zero(cost_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); - } + if (!numeric_traits::is_zero(cost_j)) + m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); } // m_costs are expressed by m_d ( additional costs), substructing the latter gives 0 for (unsigned j = 0; j < m_r_solver.m_n(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; const mpq & d_j = m_r_solver.m_d[j]; - if (!numeric_traits::is_zero(d_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); - } + if (!numeric_traits::is_zero(d_j)) + m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); } } unsigned lar_core_solver::get_number_of_non_ints() const { unsigned n = 0; - for (auto & x : m_r_solver.m_x) { - if (x.is_int() == false) - n++; - } + for (auto & x : m_r_solver.m_x) + if (!x.is_int()) + n++; return n; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 46ed0b5a9..86ecb02b9 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -8,8 +8,8 @@ namespace lp { - ////////////////// methods //////////////////////////////// static_matrix& lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A; } + static_matrix const& lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A; } lp_settings& lar_solver::settings() { return m_settings; } @@ -18,7 +18,6 @@ namespace lp { statistics& lar_solver::stats() { return m_settings.stats(); } - void lar_solver::updt_params(params_ref const& _p) { smt_params_helper p(_p); set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); @@ -42,7 +41,6 @@ namespace lp { } lar_solver::~lar_solver() { - for (auto t : m_terms) delete t; } @@ -1406,7 +1404,6 @@ namespace lp { A_r().m_rows.pop_back(); A_r().m_columns.pop_back(); CASSERT("check_static_matrix", A_r().is_correct()); - slv.m_b.pop_back(); } void lar_solver::remove_last_column_from_A() { @@ -1716,8 +1713,6 @@ namespace lp { m_terms.push_back(t); } - - // terms bool lar_solver::all_vars_are_registered(const vector>& coeffs) { for (const auto& p : coeffs) { @@ -1787,8 +1782,7 @@ namespace lp { if (use_tableau()) { A_r().fill_last_row_with_pivoting(*term, j, - m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); } else { fill_last_row_of_A_r(A_r(), term); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 83da68d9d..c8b1692d2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -36,8 +36,8 @@ template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base::lp_core_solver_base( - lp::static_matrix&, vector&, - vector&, + lp::static_matrix&, // vector&, + vector&, vector &, vector &, vector&, vector&, @@ -80,7 +80,9 @@ template void lp::lp_core_solver_base >::calc template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); -template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, +template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, + // vector >&, + vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); @@ -91,7 +93,7 @@ template bool lp::lp_core_solver_base >::upda template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, - vector&, + //vector&, vector&, vector &, vector &, vector&, diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5cde4485d..b7010aa54 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -67,7 +67,7 @@ public: indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A - vector & m_b; // the right side + // vector const & m_b; // the right side vector & m_basis; vector& m_nbasis; vector& m_basis_heading; @@ -118,7 +118,7 @@ public: unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + //vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -213,7 +213,6 @@ public: return !below_bound(x, bound) && !above_bound(x, bound); } - bool need_to_pivot_to_basis_tableau() const { unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { @@ -284,7 +283,6 @@ public: return below_bound(m_x[p], m_upper_bounds[p]); } - bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } @@ -310,7 +308,6 @@ public: bool d_is_not_positive(unsigned j) const; - bool time_is_over(); void rs_minus_Anx(vector & rs); @@ -351,8 +348,6 @@ public: std::string column_name(unsigned column) const; - void copy_right_side(vector & rs); - void add_delta_to_xB(vector & del); void find_error_in_BxB(vector& rs); @@ -366,8 +361,6 @@ public: ret = snap_column_to_bound(j) || ret; return ret; } - - bool snap_column_to_bound(unsigned j) { switch (m_column_types[j]) { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index f85de1111..4d29234a8 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -28,7 +28,7 @@ namespace lp { template lp_core_solver_base:: lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + // vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -47,7 +47,7 @@ lp_core_solver_base(static_matrix & A, m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), - m_b(b), + // m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), @@ -220,7 +220,7 @@ A_mult_x_is_off() const { lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) { for (unsigned i = 0; i < m_m(); i++) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); + X delta = /*m_b[i] */- m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { return true; } @@ -230,8 +230,8 @@ A_mult_x_is_off() const { T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i = 0; i < m_m(); i++) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); + X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); + auto eps = feps /* * (one + T(0.1) * abs(m_b[i])) */; if (delta > eps) { #if 0 @@ -263,8 +263,8 @@ A_mult_x_is_off_on_index(const vector & index) const { T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i : index) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); + X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); + auto eps = feps /* *(one + T(0.1) * abs(m_b[i])) */; if (delta > eps) { #if 0 @@ -400,7 +400,8 @@ column_is_dual_feasible(unsigned j) const { case column_type::lower_bound: return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: - lp_assert(false); // impossible case + UNREACHABLE(); + break; case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: @@ -441,7 +442,7 @@ template void lp_core_solver_base:: rs_minus_Anx(vector & rs) { unsigned row = m_m(); while (row--) { - auto &rsv = rs[row] = m_b[row]; + auto& rsv = rs[row] = zero_of_type(); //m_b[row]; for (auto & it : m_A.m_rows[row]) { unsigned j = it.var(); if (m_basis_heading[j] < 0) { @@ -454,8 +455,7 @@ rs_minus_Anx(vector & rs) { template bool lp_core_solver_base:: find_x_by_solving() { solve_Ax_eq_b(); - bool ret= !A_mult_x_is_off(); - return ret; + return !A_mult_x_is_off(); } template bool lp_core_solver_base::column_is_feasible(unsigned j) const { @@ -463,28 +463,12 @@ template bool lp_core_solver_base::column_is_feas switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]) && + !this->below_bound(x, this->m_lower_bounds[j]); case column_type::lower_bound: - if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->below_bound(x, this->m_lower_bounds[j]); case column_type::upper_bound: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]); case column_type::free_column: return true; break; @@ -598,7 +582,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { if (is_zero(coeff)) return false; - this->m_b[pivot_row] /= coeff; + // this->m_b[pivot_row] /= coeff; for (unsigned j = 0; j < size; j++) { auto & c = row[j]; if (c.var() != pivot_col) { @@ -662,59 +646,52 @@ basis_has_no_doubles() const { template bool lp_core_solver_base:: non_basis_has_no_doubles() const { std::set bm; - for (auto j : m_nbasis) { - bm.insert(j); - } + for (auto j : m_nbasis) + bm.insert(j); return bm.size() == m_nbasis.size(); } template bool lp_core_solver_base:: basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_m(); i++) { + for (unsigned i = 0; i < m_m(); i++) if (m_basis_heading[m_basis[i]] != static_cast(i)) - return false; - } + return false; return true; } template bool lp_core_solver_base:: non_basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_nbasis.size(); i++) { + for (unsigned i = 0; i < m_nbasis.size(); i++) if (m_basis_heading[m_nbasis[i]] != - static_cast(i) - 1) return false; - } - for (unsigned j = 0; j < m_A.column_count(); j++) { - if (m_basis_heading[j] >= 0) { + + for (unsigned j = 0; j < m_A.column_count(); j++) + if (m_basis_heading[j] >= 0) lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); - } - } + return true; } template bool lp_core_solver_base:: basis_heading_is_correct() const { - if ( m_A.column_count() > 10 ) { // for the performance reason + if ( m_A.column_count() > 10 ) // for the performance reason return true; - } + lp_assert(m_basis_heading.size() == m_A.column_count()); lp_assert(m_basis.size() == m_A.row_count()); lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller - if (!basis_has_no_doubles()) { + + if (!basis_has_no_doubles()) return false; - } - - if (!non_basis_has_no_doubles()) { + + if (!non_basis_has_no_doubles()) return false; - } + + if (!basis_is_correctly_represented_in_heading()) + return false; - if (!basis_is_correctly_represented_in_heading()) { + if (!non_basis_is_correctly_represented_in_heading()) return false; - } - - if (!non_basis_is_correctly_represented_in_heading()) { - return false; - } - - + return true; } @@ -781,14 +758,6 @@ column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template void lp_core_solver_base:: -copy_right_side(vector & rs) { - unsigned i = m_m(); - while (i --) { - rs[i] = m_b[i]; - } -} - template void lp_core_solver_base:: add_delta_to_xB(vector & del) { unsigned i = m_m(); @@ -819,7 +788,8 @@ solve_Ax_eq_b() { rs_minus_Anx(rs); m_factorization->solve_By(rs); copy_rs_to_xB(rs); - } else { + } + else { vector rs(m_m()); rs_minus_Anx(rs); vector rrs = rs; // another copy of rs diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h index f4aa4b44d..804879c3d 100644 --- a/src/math/lp/lp_dual_core_solver.h +++ b/src/math/lp/lp_dual_core_solver.h @@ -61,7 +61,7 @@ public: lp_settings & settings, const column_namer & column_names): lp_core_solver_base(A, - b, + // b, basis, nbasis, heading, diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 3abf9dbc0..4b6163df8 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -948,7 +948,7 @@ public: const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): - lp_core_solver_base(A, b, + lp_core_solver_base(A, // b, basis, nbasis, heading, @@ -983,7 +983,7 @@ public: const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): - lp_core_solver_base(A, b, + lp_core_solver_base(A, // b, basis, nbasis, heading, From 76aad689c65383f207684510afda9ca5b54c999a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:38:36 -0800 Subject: [PATCH 060/220] Update smt_context_pp.cpp print units in statistics --- src/smt/smt_context_pp.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 40e789204..a6088fdf7 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -704,14 +704,20 @@ namespace smt { for (clause* cp : m_lemmas) if (cp->get_num_literals() == 2) ++bin_lemmas; + auto num_units = [&]() { + if (m_scopes.empty()) + return m_assigned_literals.size(); + else + return m_scopes[0].m_assigned_literals_lim; + }; 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 << " " - << std::setw(5) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; + << 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(7) << mem_stat() << ")\n"; @@ -739,8 +745,8 @@ 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, -3, -4, -4, -1 }; - char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; + 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; l1 << "(smt.stats "; From 79d47eb3029e01635ac12d71bd47a06bfc67e330 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:39:00 -0800 Subject: [PATCH 061/220] add preprocessor parameter whether to use bound simplifier --- src/smt/params/preprocessor_params.cpp | 2 ++ src/smt/params/preprocessor_params.h | 1 + src/smt/params/smt_params_helper.pyg | 1 + src/solver/solver_preprocess.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 180242f85..b6b80a34e 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -30,6 +30,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { m_elim_unconstrained = p.elim_unconstrained(); m_solve_eqs = p.solve_eqs(); m_ng_lift_ite = static_cast(p.q_lift_ite()); + m_bound_simplifier = p.bound_simplifier(); } void preprocessor_params::updt_params(params_ref const & p) { @@ -63,4 +64,5 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_max_bv_sharing); DISPLAY_PARAM(m_pre_simplifier); DISPLAY_PARAM(m_nlquant_elim); + DISPLAY_PARAM(m_bound_simplifier); } diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index 55a55980b..d53cbbe8f 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -49,6 +49,7 @@ struct preprocessor_params : public pattern_inference_params, bool m_max_bv_sharing = true; bool m_pre_simplifier = true; bool m_nlquant_elim = false; + bool m_bound_simplifier = true; public: preprocessor_params(params_ref const & p = params_ref()): diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 86a0371f2..4b4a453c8 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('elim_unconstrained', BOOL, True, 'pre-processing: eliminate unconstrained subterms'), ('solve_eqs', BOOL, True, 'pre-processing: solve equalities'), ('propagate_values', BOOL, True, 'pre-processing: propagate values'), + ('bound_simplifier', BOOL, True, 'apply bounds simplification during pre-processing'), ('pull_nested_quantifiers', BOOL, False, 'pre-processing: pull nested quantifiers'), ('refine_inj_axioms', BOOL, True, 'pre-processing: refine injectivity axioms'), ('candidate_models', BOOL, False, 'create candidate models even when quantifier or theory reasoning is incomplete'), diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 38b0584b5..7fd9d1dba 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -39,6 +39,7 @@ Notes: #include "ast/simplifiers/push_ite.h" #include "ast/simplifiers/elim_term_ite.h" #include "ast/simplifiers/flatten_clauses.h" +#include "ast/simplifiers/bound_simplifier.h" #include "ast/simplifiers/cnf_nnf.h" #include "smt/params/smt_params.h" #include "solver/solver_preprocess.h" @@ -59,6 +60,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); + if (smtp.m_bound_simplifier) s.add_simplifier(alloc(bound_simplifier, m, p, st)); if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); From e87fa1c299b9a24503eba580281d154a41ff46d1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:39:30 -0800 Subject: [PATCH 062/220] remove stale file --- src/tactic/arith/bound_simplifier_tactic.h | 42 ---------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/tactic/arith/bound_simplifier_tactic.h diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h deleted file mode 100644 index b61a10004..000000000 --- a/src/tactic/arith/bound_simplifier_tactic.h +++ /dev/null @@ -1,42 +0,0 @@ -/*++ -Copyright (c) 2023 Microsoft Corporation - -Module Name: - - bound_simplifier_tactic.h - -Author: - - Nikolaj Bjorner (nbjorner) 2023-01-22 - -Tactic Documentation: - -## Tactic bound-simplifier - -### Short Description - -Tactic for simplifying arithmetical expressions modulo bounds - -### Long Description - -The tactic is used to eliminate occurrences of modulus expressions when it is known that terms are within the bounds -of the modulus. - - ---*/ -#pragma once - -#include "util/params.h" -#include "tactic/tactic.h" -#include "tactic/dependent_expr_state_tactic.h" -#include "ast/simplifiers/bound_simplifier.h" - -inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, - [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }); -} - -/* - ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") - ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "alloc(bound_simplifier, m, p, s)") -*/ From 25d45a3500cac1291198b00da0f83b147ef08914 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Feb 2023 17:40:00 -0800 Subject: [PATCH 063/220] fixes and tests for arith-sls --- src/sat/sat_ddfw.cpp | 28 +++--- src/sat/sat_ddfw.h | 5 +- src/sat/sat_solver.cpp | 48 ++++------- src/sat/sat_solver.h | 19 +--- src/sat/sat_types.h | 34 ++++++++ src/sat/smt/arith_sls.cpp | 177 ++++++++++++++++++++++---------------- src/sat/smt/arith_sls.h | 7 +- 7 files changed, 182 insertions(+), 136 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index 5d80e5af4..ca274be51 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -62,13 +62,16 @@ namespace sat { void ddfw::check_with_plugin() { m_plugin->init_search(); m_steps_since_progress = 0; - while (m_min_sz > 0 && m_steps_since_progress++ <= 150000) { + unsigned steps = 0; + while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) { if (should_reinit_weights()) do_reinit_weights(); + else if (steps % 5000 == 0) shift_weights(), m_plugin->on_rescale(); + else if (should_restart()) do_restart(), m_plugin->on_restart(); else if (do_flip()); else if (do_literal_flip()); - else if (should_restart()) do_restart(), m_plugin->on_restart(); else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(), m_plugin->on_rescale(); + ++steps; } m_plugin->finish_search(); } @@ -135,7 +138,7 @@ namespace sat { if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { - r = uses_plugin ? plugin_reward(v) : reward(v); + r = uses_plugin && is_external(v) ? m_vars[v].m_last_reward : reward(v); if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) @@ -472,9 +475,7 @@ namespace sat { void ddfw::save_best_values() { - if (m_unsat.empty()) - save_model(); - else if (m_unsat.size() < m_min_sz) { + if (m_unsat.size() < m_min_sz) { m_steps_since_progress = 0; if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11) save_model(); @@ -489,13 +490,20 @@ namespace sat { } } } + unsigned h = value_hash(); + unsigned occs = 0; + bool contains = m_models.find(h, occs); if (!m_models.contains(h)) { - for (unsigned v = 0; v < num_vars(); ++v) + for (unsigned v = 0; v < num_vars(); ++v) bias(v) += value(v) ? 1 : -1; - m_models.insert(h); - if (m_models.size() > m_config.m_max_num_models) - m_models.erase(*m_models.begin()); + if (m_models.size() > m_config.m_max_num_models) + m_models.erase(m_models.begin()->m_key); + } + m_models.insert(h, occs + 1); + if (occs > 100) { + m_restart_next = m_flips; + m_models.erase(h); } m_min_sz = m_unsat.size(); } diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 8c4f9287f..988365285 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -98,6 +98,7 @@ namespace sat { var_info() {} bool m_value = false; double m_reward = 0; + double m_last_reward = 0; unsigned m_make_count = 0; int m_bias = 0; bool m_external = false; @@ -127,7 +128,7 @@ namespace sat { uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0; uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0; unsigned m_min_sz = 0, m_steps_since_progress = 0; - hashtable> m_models; + u_map m_models; stopwatch m_stopwatch; parallel* m_par; @@ -153,7 +154,7 @@ namespace sat { inline double reward(bool_var v) const { return m_vars[v].m_reward; } - inline double plugin_reward(bool_var v) const { return is_external(v) ? m_plugin->reward(v) : reward(v); } + inline double plugin_reward(bool_var v) { return is_external(v) ? (m_vars[v].m_last_reward = m_plugin->reward(v)) : reward(v); } void set_external(bool_var v) { m_vars[v].m_external = true; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index d8d68d262..5c1ed6dae 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -40,26 +40,6 @@ Revision History: namespace sat { - /** - * Special cases of kissat style general backoff calculation. - * The version here calculates - * limit := value*log(C)^2*n*log(n) - * (effort calculation in kissat is based on ticks not clauses) - * - * respectively - * limit := conflicts + value*log(C)^2*n*log(n) - */ - void backoff::delta_effort(solver& s) { - count++; - unsigned d = value * count * log2(count + 1); - unsigned cl = log2(s.num_clauses() + 2); - limit = cl * cl * d; - } - - void backoff::delta_conflicts(solver& s) { - delta_effort(s); - limit += s.m_conflicts_since_init; - } solver::solver(params_ref const & p, reslimit& l): solver_core(l), @@ -1302,10 +1282,9 @@ namespace sat { return l_undef; } - if (false && m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { - IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); + if (m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { bounded_local_search(); - exit(0); + // exit(0); } log_stats(); @@ -1367,7 +1346,7 @@ namespace sat { void solver::bounded_local_search() { if (m_ext) { - verbose_stream() << "bounded local search\n"; + IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); do_restart(true); lbool r = m_ext->local_search(m_best_phase); verbose_stream() << r << "\n"; @@ -1388,8 +1367,8 @@ namespace sat { m_local_search->set_seed(m_rand()); scoped_rl.push_child(&(m_local_search->rlimit())); - m_backoffs.m_local_search.delta_effort(*this); - m_local_search->rlimit().push(m_backoffs.m_local_search.limit); + m_local_search_lim.inc(num_clauses()); + m_local_search->rlimit().push(m_local_search_lim.limit); m_local_search->reinit(*this, m_best_phase); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); @@ -1977,11 +1956,13 @@ namespace sat { m_search_sat_conflicts = m_config.m_search_sat_conflicts; m_search_next_toggle = m_search_unsat_conflicts; m_best_phase_size = 0; + + m_reorder.lo = m_config.m_reorder_base; + m_rephase.base = m_config.m_rephase_base; m_rephase_lim = 0; m_rephase_inc = 0; - m_reorder_lim = m_config.m_reorder_base; - m_backoffs.m_local_search.value = 500; - m_reorder_inc = 0; + m_local_search_lim.base = 500; + m_conflicts_since_restart = 0; m_force_conflict_analysis = false; m_restart_threshold = m_config.m_restart_initial; @@ -2981,6 +2962,7 @@ namespace sat { bool solver::should_rephase() { return m_conflicts_since_init > m_rephase_lim; +// return m_rephase.should_apply(m_conflicts_since_init); } void solver::do_rephase() { @@ -2994,7 +2976,7 @@ namespace sat { case PS_FROZEN: break; case PS_BASIC_CACHING: - switch (m_rephase_lim % 4) { + switch (m_rephase.count % 4) { case 0: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; @@ -3031,10 +3013,11 @@ namespace sat { } m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; + m_rephase.inc(m_conflicts_since_init, num_clauses()); } bool solver::should_reorder() { - return m_conflicts_since_init > m_reorder_lim; + return m_reorder.should_apply(m_conflicts_since_init); } void solver::do_reorder() { @@ -3078,8 +3061,7 @@ namespace sat { update_activity(v, m_rand(10)/10.0); } #endif - m_reorder_inc += m_config.m_reorder_base; - m_reorder_lim += m_reorder_inc; + m_reorder.inc(m_conflicts_since_init, num_clauses()); } void solver::updt_phase_counters() { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 703b36dd0..3a437855e 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -87,23 +87,10 @@ namespace sat { struct no_drat_params : public params_ref { no_drat_params() { set_bool("drat.disable", true); } }; - - struct backoff { - unsigned value = 1; - unsigned lo = 0; - unsigned hi = 0; - unsigned limit = 0; - unsigned count = 0; - void delta_effort(solver& s); - void delta_conflicts(solver& s); - }; class solver : public solver_core { public: struct abort_solver {}; - struct backoffs { - backoff m_local_search; - }; protected: enum search_state { s_sat, s_unsat }; @@ -172,11 +159,11 @@ namespace sat { unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; - backoffs m_backoffs; + backoff m_local_search_lim; unsigned m_rephase_lim; unsigned m_rephase_inc; - unsigned m_reorder_lim; - unsigned m_reorder_inc; + backoff m_rephase; + backoff m_reorder; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index d5d457cb0..427b6fb70 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -136,6 +136,40 @@ namespace sat { std::ostream& operator<<(std::ostream& out, sat::status const& st); std::ostream& operator<<(std::ostream& out, sat::status_pp const& p); + /** + * Special cases of kissat style general backoff calculation. + * The version here calculates + * limit := value*log(C)^2*n*log(n) + * (effort calculation in kissat is based on ticks not clauses) + * + * respectively + * limit := conflicts + value*log(C)^2*n*log(n) + */ + struct backoff { + unsigned base = 1; + unsigned lo = 0; + unsigned hi = UINT_MAX; + unsigned limit = 0; + unsigned count = 0; + + bool should_apply(unsigned n) const { + return limit <= n && lo <= n && n <= hi; + } + + void inc(unsigned num_clauses) { + count++; + unsigned d = base * count * log2(count + 1); + unsigned cl = log2(num_clauses + 2); + limit = cl * cl * d; + } + + void inc(unsigned num_conflicts, unsigned num_clauses) { + inc(num_clauses); + limit += num_conflicts; + } + + }; + }; diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp index b32358a3c..4fe153289 100644 --- a/src/sat/smt/arith_sls.cpp +++ b/src/sat/smt/arith_sls.cpp @@ -29,42 +29,49 @@ namespace arith { m_terms.reset(); } - void sls::log() { - IF_VERBOSE(2, verbose_stream() << "(sls :flips " << m_stats.m_num_flips << " :unsat " << unsat().size() << ")\n"); - } - void sls::save_best_values() { for (unsigned v = 0; v < s.get_num_vars(); ++v) m_vars[v].m_best_value = m_vars[v].m_value; - - auto check_bool_var = [&](sat::bool_var bv) { - auto const* ineq = atom(bv); - if (!ineq) - return; - sat::literal lit(bv, !m_bool_search->get_value(bv)); - int64_t d = dtt(lit.sign(), *ineq); - // verbose_stream() << "check " << lit << " " << *ineq << "\n"; - if (is_true(lit) != (d == 0)) { - verbose_stream() << lit << " " << *ineq << "\n"; + check_ineqs(); + if (unsat().size() == 1) { + auto idx = *unsat().begin(); + verbose_stream() << idx << "\n"; + auto const& c = *m_bool_search->m_clauses[idx].m_clause; + verbose_stream() << c << "\n"; + for (auto lit : c) { + bool_var bv = lit.var(); + ineq* i = atom(bv); + if (i) + verbose_stream() << lit << ": " << *i << "\n"; } - VERIFY(is_true(lit) == (d == 0)); - }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) - check_bool_var(v); + verbose_stream() << "\n"; + } } void sls::store_best_values() { // first compute assignment to terms // then update non-basic variables in tableau. - for (auto const& [t, v] : m_terms) { + + if (!unsat().empty()) + return; + + for (auto const& [t,v] : m_terms) { int64_t val = 0; lp::lar_term const& term = s.lp().get_term(t); - for (lp::lar_term::ival arg : term) { + for (lp::lar_term::ival const& arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; } - update(v, val); + if (v == 52) { + verbose_stream() << "update v" << v << " := " << val << "\n"; + for (lp::lar_term::ival const& arg : term) { + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + verbose_stream() << "v" << w << " := " << m_vars[w].m_best_value << " * " << to_numeral(arg.coeff()) << "\n"; + } + } + m_vars[v].m_best_value = val; } for (unsigned v = 0; v < s.get_num_vars(); ++v) { @@ -80,16 +87,15 @@ namespace arith { rational new_value_(new_value, rational::i64()); lp::impq val(new_value_, rational::zero()); s.lp().set_value_for_nbasic_column(vj.index(), val); - // TODO - figure out why this leads to unsound (unsat). } } lbool r = s.make_feasible(); VERIFY (!unsat().empty() || r == l_true); - if (unsat().empty()) { +#if 0 + if (unsat().empty()) s.m_num_conflicts = s.get_config().m_arith_propagation_threshold; - } - verbose_stream() << "has changed " << s.m_solver->has_changed_columns() << "\n"; +#endif auto check_bool_var = [&](sat::bool_var bv) { auto* ineq = m_bool_vars.get(bv, nullptr); @@ -105,10 +111,10 @@ namespace arith { return; switch (b->get_bound_kind()) { case lp_api::lower_t: - verbose_stream() << bv << " " << bound << " <= " << s.get_value(v) << "\n"; + verbose_stream() << "v" << v << " " << bound << " <= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n"; break; case lp_api::upper_t: - verbose_stream() << bv << " " << bound << " >= " << s.get_value(v) << "\n"; + verbose_stream() << "v" << v << " " << bound << " >= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n"; break; } int64_t value = 0; @@ -117,6 +123,12 @@ namespace arith { } ineq->m_args_value = value; verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n"; + for (auto const& [coeff, v] : ineq->m_args) + verbose_stream() << "v" << v << " := " << m_vars[v].m_best_value << "\n"; + s.display(verbose_stream()); + display(verbose_stream()); + UNREACHABLE(); + exit(0); }; if (unsat().empty()) { @@ -200,16 +212,16 @@ namespace arith { return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq); } - bool sls::cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value) { + bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t& new_value) { for (auto const& [coeff, w] : ineq.m_args) if (w == v) - return cm(sign, ineq, v, coeff, new_value); + return cm(old_sign, ineq, v, coeff, new_value); return false; } - bool sls::cm(bool new_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { - SASSERT(ineq.is_true() == new_sign); - VERIFY(ineq.is_true() == new_sign); + bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) { + SASSERT(ineq.is_true() != old_sign); + VERIFY(ineq.is_true() != old_sign); auto bound = ineq.m_bound; auto argsv = ineq.m_args_value; bool solved = false; @@ -239,7 +251,7 @@ namespace arith { return true; }; - if (new_sign) { + if (!old_sign) { switch (ineq.m_op) { case ineq_kind::LE: // args <= bound -> args > bound @@ -300,10 +312,10 @@ namespace arith { int64_t new_value; auto v = ineq.m_var_to_flip; if (v == UINT_MAX) { - // verbose_stream() << "no var to flip\n"; + IF_VERBOSE(1, verbose_stream() << "no var to flip\n"); return false; } - if (!cm(!sign, ineq, v, new_value)) { + if (!cm(sign, ineq, v, new_value)) { verbose_stream() << "no critical move for " << v << "\n"; return false; } @@ -316,16 +328,16 @@ namespace arith { // TODO - use cached dts instead of computed dts // cached dts has to be updated when the score of literals are updated. // - double sls::dscore(var_t v, int64_t new_value) const { - verbose_stream() << "dscore\n"; + double sls::dscore(var_t v, int64_t new_value) const { double score = 0; -#if 0 auto const& vi = m_vars[v]; - verbose_stream() << "dscore " << v << "\n"; - for (auto const& [coeff, lit] : vi.m_literals) - for (auto cl : m_bool_search->get_use_list(lit)) - score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); -#endif + for (auto const& [coeff, bv] : vi.m_bool_vars) { + sat::literal lit(bv, false); + for (auto cl : m_bool_search->get_use_list(lit)) + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + for (auto cl : m_bool_search->get_use_list(~lit)) + score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl); + } return score; } @@ -341,12 +353,12 @@ namespace arith { int64_t old_value = vi.m_value; for (auto const& [coeff, bv] : vi.m_bool_vars) { auto const& ineq = *atom(bv); - bool sign = !m_bool_search->value(bv); - int64_t dtt_old = dtt(sign, ineq); - int64_t dtt_new = dtt(sign, ineq, coeff, old_value, new_value); + bool old_sign = sign(bv); + int64_t dtt_old = dtt(old_sign, ineq); + int64_t dtt_new = dtt(old_sign, ineq, coeff, old_value, new_value); if ((dtt_old == 0) == (dtt_new == 0)) continue; - sat::literal lit(bv, sign); + sat::literal lit(bv, old_sign); if (dtt_old == 0) // flip from true to false lit.neg(); @@ -408,14 +420,14 @@ namespace arith { auto old_value = vi.m_value; for (auto const& [coeff, bv] : vi.m_bool_vars) { auto& ineq = *atom(bv); - bool sign = !m_bool_search->value(bv); - sat::literal lit(bv, sign); + bool old_sign = sign(bv); + sat::literal lit(bv, old_sign); SASSERT(is_true(lit)); ineq.m_args_value += coeff * (new_value - old_value); - int64_t dtt_new = dtt(sign, ineq); + int64_t dtt_new = dtt(old_sign, ineq); if (dtt_new != 0) m_bool_search->flip(bv); - SASSERT(dtt(!m_bool_search->value(bv), ineq) == 0); + SASSERT(dtt(sign(bv), ineq) == 0); } vi.m_value = new_value; } @@ -451,7 +463,7 @@ namespace arith { void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) { if (t.is_term()) { lp::lar_term const& term = s.lp().get_term(t); - + m_terms.push_back({t,v}); for (lp::lar_term::ival arg : term) { auto t2 = s.lp().column2tv(arg.column()); auto w = s.lp().local_to_external(t2.id()); @@ -479,6 +491,7 @@ namespace arith { auto& ineq = new_ineq(op, to_numeral(bound)); + add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1); m_bool_vars.set(bv, &ineq); m_bool_search->set_external(bv); @@ -516,7 +529,7 @@ namespace arith { } void sls::flip(sat::bool_var v) { - sat::literal lit(v, m_bool_search->get_value(v)); + sat::literal lit(v, !sign(v)); SASSERT(!is_true(lit)); auto const* ineq = atom(v); if (!ineq) @@ -524,7 +537,7 @@ namespace arith { if (!ineq) return; SASSERT(ineq->is_true() == lit.sign()); - flip(!lit.sign(), *ineq); + flip(sign(v), *ineq); } double sls::reward(sat::bool_var v) { @@ -535,21 +548,23 @@ namespace arith { } double sls::dtt_reward(sat::bool_var bv0) { - bool sign0 = !m_bool_search->get_value(bv0); + bool sign0 = sign(bv0); auto* ineq = atom(bv0); if (!ineq) return -1; int64_t new_value; double max_result = -1; for (auto const & [coeff, x] : ineq->m_args) { - if (!cm(!sign0, *ineq, x, coeff, new_value)) + if (!cm(sign0, *ineq, x, coeff, new_value)) continue; double result = 0; auto old_value = m_vars[x].m_value; for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) { - bool sign = !m_bool_search->value(bv); - auto dtt_old = dtt(sign, *atom(bv)); - auto dtt_new = dtt(sign, *atom(bv), coeff, old_value, new_value); + result += m_bool_search->reward(bv); + continue; + bool old_sign = sign(bv); + auto dtt_old = dtt(old_sign, *atom(bv)); + auto dtt_new = dtt(old_sign, *atom(bv), coeff, old_value, new_value); if ((dtt_new == 0) != (dtt_old == 0)) result += m_bool_search->reward(bv); } @@ -563,17 +578,17 @@ namespace arith { double sls::dscore_reward(sat::bool_var bv) { m_dscore_mode = false; - bool sign = !m_bool_search->get_value(bv); - sat::literal litv(bv, sign); + bool old_sign = sign(bv); + sat::literal litv(bv, old_sign); auto* ineq = atom(bv); if (!ineq) return 0; - SASSERT(ineq->is_true() != sign); + SASSERT(ineq->is_true() != old_sign); int64_t new_value; for (auto const& [coeff, v] : ineq->m_args) { double result = 0; - if (cm(!sign, *ineq, v, coeff, new_value)) + if (cm(old_sign, *ineq, v, coeff, new_value)) result = dscore(v, new_value); // just pick first positive, or pick a max? if (result > 0) { @@ -586,7 +601,7 @@ namespace arith { // switch to dscore mode void sls::on_rescale() { - // m_dscore_mode = true; + m_dscore_mode = true; } void sls::on_save_model() { @@ -597,23 +612,39 @@ namespace arith { for (unsigned v = 0; v < s.s().num_vars(); ++v) init_bool_var_assignment(v); - verbose_stream() << "on-restart\n"; + check_ineqs(); + } + + void sls::check_ineqs() { + auto check_bool_var = [&](sat::bool_var bv) { auto const* ineq = atom(bv); if (!ineq) return; - bool sign = !m_bool_search->get_value(bv); - int64_t d = dtt(sign, *ineq); - sat::literal lit(bv, sign); - // verbose_stream() << "check " << lit << " " << *ineq << "\n"; + int64_t d = dtt(sign(bv), *ineq); + sat::literal lit(bv, sign(bv)); if (is_true(lit) != (d == 0)) { - verbose_stream() << "restart " << bv << " " << *ineq << "\n"; + verbose_stream() << "invalid assignment " << bv << " " << *ineq << "\n"; } VERIFY(is_true(lit) == (d == 0)); }; - for (unsigned v = 0; v < s.get_num_vars(); ++v) + for (unsigned v = 0; v < s.get_num_vars(); ++v) check_bool_var(v); - - verbose_stream() << "on-restart-done\n"; } + + std::ostream& sls::display(std::ostream& out) const { + for (bool_var bv = 0; bv < s.s().num_vars(); ++bv) { + auto const* ineq = atom(bv); + if (!ineq) + continue; + out << bv << " " << *ineq << "\n"; + } + for (unsigned v = 0; v < s.get_num_vars(); ++v) { + if (s.is_bool(v)) + continue; + out << "v" << v << " := " << m_vars[v].m_value << " " << m_vars[v].m_best_value << "\n"; + } + return out; + } + } diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 3c9daaa51..af3a46234 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -119,12 +119,11 @@ namespace arith { sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); } sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); } bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); } + bool sign(sat::bool_var v) const { return !m_bool_search->get_value(v); } void reset(); ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; } - void log(); - bool flip(bool sign, ineq const& ineq); int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); } int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const; @@ -151,6 +150,10 @@ namespace arith { int64_t value(var_t v) const { return m_vars[v].m_value; } int64_t to_numeral(rational const& r); + void check_ineqs(); + + std::ostream& display(std::ostream& out) const; + public: sls(solver& s); ~sls() override {} From 027770930e251b72d8c6a4db6e77aab5d73a553d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 17:03:15 -0800 Subject: [PATCH 064/220] fix bug in quasi macro identification: require quantifiers --- src/ast/simplifiers/eliminate_predicates.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 05f347f25..b2cc5e25d 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -205,6 +205,7 @@ void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause cons // forall vars . f(args) = if eqs then body else f'(args) f1 = m.mk_fresh_func_decl(f->get_name(), symbol::null, sorts.size(), sorts.data(), f->get_range()); + lhs = m.mk_app(f, args); rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); insert_macro(lhs, rhs, cl.m_dep); @@ -572,7 +573,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { !occurs(x->get_decl(), y); }; - if (cl.is_unit() && m.is_eq(cl.atom(0), x, y)) { + if (cl.is_unit() && m.is_eq(cl.atom(0), x, y) && !cl.m_bound.empty()) { if (!cl.sign(0) && can_be_qdef(x, y)) { insert_quasi_macro(to_app(x), y, cl); return; @@ -590,7 +591,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { return; } } - if (cl.is_unit()) { + if (cl.is_unit() && !cl.m_bound.empty()) { expr* body = cl.sign(0) ? m.mk_false() : m.mk_true(); expr* x = cl.atom(0); if (can_be_qdef(x, body)) { From 46d37b6e307abba2e782e18d907dc2024ebe68d9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 17:30:07 -0800 Subject: [PATCH 065/220] fix #6615 make rewriting exception safe (for cancelation). The state during restart in smt_context is not exception safe. --- src/ast/rewriter/seq_axioms.cpp | 2 +- src/ast/rewriter/th_rewriter.cpp | 34 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index e08ead08b..4d7da4d7f 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -1237,7 +1237,7 @@ namespace seq { seq.str.is_string(x)) { expr_ref len(n, m); m_rewrite(len); - SASSERT(n != len); + SASSERT(m.limit().is_canceled() || n != len); add_clause(mk_eq(len, n)); } else { diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 93b867c45..d75a31a66 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -967,17 +967,41 @@ void th_rewriter::reset() { } void th_rewriter::operator()(expr_ref & term) { - expr_ref result(term.get_manager()); - m_imp->operator()(term, result); - term = std::move(result); + expr_ref result(term.get_manager()); + try { + m_imp->operator()(term, result); + term = std::move(result); + } + catch (...) { + if (!term.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result) { - m_imp->operator()(t, result); + try { + m_imp->operator()(t, result); + } + catch (...) { + result = t; + if (!result.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { - m_imp->operator()(t, result, result_pr); + try { + m_imp->operator()(t, result, result_pr); + } + catch (...) { + result = t; + result_pr = nullptr; + if (!result.get_manager().inc()) + return; + throw; + } } expr_ref th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings) { From acd2eaa39070057af515a43d992de96e988dc64c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 20:39:39 -0800 Subject: [PATCH 066/220] add (disabled) code path to enable nested conjunctions for experiments with disabling flat-and-or dependency --- src/ast/rewriter/hoist_rewriter.cpp | 32 +++++++++++++++++++++++++++-- src/ast/rewriter/hoist_rewriter.h | 2 +- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index d3f5af2b5..d5b2042a2 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -23,7 +23,7 @@ Author: #include "ast/ast_ll_pp.h" hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): - m(m), m_args1(m), m_args2(m), m_subst(m) { + m(m), m_args1(m), m_args2(m), m_refs(m), m_subst(m) { updt_params(p); } @@ -185,7 +185,7 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { switch (f->get_decl_kind()) { - case OP_OR: + case OP_OR: return mk_or(num_args, args, result); default: return BR_FAILED; @@ -193,6 +193,33 @@ br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c } bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { +#if 0 + if (!args) + return m.is_and(e) || (m.is_not(e, e) && m.is_or(e)); + expr_fast_mark1 visited; + args->reset(); + args->push_back(e); + m_refs.reset(); + for (unsigned i = 0; i < args->size(); ++i) { + e = args->get(i); + if (visited.is_marked(e)) + goto drop; + m_refs.push_back(e); + visited.mark(e, true); + if (m.is_and(e)) + args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); + else if (m.is_not(e, e) && m.is_or(e)) + for (expr* arg : *to_app(e)) + args->push_back(::mk_not(m, arg)); + else + continue; + drop: + (*args)[i] = args->back(); + args->pop_back(); + --i; + } + return args->size() > 1; +#else if (m.is_and(e)) { if (args) { args->reset(); @@ -209,6 +236,7 @@ bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { } return true; } +#endif return false; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index ae7dff3bc..b64325584 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -29,7 +29,7 @@ class bool_rewriter; class hoist_rewriter { ast_manager & m; - expr_ref_vector m_args1, m_args2; + expr_ref_vector m_args1, m_args2, m_refs; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; ptr_vector m_es; From 94b79eefea222f1250e3f569bbd003fb08be0c10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 20:40:22 -0800 Subject: [PATCH 067/220] add back max_occs parameter dependency to solve-eqs --- src/ast/simplifiers/solve_eqs.cpp | 42 +++++++++++++++++++++++++++++++ src/ast/simplifiers/solve_eqs.h | 7 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 65fe8374a..c59ae1e49 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -262,6 +262,48 @@ namespace euf { } } + void solve_eqs::collect_num_occs(expr * t, expr_fast_mark1 & visited) { + ptr_buffer stack; + + auto visit = [&](expr* arg) { + if (is_uninterp_const(arg)) { + m_num_occs.insert_if_not_there(arg, 0)++; + } + if (!visited.is_marked(arg) && is_app(arg)) { + visited.mark(arg, true); + stack.push_back(to_app(arg)); + } + }; + + visit(t); + + while (!stack.empty()) { + app * t = stack.back(); + stack.pop_back(); + for (expr* arg : *t) + visit(arg); + } + } + + void solve_eqs::collect_num_occs() { + if (m_config.m_max_occs == UINT_MAX) + return; // no need to compute num occs + m_num_occs.reset(); + expr_fast_mark1 visited; + for (unsigned i : indices()) + collect_num_occs(m_fmls[i].fml(), visited); + } + + // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs + bool solve_eqs::check_occs(expr * t) const { + if (m_config.m_max_occs == UINT_MAX) + return true; + unsigned num = 0; + m_num_occs.find(t, num); + TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); + return num <= m_config.m_max_occs; + } + void solve_eqs::save_subst(vector const& old_fmls) { if (!m_subst->empty()) m_fmls.model_trail().push(m_subst.detach(), old_fmls); diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index c8fbe3a40..dde42dd94 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -56,10 +56,12 @@ namespace euf { expr_mark m_unsafe_vars; // expressions that cannot be replaced ptr_vector m_todo; expr_mark m_visited; + obj_map m_num_occs; + bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } - bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e); } + bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e) && check_occs(e); } void get_eqs(dep_eq_vector& eqs); void filter_unsafe_vars(); void extract_subst(); @@ -67,6 +69,9 @@ namespace euf { void normalize(); void apply_subst(vector& old_fmls); void save_subst(vector const& old_fmls); + void collect_num_occs(expr * t, expr_fast_mark1 & visited); + void collect_num_occs(); + bool check_occs(expr* t) const; public: From fd97be0e3e3f20270ff5968414c184b9894d45c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 21:03:27 -0800 Subject: [PATCH 068/220] move sat.smt.proof.check_rup into solver.proof.check_rup #6616 --- src/params/solver_params.pyg | 1 + src/sat/sat_config.cpp | 1 - src/sat/sat_config.h | 1 - src/sat/sat_params.pyg | 3 +-- src/sat/smt/euf_proof_checker.cpp | 6 +++--- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/params/solver_params.pyg b/src/params/solver_params.pyg index 1c2be3213..0912b4c7f 100644 --- a/src/params/solver_params.pyg +++ b/src/params/solver_params.pyg @@ -10,6 +10,7 @@ def_module_params('solver', ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'), ('proof.log', SYMBOL, '', 'log clause proof trail into a file'), ('proof.check', BOOL, True, 'check proof logs'), + ('proof.check_rup', BOOL, True, 'check proof RUP inference in proof logs'), ('proof.save', BOOL, False, 'save proof log into a proof object that can be extracted using (get-proof)'), ('proof.trim', BOOL, False, 'trim and save proof into a proof object that an be extracted using (get-proof)'), )) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 0a9e803a9..73516f66d 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -200,7 +200,6 @@ namespace sat { m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); m_smt_proof_check = p.smt_proof_check(); - m_smt_proof_check_rup = p.smt_proof_check_rup(); m_drat_disable = p.drat_disable(); m_drat = !m_drat_disable && p.threads() == 1 && diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index f8c0775b1..a47f041b0 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -180,7 +180,6 @@ namespace sat { bool m_drat_binary; symbol m_drat_file; bool m_smt_proof_check; - bool m_smt_proof_check_rup; bool m_drat_check_unsat; bool m_drat_check_sat; bool m_drat_activity; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 6aedf1c89..d40d606d1 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -48,8 +48,7 @@ def_module_params('sat', ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.disable', BOOL, False, 'override anything that enables DRAT'), ('smt', BOOL, False, 'use the SAT solver based incremental SMT core'), - ('smt.proof.check', BOOL, False, 'check SMT proof while it is created'), - ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), + ('smt.proof.check', BOOL, False, 'check proofs on the fly during SMT search'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), ('drat.binary', BOOL, False, 'use Binary DRAT output format'), ('drat.check_unsat', BOOL, False, 'build up internal proof and check'), diff --git a/src/sat/smt/euf_proof_checker.cpp b/src/sat/smt/euf_proof_checker.cpp index 2d4f67cd2..a538b2a80 100644 --- a/src/sat/smt/euf_proof_checker.cpp +++ b/src/sat/smt/euf_proof_checker.cpp @@ -28,7 +28,7 @@ Author: #include "sat/smt/bv_theory_checker.h" #include "sat/smt/distinct_theory_checker.h" #include "sat/smt/tseitin_theory_checker.h" - +#include "params/solver_params.hpp" namespace euf { @@ -388,8 +388,8 @@ namespace euf { m_sat_solver.updt_params(m_params); m_drat.updt_config(); m_rup = symbol("rup"); - sat_params sp(m_params); - m_check_rup = sp.smt_proof_check_rup(); + solver_params sp(m_params); + m_check_rup = sp.proof_check_rup(); } void smt_proof_checker::ensure_solver() { From aa75ba8a6b213e3bb4e2293893a061864cb95fb1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Mar 2023 21:03:41 -0800 Subject: [PATCH 069/220] remove parenthesis --- src/ast/simplifiers/solve_eqs.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index c59ae1e49..160b16b3b 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -266,9 +266,8 @@ namespace euf { ptr_buffer stack; auto visit = [&](expr* arg) { - if (is_uninterp_const(arg)) { - m_num_occs.insert_if_not_there(arg, 0)++; - } + if (is_uninterp_const(arg)) + m_num_occs.insert_if_not_there(arg, 0)++; if (!visited.is_marked(arg) && is_app(arg)) { visited.mark(arg, true); stack.push_back(to_app(arg)); From b82d177276ae7ff87691c695b086c5b50cc878bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 3 Mar 2023 11:26:13 -0800 Subject: [PATCH 070/220] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_eqs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 160b16b3b..fbc6fbb02 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -299,7 +299,7 @@ namespace euf { return true; unsigned num = 0; m_num_occs.find(t, num); - TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); + TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m) << " num_occs: " << num << " max: " << m_config.m_max_occs << "\n";); return num <= m_config.m_max_occs; } From 55d45e0c0c2075ebca1b597c8230716a0c26a93a Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Fri, 3 Mar 2023 15:32:23 -0500 Subject: [PATCH 071/220] bug fix. Prevent resetting gg stats #6062 (#6618) --- src/muz/spacer/spacer_context.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 4c5c13e34..38b1c7149 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -114,18 +114,10 @@ void pob::inherit(pob const &p) { m_desired_level = std::max(m_desired_level, p.m_desired_level); m_open = p.m_open; m_use_farkas = p.m_use_farkas; - - m_is_conjecture = p.m_is_conjecture; - m_enable_local_gen = p.m_enable_local_gen; - m_enable_concretize = p.m_enable_concretize; - m_is_subsume = p.m_is_subsume; - m_enable_expand_bnd_gen = p.m_enable_expand_bnd_gen; - m_weakness = p.m_weakness; m_derivation = nullptr; - m_gas = p.m_gas; } void pob::close () { From f986ac6a75b22ea0b7bafe9c5ddaed6cde048007 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 14:50:10 -0800 Subject: [PATCH 072/220] remove mps_reader --- src/math/lp/mps_reader.h | 891 ------------------------------------ src/shell/CMakeLists.txt | 1 - src/shell/lp_frontend.cpp | 109 ----- src/shell/lp_frontend.h | 7 - src/shell/main.cpp | 10 +- src/test/lp/lp.cpp | 922 +++----------------------------------- src/test/lp/smt_reader.h | 2 - 7 files changed, 68 insertions(+), 1874 deletions(-) delete mode 100644 src/math/lp/mps_reader.h delete mode 100644 src/shell/lp_frontend.cpp delete mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h deleted file mode 100644 index 8093954b1..000000000 --- a/src/math/lp/mps_reader.h +++ /dev/null @@ -1,891 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -// reads an MPS file representing a Mixed Integer Program -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" -#include "math/lp/lar_solver.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -namespace lp { -inline bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -inline size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -inline size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - - // trim from start -inline std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - -inline std::string trim(std::string const &r) { - std::string s = r; - return ltrim(rtrim(s)); -} - - -inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -inline vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - -template -class mps_reader { - enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; - struct bound { - T m_low; - T m_upper; - bool m_low_is_set; - bool m_upper_is_set; - bool m_value_is_fixed; - T m_fixed_value; - bool m_free; - // constructor - bound() : m_low(numeric_traits::zero()), - m_low_is_set(true), - m_upper_is_set(false), - m_value_is_fixed(false), - m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable - }; - - struct column { - std::string m_name; - bound * m_bound; - unsigned m_index; - column(const std::string &name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { - } - }; - - struct row { - row_type m_type; - std::string m_name; - std::unordered_map m_row_columns; - unsigned m_index; - T m_right_side; - T m_range; - row(row_type type, const std::string &name, unsigned index) : - m_type(type), - m_name(name), - m_index(index), - m_right_side(zero_of_type()), - m_range(zero_of_type()) - { - } - }; - - bool m_is_OK; - std::string m_file_name; - std::unordered_map m_rows; - std::unordered_map m_columns; - std::unordered_map m_names_to_var_index; - std::string m_line; - std::string m_name; - std::string m_cost_row_name; - std::ifstream m_file_stream; - // needed to adjust the index row - unsigned m_cost_line_count; - unsigned m_line_number; - std::ostream * m_message_stream; - - void set_m_ok_to_false() { - *m_message_stream << "setting m_is_OK to false" << std::endl; - m_is_OK = false; - } - - std::string get_string_from_position(unsigned offset) { - unsigned i = offset; - for (; i < m_line.size(); i++){ - if (m_line[i] == ' ') - break; - } - lp_assert(m_line.size() >= offset); - lp_assert(m_line.size() >> i); - lp_assert(i >= offset); - return m_line.substr(offset, i - offset); - } - - void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ - if (b == nullptr) { - solver->set_lower_bound(col, numeric_traits::zero()); - return; - } - - if (b->m_free) { - return; - } - if (b->m_low_is_set) { - solver->set_lower_bound(col, b->m_low); - } - if (b->m_upper_is_set) { - solver->set_upper_bound(col, b->m_upper); - } - - if (b->m_value_is_fixed) { - solver->set_fixed_value(col, b->m_fixed_value); - } - } - - bool all_white_space() { - for (unsigned i = 0; i < m_line.size(); i++) { - char c = m_line[i]; - if (c != ' ' && c != '\t') { - return false; - } - } - return true; - } - - void read_line() { - while (m_is_OK) { - if (!getline(m_file_stream, m_line)) { - m_line_number++; - set_m_ok_to_false(); - *m_message_stream << "cannot read from file" << std::endl; - } - m_line_number++; - if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) - break; - } - } - - void read_name() { - do { - read_line(); - if (m_line.find("NAME") != 0) { - continue; - } - m_line = m_line.substr(4); - m_name = trim(m_line); - break; - } while (m_is_OK); - } - - void read_rows() { - // look for start of the rows - read_line(); - do { - if (static_cast(m_line.find("ROWS")) >= 0) { - break; - } - } while (m_is_OK); - do { - read_line(); - if (m_line.find("COLUMNS") == 0) { - break; - } - add_row(); - } while (m_is_OK); - } - - void read_column_by_columns(const std::string & column_name, std::string column_data) { - // uph, let us try to work with columns - if (column_data.size() >= 22) { - std::string ss = column_data.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - *m_message_stream << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); - if (column_data.size() > 24) { - column_data = column_data.substr(25); - if (column_data.size() >= 22) { - read_column_by_columns(column_name, column_data); - } - } - } - } else { - fail: - set_m_ok_to_false(); - *m_message_stream << "cannot understand this line\n" - "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void read_column(const std::string & column_name, const std::string & column_data){ - auto tokens = split_and_trim(column_data); - for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { - auto row_name = tokens[i]; - if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here - auto t = m_rows.find(row_name); - if (t == m_rows.end()) { - read_column_by_columns(column_name, column_data); - return; - } - row *r = t->second; - r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); - } - } - - void read_columns(){ - std::string column_name; - do { - read_line(); - if (m_line.find("RHS") == 0) { - break; - } - if (m_line.size() < 22) { - (*m_message_stream) << "line is too short for a column" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string column_name_tmp = trim(m_line.substr(4, 8)); - if (!column_name_tmp.empty()) { - column_name = column_name_tmp; - } - auto col_it = m_columns.find(column_name); - mps_reader::column * col; - if (col_it == m_columns.end()) { - col = new mps_reader::column(column_name, static_cast(m_columns.size())); - m_columns[column_name] = col; - // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; - } else { - col = col_it->second; - } - read_column(column_name, m_line.substr(14)); - } while (m_is_OK); - } - - void read_rhs() { - do { - read_line(); - if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { - break; - } - fill_rhs(); - } while (m_is_OK); - } - - - void fill_rhs_by_columns(std::string rhsides) { - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - fill_rhs_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void fill_rhs() { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string rhsides = m_line.substr(14); - vector splitted_line = split_and_trim(rhsides); - - for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { - auto t = m_rows.find(splitted_line[i]); - if (t == m_rows.end()) { - fill_rhs_by_columns(rhsides); - return; - } - row * row = t->second; - row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); - } - } - - void read_bounds() { - if (m_line.find("BOUNDS") != 0) { - return; - } - - do { - read_line(); - if (m_line[0] != ' ') { - break; - } - create_or_update_bound(); - } while (m_is_OK); - } - - void read_ranges() { - if (m_line.find("RANGES") != 0) { - return; - } - do { - read_line(); - auto sl = split_and_trim(m_line); - if (sl.size() < 2) { - break; - } - read_range(sl); - } while (m_is_OK); - } - - - void read_bound_by_columns(const std::string & colstr) { - if (colstr.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (colstr.size() >= 22) { - std::string ss = colstr.substr(0, 8); - std::string column_name = trim(ss); - auto t = m_columns.find(column_name); - - if (t == m_columns.end()) { - (*m_message_stream) << "cannot find " << column_name << std::endl; - goto fail; - } else { - vector bound_string; - bound_string.push_back(column_name); - if (colstr.size() > 14) { - bound_string.push_back(colstr.substr(14)); - } - mps_reader::column * col = t->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void update_bound(bound * b, vector bound_string) { - /* - UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. - */ - - std::string bound_type = get_string_from_position(1); - if (bound_type == "BV") { - b->m_upper_is_set = true; - b->m_upper = 1; - return; - } - - if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - b->m_upper_is_set = true; - b->m_upper= numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "LO" || bound_type == "LI") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_low_is_set = true; - b->m_low = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "FR") { - b->m_free = true; - } else if (bound_type == "FX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_value_is_fixed = true; - b->m_fixed_value = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "PL") { - b->m_low_is_set = true; - b->m_low = 0; - } else if (bound_type == "MI") { - b->m_upper_is_set = true; - b->m_upper = 0; - } else { - (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - } - - void create_or_update_bound() { - const unsigned name_offset = 14; - lp_assert(m_line.size() >= 14); - vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - - if (bound_string.empty()) { - set_m_ok_to_false(); - (*m_message_stream) << "error at line " << m_line_number << std::endl; - throw m_line; - } - - std::string name = bound_string[0]; - auto it = m_columns.find(name); - if (it == m_columns.end()){ - read_bound_by_columns(m_line.substr(14)); - return; - } - mps_reader::column * col = it->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - - - - void read_range_by_columns(std::string rhsides) { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_range = numeric_traits::from_string(rhsides.substr(8)); - maybe_modify_current_row_and_add_row_for_range(row); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - read_range_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - - void read_range(vector & splitted_line){ - for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { - auto it = m_rows.find(splitted_line[i]); - if (it == m_rows.end()) { - read_range_by_columns(m_line.substr(14)); - return; - } - row * row = it->second; - row->m_range = numeric_traits::from_string(splitted_line[i + 1]); - maybe_modify_current_row_and_add_row_for_range(row); - } - } - - void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - std::string row_name = row_with_range->m_name + "_range"; - row * other_bound_range_row; - switch (row_with_range->m_type) { - case row_type::Greater_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); - break; - case row_type::Less_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); - break; - case row_type::Equal: - if (row_with_range->m_range > 0) { - row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - } else { // row->m_range < 0; - row_with_range->m_type = row_type::Less_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - } - other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; - break; - default: - (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - - for (auto s : row_with_range->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - other_bound_range_row->m_row_columns[s.first] = s.second; - } - } - - void add_row() { - if (m_line.length() < 2) { - return; - } - - m_line = trim(m_line); - char c = m_line[0]; - m_line = m_line.substr(1); - m_line = trim(m_line); - add_row(c); - } - - void add_row(char c) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - switch (c) { - case 'E': - m_rows[m_line] = new row(row_type::Equal, m_line, index); - break; - case 'L': - m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); - break; - case 'G': - m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); - break; - case 'N': - m_rows[m_line] = new row(row_type::Cost, m_line, index); - m_cost_row_name = m_line; - m_cost_line_count++; - break; - } - } - unsigned range_count() { - unsigned ret = 0; - for (auto s : m_rows) { - if (s.second->m_range != 0) { - ret++; - } - } - return ret; - } - - /* - If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: - sense interval - G [rhs, rhs + |range|] - L [rhs - |range|, rhs] - E [rhs, rhs + |range|] if range > 0, - [rhs - |range|, rhs] if range < 0 - where |range| is range's absolute value. - */ - - lp_relation get_relation_from_row(row_type rt) { - switch (rt) { - case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; - case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; - case mps_reader::Equal: return lp_relation::Equal; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned solver_row_count() { - return m_rows.size() - m_cost_line_count + range_count(); - } - - void fill_solver_on_row(row * row, lp_solver *solver) { - if (row->m_name != m_cost_row_name) { - solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); - for (auto s : row->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); - } - } else { - set_solver_cost(row, solver); - } - } - - T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } - - void fill_solver_on_rows(lp_solver * solver) { - for (auto row_it : m_rows) { - fill_solver_on_row(row_it.second, solver); - } - } - - - void fill_solver_on_columns(lp_solver * solver){ - for (auto s : m_columns) { - mps_reader::column * col = s.second; - unsigned index = col->m_index; - set_boundary_for_column(index, col->m_bound, solver); - // optional call - solver->give_symbolic_name_to_column(col->m_name, col->m_index); - } - } - - void fill_solver(lp_solver *solver) { - fill_solver_on_rows(solver); - fill_solver_on_columns(solver); - } - - void set_solver_cost(row * row, lp_solver *solver) { - for (auto s : row->m_row_columns) { - std::string name = s.first; - lp_assert(m_columns.find(name) != m_columns.end()); - mps_reader::column * col = m_columns[name]; - solver->set_cost_for_column(col->m_index, s.second); - } - } - -public: - - void set_message_stream(std::ostream * o) { - lp_assert(o != nullptr); - m_message_stream = o; - } - vector column_names() { - vector v; - for (auto s : m_columns) { - v.push_back(s.first); - } - return v; - } - - ~mps_reader() { - for (auto s : m_rows) { - delete s.second; - } - for (auto s : m_columns) { - auto col = s.second; - delete col->m_bound; - delete col; - } - } - - mps_reader(const std::string & file_name): - m_is_OK(true), - m_file_name(file_name), - m_file_stream(file_name), - m_cost_line_count(0), - m_line_number(0), - m_message_stream(& std::cout) {} - void read() { - if (!m_file_stream.is_open()){ - set_m_ok_to_false(); - return; - } - - read_name(); - read_rows(); - read_columns(); - read_rhs(); - if (m_line.find("BOUNDS") == 0) { - read_bounds(); - read_ranges(); - } else if (m_line.find("RANGES") == 0) { - read_ranges(); - read_bounds(); - } - } - - bool is_ok() { - return m_is_OK; - } - - lp_solver * create_solver(bool dual) { - lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); - fill_solver(solver); - return solver; - } - - lconstraint_kind get_lar_relation_from_row(row_type rt) { - switch (rt) { - case Less_or_equal: return LE; - case Greater_or_equal: return GE; - case Equal: return EQ; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned get_var_index(std::string s) { - auto it = m_names_to_var_index.find(s); - if (it != m_names_to_var_index.end()) - return it->second; - unsigned ret = static_cast(m_names_to_var_index.size()); - m_names_to_var_index[s] = ret; - return ret; - } - - void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { - if (row->m_name != m_cost_row_name) { - auto kind = get_lar_relation_from_row(row->m_type); - vector> ls; - for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first), false); - ls.push_back(std::make_pair(s.second, i)); - } - unsigned j = solver->add_term(ls, row_index); - solver->add_var_bound(j, kind, row->m_right_side); - } else { - // ignore the cost row - } - } - - - void fill_lar_solver_on_rows(lar_solver * solver) { - int row_index = 0; - for (auto row_it : m_rows) { - fill_lar_solver_on_row(row_it.second, solver, row_index++); - } - } - - void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, GE, b->m_low); - } - - void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_upper); - } - - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_fixed_value); - solver->add_var_bound(i, GE, b->m_fixed_value); - } - - void fill_lar_solver_on_columns(lar_solver * solver) { - for (auto s : m_columns) { - mps_reader::column * col = s.second; - solver->add_var(col->m_index, false); - auto b = col->m_bound; - if (b == nullptr) return; - - if (b->m_free) continue; - - if (b->m_low_is_set) { - create_low_constraint_for_var(col, b, solver); - } - if (b->m_upper_is_set) { - create_upper_constraint_for_var(col, b, solver); - } - if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); - } - } - } - - - void fill_lar_solver(lar_solver * solver) { - fill_lar_solver_on_columns(solver); - fill_lar_solver_on_rows(solver); - } - - lar_solver * create_lar_solver() { - lar_solver * solver = new lar_solver(); - fill_lar_solver(solver); - return solver; - } -}; -} diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index d9b74f162..c0e9c8505 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp - lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp deleted file mode 100644 index 8d6425533..000000000 --- a/src/shell/lp_frontend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Author: - - Lev Nachmanson 2016-10-27 - ---*/ - -#include "math/lp/lp_settings.h" -#include "math/lp/mps_reader.h" -#include "util/timeout.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "util/rlimit.h" -#include "util/gparams.h" -#include "util/mutex.h" -#include -#include -#include "smt/params/smt_params_helper.hpp" - -namespace { -static mutex *display_stats_mux = new mutex; - -static lp::lp_solver* g_solver = nullptr; - -static void display_statistics() { - lock_guard lock(*display_stats_mux); - if (g_solver && g_solver->settings().print_statistics) { - // TBD display relevant information about statistics - } -} - -static void STD_CALL on_ctrl_c(int) { - signal (SIGINT, SIG_DFL); - display_statistics(); - raise(SIGINT); -} - -static void on_timeout() { - display_statistics(); - _Exit(0); -} - -struct front_end_resource_limit : public lp::lp_resource_limit { - reslimit& m_reslim; - - front_end_resource_limit(reslimit& lim): - m_reslim(lim) - {} - - bool get_cancel_flag() override { return !m_reslim.inc(); } -}; - -void run_solver(smt_params_helper & params, char const * mps_file_name) { - - reslimit rlim; - unsigned timeout = gparams::get_ref().get_uint("timeout", 0); - unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); - front_end_resource_limit lp_limit(rlim); - - scoped_rlimit _rlimit(rlim, rlimit); - cancel_eh eh(rlim); - scoped_timer timer(timeout, &eh); - - std::string fn(mps_file_name); - lp::mps_reader reader(fn); - reader.set_message_stream(&std::cout); // can be redirected - reader.read(); - if (!reader.is_ok()) { - std::cerr << "cannot process " << mps_file_name << std::endl; - return; - } - lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver - solver->settings().set_resource_limit(lp_limit); - g_solver = solver; - if (params.arith_min()) { - solver->flip_costs(); - } - solver->settings().set_message_ostream(&std::cout); - solver->settings().report_frequency = params.arith_rep_freq(); - solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); - - solver->find_maximal_solution(); - - *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::lp_status::OPTIMAL) { - if (params.arith_min()) { - solver->flip_costs(); - } - solver->print_model(std::cout); - } - - display_statistics(); - g_solver = nullptr; - delete solver; -} -} - -unsigned read_mps_file(char const * mps_file_name) { - signal(SIGINT, on_ctrl_c); - register_on_timeout_proc(on_timeout); - smt_params_helper p; - param_descrs r; - p.collect_param_descrs(r); - run_solver(p, mps_file_name); - return 0; -} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h deleted file mode 100644 index b24be811f..000000000 --- a/src/shell/lp_frontend.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (c) 2013 Microsoft Corporation. All rights reserved. - - Author: Lev Nachmanson -*/ -#pragma once -unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 4c26d91d9..26325d3d0 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,14 +37,13 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" -#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -377,10 +376,6 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } - else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || - strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { - g_input_kind = IN_MPS; - } } } switch (g_input_kind) { @@ -406,9 +401,6 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; - case IN_MPS: - return_value = read_mps_file(g_input_file); - break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c82cdd0a4..984c7b4bb 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -34,7 +34,6 @@ #include #include "math/lp/lp_utils.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -59,6 +58,71 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" + +bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + +std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + + +vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + + namespace nla { void test_horner(); void test_monics(); @@ -1408,156 +1472,12 @@ void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args bool values_are_one_percent_close(double a, double b); -void print_x(mps_reader & reader, lp_solver * solver) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; -} - -void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (const auto & name : reader.column_names()) { - double a = solver->get_column_value_by_name(name); - double b = solver0->get_column_value_by_name(name); - if (!values_are_one_percent_close(a, b)) { - std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; - } - } -} -void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return; - } - - lp_solver * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - if (dual) { - std::cout << "solving for dual" << std::endl; - } - solver->find_maximal_solution(); - sw.stop(); - double span = sw.get_seconds(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - print_x(reader, solver); - } - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost << std::endl; - } - std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; - if (compare_with_primal) { - auto * primal_solver = reader.create_solver(false); - setup_solver(time_limit, look_for_min, args_parser, primal_solver); - primal_solver->find_maximal_solution(); - if (solver->get_status() != primal_solver->get_status()) { - std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; - } else { - if (solver->get_status() == lp_status::OPTIMAL) { - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - double primal_cost = primal_solver->get_current_cost(); - if (look_for_min) { - primal_cost = -primal_cost; - } - std::cout << "primal cost = " << primal_cost << std::endl; - if (!values_are_one_percent_close(cost, primal_cost)) { - compare_solutions(reader, primal_solver, solver); - print_x(reader, primal_solver); - std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - lp_assert(false); - } - } - } - delete primal_solver; - } - delete solver; -} -void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - solver->find_maximal_solution(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - - if (solver->get_status() == lp_status::OPTIMAL) { - // for (auto name: reader.column_names()) { - // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - // } - lp::mpq cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost.get_double() << std::endl; - } - std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition -void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { - if (!solve_for_rational) { - std::cout << "solving " << file_name << std::endl; - solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); - } - else { - std::cout << "solving " << file_name << " in rationals " << std::endl; - solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); - } -} -void solve_mps(std::string file_name, argument_parser & args_parser) { - bool look_for_min = args_parser.option_is_used("--min"); - unsigned time_limit; - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); -} - -void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { - std::cout << "solving " << file_name << std::endl; - - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; - } - } - std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void test_upair_queue() { int n = 10; @@ -1626,53 +1546,6 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } -bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); - return solver->solution_is_feasible(solution); - } - return false; -} - - -void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { - std::cout << "solving " << file_name << std::endl; - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (status != solver->get_status()){ - std::cout << "status should be " << lp_status_to_string(status) << std::endl; - lp_assert(status == solver->get_status()); - throw "status is wrong"; - } - if (solver->get_status() == lp_status::OPTIMAL) { - std::cout << "cost = " << solver->get_current_cost() << std::endl; - if (solution != nullptr) { - for (auto it : *solution) { - if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { - std::cout << "expected:" << it.first << "=" << - it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; - } - lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); - } - } - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; - } - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} int get_random_rows() { return 5 + my_random() % 2; @@ -1896,140 +1769,9 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); -void solve_some_mps(argument_parser & args_parser) { - unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0; - unsigned failures = 0; - unsigned inconclusives = 0; - std::set minimums; - vector file_names; - fill_file_names(file_names, minimums); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); - if (compare_with_glpk) { - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - test_out_dir(out_dir); - for (auto& a : file_names) { - try { - std::string file_dir; - std::string file_name; - find_dir_and_file_name(a, file_dir, file_name); - process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - return; - } - if (!solve_for_rational) { - solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) - std::unordered_map sol; - sol["X1"] = 0; - sol["X2"] = 6; - sol["X3"] = 0; - sol["X4"] = 15; - sol["X5"] = 2; - sol["X6"] = 1; - sol["X7"] = 1; - sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); - sol.clear(); - sol["X1"] = 25.0/14.0; - // sol["X2"] = 0; - // sol["X3"] = 0; - // sol["X4"] = 0; - // sol["X5"] = 0; - // sol["X6"] = 0; - // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) - solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); - // return; - for (auto& s : file_names) { - try { - solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } else { - // unsigned i = 0; - for (auto& s : file_names) { - // if (i++ > 9) return; - try { - solve_mps_in_rational(s, dual, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } -} #endif -void solve_rational() { - lp_primal_simplex solver; - solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); - solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); - - // setting the cost - int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; - std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; - - for (unsigned i = 0; i < 8; i++) { - solver.set_cost_for_column(i, lp::mpq(cost[i])); - solver.give_symbolic_name_to_column(var_names[i], i); - } - - int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); - } - - int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); - } - - int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; - for (unsigned i = 0; i < 8; i++) { - solver.set_lower_bound(i, lp::mpq(0)); - solver.set_upper_bound(i, lp::mpq(bounds[i])); - } - - std::unordered_map expected_sol; - expected_sol["x1"] = lp::mpq(0); - expected_sol["x2"] = lp::mpq(6); - expected_sol["x3"] = lp::mpq(0); - expected_sol["x4"] = lp::mpq(15); - expected_sol["x5"] = lp::mpq(2); - expected_sol["x6"] = lp::mpq(1); - expected_sol["x7"] = lp::mpq(1); - expected_sol["x8"] = lp::mpq(0); - solver.find_maximal_solution(); - lp_assert(solver.get_status() == lp_status::OPTIMAL); -#ifdef Z3DEBUG - for (const auto & it : expected_sol) { - (void)it; - lp_assert(it.second == solver.get_column_value_by_name(it.first)); - } -#endif -} std::string read_line(bool & end, std::ifstream & file) { @@ -2047,49 +1789,6 @@ bool contains(std::string const & s, char const * pattern) { } -std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { - std::ifstream file(file_name); - if (!file.is_open()){ - std::cerr << "cannot open " << file_name << std::endl; - return nullptr; - } - std::string s; - bool end; - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - if (contains(s, "Column name")){ - break; - } - } while (true); - - read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - - auto ret = new std::unordered_map(); - - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - auto split = string_split(s, " \t", false); - if (split.empty()) { - return ret; - } - - lp_assert(split.size() > 3); - (*ret)[split[1]] = atof(split[3].c_str()); - } while (true); -} - void test_init_U() { @@ -2189,7 +1888,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); - parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -2360,237 +2058,9 @@ std::string get_status(std::string file_name) { throw 0; } -// returns true if the costs should be compared too -bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { - std::string glpk_status = get_status(glpk_out_file_name); - std::string lp_tst_status = get_status(lp_out_file_name); - - if (glpk_status != lp_tst_status) { - if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { - successes++; - return false; - } else { - std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; - std::cout << " but lp_tst status is " << lp_tst_status << std::endl; - failures++; - return false; - } - } - return lp_tst_status == "OPTIMAL"; -} - -double get_glpk_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - while (getline(f, str)) { - if (str.find("Objective") != std::string::npos) { - vector tokens = split_and_trim(str); - if (tokens.size() != 5) { - std::cout << "unexpected Objective std::string " << str << std::endl; - throw 0; - } - return atof(tokens[3].c_str()); - } - } - std::cout << "cannot find the Objective line in " << file_name << std::endl; - throw 0; -} - -double get_lp_tst_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - std::string cost_string; - while (getline(f, str)) { - if (str.find("cost") != std::string::npos) { - cost_string = str; - } - } - if (cost_string.empty()) { - std::cout << "cannot find the cost line in " << file_name << std::endl; - throw 0; - } - - vector tokens = split_and_trim(cost_string); - if (tokens.size() != 3) { - std::cout << "unexpected cost string " << cost_string << std::endl; - throw 0; - } - return atof(tokens[2].c_str()); -} - -bool values_are_one_percent_close(double a, double b) { - double maxval = std::max(fabs(a), fabs(b)); - if (maxval < 0.000001) { - return true; - } - - double one_percent = maxval / 100; - return fabs(a - b) <= one_percent; -} - -// returns true if both are optimal -void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, - unsigned & successes, - unsigned & failures) { - double a = get_glpk_cost(glpk_out_file_name); - double b = get_lp_tst_cost(lp_out_file_name); - - if (values_are_one_percent_close(a, b)) { - successes++; - } else { - failures++; - std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; - } -} -void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { -#ifdef CHECK_GLPK_SOLUTION - std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); - if (solution_is_feasible(lp_file_name, *solution_table)) { - std::cout << "glpk solution is feasible" << std::endl; - } else { - std::cout << "glpk solution is infeasible" << std::endl; - } - delete solution_table; -#endif - if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { - compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); - } -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser); - -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { - bool use_mpq = args_parser.option_is_used("--mpq"); - bool minimize = args_parser.option_is_used("--min"); - std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); - - std::string input_file_name = test_dir + "/" + test_file_name; - if (input_file_name[input_file_name.size() - 1] == '~') { - // std::cout << "ignoring " << input_file_name << std::endl; - return; - } - std::cout <<"processing " << input_file_name << std::endl; - - std::ofstream out(full_lp_tst_out_name); - if (!out.is_open()) { - std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; - throw 0; - } - std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer - std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! - bool dual = args_parser.option_is_used("--dual"); - try { - if (args_parser.option_is_used("--lar")) - test_lar_on_file(input_file_name, args_parser); - else - solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); - } - catch(...) { - std::cout << "catching the failure" << std::endl; - failures++; - std::cout.rdbuf(coutbuf); // reset to standard output again - return; - } - std::cout.rdbuf(coutbuf); // reset to standard output again - - if (args_parser.option_is_used("--compare_with_glpk")) { - std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); - int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); - if (glpk_exit_code != 0) { - std::cout << "glpk failed" << std::endl; - inconclusives++; - } else { - compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); - } - } -} -/* - int my_readdir(DIR *dirp, struct dirent * - #ifndef LEAN_WINDOWS - entry - #endif - , struct dirent **result) { - #ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; - #else - return readdir_r(dirp, entry, result); - #endif - } -*/ -/* - vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); - #ifndef LEAN_WINDOWS - result != nullptr && - #endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; - } -*/ -/* - struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; - - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } - }; - -*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2598,121 +2068,11 @@ struct sort_pred { }; -void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { - /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - */ -} -std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { - std::unordered_map ret; - for (const auto & it : reader.column_names()) { - ret[it] = lps->get_column_value_by_name(it); - } - return ret; -} -void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { - std::string maxng = args_parser.get_option_value("--maxng"); - if (!maxng.empty()) { - solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); - } - if (args_parser.option_is_used("-pd")){ - solver->settings().presolve_with_double_solver_for_lar = true; - } - - if (args_parser.option_is_used("--compare_with_primal")){ - if (reader == nullptr) { - std::cout << "cannot compare with primal, the reader is null " << std::endl; - return; - } - auto * lps = reader->create_solver(false); - lps->find_maximal_solution(); - std::unordered_map sol = get_solution_map(lps, *reader); - std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; - return; - } - stopwatch sw; - sw.start(); - lp_status status = solver->solve(); - std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == lp_status::INFEASIBLE) { - explanation evidence; - solver->get_infeasibility_explanation(evidence); - } - if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != lp_status::OPTIMAL) { - std::cout << "cannot check randomize on an infeazible problem" << std::endl; - return; - } - std::cout << "checking randomize" << std::endl; - vector all_vars; - for (unsigned j = 0; j < solver->number_of_vars(); j++) - all_vars.push_back(j); - - unsigned m = all_vars.size(); - if (m > 100) - m = 100; - - var_index *vars = new var_index[m]; - for (unsigned i = 0; i < m; i++) - vars[i]=all_vars[i]; - - solver->random_update(m, vars); - delete []vars; - } -} -lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { - if (args_parser.option_is_used("--smt")) { - smt_reader reader(file_name); - reader.read(); - if (!reader.is_ok()){ - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); - } - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser) { - lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); - mps_reader reader(file_name); - mps_reader * mps_reader = nullptr; - reader.read(); - if (reader.is_ok()) { - mps_reader = & reader; - run_lar_solver(args_parser, solver, mps_reader); - } - delete solver; -} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2733,23 +2093,6 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } -void test_lar_solver(argument_parser & args_parser) { - - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - test_lar_on_file(file_name, args_parser); - return; - } - - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - test_lar_on_file(fn, args_parser); - return; - } - - std::cout << "give option --file or --filelist to test_lar_solver\n"; -} void test_numeric_pair() { numeric_pair a; @@ -3934,22 +3277,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq")) { - test_rationals(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np")) { - test_rationals_no_numeric_pairs(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np_plus")) { - test_rationals_no_numeric_pairs_plus(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3967,29 +3294,8 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } -#ifdef Z3DEBUG - if (args_parser.option_is_used("--test_swaps")) { - square_sparse_matrix m(10, 0); - fill_matrix(m); - test_swap_rows_with_permutation(m); - test_swap_cols_with_permutation(m); - return finalize(0); - } -#endif - if (args_parser.option_is_used("--test_perm")) { - test_permutations(); - return finalize(0); - } - if (args_parser.option_is_used("--test_file_directory")) { - test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); - return finalize(0); - } - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - solve_mps(fn, args_parser); - return finalize(0); - } + + if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3997,100 +3303,6 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } -#ifdef Z3DEBUG - lp_settings settings; - update_settings(args_parser, settings); - if (args_parser.option_is_used("--test_lu")) { - test_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_small_lu")) { - test_small_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--lar")){ - std::cout <<"calling test_lar_solver" << std::endl; - test_lar_solver(args_parser); - return finalize(0); - } - - - - if (args_parser.option_is_used("--test_larger_lu")) { - test_larger_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_larger_lu_with_holes")) { - test_larger_lu_with_holes(settings); - ret = 0; - return finalize(ret); - } -#endif - if (args_parser.option_is_used("--eti")) { - test_evidence_for_total_inf_simple(args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--maximize_term")) { - test_maximize_term(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_lp_0")) { - test_lp_0(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--smap")) { - test_stacked(); - ret = 0; - return finalize(ret); - } - if (args_parser.option_is_used("--term")) { - test_term(); - ret = 0; - return finalize(ret); - } - unsigned time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - bool dual = args_parser.option_is_used("--dual"); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--solve_some_mps")) { -#ifndef _WINDOWS - solve_some_mps(args_parser); -#endif - ret = 0; - return finalize(ret); - } - // lp::ccc = 0; - return finalize(0); - test_init_U(); - test_replace_column(); -#ifdef Z3DEBUG - square_sparse_matrix_with_permutations_test(); - test_dense_matrix(); - test_swap_operations(); - test_permutations(); - test_pivot_like_swaps_and_pivot(); -#endif - tst1(); - std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 2ab0c1ea6..ee41a418a 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,7 +20,6 @@ Revision History: #pragma once -// reads an MPS file representing a Mixed Integer Program #include #include #include @@ -31,7 +30,6 @@ Revision History: #include #include #include -#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include From cd24c9973945b1338784c43c1b4dc5d2e760534f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:26:06 -0800 Subject: [PATCH 073/220] remove a lp_primal_simplex.cpp from CMakeLists --- src/math/lp/CMakeLists.txt | 1 - src/test/lp/lp.cpp | 33 --------------------------------- 2 files changed, 34 deletions(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 9f0fae6bc..30724fcf7 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -22,7 +22,6 @@ z3_add_component(lp lp_dual_core_solver.cpp lp_dual_simplex.cpp lp_primal_core_solver.cpp - lp_primal_simplex.cpp lp_settings.cpp lp_solver.cpp lu.cpp diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 984c7b4bb..5193c6167 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1459,16 +1459,6 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } -template -void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { - if (time_limit > 0) - solver->set_time_limit(time_limit); - - if (look_for_min) - solver->flip_costs(); - - update_settings(args_parser, solver->settings()); -} bool values_are_one_percent_close(double a, double b); @@ -1572,29 +1562,6 @@ void add_random_cost(lp_primal_simplex * solver, int cols) { } } -lp_primal_simplex * generate_random_solver() { - int rows = get_random_rows(); - int cols = get_random_columns(); - auto * solver = new lp_primal_simplex(); - for (int i = 0; i < rows; i++) { - add_random_row(solver, cols, i); - } - add_random_cost(solver, cols); - return solver; -} - - - -void random_test_on_i(unsigned i) { - if (i % 1000 == 0) { - std::cout << "."; - } - srand(i); - auto *solver = generate_random_solver(); - solver->find_maximal_solution(); - // std::cout << lp_status_to_string(solver->get_status()) << std::endl; - delete solver; -} void random_test() { for (unsigned i = 0; i < std::numeric_limits::max(); i++) { From 8db2f1409b2c1a17402ac8c3911b83308f252d86 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:27:57 -0800 Subject: [PATCH 074/220] lp_dual_simplex.cpp removed from CMakeLists.txt --- src/math/lp/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 30724fcf7..41813b104 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,7 +20,6 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp - lp_dual_simplex.cpp lp_primal_core_solver.cpp lp_settings.cpp lp_solver.cpp From a44772424c75a2b5b9219cbdb6799c46602df9f4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:30:15 -0800 Subject: [PATCH 075/220] more removals --- src/test/lp/lp.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 5193c6167..9e0425194 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1563,17 +1563,6 @@ void add_random_cost(lp_primal_simplex * solver, int cols) { } -void random_test() { - for (unsigned i = 0; i < std::numeric_limits::max(); i++) { - try { - random_test_on_i(i); - } - catch (const char * error) { - std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; - break; - } - } -} #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { From 2ec09944d726b2bba610423692e2696c729a3b68 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:32:44 -0800 Subject: [PATCH 076/220] removals --- src/math/lp/CMakeLists.txt | 1 - src/test/lp/lp.cpp | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 41813b104..5719de44f 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -22,7 +22,6 @@ z3_add_component(lp lp_dual_core_solver.cpp lp_primal_core_solver.cpp lp_settings.cpp - lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 9e0425194..c2b297e6b 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1549,21 +1549,6 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -void add_random_row(lp_primal_simplex * solver, int cols, int row) { - solver->add_constraint(lp_relation::Greater_or_equal, 1, row); - for (int i = 0; i < cols; i++) { - solver->set_row_column_coefficient(row, i, get_random_int()); - } -} - -void add_random_cost(lp_primal_simplex * solver, int cols) { - for (int i = 0; i < cols; i++) { - solver->set_cost_for_column(i, get_random_int()); - } -} - - - #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); From d2e8297d4142f9b58c9bdd16293dae2bdb72af37 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:38:47 -0800 Subject: [PATCH 077/220] remove includes of lp_dual_simplex --- src/math/lp/static_matrix.cpp | 1 - src/sat/smt/arith_sls.h | 1 - src/smt/theory_lra.cpp | 1 - src/test/lp/smt_reader.h | 1 - 4 files changed, 4 deletions(-) diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 571e9b1d0..cde96277b 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index af3a46234..f50441871 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -21,7 +21,6 @@ Author: #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d61910ff2..4180f9ec3 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -21,7 +21,6 @@ #include "util/stopwatch.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index ee41a418a..6060370a8 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -24,7 +24,6 @@ Revision History: #include #include #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include From 8989e10e7141d52d9696b5c1a39100e16fb0e6e1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:41:30 -0800 Subject: [PATCH 078/220] rm lp_dual_simplex --- src/math/lp/lp_dual_simplex.cpp | 24 -- src/math/lp/lp_dual_simplex.h | 93 -------- src/math/lp/lp_dual_simplex_def.h | 376 ------------------------------ src/sat/smt/arith_solver.h | 1 - 4 files changed, 494 deletions(-) delete mode 100644 src/math/lp/lp_dual_simplex.cpp delete mode 100644 src/math/lp/lp_dual_simplex.h delete mode 100644 src/math/lp/lp_dual_simplex_def.h diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp deleted file mode 100644 index aaf612f56..000000000 --- a/src/math/lp/lp_dual_simplex.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_dual_simplex_def.h" -template lp::mpq lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); -template double lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h deleted file mode 100644 index 75ef87492..000000000 --- a/src/math/lp/lp_dual_simplex.h +++ /dev/null @@ -1,93 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_dual_core_solver.h" -namespace lp { - -template -class lp_dual_simplex: public lp_solver { - lp_dual_core_solver * m_core_solver; - vector m_b_copy; - vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver - vector m_column_types_of_core_solver; - vector m_column_types_of_logicals; - vector m_can_enter_basis; -public: - ~lp_dual_simplex() override { - delete m_core_solver; - } - - lp_dual_simplex() : m_core_solver(nullptr) {} - - - void decide_on_status_after_stage1(); - - void fix_logical_for_stage2(unsigned j); - - void fix_structural_for_stage2(unsigned j); - - void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - - void restore_right_sides(); - - void solve_for_stage2(); - - void fill_x_with_zeros(); - - void stage1(); - - void stage2(); - - void fill_first_stage_solver_fields(); - - column_type get_column_type(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); - - void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); - - void set_type_for_logical(unsigned j, column_type col_type) { - this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; - } - - void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial); - - void augment_matrix_A_and_fill_x_and_allocate_some_fields(); - - - - void copy_m_b_aside_and_set_it_to_zeros(); - - void find_maximal_solution() override; - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; -}; -} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h deleted file mode 100644 index 8af9d87c1..000000000 --- a/src/math/lp/lp_dual_simplex_def.h +++ /dev/null @@ -1,376 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "math/lp/lp_dual_simplex.h" -namespace lp{ - -template void lp_dual_simplex::decide_on_status_after_stage1() { - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = lp_status::FEASIBLE; - } else { - this->m_status = lp_status::UNBOUNDED; - } - break; - case lp_status::DUAL_UNBOUNDED: - lp_unreachable(); - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - lp_assert(j >= this->number_of_core_structurals()); - switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::fixed; - m_can_enter_basis[j] = false; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - switch (ci->get_column_type()) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - case column_type::upper_bound: - lp_unreachable(); - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::boxed; - m_can_enter_basis[j] = true; - break; - case column_type::free_column: - m_can_enter_basis[j] = true; - m_column_types_of_core_solver[j] = column_type::free_column; - break; - default: - lp_unreachable(); - } - // T cost_was = this->m_costs[j]; - this->set_scaled_cost(j); -} - -template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { - fix_logical_for_stage2(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fix_structural_for_stage2(j); - } -} - -template void lp_dual_simplex::restore_right_sides() { - unsigned i = this->m_A->row_count(); - while (i--) { - this->m_b[i] = m_b_copy[i]; - } -} - -template void lp_dual_simplex::solve_for_stage2() { - m_core_solver->restore_non_basis(); - m_core_solver->solve_yB(m_core_solver->m_y); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(lp_status::FEASIBLE); - m_core_solver->solve(); - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - this->m_status = lp_status::OPTIMAL; - break; - case lp_status::DUAL_UNBOUNDED: - this->m_status = lp_status::INFEASIBLE; - break; - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } - this->m_second_stage_iterations = m_core_solver->total_iterations(); - this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); -} - -template void lp_dual_simplex::fill_x_with_zeros() { - unsigned j = this->m_A->column_count(); - while (j--) { - this->m_x[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::stage1() { - lp_assert(m_core_solver == nullptr); - this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - m_core_solver = new lp_dual_core_solver( - *this->m_A, - m_can_enter_basis, - this->m_b, // the right side vector - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types_of_core_solver, - this->m_lower_bounds, - this->m_upper_bounds, - this->m_settings, - *this); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - // skipping stage 1 - m_core_solver->set_status(lp_status::OPTIMAL); - m_core_solver->set_total_iterations(0); - } else { - m_core_solver->solve(); - } - decide_on_status_after_stage1(); - this->m_first_stage_iterations = m_core_solver->total_iterations(); -} - -template void lp_dual_simplex::stage2() { - unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - restore_right_sides(); - solve_for_stage2(); -} - -template void lp_dual_simplex::fill_first_stage_solver_fields() { - unsigned slack_var = this->number_of_core_structurals(); - unsigned artificial = this->number_of_core_structurals() + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); - } - fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); -} - -template column_type lp_dual_simplex::get_column_type(unsigned j) { - lp_assert(j < this->m_A->column_count()); - if (j >= this->number_of_core_structurals()) { - return m_column_types_of_logicals[j - this->number_of_core_structurals()]; - } - return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { - // see 4.7 in the dissertation of Achim Koberstein - lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != - this->m_core_solver_columns_to_external_columns.end()); - - T free_bound = T(1e4); // see 4.8 - unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = this->m_map_from_var_index_to_column_info[jj]; - switch (ci->get_column_type()) { - case column_type::upper_bound: { - std::stringstream s; - s << "unexpected bound type " << j << " " - << column_type_to_string(get_column_type(j)); - throw_exception(s.str()); - break; - } - case column_type::lower_bound: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - break; - } - case column_type::free_column: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; - break; - } - case column_type::boxed: - m_can_enter_basis[j] = false; - this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? - break; - default: - lp_unreachable(); - } - m_column_types_of_core_solver[j] = column_type::boxed; -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { - this->m_costs[j] = 0; - lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { - m_column_types_of_core_solver[j] = column_type::boxed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = column_type::fixed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { // go over logicals here - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); - } -} - -template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - switch (constraint.m_relation) { - case Equal: // no slack variable here - set_type_for_logical(artificial, column_type::fixed); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - (*this->m_A)(row, artificial) = numeric_traits::one(); - artificial++; - break; - - case Greater_or_equal: - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put a slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = numeric_traits::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - } -} - -template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { - this->count_slacks_and_artificials(); - this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); - unsigned n = this->m_A->column_count(); - this->m_column_types_of_core_solver.resize(n); - m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); - this->m_costs.resize(n); - this->m_upper_bounds.resize(n); - this->m_lower_bounds.resize(n); - m_can_enter_basis.resize(n); - this->m_basis.resize(this->m_A->row_count()); -} - - - -template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { - for (unsigned i = 0; i < this->m_b.size(); i++) { - m_b_copy.push_back(this->m_b[i]); - this->m_b[i] = numeric_traits::zero(); // preparing for the first stage - } -} - -template void lp_dual_simplex::find_maximal_solution(){ - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->flip_costs(); // do it for now, todo ( remove the flipping) - - this->cleanup(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->fill_matrix_A_and_init_right_side(); - this->fill_m_b(); - this->scale(); - augment_matrix_A_and_fill_x_and_allocate_some_fields(); - fill_first_stage_solver_fields(); - copy_m_b_aside_and_set_it_to_zeros(); - stage1(); - if (this->m_status == lp_status::FEASIBLE) { - stage2(); - } -} - - -template T lp_dual_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return -ret; // we flip costs for now -} -} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 9ae671c16..eac73c719 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -21,7 +21,6 @@ Author: #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" #include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" From 2dd30fa350a78c9ef7419f8ae8ffde2cc6090000 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:44:50 -0800 Subject: [PATCH 079/220] rm lp_primal_simplex --- src/math/lp/lp_primal_simplex.cpp | 35 --- src/math/lp/lp_primal_simplex.h | 106 -------- src/math/lp/lp_primal_simplex_def.h | 367 ---------------------------- src/sat/smt/arith_sls.h | 1 - src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 1 - src/test/lp/lp.cpp | 1 - src/test/lp/smt_reader.h | 1 - 8 files changed, 513 deletions(-) delete mode 100644 src/math/lp/lp_primal_simplex.cpp delete mode 100644 src/math/lp/lp_primal_simplex.h delete mode 100644 src/math/lp/lp_primal_simplex_def.h diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp deleted file mode 100644 index 634f52900..000000000 --- a/src/math/lp/lp_primal_simplex.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_primal_simplex_def.h" -template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template double lp::lp_primal_simplex::get_current_cost() const; -template double lp::lp_primal_simplex::get_column_value(unsigned int) const; -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::mpq lp::lp_primal_simplex::get_current_cost() const; -template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; -template void lp::lp_primal_simplex::find_maximal_solution(); -template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h deleted file mode 100644 index 77e12d088..000000000 --- a/src/math/lp/lp_primal_simplex.h +++ /dev/null @@ -1,106 +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_utils.h" -#include "math/lp/column_info.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/lp_solver.h" -namespace lp { -template -class lp_primal_simplex: public lp_solver { - lp_primal_core_solver * m_core_solver; - vector m_lower_bounds; -private: - unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } - - void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); - - void init_buffer(unsigned k, vector & r); - - void refactor(); - - void set_scaled_costs(); -public: - lp_primal_simplex(): m_core_solver(nullptr) {} - - column_info * get_or_create_column_info(unsigned column); - - void set_status(lp_status status) { - this->m_status = status; - } - - lp_status get_status() { - return this->m_status; - } - - void fill_acceptable_values_for_x(); - - - void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); - - void fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial); - - - - - void set_core_solver_bounds(); - - void find_maximal_solution() override; - - void fill_A_x_and_basis_for_stage_one_total_inf(); - - void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); - - void solve_with_total_inf(); - - - ~lp_primal_simplex() override; - - bool bounds_hold(std::unordered_map const & solution); - - T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraints_hold(std::unordered_map const & solution); - - - T * get_array_from_map(std::unordered_map const & solution); - - bool solution_is_feasible(std::unordered_map const & solution) { - return bounds_hold(solution) && row_constraints_hold(solution); - } - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; - - -}; -} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h deleted file mode 100644 index 7ffe819b2..000000000 --- a/src/math/lp/lp_primal_simplex_def.h +++ /dev/null @@ -1,367 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "util/vector.h" -#include "math/lp/lp_primal_simplex.h" - -namespace lp { -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { - unsigned slack_var = original_number_of_columns; - unsigned artificial = original_number_of_columns + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); - } -} - -template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { - for (unsigned i = 0; i < k; i++) { - r[i] = 0; - } - r[k] = 1; - for (unsigned i = this->row_count() -1; i > k; i--) { - r[i] = 0; - } -} - -template void lp_primal_simplex::refactor() { - m_core_solver->init_lu(); - if (m_core_solver->factorization()->get_status() != LU_status::OK) { - throw_exception("cannot refactor"); - } -} - -template void lp_primal_simplex::set_scaled_costs() { - unsigned j = this->number_of_core_structurals(); - while (j-- > 0) { - this->set_scaled_cost(j); - } -} - -template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { - auto it = this->m_columns.find(column); - return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; -} - -template void lp_primal_simplex::fill_acceptable_values_for_x() { - for (auto t : this->m_core_solver_columns_to_external_columns) { - this->m_x[t.first] = numeric_traits::zero(); - } -} - - -template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { - bound_is_set[i] = true; - bounds[i] = numeric_traits::zero(); -} - -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row >= 0 && row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - T artificial_cost = - numeric_traits::one(); - switch (constraint.m_relation) { - case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::lower_bound; - this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero - this->m_basis[row] = artificial; - if (rs >= 0) { - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_x[artificial] = - rs; - } - artificial++; - break; - - case Greater_or_equal: - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - - if (rs > 0) { - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_basis[row] = artificial; - this->m_x[artificial] = rs; - artificial++; - } else { - // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = - rs; - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = numeric_traits::one(); - - if (rs < 0) { - // adding one artificial - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_x[artificial] = - rs; - this->m_basis[row] = artificial++; - } else { - // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = rs; - } - slack_var++; - break; - } -} - - - - - -template void lp_primal_simplex::set_core_solver_bounds() { - unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; - this->m_column_types.resize(total_vars); - this->m_upper_bounds.resize(total_vars); - for (auto cit : this->m_map_from_var_index_to_column_info) { - column_info * ci = cit.second; - unsigned j = ci->get_column_index(); - if (!is_valid(j)) - continue; // the variable is not mapped to a column - switch (this->m_column_types[j] = ci->get_column_type()){ - case column_type::fixed: - this->m_upper_bounds[j] = numeric_traits::zero(); - break; - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - break; - - default: break; // do nothing - } - } -} - - -template void lp_primal_simplex::find_maximal_solution() { - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->cleanup(); - this->fill_matrix_A_and_init_right_side(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->m_x.resize(this->m_A->column_count()); - this->fill_m_b(); - this->scale(); - fill_acceptable_values_for_x(); - this->count_slacks_and_artificials(); - set_core_solver_bounds(); - solve_with_total_inf(); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { - for (unsigned row = 0; row < this->row_count(); row++) - fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - lp_assert(row < this->row_count()); - auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); - unsigned ext_row = ext_row_it->second; - auto constr_it = this->m_constraints.find(ext_row); - lp_assert(constr_it != this->m_constraints.end()); - auto & constraint = constr_it->second; - unsigned j = this->m_A->column_count(); // j is a slack variable - this->m_A->add_column(); - // we need to bring the program to the form Ax = b - this->m_basis[row] = j; - switch (constraint.m_relation) { - case Equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - default: - lp_unreachable(); - } -} - -template void lp_primal_simplex::solve_with_total_inf() { - int total_vars = this->m_A->column_count() + this->row_count(); - if (total_vars == 0) { - this->m_status = lp_status::OPTIMAL; - return; - } - m_lower_bounds.clear(); - m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - fill_A_x_and_basis_for_stage_one_total_inf(); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - set_scaled_costs(); - - m_core_solver = new lp_primal_core_solver(*this->m_A, - this->m_b, - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types, - m_lower_bounds, - this->m_upper_bounds, - this->m_settings, *this); - m_core_solver->solve(); - this->set_status(m_core_solver->get_status()); - this->m_total_iterations = m_core_solver->total_iterations(); -} - - -template lp_primal_simplex::~lp_primal_simplex() { - delete m_core_solver; -} - -template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { - for (auto it : this->m_map_from_var_index_to_column_info) { - auto sol_it = solution.find(it.second->get_name()); - if (sol_it == solution.end()) { - std::stringstream s; - s << "cannot find column " << it.first << " in solution"; - throw_exception(s.str() ); - } - - if (!it.second->bounds_hold(sol_it->second)) { - it.second->bounds_hold(sol_it->second); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { - auto it = this->m_A_values.find(i); - if (it == this->m_A_values.end()) { - std::stringstream s; - s << "cannot find row " << i; - throw_exception(s.str() ); - } - T ret = numeric_traits::zero(); - for (auto & pair : it->second) { - auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = cit->second; - auto sol_it = solution.find(ci->get_name()); - lp_assert(sol_it != solution.end()); - T column_val = sol_it->second; - if (out != nullptr) { - (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; - } - ret += pair.second * column_val; - } - if (out != nullptr) { - (*out) << " = " << ret << std::endl; - } - return ret; -} - -template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { - T row_val = get_row_value(i, solution, out); - auto & constraint = this->m_constraints[i]; - T rs = constraint.m_rs; - bool print = out != nullptr; - switch (constraint.m_relation) { - case Equal: - if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { - if (print) { - (*out) << "should be = " << rs << std::endl; - } - return false; - } - return true; - case Greater_or_equal: - if (numeric_traits::get_double(row_val - rs) < -0.00001) { - if (print) { - (*out) << "should be >= " << rs << std::endl; - } - return false; - } - return true;; - - case Less_or_equal: - if (numeric_traits::get_double(row_val - rs) > 0.00001) { - if (print) { - (*out) << "should be <= " << rs << std::endl; - } - return false; - } - return true;; - } - lp_unreachable(); - return false; // it is unreachable -} - -template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { - for (auto it : this->m_A_values) { - if (!row_constraint_holds(it.first, solution, nullptr)) { - row_constraint_holds(it.first, solution, nullptr); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; -} -} diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index f50441871..b0b4fb48b 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -20,7 +20,6 @@ Author: #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index eac73c719..3152485ec 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -20,7 +20,6 @@ Author: #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 4180f9ec3..a654366a2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -20,7 +20,6 @@ --*/ #include "util/stopwatch.h" #include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c2b297e6b..78abf1a6f 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,7 +33,6 @@ #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_primal_simplex.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 6060370a8..75edb23b9 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -23,7 +23,6 @@ Revision History: #include #include #include -#include "math/lp/lp_primal_simplex.h" #include "math/lp/lar_solver.h" #include #include From 5e4bca3d26d5f86edce6baf1331d6e4d72c00ae0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 15:58:25 -0800 Subject: [PATCH 080/220] small removals --- src/sat/smt/arith_sls.h | 1 - src/test/lp/test_file_reader.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index b0b4fb48b..09a56c84e 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,7 +19,6 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 8f461ea1c..36b273740 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,7 +27,6 @@ Revision History: #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" namespace lp { From ff1dc0424ca73b70c314f710737213ea30a3fefe Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 16:32:49 -0800 Subject: [PATCH 081/220] rm lp_solver --- src/math/lp/indexed_value.h | 11 - src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/lp_solver.cpp | 55 --- src/math/lp/lp_solver.h | 260 ------------- src/math/lp/lp_solver_def.h | 571 ---------------------------- src/sat/smt/arith_solver.h | 2 +- src/smt/theory_lra.cpp | 1 - 8 files changed, 2 insertions(+), 929 deletions(-) delete mode 100644 src/math/lp/lp_solver.cpp delete mode 100644 src/math/lp/lp_solver.h delete mode 100644 src/math/lp/lp_solver_def.h diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index c48376470..92d8f2adf 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,15 +43,4 @@ public: m_value = val; } }; -#ifdef Z3DEBUG -template -bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - const X & v = w[i]; - if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) - return false; - } - return true; -} -#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 8a6c64ef0..de8fe68ad 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,35 +651,7 @@ public: } } - void scale_problem_for_doubles( - static_matrix& A, - vector & lower_bounds, - vector & upper_bounds) { - vector column_scale_vector; - vector right_side_vector(A.column_count()); - settings().reps_in_scaler = 5; - scaler scaler(right_side_vector, - A, - settings().scaling_minimum, - settings().scaling_maximum, - column_scale_vector, - settings()); - if (! scaler.scale()) { - // the scale did not succeed, unscaling - A.clear(); - create_double_matrix(A); - } else { - for (unsigned j = 0; j < A.column_count(); j++) { - if (m_r_solver.column_has_upper_bound(j)) { - upper_bounds[j] /= column_scale_vector[j]; - } - if (m_r_solver.column_has_lower_bound(j)) { - lower_bounds[j] /= column_scale_vector[j]; - } - } - } - - } + // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 4b6163df8..a60395ab0 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,7 +30,6 @@ Revision History: #include #include #include "math/lp/lu.h" -#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp deleted file mode 100644 index fc9514098..000000000 --- a/src/math/lp/lp_solver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "math/lp/lp_solver_def.h" -template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h deleted file mode 100644 index ab16a686f..000000000 --- a/src/math/lp/lp_solver.h +++ /dev/null @@ -1,260 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/column_info.h" -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include "math/lp/scaler.h" -#include "math/lp/bound_analyzer_on_row.h" -namespace lp { -enum lp_relation { - Less_or_equal, - Equal, - Greater_or_equal -}; - -template -struct lp_constraint { - X m_rs; // right side of the constraint - lp_relation m_relation; - lp_constraint() {} // empty constructor - lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} -}; - - -template -class lp_solver : public column_namer { - column_info * get_or_create_column_info(unsigned column); - -protected: - T get_column_cost_value(unsigned j, column_info * ci) const; -public: - unsigned m_total_iterations; - static_matrix* m_A; // this is the matrix of constraints - vector m_b; // the right side vector - unsigned m_first_stage_iterations; - unsigned m_second_stage_iterations; - std::unordered_map> m_constraints; - std::unordered_map*> m_map_from_var_index_to_column_info; - std::unordered_map > m_A_values; - std::unordered_map m_names_to_columns; // don't have to use it - std::unordered_map m_external_rows_to_core_solver_rows; - std::unordered_map m_core_solver_rows_to_external_rows; - std::unordered_map m_core_solver_columns_to_external_columns; - vector m_column_scale; - std::unordered_map m_name_map; - unsigned m_artificials; - unsigned m_slacks; - vector m_column_types; - vector m_costs; - vector m_x; - vector m_upper_bounds; - vector m_basis; - vector m_nbasis; - vector m_heading; - - - lp_status m_status; - - lp_settings m_settings; - lp_solver(): - m_A(nullptr), // this is the matrix of constraints - m_first_stage_iterations (0), - m_second_stage_iterations (0), - m_artificials (0), - m_slacks (0), - m_status(lp_status::UNKNOWN) - {} - - unsigned row_count() const { return this->m_A->row_count(); } - - void add_constraint(lp_relation relation, T right_side, unsigned row_index); - - void set_cost_for_column(unsigned column, T column_cost) { - get_or_create_column_info(column)->set_cost(column_cost); - } - std::string get_variable_name(unsigned j) const override; - - void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { - m_A_values[row][column] = val; - } - // returns the current cost - virtual T get_current_cost() const = 0; - // do not have to call it - void give_symbolic_name_to_column(std::string name, unsigned column); - - virtual T get_column_value(unsigned column) const = 0; - - T get_column_value_by_name(std::string name) const; - - // returns -1 if not found - virtual int get_column_index_by_name(std::string name) const; - - void set_lower_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_lower_bound(bound); - } - - void set_upper_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_upper_bound(bound); - } - - void unset_lower_bound(unsigned i) { - get_or_create_column_info(i)->unset_lower_bound(); - } - - void unset_upper_bound(unsigned i) { - get_or_create_column_info(i)->unset_upper_bound(); - } - - void set_fixed_value(unsigned i, T val) { - column_info *ci = get_or_create_column_info(i); - ci->set_fixed_value(val); - } - - void unset_fixed_value(unsigned i) { - get_or_create_column_info(i)->unset_fixed(); - } - - lp_status get_status() const { - return m_status; - } - - void set_status(lp_status st) { - m_status = st; - } - - - ~lp_solver() override; - - void flip_costs(); - - virtual void find_maximal_solution() = 0; - void set_time_limit(unsigned time_limit_in_seconds) { - m_settings.time_limit = time_limit_in_seconds; - } - - -protected: - bool problem_is_empty(); - - void scale(); - - - void print_rows_scale_stats(std::ostream & out); - - void print_columns_scale_stats(std::ostream & out); - - void print_row_scale_stats(unsigned i, std::ostream & out); - - void print_column_scale_stats(unsigned j, std::ostream & out); - - void print_scale_stats(std::ostream & out); - - void get_max_abs_in_row(std::unordered_map & row_map); - - void pin_vars_down_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, - numeric_traits::one()); - } - - void pin_vars_up_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, numeric_traits::one()); - } - - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - - bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - - bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); - - bool row_is_zero(std::unordered_map & row); - - bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); - - // analyse possible max and min values that are derived from var boundaries - // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. - // Then we know what values of the variables are. For each positive coeff of the row it has to be - // the low boundary of the var and for a negative - the upper. - - // this routing also pins the variables to the boundaries - bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); - - void remove_fixed_or_zero_columns(); - - void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); - - unsigned try_to_remove_some_rows(); - - void cleanup(); - - void map_external_rows_to_core_solver_rows(); - - void map_external_columns_to_core_solver_columns(); - - unsigned number_of_core_structurals() { - return static_cast(m_core_solver_columns_to_external_columns.size()); - } - - void restore_column_scales_to_one() { - for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); - } - - void unscale(); - - void fill_A_from_A_values(); - - void fill_matrix_A_and_init_right_side(); - - void count_slacks_and_artificials(); - - void count_slacks_and_artificials_for_row(unsigned i); - - T lower_bound_shift_for_row(unsigned i); - - void fill_m_b(); - - T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; - void set_scaled_cost(unsigned j); - void print_statistics_on_A(std::ostream & out) { - out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; - } - -public: - lp_settings & settings() { return m_settings;} - void print_model(std::ostream & s) const { - s << "objective = " << get_current_cost() << std::endl; - s << "column values\n"; - for (auto & it : m_names_to_columns) { - s << it.first << " = " << get_column_value(it.second) << std::endl; - } - } -}; -} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h deleted file mode 100644 index 191832a24..000000000 --- a/src/math/lp/lp_solver_def.h +++ /dev/null @@ -1,571 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_solver.h" -namespace lp { -template column_info * lp_solver::get_or_create_column_info(unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; -} - -template -std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index - if (!m_settings.print_external_var_name()) - return std::string("j")+T_to_string(j); - auto it = this->m_core_solver_columns_to_external_columns.find(j); - if (it == this->m_core_solver_columns_to_external_columns.end()) - return std::string("x")+T_to_string(j); - unsigned external_j = it->second; - auto t = this->m_map_from_var_index_to_column_info.find(external_j); - if (t == this->m_map_from_var_index_to_column_info.end()) { - return std::string("x") +T_to_string(external_j); - } - return t->second->get_name(); -} - -template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { - if (ci->is_fixed()) { - return ci->get_cost() * ci->get_fixed_value(); - } - return ci->get_cost() * get_column_value(j); -} -template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - lp_assert(m_constraints.find(row_index) == m_constraints.end()); - lp_constraint cs(right_side, relation); - m_constraints[row_index] = cs; -} - -template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - column_info *ci; - if (it == m_map_from_var_index_to_column_info.end()){ - m_map_from_var_index_to_column_info[column] = ci = new column_info; - } else { - ci = it->second; - } - ci->set_name(name); - m_names_to_columns[name] = column; -} - - -template T lp_solver::get_column_value_by_name(std::string name) const { - auto it = m_names_to_columns.find(name); - if (it == m_names_to_columns.end()) { - std::stringstream s; - s << "get_column_value_by_name " << name; - throw_exception(s.str()); - } - return get_column_value(it -> second); -} - -// returns -1 if not found -template int lp_solver::get_column_index_by_name(std::string name) const { - auto t = m_names_to_columns.find(name); - if (t == m_names_to_columns.end()) { - return -1; - } - return t->second; -} - - -template lp_solver::~lp_solver(){ - delete m_A; - for (auto t : m_map_from_var_index_to_column_info) { - delete t.second; - } -} - -template void lp_solver::flip_costs() { - for (auto t : m_map_from_var_index_to_column_info) { - column_info *ci = t.second; - ci->set_cost(-ci->get_cost()); - } -} - -template bool lp_solver::problem_is_empty() { - for (auto & c : m_A_values) - if (!c.second.empty()) - return false; - return true; -} - -template void lp_solver::scale() { - if (numeric_traits::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); - if (!scaler.scale()) { - unscale(); - } -} - - -template void lp_solver::print_rows_scale_stats(std::ostream & out) { - out << "rows max" << std::endl; - for (unsigned i = 0; i < m_A->row_count(); i++) { - print_row_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_columns_scale_stats(std::ostream & out) { - out << "columns max" << std::endl; - for (unsigned i = 0; i < m_A->column_count(); i++) { - print_column_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { - out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; - out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; -} - -template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { - out << "(" << m_A->get_min_abs_in_row(j) << " "; - out << m_A->get_max_abs_in_column(j) << ")"; -} - -template void lp_solver::print_scale_stats(std::ostream & out) { - print_rows_scale_stats(out); - print_columns_scale_stats(out); -} - -template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; -} - -template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_map_from_var_index_to_column_info[j]; - T a = t.second; - if (a * sign > numeric_traits::zero()) { - lp_assert(ci->upper_bound_is_set()); - ci->set_fixed_value(ci->get_upper_bound()); - } else { - lp_assert(ci->lower_bound_is_set()); - ci->set_fixed_value(ci->get_lower_bound()); - } - } -} - -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a > numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a < numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; -} - -template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (!is_zero(rs)) - m_status = lp_status::INFEASIBLE; - return true; - } - - T lower_bound; - bool lb = get_minimal_row_value(row, lower_bound); - if (lb) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_epsilon - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - T upper_bound; - bool ub = get_maximal_row_value(row, upper_bound); - if (ub) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - T upper_bound; - if (get_maximal_row_value(row, upper_bound)) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T lower_bound; - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs < zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - if (get_minimal_row_value(row, lower_bound)) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - return false; -} - -// analyse possible max and min values that are derived from var boundaries -// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. -// Then we know what values of the variables are. For each positive coeff of the row it has to be -// the low boundary of the var and for a negative - the upper. - -// this routing also pins the variables to the boundaries -template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { - auto & constraint = m_constraints[row_index]; - switch (constraint.m_relation) { - case lp_relation::Equal: - return row_e_is_obsolete(row, row_index); - - case lp_relation::Greater_or_equal: - return row_ge_is_obsolete(row, row_index); - - case lp_relation::Less_or_equal: - return row_le_is_obsolete(row, row_index); - } - lp_unreachable(); - return false; // it is unreachable -} - -template void lp_solver::remove_fixed_or_zero_columns() { - for (auto & i_row : m_A_values) { - remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); - } -} - -template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { - auto & constraint = m_constraints[i]; - vector removed; - for (auto & col : row) { - unsigned j = col.first; - lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); - column_info * ci = m_map_from_var_index_to_column_info[j]; - if (ci->is_fixed()) { - removed.push_back(j); - T aj = col.second; - constraint.m_rs -= aj * ci->get_fixed_value(); - } else { - if (numeric_traits::is_zero(col.second)){ - removed.push_back(j); - } - } - } - - for (auto j : removed) { - row.erase(j); - } -} - -template unsigned lp_solver::try_to_remove_some_rows() { - vector rows_to_delete; - for (auto & t : m_A_values) { - if (row_is_obsolete(t.second, t.first)) { - rows_to_delete.push_back(t.first); - } - - if (m_status == lp_status::INFEASIBLE) { - return 0; - } - } - if (!rows_to_delete.empty()) { - for (unsigned k : rows_to_delete) { - m_A_values.erase(k); - } - } - remove_fixed_or_zero_columns(); - return static_cast(rows_to_delete.size()); -} - -template void lp_solver::cleanup() { - int n = 0; // number of deleted rows - int d; - while ((d = try_to_remove_some_rows()) > 0) - n += d; - - if (n == 1) { - LP_OUT(m_settings, "deleted one row" << std::endl); - } else if (n) { - LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); - } -} - -template void lp_solver::map_external_rows_to_core_solver_rows() { - unsigned size = 0; - for (auto & row : m_A_values) { - m_external_rows_to_core_solver_rows[row.first] = size; - m_core_solver_rows_to_external_rows[size] = row.first; - size++; - } -} - -template void lp_solver::map_external_columns_to_core_solver_columns() { - unsigned size = 0; - for (auto & row : m_A_values) { - for (auto & col : row.second) { - if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { - throw_exception("found fixed column"); - } - unsigned j = col.first; - auto column_info_it = m_map_from_var_index_to_column_info.find(j); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - - auto j_column = column_info_it->second->get_column_index(); - if (!is_valid(j_column)) { // j is a newcomer - m_map_from_var_index_to_column_info[j]->set_column_index(size); - m_core_solver_columns_to_external_columns[size++] = j; - } - } - } -} - -template void lp_solver::unscale() { - delete m_A; - m_A = nullptr; - fill_A_from_A_values(); - restore_column_scales_to_one(); - fill_m_b(); -} - -template void lp_solver::fill_A_from_A_values() { - m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); - for (auto & t : m_A_values) { - auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); - unsigned row = row_it->second; - for (auto k : t.second) { - auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - column_info *ci = column_info_it->second; - unsigned col = ci->get_column_index(); - lp_assert(is_valid(col)); - bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); - if (!col_is_flipped) { - (*m_A)(row, col) = k.second; - } else { - (*m_A)(row, col) = - k.second; - } - } - } -} - -template void lp_solver::fill_matrix_A_and_init_right_side() { - map_external_rows_to_core_solver_rows(); - map_external_columns_to_core_solver_columns(); - lp_assert(m_A == nullptr); - fill_A_from_A_values(); - m_b.resize(m_A->row_count()); -} - -template void lp_solver::count_slacks_and_artificials() { - for (int i = row_count() - 1; i >= 0; i--) { - count_slacks_and_artificials_for_row(i); - } -} - -template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; - switch (constraint.m_relation) { - case Equal: - m_artificials++; - break; - case Greater_or_equal: - m_slacks++; - if (this->m_b[i] > 0) { - m_artificials++; - } - break; - case Less_or_equal: - m_slacks++; - if (this->m_b[i] < 0) { - m_artificials++; - } - break; - } -} - -template T lp_solver::lower_bound_shift_for_row(unsigned i) { - T ret = numeric_traits::zero(); - - auto row = this->m_A_values.find(i); - if (row == this->m_A_values.end()) { - throw_exception("cannot find row"); - } - for (auto col : row->second) { - ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); - } - return ret; -} - -template void lp_solver::fill_m_b() { - for (int i = this->row_count() - 1; i >= 0; i--) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; - auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); - } -} - -template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { - auto cit = this->m_map_from_var_index_to_column_info.find(column); - if (cit == this->m_map_from_var_index_to_column_info.end()) { - return numeric_traits::zero(); - } - - column_info * ci = cit->second; - - if (ci->is_fixed()) { - return ci->get_fixed_value(); - } - - unsigned cj = ci->get_column_index(); - if (cj != static_cast(-1)) { - T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; - if (ci->is_free()) { - return v; - } - if (!ci->is_flipped()) { - return v + ci->get_lower_bound(); - } - - // the flipped case when there is only upper bound - return -v + ci->get_upper_bound(); // - } - - return numeric_traits::zero(); // returns zero for out of boundary columns -} - -template void lp_solver::set_scaled_cost(unsigned j) { - // grab original costs but modify it with the column scales - lp_assert(j < this->m_column_scale.size()); - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - T cost = ci->get_cost(); - if (ci->is_flipped()){ - cost *= T(-1); - } - lp_assert(ci->is_fixed() == false); - this->m_costs[j] = cost * this->m_column_scale[j]; -} -} diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 3152485ec..68d5f8025 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,7 +19,7 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" + #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a654366a2..2aa988282 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,7 +19,6 @@ --*/ #include "util/stopwatch.h" -#include "math/lp/lp_solver.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" From 92fe8c59688b89b165287e63cb560a43139e427e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 3 Mar 2023 17:53:00 -0800 Subject: [PATCH 082/220] restore the previous state Signed-off-by: Lev Nachmanson --- src/math/lp/CMakeLists.txt | 3 + src/math/lp/indexed_value.h | 11 + src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_dual_simplex.cpp | 24 + src/math/lp/lp_dual_simplex.h | 93 +++ src/math/lp/lp_dual_simplex_def.h | 376 +++++++++++ src/math/lp/lp_primal_core_solver.h | 1 + src/math/lp/lp_primal_simplex.cpp | 35 + src/math/lp/lp_primal_simplex.h | 106 +++ src/math/lp/lp_primal_simplex_def.h | 367 +++++++++++ src/math/lp/lp_solver.cpp | 55 ++ src/math/lp/lp_solver.h | 260 ++++++++ src/math/lp/lp_solver_def.h | 571 ++++++++++++++++ src/math/lp/mps_reader.h | 891 +++++++++++++++++++++++++ src/math/lp/static_matrix.cpp | 1 + src/sat/smt/arith_sls.h | 3 + src/sat/smt/arith_solver.h | 4 +- src/shell/CMakeLists.txt | 1 + src/shell/lp_frontend.cpp | 109 +++ src/shell/lp_frontend.h | 7 + src/shell/main.cpp | 10 +- src/smt/theory_lra.cpp | 3 + src/test/lp/lp.cpp | 982 ++++++++++++++++++++++++++-- src/test/lp/smt_reader.h | 4 + src/test/lp/test_file_reader.h | 1 + 25 files changed, 3878 insertions(+), 70 deletions(-) create mode 100644 src/math/lp/lp_dual_simplex.cpp create mode 100644 src/math/lp/lp_dual_simplex.h create mode 100644 src/math/lp/lp_dual_simplex_def.h create mode 100644 src/math/lp/lp_primal_simplex.cpp create mode 100644 src/math/lp/lp_primal_simplex.h create mode 100644 src/math/lp/lp_primal_simplex_def.h create mode 100644 src/math/lp/lp_solver.cpp create mode 100644 src/math/lp/lp_solver.h create mode 100644 src/math/lp/lp_solver_def.h create mode 100644 src/math/lp/mps_reader.h create mode 100644 src/shell/lp_frontend.cpp create mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5719de44f..9f0fae6bc 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,8 +20,11 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp + lp_dual_simplex.cpp lp_primal_core_solver.cpp + lp_primal_simplex.cpp lp_settings.cpp + lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index 92d8f2adf..c48376470 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,4 +43,15 @@ public: m_value = val; } }; +#ifdef Z3DEBUG +template +bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { + for (unsigned i : w.m_index) { + const X & v = w[i]; + if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) + return false; + } + return true; +} +#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index de8fe68ad..8a6c64ef0 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,7 +651,35 @@ public: } } - + void scale_problem_for_doubles( + static_matrix& A, + vector & lower_bounds, + vector & upper_bounds) { + vector column_scale_vector; + vector right_side_vector(A.column_count()); + settings().reps_in_scaler = 5; + scaler scaler(right_side_vector, + A, + settings().scaling_minimum, + settings().scaling_maximum, + column_scale_vector, + settings()); + if (! scaler.scale()) { + // the scale did not succeed, unscaling + A.clear(); + create_double_matrix(A); + } else { + for (unsigned j = 0; j < A.column_count(); j++) { + if (m_r_solver.column_has_upper_bound(j)) { + upper_bounds[j] /= column_scale_vector[j]; + } + if (m_r_solver.column_has_lower_bound(j)) { + lower_bounds[j] /= column_scale_vector[j]; + } + } + } + + } // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp new file mode 100644 index 000000000..aaf612f56 --- /dev/null +++ b/src/math/lp/lp_dual_simplex.cpp @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include "math/lp/lp_dual_simplex_def.h" +template lp::mpq lp::lp_dual_simplex::get_current_cost() const; +template void lp::lp_dual_simplex::find_maximal_solution(); +template double lp::lp_dual_simplex::get_current_cost() const; +template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h new file mode 100644 index 000000000..75ef87492 --- /dev/null +++ b/src/math/lp/lp_dual_simplex.h @@ -0,0 +1,93 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/vector.h" +#include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_dual_core_solver.h" +namespace lp { + +template +class lp_dual_simplex: public lp_solver { + lp_dual_core_solver * m_core_solver; + vector m_b_copy; + vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver + vector m_column_types_of_core_solver; + vector m_column_types_of_logicals; + vector m_can_enter_basis; +public: + ~lp_dual_simplex() override { + delete m_core_solver; + } + + lp_dual_simplex() : m_core_solver(nullptr) {} + + + void decide_on_status_after_stage1(); + + void fix_logical_for_stage2(unsigned j); + + void fix_structural_for_stage2(unsigned j); + + void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); + + void restore_right_sides(); + + void solve_for_stage2(); + + void fill_x_with_zeros(); + + void stage1(); + + void stage2(); + + void fill_first_stage_solver_fields(); + + column_type get_column_type(unsigned j); + + void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); + + void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); + + void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); + + void set_type_for_logical(unsigned j, column_type col_type) { + this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; + } + + void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, + unsigned & slack_var, + unsigned & artificial); + + void augment_matrix_A_and_fill_x_and_allocate_some_fields(); + + + + void copy_m_b_aside_and_set_it_to_zeros(); + + void find_maximal_solution() override; + + T get_column_value(unsigned column) const override { + return this->get_column_value_with_core_solver(column, m_core_solver); + } + + T get_current_cost() const override; +}; +} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h new file mode 100644 index 000000000..8af9d87c1 --- /dev/null +++ b/src/math/lp/lp_dual_simplex_def.h @@ -0,0 +1,376 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include "math/lp/lp_dual_simplex.h" +namespace lp{ + +template void lp_dual_simplex::decide_on_status_after_stage1() { + switch (m_core_solver->get_status()) { + case lp_status::OPTIMAL: + if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { + this->m_status = lp_status::FEASIBLE; + } else { + this->m_status = lp_status::UNBOUNDED; + } + break; + case lp_status::DUAL_UNBOUNDED: + lp_unreachable(); + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; + break; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; + break; + default: + lp_unreachable(); + } +} + +template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { + lp_assert(j >= this->number_of_core_structurals()); + switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; + m_can_enter_basis[j] = true; + break; + case column_type::fixed: + this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::fixed; + m_can_enter_basis[j] = false; + break; + default: + lp_unreachable(); + } +} + +template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { + column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; + switch (ci->get_column_type()) { + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; + m_can_enter_basis[j] = true; + break; + case column_type::fixed: + case column_type::upper_bound: + lp_unreachable(); + case column_type::boxed: + this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::boxed; + m_can_enter_basis[j] = true; + break; + case column_type::free_column: + m_can_enter_basis[j] = true; + m_column_types_of_core_solver[j] = column_type::free_column; + break; + default: + lp_unreachable(); + } + // T cost_was = this->m_costs[j]; + this->set_scaled_cost(j); +} + +template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { + unsigned j = this->m_A->column_count(); + while (j-- > this->number_of_core_structurals()) { + fix_logical_for_stage2(j); + } + j = this->number_of_core_structurals(); + while (j--) { + fix_structural_for_stage2(j); + } +} + +template void lp_dual_simplex::restore_right_sides() { + unsigned i = this->m_A->row_count(); + while (i--) { + this->m_b[i] = m_b_copy[i]; + } +} + +template void lp_dual_simplex::solve_for_stage2() { + m_core_solver->restore_non_basis(); + m_core_solver->solve_yB(m_core_solver->m_y); + m_core_solver->fill_reduced_costs_from_m_y_by_rows(); + m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); + m_core_solver->set_status(lp_status::FEASIBLE); + m_core_solver->solve(); + switch (m_core_solver->get_status()) { + case lp_status::OPTIMAL: + this->m_status = lp_status::OPTIMAL; + break; + case lp_status::DUAL_UNBOUNDED: + this->m_status = lp_status::INFEASIBLE; + break; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; + break; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; + break; + default: + lp_unreachable(); + } + this->m_second_stage_iterations = m_core_solver->total_iterations(); + this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); +} + +template void lp_dual_simplex::fill_x_with_zeros() { + unsigned j = this->m_A->column_count(); + while (j--) { + this->m_x[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex::stage1() { + lp_assert(m_core_solver == nullptr); + this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); + if (this->m_settings.get_message_ostream() != nullptr) + this->print_statistics_on_A(*this->m_settings.get_message_ostream()); + m_core_solver = new lp_dual_core_solver( + *this->m_A, + m_can_enter_basis, + this->m_b, // the right side vector + this->m_x, + this->m_basis, + this->m_nbasis, + this->m_heading, + this->m_costs, + this->m_column_types_of_core_solver, + this->m_lower_bounds, + this->m_upper_bounds, + this->m_settings, + *this); + m_core_solver->fill_reduced_costs_from_m_y_by_rows(); + m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); + if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { + // skipping stage 1 + m_core_solver->set_status(lp_status::OPTIMAL); + m_core_solver->set_total_iterations(0); + } else { + m_core_solver->solve(); + } + decide_on_status_after_stage1(); + this->m_first_stage_iterations = m_core_solver->total_iterations(); +} + +template void lp_dual_simplex::stage2() { + unmark_boxed_and_fixed_columns_and_fix_structural_costs(); + restore_right_sides(); + solve_for_stage2(); +} + +template void lp_dual_simplex::fill_first_stage_solver_fields() { + unsigned slack_var = this->number_of_core_structurals(); + unsigned artificial = this->number_of_core_structurals() + this->m_slacks; + + for (unsigned row = 0; row < this->row_count(); row++) { + fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); + } + fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); +} + +template column_type lp_dual_simplex::get_column_type(unsigned j) { + lp_assert(j < this->m_A->column_count()); + if (j >= this->number_of_core_structurals()) { + return m_column_types_of_logicals[j - this->number_of_core_structurals()]; + } + return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); +} + +template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { + // see 4.7 in the dissertation of Achim Koberstein + lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != + this->m_core_solver_columns_to_external_columns.end()); + + T free_bound = T(1e4); // see 4.8 + unsigned jj = this->m_core_solver_columns_to_external_columns[j]; + lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); + column_info * ci = this->m_map_from_var_index_to_column_info[jj]; + switch (ci->get_column_type()) { + case column_type::upper_bound: { + std::stringstream s; + s << "unexpected bound type " << j << " " + << column_type_to_string(get_column_type(j)); + throw_exception(s.str()); + break; + } + case column_type::lower_bound: { + m_can_enter_basis[j] = true; + this->set_scaled_cost(j); + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::one(); + break; + } + case column_type::free_column: { + m_can_enter_basis[j] = true; + this->set_scaled_cost(j); + this->m_upper_bounds[j] = free_bound; + this->m_lower_bounds[j] = -free_bound; + break; + } + case column_type::boxed: + m_can_enter_basis[j] = false; + this->m_costs[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? + break; + default: + lp_unreachable(); + } + m_column_types_of_core_solver[j] = column_type::boxed; +} + +template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { + this->m_costs[j] = 0; + lp_assert(get_column_type(j) != column_type::upper_bound); + if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { + m_column_types_of_core_solver[j] = column_type::boxed; + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::one(); + } else { + m_column_types_of_core_solver[j] = column_type::fixed; + this->m_lower_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { + unsigned j = this->m_A->column_count(); + while (j-- > this->number_of_core_structurals()) { // go over logicals here + fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); + } + j = this->number_of_core_structurals(); + while (j--) { + fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); + } +} + +template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, + unsigned & slack_var, + unsigned & artificial) { + lp_assert(row < this->row_count()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; + // we need to bring the program to the form Ax = b + T rs = this->m_b[row]; + switch (constraint.m_relation) { + case Equal: // no slack variable here + set_type_for_logical(artificial, column_type::fixed); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + (*this->m_A)(row, artificial) = numeric_traits::one(); + artificial++; + break; + + case Greater_or_equal: + set_type_for_logical(slack_var, column_type::lower_bound); + (*this->m_A)(row, slack_var) = - numeric_traits::one(); + if (rs > 0) { + // adding one artificial + set_type_for_logical(artificial, column_type::fixed); + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put a slack_var into the basis, and avoid adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::zero(); + } + slack_var++; + break; + case Less_or_equal: + // introduce a non-negative slack variable + set_type_for_logical(slack_var, column_type::lower_bound); + (*this->m_A)(row, slack_var) = numeric_traits::one(); + if (rs < 0) { + // adding one artificial + set_type_for_logical(artificial, column_type::fixed); + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put slack_var into the basis, and avoid adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::zero(); + } + slack_var++; + break; + } +} + +template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { + this->count_slacks_and_artificials(); + this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); + unsigned n = this->m_A->column_count(); + this->m_column_types_of_core_solver.resize(n); + m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); + this->m_costs.resize(n); + this->m_upper_bounds.resize(n); + this->m_lower_bounds.resize(n); + m_can_enter_basis.resize(n); + this->m_basis.resize(this->m_A->row_count()); +} + + + +template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { + for (unsigned i = 0; i < this->m_b.size(); i++) { + m_b_copy.push_back(this->m_b[i]); + this->m_b[i] = numeric_traits::zero(); // preparing for the first stage + } +} + +template void lp_dual_simplex::find_maximal_solution(){ + if (this->problem_is_empty()) { + this->m_status = lp_status::EMPTY; + return; + } + + this->flip_costs(); // do it for now, todo ( remove the flipping) + + this->cleanup(); + if (this->m_status == lp_status::INFEASIBLE) { + return; + } + this->fill_matrix_A_and_init_right_side(); + this->fill_m_b(); + this->scale(); + augment_matrix_A_and_fill_x_and_allocate_some_fields(); + fill_first_stage_solver_fields(); + copy_m_b_aside_and_set_it_to_zeros(); + stage1(); + if (this->m_status == lp_status::FEASIBLE) { + stage2(); + } +} + + +template T lp_dual_simplex::get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_map_from_var_index_to_column_info) { + ret += this->get_column_cost_value(it.first, it.second); + } + return -ret; // we flip costs for now +} +} diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index a60395ab0..4b6163df8 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,6 +30,7 @@ Revision History: #include #include #include "math/lp/lu.h" +#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp new file mode 100644 index 000000000..634f52900 --- /dev/null +++ b/src/math/lp/lp_primal_simplex.cpp @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include +#include +#include "util/vector.h" +#include +#include "math/lp/lp_primal_simplex_def.h" +template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template double lp::lp_primal_simplex::get_current_cost() const; +template double lp::lp_primal_simplex::get_column_value(unsigned int) const; +template lp::lp_primal_simplex::~lp_primal_simplex(); +template lp::lp_primal_simplex::~lp_primal_simplex(); +template lp::mpq lp::lp_primal_simplex::get_current_cost() const; +template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; +template void lp::lp_primal_simplex::find_maximal_solution(); +template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h new file mode 100644 index 000000000..77e12d088 --- /dev/null +++ b/src/math/lp/lp_primal_simplex.h @@ -0,0 +1,106 @@ +/*++ +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_utils.h" +#include "math/lp/column_info.h" +#include "math/lp/lp_primal_core_solver.h" +#include "math/lp/lp_solver.h" +namespace lp { +template +class lp_primal_simplex: public lp_solver { + lp_primal_core_solver * m_core_solver; + vector m_lower_bounds; +private: + unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } + + void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); + + void init_buffer(unsigned k, vector & r); + + void refactor(); + + void set_scaled_costs(); +public: + lp_primal_simplex(): m_core_solver(nullptr) {} + + column_info * get_or_create_column_info(unsigned column); + + void set_status(lp_status status) { + this->m_status = status; + } + + lp_status get_status() { + return this->m_status; + } + + void fill_acceptable_values_for_x(); + + + void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); + + void fill_costs_and_x_for_first_stage_solver_for_row( + int row, + unsigned & slack_var, + unsigned & artificial); + + + + + void set_core_solver_bounds(); + + void find_maximal_solution() override; + + void fill_A_x_and_basis_for_stage_one_total_inf(); + + void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); + + void solve_with_total_inf(); + + + ~lp_primal_simplex() override; + + bool bounds_hold(std::unordered_map const & solution); + + T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); + + bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); + + bool row_constraints_hold(std::unordered_map const & solution); + + + T * get_array_from_map(std::unordered_map const & solution); + + bool solution_is_feasible(std::unordered_map const & solution) { + return bounds_hold(solution) && row_constraints_hold(solution); + } + + T get_column_value(unsigned column) const override { + return this->get_column_value_with_core_solver(column, m_core_solver); + } + + T get_current_cost() const override; + + +}; +} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h new file mode 100644 index 000000000..7ffe819b2 --- /dev/null +++ b/src/math/lp/lp_primal_simplex_def.h @@ -0,0 +1,367 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include +#include "util/vector.h" +#include "math/lp/lp_primal_simplex.h" + +namespace lp { +template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { + unsigned slack_var = original_number_of_columns; + unsigned artificial = original_number_of_columns + this->m_slacks; + + for (unsigned row = 0; row < this->row_count(); row++) { + fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); + } +} + +template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { + for (unsigned i = 0; i < k; i++) { + r[i] = 0; + } + r[k] = 1; + for (unsigned i = this->row_count() -1; i > k; i--) { + r[i] = 0; + } +} + +template void lp_primal_simplex::refactor() { + m_core_solver->init_lu(); + if (m_core_solver->factorization()->get_status() != LU_status::OK) { + throw_exception("cannot refactor"); + } +} + +template void lp_primal_simplex::set_scaled_costs() { + unsigned j = this->number_of_core_structurals(); + while (j-- > 0) { + this->set_scaled_cost(j); + } +} + +template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { + auto it = this->m_columns.find(column); + return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; +} + +template void lp_primal_simplex::fill_acceptable_values_for_x() { + for (auto t : this->m_core_solver_columns_to_external_columns) { + this->m_x[t.first] = numeric_traits::zero(); + } +} + + +template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { + bound_is_set[i] = true; + bounds[i] = numeric_traits::zero(); +} + +template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( + int row, + unsigned & slack_var, + unsigned & artificial) { + lp_assert(row >= 0 && row < this->row_count()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; + // we need to bring the program to the form Ax = b + T rs = this->m_b[row]; + T artificial_cost = - numeric_traits::one(); + switch (constraint.m_relation) { + case Equal: // no slack variable here + this->m_column_types[artificial] = column_type::lower_bound; + this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero + this->m_basis[row] = artificial; + if (rs >= 0) { + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_x[artificial] = rs; + } else { + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_x[artificial] = - rs; + } + artificial++; + break; + + case Greater_or_equal: + this->m_column_types[slack_var] = column_type::lower_bound; + (*this->m_A)(row, slack_var) = - numeric_traits::one(); + + if (rs > 0) { + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + // adding one artificial + this->m_column_types[artificial] = column_type::lower_bound; + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_costs[artificial] = artificial_cost; + this->m_basis[row] = artificial; + this->m_x[artificial] = rs; + artificial++; + } else { + // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable + this->m_basis[row] = slack_var; + this->m_x[slack_var] = - rs; + } + slack_var++; + break; + case Less_or_equal: + // introduce a non-negative slack variable + this->m_column_types[slack_var] = column_type::lower_bound; + (*this->m_A)(row, slack_var) = numeric_traits::one(); + + if (rs < 0) { + // adding one artificial + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + this->m_column_types[artificial] = column_type::lower_bound; + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_costs[artificial] = artificial_cost; + this->m_x[artificial] = - rs; + this->m_basis[row] = artificial++; + } else { + // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable + this->m_basis[row] = slack_var; + this->m_x[slack_var] = rs; + } + slack_var++; + break; + } +} + + + + + +template void lp_primal_simplex::set_core_solver_bounds() { + unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; + this->m_column_types.resize(total_vars); + this->m_upper_bounds.resize(total_vars); + for (auto cit : this->m_map_from_var_index_to_column_info) { + column_info * ci = cit.second; + unsigned j = ci->get_column_index(); + if (!is_valid(j)) + continue; // the variable is not mapped to a column + switch (this->m_column_types[j] = ci->get_column_type()){ + case column_type::fixed: + this->m_upper_bounds[j] = numeric_traits::zero(); + break; + case column_type::boxed: + this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; + break; + + default: break; // do nothing + } + } +} + + +template void lp_primal_simplex::find_maximal_solution() { + if (this->problem_is_empty()) { + this->m_status = lp_status::EMPTY; + return; + } + + this->cleanup(); + this->fill_matrix_A_and_init_right_side(); + if (this->m_status == lp_status::INFEASIBLE) { + return; + } + this->m_x.resize(this->m_A->column_count()); + this->fill_m_b(); + this->scale(); + fill_acceptable_values_for_x(); + this->count_slacks_and_artificials(); + set_core_solver_bounds(); + solve_with_total_inf(); +} + +template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { + for (unsigned row = 0; row < this->row_count(); row++) + fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); +} + +template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { + lp_assert(row < this->row_count()); + auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); + lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); + unsigned ext_row = ext_row_it->second; + auto constr_it = this->m_constraints.find(ext_row); + lp_assert(constr_it != this->m_constraints.end()); + auto & constraint = constr_it->second; + unsigned j = this->m_A->column_count(); // j is a slack variable + this->m_A->add_column(); + // we need to bring the program to the form Ax = b + this->m_basis[row] = j; + switch (constraint.m_relation) { + case Equal: + this->m_x[j] = this->m_b[row]; + (*this->m_A)(row, j) = numeric_traits::one(); + this->m_column_types[j] = column_type::fixed; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); + break; + + case Greater_or_equal: + this->m_x[j] = - this->m_b[row]; + (*this->m_A)(row, j) = - numeric_traits::one(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = zero_of_type(); + break; + case Less_or_equal: + this->m_x[j] = this->m_b[row]; + (*this->m_A)(row, j) = numeric_traits::one(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); + break; + default: + lp_unreachable(); + } +} + +template void lp_primal_simplex::solve_with_total_inf() { + int total_vars = this->m_A->column_count() + this->row_count(); + if (total_vars == 0) { + this->m_status = lp_status::OPTIMAL; + return; + } + m_lower_bounds.clear(); + m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero + this->m_x.resize(total_vars, numeric_traits::zero()); + this->m_basis.resize(this->row_count()); + this->m_costs.clear(); + this->m_costs.resize(total_vars, zero_of_type()); + fill_A_x_and_basis_for_stage_one_total_inf(); + if (this->m_settings.get_message_ostream() != nullptr) + this->print_statistics_on_A(*this->m_settings.get_message_ostream()); + set_scaled_costs(); + + m_core_solver = new lp_primal_core_solver(*this->m_A, + this->m_b, + this->m_x, + this->m_basis, + this->m_nbasis, + this->m_heading, + this->m_costs, + this->m_column_types, + m_lower_bounds, + this->m_upper_bounds, + this->m_settings, *this); + m_core_solver->solve(); + this->set_status(m_core_solver->get_status()); + this->m_total_iterations = m_core_solver->total_iterations(); +} + + +template lp_primal_simplex::~lp_primal_simplex() { + delete m_core_solver; +} + +template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { + for (auto it : this->m_map_from_var_index_to_column_info) { + auto sol_it = solution.find(it.second->get_name()); + if (sol_it == solution.end()) { + std::stringstream s; + s << "cannot find column " << it.first << " in solution"; + throw_exception(s.str() ); + } + + if (!it.second->bounds_hold(sol_it->second)) { + it.second->bounds_hold(sol_it->second); + return false; + } + } + return true; +} + +template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { + auto it = this->m_A_values.find(i); + if (it == this->m_A_values.end()) { + std::stringstream s; + s << "cannot find row " << i; + throw_exception(s.str() ); + } + T ret = numeric_traits::zero(); + for (auto & pair : it->second) { + auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); + lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); + column_info * ci = cit->second; + auto sol_it = solution.find(ci->get_name()); + lp_assert(sol_it != solution.end()); + T column_val = sol_it->second; + if (out != nullptr) { + (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; + } + ret += pair.second * column_val; + } + if (out != nullptr) { + (*out) << " = " << ret << std::endl; + } + return ret; +} + +template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { + T row_val = get_row_value(i, solution, out); + auto & constraint = this->m_constraints[i]; + T rs = constraint.m_rs; + bool print = out != nullptr; + switch (constraint.m_relation) { + case Equal: + if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { + if (print) { + (*out) << "should be = " << rs << std::endl; + } + return false; + } + return true; + case Greater_or_equal: + if (numeric_traits::get_double(row_val - rs) < -0.00001) { + if (print) { + (*out) << "should be >= " << rs << std::endl; + } + return false; + } + return true;; + + case Less_or_equal: + if (numeric_traits::get_double(row_val - rs) > 0.00001) { + if (print) { + (*out) << "should be <= " << rs << std::endl; + } + return false; + } + return true;; + } + lp_unreachable(); + return false; // it is unreachable +} + +template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { + for (auto it : this->m_A_values) { + if (!row_constraint_holds(it.first, solution, nullptr)) { + row_constraint_holds(it.first, solution, nullptr); + return false; + } + } + return true; +} + +template T lp_primal_simplex::get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_map_from_var_index_to_column_info) { + ret += this->get_column_cost_value(it.first, it.second); + } + return ret; +} +} diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp new file mode 100644 index 000000000..fc9514098 --- /dev/null +++ b/src/math/lp/lp_solver.cpp @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include "math/lp/lp_solver_def.h" +template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); +template void lp::lp_solver::cleanup(); +template void lp::lp_solver::count_slacks_and_artificials(); +template void lp::lp_solver::fill_m_b(); +template void lp::lp_solver::fill_matrix_A_and_init_right_side(); +template void lp::lp_solver::flip_costs(); +template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; +template int lp::lp_solver::get_column_index_by_name(std::string) const; +template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; +template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); +template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lp::lp_solver::print_statistics_on_A(std::ostream & out); +template bool lp::lp_solver::problem_is_empty(); +template void lp::lp_solver::scale(); +template void lp::lp_solver::set_scaled_cost(unsigned int); +template lp::lp_solver::~lp_solver(); +template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); +template void lp::lp_solver::cleanup(); +template void lp::lp_solver::count_slacks_and_artificials(); +template void lp::lp_solver::fill_m_b(); +template void lp::lp_solver::fill_matrix_A_and_init_right_side(); +template void lp::lp_solver::flip_costs(); +template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; +template int lp::lp_solver::get_column_index_by_name(std::string) const; +template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; +template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; +template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); +template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lp::lp_solver::print_statistics_on_A(std::ostream & out); +template bool lp::lp_solver::problem_is_empty(); +template void lp::lp_solver::scale(); +template void lp::lp_solver::set_scaled_cost(unsigned int); +template lp::lp_solver::~lp_solver(); +template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h new file mode 100644 index 000000000..ab16a686f --- /dev/null +++ b/src/math/lp/lp_solver.h @@ -0,0 +1,260 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include +#include +#include +#include "util/vector.h" +#include "math/lp/lp_settings.h" +#include "math/lp/column_info.h" +#include "math/lp/static_matrix.h" +#include "math/lp/lp_core_solver_base.h" +#include "math/lp/scaler.h" +#include "math/lp/bound_analyzer_on_row.h" +namespace lp { +enum lp_relation { + Less_or_equal, + Equal, + Greater_or_equal +}; + +template +struct lp_constraint { + X m_rs; // right side of the constraint + lp_relation m_relation; + lp_constraint() {} // empty constructor + lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} +}; + + +template +class lp_solver : public column_namer { + column_info * get_or_create_column_info(unsigned column); + +protected: + T get_column_cost_value(unsigned j, column_info * ci) const; +public: + unsigned m_total_iterations; + static_matrix* m_A; // this is the matrix of constraints + vector m_b; // the right side vector + unsigned m_first_stage_iterations; + unsigned m_second_stage_iterations; + std::unordered_map> m_constraints; + std::unordered_map*> m_map_from_var_index_to_column_info; + std::unordered_map > m_A_values; + std::unordered_map m_names_to_columns; // don't have to use it + std::unordered_map m_external_rows_to_core_solver_rows; + std::unordered_map m_core_solver_rows_to_external_rows; + std::unordered_map m_core_solver_columns_to_external_columns; + vector m_column_scale; + std::unordered_map m_name_map; + unsigned m_artificials; + unsigned m_slacks; + vector m_column_types; + vector m_costs; + vector m_x; + vector m_upper_bounds; + vector m_basis; + vector m_nbasis; + vector m_heading; + + + lp_status m_status; + + lp_settings m_settings; + lp_solver(): + m_A(nullptr), // this is the matrix of constraints + m_first_stage_iterations (0), + m_second_stage_iterations (0), + m_artificials (0), + m_slacks (0), + m_status(lp_status::UNKNOWN) + {} + + unsigned row_count() const { return this->m_A->row_count(); } + + void add_constraint(lp_relation relation, T right_side, unsigned row_index); + + void set_cost_for_column(unsigned column, T column_cost) { + get_or_create_column_info(column)->set_cost(column_cost); + } + std::string get_variable_name(unsigned j) const override; + + void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { + m_A_values[row][column] = val; + } + // returns the current cost + virtual T get_current_cost() const = 0; + // do not have to call it + void give_symbolic_name_to_column(std::string name, unsigned column); + + virtual T get_column_value(unsigned column) const = 0; + + T get_column_value_by_name(std::string name) const; + + // returns -1 if not found + virtual int get_column_index_by_name(std::string name) const; + + void set_lower_bound(unsigned i, T bound) { + column_info *ci = get_or_create_column_info(i); + ci->set_lower_bound(bound); + } + + void set_upper_bound(unsigned i, T bound) { + column_info *ci = get_or_create_column_info(i); + ci->set_upper_bound(bound); + } + + void unset_lower_bound(unsigned i) { + get_or_create_column_info(i)->unset_lower_bound(); + } + + void unset_upper_bound(unsigned i) { + get_or_create_column_info(i)->unset_upper_bound(); + } + + void set_fixed_value(unsigned i, T val) { + column_info *ci = get_or_create_column_info(i); + ci->set_fixed_value(val); + } + + void unset_fixed_value(unsigned i) { + get_or_create_column_info(i)->unset_fixed(); + } + + lp_status get_status() const { + return m_status; + } + + void set_status(lp_status st) { + m_status = st; + } + + + ~lp_solver() override; + + void flip_costs(); + + virtual void find_maximal_solution() = 0; + void set_time_limit(unsigned time_limit_in_seconds) { + m_settings.time_limit = time_limit_in_seconds; + } + + +protected: + bool problem_is_empty(); + + void scale(); + + + void print_rows_scale_stats(std::ostream & out); + + void print_columns_scale_stats(std::ostream & out); + + void print_row_scale_stats(unsigned i, std::ostream & out); + + void print_column_scale_stats(unsigned j, std::ostream & out); + + void print_scale_stats(std::ostream & out); + + void get_max_abs_in_row(std::unordered_map & row_map); + + void pin_vars_down_on_row(std::unordered_map & row) { + pin_vars_on_row_with_sign(row, - numeric_traits::one()); + } + + void pin_vars_up_on_row(std::unordered_map & row) { + pin_vars_on_row_with_sign(row, numeric_traits::one()); + } + + void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); + + bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); + + bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); + + bool row_is_zero(std::unordered_map & row); + + bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); + + bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); + + bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); + + // analyse possible max and min values that are derived from var boundaries + // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. + // Then we know what values of the variables are. For each positive coeff of the row it has to be + // the low boundary of the var and for a negative - the upper. + + // this routing also pins the variables to the boundaries + bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); + + void remove_fixed_or_zero_columns(); + + void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); + + unsigned try_to_remove_some_rows(); + + void cleanup(); + + void map_external_rows_to_core_solver_rows(); + + void map_external_columns_to_core_solver_columns(); + + unsigned number_of_core_structurals() { + return static_cast(m_core_solver_columns_to_external_columns.size()); + } + + void restore_column_scales_to_one() { + for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); + } + + void unscale(); + + void fill_A_from_A_values(); + + void fill_matrix_A_and_init_right_side(); + + void count_slacks_and_artificials(); + + void count_slacks_and_artificials_for_row(unsigned i); + + T lower_bound_shift_for_row(unsigned i); + + void fill_m_b(); + + T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; + void set_scaled_cost(unsigned j); + void print_statistics_on_A(std::ostream & out) { + out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; + } + +public: + lp_settings & settings() { return m_settings;} + void print_model(std::ostream & s) const { + s << "objective = " << get_current_cost() << std::endl; + s << "column values\n"; + for (auto & it : m_names_to_columns) { + s << it.first << " = " << get_column_value(it.second) << std::endl; + } + } +}; +} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h new file mode 100644 index 000000000..191832a24 --- /dev/null +++ b/src/math/lp/lp_solver_def.h @@ -0,0 +1,571 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once + +#include +#include +#include "util/vector.h" +#include "math/lp/lp_solver.h" +namespace lp { +template column_info * lp_solver::get_or_create_column_info(unsigned column) { + auto it = m_map_from_var_index_to_column_info.find(column); + return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; +} + +template +std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index + if (!m_settings.print_external_var_name()) + return std::string("j")+T_to_string(j); + auto it = this->m_core_solver_columns_to_external_columns.find(j); + if (it == this->m_core_solver_columns_to_external_columns.end()) + return std::string("x")+T_to_string(j); + unsigned external_j = it->second; + auto t = this->m_map_from_var_index_to_column_info.find(external_j); + if (t == this->m_map_from_var_index_to_column_info.end()) { + return std::string("x") +T_to_string(external_j); + } + return t->second->get_name(); +} + +template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { + if (ci->is_fixed()) { + return ci->get_cost() * ci->get_fixed_value(); + } + return ci->get_cost() * get_column_value(j); +} +template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { + lp_assert(m_constraints.find(row_index) == m_constraints.end()); + lp_constraint cs(right_side, relation); + m_constraints[row_index] = cs; +} + +template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { + auto it = m_map_from_var_index_to_column_info.find(column); + column_info *ci; + if (it == m_map_from_var_index_to_column_info.end()){ + m_map_from_var_index_to_column_info[column] = ci = new column_info; + } else { + ci = it->second; + } + ci->set_name(name); + m_names_to_columns[name] = column; +} + + +template T lp_solver::get_column_value_by_name(std::string name) const { + auto it = m_names_to_columns.find(name); + if (it == m_names_to_columns.end()) { + std::stringstream s; + s << "get_column_value_by_name " << name; + throw_exception(s.str()); + } + return get_column_value(it -> second); +} + +// returns -1 if not found +template int lp_solver::get_column_index_by_name(std::string name) const { + auto t = m_names_to_columns.find(name); + if (t == m_names_to_columns.end()) { + return -1; + } + return t->second; +} + + +template lp_solver::~lp_solver(){ + delete m_A; + for (auto t : m_map_from_var_index_to_column_info) { + delete t.second; + } +} + +template void lp_solver::flip_costs() { + for (auto t : m_map_from_var_index_to_column_info) { + column_info *ci = t.second; + ci->set_cost(-ci->get_cost()); + } +} + +template bool lp_solver::problem_is_empty() { + for (auto & c : m_A_values) + if (!c.second.empty()) + return false; + return true; +} + +template void lp_solver::scale() { + if (numeric_traits::precise() || m_settings.use_scaling == false) { + m_column_scale.clear(); + m_column_scale.resize(m_A->column_count(), one_of_type()); + return; + } + + T smin = T(m_settings.scaling_minimum); + T smax = T(m_settings.scaling_maximum); + + scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); + if (!scaler.scale()) { + unscale(); + } +} + + +template void lp_solver::print_rows_scale_stats(std::ostream & out) { + out << "rows max" << std::endl; + for (unsigned i = 0; i < m_A->row_count(); i++) { + print_row_scale_stats(i, out); + } + out << std::endl; +} + +template void lp_solver::print_columns_scale_stats(std::ostream & out) { + out << "columns max" << std::endl; + for (unsigned i = 0; i < m_A->column_count(); i++) { + print_column_scale_stats(i, out); + } + out << std::endl; +} + +template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { + out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; + out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; +} + +template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { + out << "(" << m_A->get_min_abs_in_row(j) << " "; + out << m_A->get_max_abs_in_column(j) << ")"; +} + +template void lp_solver::print_scale_stats(std::ostream & out) { + print_rows_scale_stats(out); + print_columns_scale_stats(out); +} + +template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { + T ret = numeric_traits::zero(); + for (auto jp : row_map) { + T ac = numeric_traits::abs(jp->second); + if (ac > ret) { + ret = ac; + } + } + return ret; +} + +template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { + for (auto t : row) { + unsigned j = t.first; + column_info * ci = m_map_from_var_index_to_column_info[j]; + T a = t.second; + if (a * sign > numeric_traits::zero()) { + lp_assert(ci->upper_bound_is_set()); + ci->set_fixed_value(ci->get_upper_bound()); + } else { + lp_assert(ci->lower_bound_is_set()); + ci->set_fixed_value(ci->get_lower_bound()); + } + } +} + +template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_map_from_var_index_to_column_info[t.first]; + if (a > numeric_traits::zero()) { + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; + } else { + return false; + } + } else { + if (ci->upper_bound_is_set()) { + lower_bound += ci->get_upper_bound() * a; + } else { + return false; + } + } + } + return true; +} + +template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_map_from_var_index_to_column_info[t.first]; + if (a < numeric_traits::zero()) { + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; + } else { + return false; + } + } else { + if (ci->upper_bound_is_set()) { + lower_bound += ci->get_upper_bound() * a; + } else { + return false; + } + } + } + return true; +} + +template bool lp_solver::row_is_zero(std::unordered_map & row) { + for (auto & t : row) { + if (!is_zero(t.second)) + return false; + } + return true; +} + +template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (!is_zero(rs)) + m_status = lp_status::INFEASIBLE; + return true; + } + + T lower_bound; + bool lb = get_minimal_row_value(row, lower_bound); + if (lb) { + T diff = lower_bound - rs; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ + // lower_bound > rs + m_settings.refactor_epsilon + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_down_on_row(row); + return true; + } + } + + T upper_bound; + bool ub = get_maximal_row_value(row, upper_bound); + if (ub) { + T diff = rs - upper_bound; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { + // upper_bound < rs - m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_up_on_row(row); + return true; + } + } + + return false; +} + +template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (rs > zero_of_type()) + m_status = lp_status::INFEASIBLE; + return true; + } + + T upper_bound; + if (get_maximal_row_value(row, upper_bound)) { + T diff = rs - upper_bound; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { + // upper_bound < rs - m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_up_on_row(row); + return true; + } + } + + return false; +} + +template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { + T lower_bound; + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (rs < zero_of_type()) + m_status = lp_status::INFEASIBLE; + return true; + } + + if (get_minimal_row_value(row, lower_bound)) { + T diff = lower_bound - rs; + if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ + // lower_bound > rs + m_settings.refactor_tolerance + m_status = lp_status::INFEASIBLE; + return true; + } + if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ + pin_vars_down_on_row(row); + return true; + } + } + + return false; +} + +// analyse possible max and min values that are derived from var boundaries +// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. +// Then we know what values of the variables are. For each positive coeff of the row it has to be +// the low boundary of the var and for a negative - the upper. + +// this routing also pins the variables to the boundaries +template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { + auto & constraint = m_constraints[row_index]; + switch (constraint.m_relation) { + case lp_relation::Equal: + return row_e_is_obsolete(row, row_index); + + case lp_relation::Greater_or_equal: + return row_ge_is_obsolete(row, row_index); + + case lp_relation::Less_or_equal: + return row_le_is_obsolete(row, row_index); + } + lp_unreachable(); + return false; // it is unreachable +} + +template void lp_solver::remove_fixed_or_zero_columns() { + for (auto & i_row : m_A_values) { + remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); + } +} + +template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { + auto & constraint = m_constraints[i]; + vector removed; + for (auto & col : row) { + unsigned j = col.first; + lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); + column_info * ci = m_map_from_var_index_to_column_info[j]; + if (ci->is_fixed()) { + removed.push_back(j); + T aj = col.second; + constraint.m_rs -= aj * ci->get_fixed_value(); + } else { + if (numeric_traits::is_zero(col.second)){ + removed.push_back(j); + } + } + } + + for (auto j : removed) { + row.erase(j); + } +} + +template unsigned lp_solver::try_to_remove_some_rows() { + vector rows_to_delete; + for (auto & t : m_A_values) { + if (row_is_obsolete(t.second, t.first)) { + rows_to_delete.push_back(t.first); + } + + if (m_status == lp_status::INFEASIBLE) { + return 0; + } + } + if (!rows_to_delete.empty()) { + for (unsigned k : rows_to_delete) { + m_A_values.erase(k); + } + } + remove_fixed_or_zero_columns(); + return static_cast(rows_to_delete.size()); +} + +template void lp_solver::cleanup() { + int n = 0; // number of deleted rows + int d; + while ((d = try_to_remove_some_rows()) > 0) + n += d; + + if (n == 1) { + LP_OUT(m_settings, "deleted one row" << std::endl); + } else if (n) { + LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); + } +} + +template void lp_solver::map_external_rows_to_core_solver_rows() { + unsigned size = 0; + for (auto & row : m_A_values) { + m_external_rows_to_core_solver_rows[row.first] = size; + m_core_solver_rows_to_external_rows[size] = row.first; + size++; + } +} + +template void lp_solver::map_external_columns_to_core_solver_columns() { + unsigned size = 0; + for (auto & row : m_A_values) { + for (auto & col : row.second) { + if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { + throw_exception("found fixed column"); + } + unsigned j = col.first; + auto column_info_it = m_map_from_var_index_to_column_info.find(j); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); + + auto j_column = column_info_it->second->get_column_index(); + if (!is_valid(j_column)) { // j is a newcomer + m_map_from_var_index_to_column_info[j]->set_column_index(size); + m_core_solver_columns_to_external_columns[size++] = j; + } + } + } +} + +template void lp_solver::unscale() { + delete m_A; + m_A = nullptr; + fill_A_from_A_values(); + restore_column_scales_to_one(); + fill_m_b(); +} + +template void lp_solver::fill_A_from_A_values() { + m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); + for (auto & t : m_A_values) { + auto row_it = m_external_rows_to_core_solver_rows.find(t.first); + lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); + unsigned row = row_it->second; + for (auto k : t.second) { + auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); + column_info *ci = column_info_it->second; + unsigned col = ci->get_column_index(); + lp_assert(is_valid(col)); + bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); + if (!col_is_flipped) { + (*m_A)(row, col) = k.second; + } else { + (*m_A)(row, col) = - k.second; + } + } + } +} + +template void lp_solver::fill_matrix_A_and_init_right_side() { + map_external_rows_to_core_solver_rows(); + map_external_columns_to_core_solver_columns(); + lp_assert(m_A == nullptr); + fill_A_from_A_values(); + m_b.resize(m_A->row_count()); +} + +template void lp_solver::count_slacks_and_artificials() { + for (int i = row_count() - 1; i >= 0; i--) { + count_slacks_and_artificials_for_row(i); + } +} + +template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; + switch (constraint.m_relation) { + case Equal: + m_artificials++; + break; + case Greater_or_equal: + m_slacks++; + if (this->m_b[i] > 0) { + m_artificials++; + } + break; + case Less_or_equal: + m_slacks++; + if (this->m_b[i] < 0) { + m_artificials++; + } + break; + } +} + +template T lp_solver::lower_bound_shift_for_row(unsigned i) { + T ret = numeric_traits::zero(); + + auto row = this->m_A_values.find(i); + if (row == this->m_A_values.end()) { + throw_exception("cannot find row"); + } + for (auto col : row->second) { + ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); + } + return ret; +} + +template void lp_solver::fill_m_b() { + for (int i = this->row_count() - 1; i >= 0; i--) { + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; + auto & constraint = this->m_constraints[external_i]; + this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); + } +} + +template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { + auto cit = this->m_map_from_var_index_to_column_info.find(column); + if (cit == this->m_map_from_var_index_to_column_info.end()) { + return numeric_traits::zero(); + } + + column_info * ci = cit->second; + + if (ci->is_fixed()) { + return ci->get_fixed_value(); + } + + unsigned cj = ci->get_column_index(); + if (cj != static_cast(-1)) { + T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; + if (ci->is_free()) { + return v; + } + if (!ci->is_flipped()) { + return v + ci->get_lower_bound(); + } + + // the flipped case when there is only upper bound + return -v + ci->get_upper_bound(); // + } + + return numeric_traits::zero(); // returns zero for out of boundary columns +} + +template void lp_solver::set_scaled_cost(unsigned j) { + // grab original costs but modify it with the column scales + lp_assert(j < this->m_column_scale.size()); + column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; + T cost = ci->get_cost(); + if (ci->is_flipped()){ + cost *= T(-1); + } + lp_assert(ci->is_fixed() == false); + this->m_costs[j] = cost * this->m_column_scale[j]; +} +} diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h new file mode 100644 index 000000000..8093954b1 --- /dev/null +++ b/src/math/lp/mps_reader.h @@ -0,0 +1,891 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once + +// reads an MPS file representing a Mixed Integer Program +#include +#include +#include +#include "util/vector.h" +#include +#include +#include +#include +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" +#include "math/lp/lar_solver.h" +#include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" +namespace lp { +inline bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +inline size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +inline size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + + // trim from start +inline std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + +inline std::string trim(std::string const &r) { + std::string s = r; + return ltrim(rtrim(s)); +} + + +inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +inline vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + +template +class mps_reader { + enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; + struct bound { + T m_low; + T m_upper; + bool m_low_is_set; + bool m_upper_is_set; + bool m_value_is_fixed; + T m_fixed_value; + bool m_free; + // constructor + bound() : m_low(numeric_traits::zero()), + m_low_is_set(true), + m_upper_is_set(false), + m_value_is_fixed(false), + m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable + }; + + struct column { + std::string m_name; + bound * m_bound; + unsigned m_index; + column(const std::string &name, unsigned index): m_name(name), + m_bound(nullptr), + m_index(index) { + } + }; + + struct row { + row_type m_type; + std::string m_name; + std::unordered_map m_row_columns; + unsigned m_index; + T m_right_side; + T m_range; + row(row_type type, const std::string &name, unsigned index) : + m_type(type), + m_name(name), + m_index(index), + m_right_side(zero_of_type()), + m_range(zero_of_type()) + { + } + }; + + bool m_is_OK; + std::string m_file_name; + std::unordered_map m_rows; + std::unordered_map m_columns; + std::unordered_map m_names_to_var_index; + std::string m_line; + std::string m_name; + std::string m_cost_row_name; + std::ifstream m_file_stream; + // needed to adjust the index row + unsigned m_cost_line_count; + unsigned m_line_number; + std::ostream * m_message_stream; + + void set_m_ok_to_false() { + *m_message_stream << "setting m_is_OK to false" << std::endl; + m_is_OK = false; + } + + std::string get_string_from_position(unsigned offset) { + unsigned i = offset; + for (; i < m_line.size(); i++){ + if (m_line[i] == ' ') + break; + } + lp_assert(m_line.size() >= offset); + lp_assert(m_line.size() >> i); + lp_assert(i >= offset); + return m_line.substr(offset, i - offset); + } + + void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ + if (b == nullptr) { + solver->set_lower_bound(col, numeric_traits::zero()); + return; + } + + if (b->m_free) { + return; + } + if (b->m_low_is_set) { + solver->set_lower_bound(col, b->m_low); + } + if (b->m_upper_is_set) { + solver->set_upper_bound(col, b->m_upper); + } + + if (b->m_value_is_fixed) { + solver->set_fixed_value(col, b->m_fixed_value); + } + } + + bool all_white_space() { + for (unsigned i = 0; i < m_line.size(); i++) { + char c = m_line[i]; + if (c != ' ' && c != '\t') { + return false; + } + } + return true; + } + + void read_line() { + while (m_is_OK) { + if (!getline(m_file_stream, m_line)) { + m_line_number++; + set_m_ok_to_false(); + *m_message_stream << "cannot read from file" << std::endl; + } + m_line_number++; + if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) + break; + } + } + + void read_name() { + do { + read_line(); + if (m_line.find("NAME") != 0) { + continue; + } + m_line = m_line.substr(4); + m_name = trim(m_line); + break; + } while (m_is_OK); + } + + void read_rows() { + // look for start of the rows + read_line(); + do { + if (static_cast(m_line.find("ROWS")) >= 0) { + break; + } + } while (m_is_OK); + do { + read_line(); + if (m_line.find("COLUMNS") == 0) { + break; + } + add_row(); + } while (m_is_OK); + } + + void read_column_by_columns(const std::string & column_name, std::string column_data) { + // uph, let us try to work with columns + if (column_data.size() >= 22) { + std::string ss = column_data.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + *m_message_stream << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); + if (column_data.size() > 24) { + column_data = column_data.substr(25); + if (column_data.size() >= 22) { + read_column_by_columns(column_name, column_data); + } + } + } + } else { + fail: + set_m_ok_to_false(); + *m_message_stream << "cannot understand this line\n" + "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void read_column(const std::string & column_name, const std::string & column_data){ + auto tokens = split_and_trim(column_data); + for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { + auto row_name = tokens[i]; + if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here + auto t = m_rows.find(row_name); + if (t == m_rows.end()) { + read_column_by_columns(column_name, column_data); + return; + } + row *r = t->second; + r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); + } + } + + void read_columns(){ + std::string column_name; + do { + read_line(); + if (m_line.find("RHS") == 0) { + break; + } + if (m_line.size() < 22) { + (*m_message_stream) << "line is too short for a column" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + std::string column_name_tmp = trim(m_line.substr(4, 8)); + if (!column_name_tmp.empty()) { + column_name = column_name_tmp; + } + auto col_it = m_columns.find(column_name); + mps_reader::column * col; + if (col_it == m_columns.end()) { + col = new mps_reader::column(column_name, static_cast(m_columns.size())); + m_columns[column_name] = col; + // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; + } else { + col = col_it->second; + } + read_column(column_name, m_line.substr(14)); + } while (m_is_OK); + } + + void read_rhs() { + do { + read_line(); + if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { + break; + } + fill_rhs(); + } while (m_is_OK); + } + + + void fill_rhs_by_columns(std::string rhsides) { + // uph, let us try to work with columns + if (rhsides.size() >= 22) { + std::string ss = rhsides.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + (*m_message_stream) << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); + if (rhsides.size() > 24) { + rhsides = rhsides.substr(25); + if (rhsides.size() >= 22) { + fill_rhs_by_columns(rhsides); + } + } + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void fill_rhs() { + if (m_line.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + std::string rhsides = m_line.substr(14); + vector splitted_line = split_and_trim(rhsides); + + for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { + auto t = m_rows.find(splitted_line[i]); + if (t == m_rows.end()) { + fill_rhs_by_columns(rhsides); + return; + } + row * row = t->second; + row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); + } + } + + void read_bounds() { + if (m_line.find("BOUNDS") != 0) { + return; + } + + do { + read_line(); + if (m_line[0] != ' ') { + break; + } + create_or_update_bound(); + } while (m_is_OK); + } + + void read_ranges() { + if (m_line.find("RANGES") != 0) { + return; + } + do { + read_line(); + auto sl = split_and_trim(m_line); + if (sl.size() < 2) { + break; + } + read_range(sl); + } while (m_is_OK); + } + + + void read_bound_by_columns(const std::string & colstr) { + if (colstr.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + // uph, let us try to work with columns + if (colstr.size() >= 22) { + std::string ss = colstr.substr(0, 8); + std::string column_name = trim(ss); + auto t = m_columns.find(column_name); + + if (t == m_columns.end()) { + (*m_message_stream) << "cannot find " << column_name << std::endl; + goto fail; + } else { + vector bound_string; + bound_string.push_back(column_name); + if (colstr.size() > 14) { + bound_string.push_back(colstr.substr(14)); + } + mps_reader::column * col = t->second; + bound * b = col->m_bound; + if (b == nullptr) { + col->m_bound = b = new bound(); + } + update_bound(b, bound_string); + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + void update_bound(bound * b, vector bound_string) { + /* + UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. + */ + + std::string bound_type = get_string_from_position(1); + if (bound_type == "BV") { + b->m_upper_is_set = true; + b->m_upper = 1; + return; + } + + if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + b->m_upper_is_set = true; + b->m_upper= numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "LO" || bound_type == "LI") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + + b->m_low_is_set = true; + b->m_low = numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "FR") { + b->m_free = true; + } else if (bound_type == "FX") { + if (bound_string.size() <= 1){ + set_m_ok_to_false(); + return; + } + + b->m_value_is_fixed = true; + b->m_fixed_value = numeric_traits::from_string(bound_string[1]); + } else if (bound_type == "PL") { + b->m_low_is_set = true; + b->m_low = 0; + } else if (bound_type == "MI") { + b->m_upper_is_set = true; + b->m_upper = 0; + } else { + (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; + set_m_ok_to_false(); + throw; + } + } + + void create_or_update_bound() { + const unsigned name_offset = 14; + lp_assert(m_line.size() >= 14); + vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); + + if (bound_string.empty()) { + set_m_ok_to_false(); + (*m_message_stream) << "error at line " << m_line_number << std::endl; + throw m_line; + } + + std::string name = bound_string[0]; + auto it = m_columns.find(name); + if (it == m_columns.end()){ + read_bound_by_columns(m_line.substr(14)); + return; + } + mps_reader::column * col = it->second; + bound * b = col->m_bound; + if (b == nullptr) { + col->m_bound = b = new bound(); + } + update_bound(b, bound_string); + } + + + + void read_range_by_columns(std::string rhsides) { + if (m_line.size() < 14) { + (*m_message_stream) << "line is too short" << std::endl; + (*m_message_stream) << m_line << std::endl; + (*m_message_stream) << "line number is " << m_line_number << std::endl; + set_m_ok_to_false(); + return; + } + // uph, let us try to work with columns + if (rhsides.size() >= 22) { + std::string ss = rhsides.substr(0, 8); + std::string row_name = trim(ss); + auto t = m_rows.find(row_name); + + if (t == m_rows.end()) { + (*m_message_stream) << "cannot find " << row_name << std::endl; + goto fail; + } else { + row * row = t->second; + row->m_range = numeric_traits::from_string(rhsides.substr(8)); + maybe_modify_current_row_and_add_row_for_range(row); + if (rhsides.size() > 24) { + rhsides = rhsides.substr(25); + if (rhsides.size() >= 22) { + read_range_by_columns(rhsides); + } + } + } + } else { + fail: + set_m_ok_to_false(); + (*m_message_stream) << "cannot understand this line" << std::endl; + (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; + return; + } + } + + + void read_range(vector & splitted_line){ + for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { + auto it = m_rows.find(splitted_line[i]); + if (it == m_rows.end()) { + read_range_by_columns(m_line.substr(14)); + return; + } + row * row = it->second; + row->m_range = numeric_traits::from_string(splitted_line[i + 1]); + maybe_modify_current_row_and_add_row_for_range(row); + } + } + + void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { + unsigned index= static_cast(m_rows.size() - m_cost_line_count); + std::string row_name = row_with_range->m_name + "_range"; + row * other_bound_range_row; + switch (row_with_range->m_type) { + case row_type::Greater_or_equal: + m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); + other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); + break; + case row_type::Less_or_equal: + m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); + other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); + break; + case row_type::Equal: + if (row_with_range->m_range > 0) { + row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change + m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); + } else { // row->m_range < 0; + row_with_range->m_type = row_type::Less_or_equal; // the existing row type change + m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); + } + other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; + break; + default: + (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; + set_m_ok_to_false(); + throw; + } + + for (auto s : row_with_range->m_row_columns) { + lp_assert(m_columns.find(s.first) != m_columns.end()); + other_bound_range_row->m_row_columns[s.first] = s.second; + } + } + + void add_row() { + if (m_line.length() < 2) { + return; + } + + m_line = trim(m_line); + char c = m_line[0]; + m_line = m_line.substr(1); + m_line = trim(m_line); + add_row(c); + } + + void add_row(char c) { + unsigned index= static_cast(m_rows.size() - m_cost_line_count); + switch (c) { + case 'E': + m_rows[m_line] = new row(row_type::Equal, m_line, index); + break; + case 'L': + m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); + break; + case 'G': + m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); + break; + case 'N': + m_rows[m_line] = new row(row_type::Cost, m_line, index); + m_cost_row_name = m_line; + m_cost_line_count++; + break; + } + } + unsigned range_count() { + unsigned ret = 0; + for (auto s : m_rows) { + if (s.second->m_range != 0) { + ret++; + } + } + return ret; + } + + /* + If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: + sense interval + G [rhs, rhs + |range|] + L [rhs - |range|, rhs] + E [rhs, rhs + |range|] if range > 0, + [rhs - |range|, rhs] if range < 0 + where |range| is range's absolute value. + */ + + lp_relation get_relation_from_row(row_type rt) { + switch (rt) { + case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; + case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; + case mps_reader::Equal: return lp_relation::Equal; + default: + (*m_message_stream) << "Unexpected rt " << rt << std::endl; + set_m_ok_to_false(); + throw; + } + } + + unsigned solver_row_count() { + return m_rows.size() - m_cost_line_count + range_count(); + } + + void fill_solver_on_row(row * row, lp_solver *solver) { + if (row->m_name != m_cost_row_name) { + solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); + for (auto s : row->m_row_columns) { + lp_assert(m_columns.find(s.first) != m_columns.end()); + solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); + } + } else { + set_solver_cost(row, solver); + } + } + + T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } + + void fill_solver_on_rows(lp_solver * solver) { + for (auto row_it : m_rows) { + fill_solver_on_row(row_it.second, solver); + } + } + + + void fill_solver_on_columns(lp_solver * solver){ + for (auto s : m_columns) { + mps_reader::column * col = s.second; + unsigned index = col->m_index; + set_boundary_for_column(index, col->m_bound, solver); + // optional call + solver->give_symbolic_name_to_column(col->m_name, col->m_index); + } + } + + void fill_solver(lp_solver *solver) { + fill_solver_on_rows(solver); + fill_solver_on_columns(solver); + } + + void set_solver_cost(row * row, lp_solver *solver) { + for (auto s : row->m_row_columns) { + std::string name = s.first; + lp_assert(m_columns.find(name) != m_columns.end()); + mps_reader::column * col = m_columns[name]; + solver->set_cost_for_column(col->m_index, s.second); + } + } + +public: + + void set_message_stream(std::ostream * o) { + lp_assert(o != nullptr); + m_message_stream = o; + } + vector column_names() { + vector v; + for (auto s : m_columns) { + v.push_back(s.first); + } + return v; + } + + ~mps_reader() { + for (auto s : m_rows) { + delete s.second; + } + for (auto s : m_columns) { + auto col = s.second; + delete col->m_bound; + delete col; + } + } + + mps_reader(const std::string & file_name): + m_is_OK(true), + m_file_name(file_name), + m_file_stream(file_name), + m_cost_line_count(0), + m_line_number(0), + m_message_stream(& std::cout) {} + void read() { + if (!m_file_stream.is_open()){ + set_m_ok_to_false(); + return; + } + + read_name(); + read_rows(); + read_columns(); + read_rhs(); + if (m_line.find("BOUNDS") == 0) { + read_bounds(); + read_ranges(); + } else if (m_line.find("RANGES") == 0) { + read_ranges(); + read_bounds(); + } + } + + bool is_ok() { + return m_is_OK; + } + + lp_solver * create_solver(bool dual) { + lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); + fill_solver(solver); + return solver; + } + + lconstraint_kind get_lar_relation_from_row(row_type rt) { + switch (rt) { + case Less_or_equal: return LE; + case Greater_or_equal: return GE; + case Equal: return EQ; + default: + (*m_message_stream) << "Unexpected rt " << rt << std::endl; + set_m_ok_to_false(); + throw; + } + } + + unsigned get_var_index(std::string s) { + auto it = m_names_to_var_index.find(s); + if (it != m_names_to_var_index.end()) + return it->second; + unsigned ret = static_cast(m_names_to_var_index.size()); + m_names_to_var_index[s] = ret; + return ret; + } + + void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { + if (row->m_name != m_cost_row_name) { + auto kind = get_lar_relation_from_row(row->m_type); + vector> ls; + for (auto s : row->m_row_columns) { + var_index i = solver->add_var(get_var_index(s.first), false); + ls.push_back(std::make_pair(s.second, i)); + } + unsigned j = solver->add_term(ls, row_index); + solver->add_var_bound(j, kind, row->m_right_side); + } else { + // ignore the cost row + } + } + + + void fill_lar_solver_on_rows(lar_solver * solver) { + int row_index = 0; + for (auto row_it : m_rows) { + fill_lar_solver_on_row(row_it.second, solver, row_index++); + } + } + + void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, GE, b->m_low); + } + + void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, LE, b->m_upper); + } + + void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { + var_index i = solver->add_var(col->m_index, false); + solver->add_var_bound(i, LE, b->m_fixed_value); + solver->add_var_bound(i, GE, b->m_fixed_value); + } + + void fill_lar_solver_on_columns(lar_solver * solver) { + for (auto s : m_columns) { + mps_reader::column * col = s.second; + solver->add_var(col->m_index, false); + auto b = col->m_bound; + if (b == nullptr) return; + + if (b->m_free) continue; + + if (b->m_low_is_set) { + create_low_constraint_for_var(col, b, solver); + } + if (b->m_upper_is_set) { + create_upper_constraint_for_var(col, b, solver); + } + if (b->m_value_is_fixed) { + create_equality_contraint_for_var(col, b, solver); + } + } + } + + + void fill_lar_solver(lar_solver * solver) { + fill_lar_solver_on_columns(solver); + fill_lar_solver_on_rows(solver); + } + + lar_solver * create_lar_solver() { + lar_solver * solver = new lar_solver(); + fill_lar_solver(solver); + return solver; + } +}; +} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index cde96277b..571e9b1d0 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,6 +24,7 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index 09a56c84e..af3a46234 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,6 +19,9 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 68d5f8025..9ae671c16 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,7 +19,9 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" - +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index c0e9c8505..d9b74f162 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp + lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp new file mode 100644 index 000000000..8d6425533 --- /dev/null +++ b/src/shell/lp_frontend.cpp @@ -0,0 +1,109 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Author: + + Lev Nachmanson 2016-10-27 + +--*/ + +#include "math/lp/lp_settings.h" +#include "math/lp/mps_reader.h" +#include "util/timeout.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" +#include "util/rlimit.h" +#include "util/gparams.h" +#include "util/mutex.h" +#include +#include +#include "smt/params/smt_params_helper.hpp" + +namespace { +static mutex *display_stats_mux = new mutex; + +static lp::lp_solver* g_solver = nullptr; + +static void display_statistics() { + lock_guard lock(*display_stats_mux); + if (g_solver && g_solver->settings().print_statistics) { + // TBD display relevant information about statistics + } +} + +static void STD_CALL on_ctrl_c(int) { + signal (SIGINT, SIG_DFL); + display_statistics(); + raise(SIGINT); +} + +static void on_timeout() { + display_statistics(); + _Exit(0); +} + +struct front_end_resource_limit : public lp::lp_resource_limit { + reslimit& m_reslim; + + front_end_resource_limit(reslimit& lim): + m_reslim(lim) + {} + + bool get_cancel_flag() override { return !m_reslim.inc(); } +}; + +void run_solver(smt_params_helper & params, char const * mps_file_name) { + + reslimit rlim; + unsigned timeout = gparams::get_ref().get_uint("timeout", 0); + unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); + front_end_resource_limit lp_limit(rlim); + + scoped_rlimit _rlimit(rlim, rlimit); + cancel_eh eh(rlim); + scoped_timer timer(timeout, &eh); + + std::string fn(mps_file_name); + lp::mps_reader reader(fn); + reader.set_message_stream(&std::cout); // can be redirected + reader.read(); + if (!reader.is_ok()) { + std::cerr << "cannot process " << mps_file_name << std::endl; + return; + } + lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver + solver->settings().set_resource_limit(lp_limit); + g_solver = solver; + if (params.arith_min()) { + solver->flip_costs(); + } + solver->settings().set_message_ostream(&std::cout); + solver->settings().report_frequency = params.arith_rep_freq(); + solver->settings().print_statistics = params.arith_print_stats(); + solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); + + solver->find_maximal_solution(); + + *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp::lp_status::OPTIMAL) { + if (params.arith_min()) { + solver->flip_costs(); + } + solver->print_model(std::cout); + } + + display_statistics(); + g_solver = nullptr; + delete solver; +} +} + +unsigned read_mps_file(char const * mps_file_name) { + signal(SIGINT, on_ctrl_c); + register_on_timeout_proc(on_timeout); + smt_params_helper p; + param_descrs r; + p.collect_param_descrs(r); + run_solver(p, mps_file_name); + return 0; +} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h new file mode 100644 index 000000000..b24be811f --- /dev/null +++ b/src/shell/lp_frontend.h @@ -0,0 +1,7 @@ +/* + Copyright (c) 2013 Microsoft Corporation. All rights reserved. + + Author: Lev Nachmanson +*/ +#pragma once +unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 26325d3d0..4c26d91d9 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,13 +37,14 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" +#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -376,6 +377,10 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } + else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || + strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { + g_input_kind = IN_MPS; + } } } switch (g_input_kind) { @@ -401,6 +406,9 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; + case IN_MPS: + return_value = read_mps_file(g_input_file); + break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2aa988282..d61910ff2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,6 +19,9 @@ --*/ #include "util/stopwatch.h" +#include "math/lp/lp_solver.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 78abf1a6f..c82cdd0a4 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,6 +33,8 @@ #include #include #include "math/lp/lp_utils.h" +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -57,71 +59,6 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" - -bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - -std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - - -vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - - namespace nla { void test_horner(); void test_monics(); @@ -1458,15 +1395,169 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } +template +void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { + if (time_limit > 0) + solver->set_time_limit(time_limit); + + if (look_for_min) + solver->flip_costs(); + + update_settings(args_parser, solver->settings()); +} bool values_are_one_percent_close(double a, double b); +void print_x(mps_reader & reader, lp_solver * solver) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + } + std::cout << std::endl; +} + +void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { + for (const auto & name : reader.column_names()) { + double a = solver->get_column_value_by_name(name); + double b = solver0->get_column_value_by_name(name); + if (!values_are_one_percent_close(a, b)) { + std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; + } + } +} +void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { + mps_reader reader(file_name); + reader.read(); + if (!reader.is_ok()) { + std::cout << "cannot process " << file_name << std::endl; + return; + } + + lp_solver * solver = reader.create_solver(dual); + setup_solver(time_limit, look_for_min, args_parser, solver); + stopwatch sw; + sw.start(); + if (dual) { + std::cout << "solving for dual" << std::endl; + } + solver->find_maximal_solution(); + sw.stop(); + double span = sw.get_seconds(); + std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp_status::OPTIMAL) { + if (reader.column_names().size() < 20) { + print_x(reader, solver); + } + double cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + std::cout << "cost = " << cost << std::endl; + } + std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; + if (compare_with_primal) { + auto * primal_solver = reader.create_solver(false); + setup_solver(time_limit, look_for_min, args_parser, primal_solver); + primal_solver->find_maximal_solution(); + if (solver->get_status() != primal_solver->get_status()) { + std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; + } else { + if (solver->get_status() == lp_status::OPTIMAL) { + double cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + double primal_cost = primal_solver->get_current_cost(); + if (look_for_min) { + primal_cost = -primal_cost; + } + std::cout << "primal cost = " << primal_cost << std::endl; + if (!values_are_one_percent_close(cost, primal_cost)) { + compare_solutions(reader, primal_solver, solver); + print_x(reader, primal_solver); + std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; + lp_assert(false); + } + } + } + delete primal_solver; + } + delete solver; +} +void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + setup_solver(time_limit, look_for_min, args_parser, solver); + stopwatch sw; + sw.start(); + solver->find_maximal_solution(); + std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; + + if (solver->get_status() == lp_status::OPTIMAL) { + // for (auto name: reader.column_names()) { + // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + // } + lp::mpq cost = solver->get_current_cost(); + if (look_for_min) { + cost = -cost; + } + std::cout << "cost = " << cost.get_double() << std::endl; + } + std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition +void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { + if (!solve_for_rational) { + std::cout << "solving " << file_name << std::endl; + solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); + } + else { + std::cout << "solving " << file_name << " in rationals " << std::endl; + solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); + } +} +void solve_mps(std::string file_name, argument_parser & args_parser) { + bool look_for_min = args_parser.option_is_used("--min"); + unsigned time_limit; + bool solve_for_rational = args_parser.option_is_used("--mpq"); + bool dual = args_parser.option_is_used("--dual"); + bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); +} + +void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { + std::cout << "solving " << file_name << std::endl; + + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + solver->find_maximal_solution(); + std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (solver->get_status() == lp_status::OPTIMAL) { + if (reader.column_names().size() < 20) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; + } + } + std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; + } + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} void test_upair_queue() { int n = 10; @@ -1535,6 +1626,53 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } +bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); + return solver->solution_is_feasible(solution); + } + return false; +} + + +void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { + std::cout << "solving " << file_name << std::endl; + mps_reader reader(file_name); + reader.read(); + if (reader.is_ok()) { + auto * solver = reader.create_solver(dual); + solver->find_maximal_solution(); + std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; + if (status != solver->get_status()){ + std::cout << "status should be " << lp_status_to_string(status) << std::endl; + lp_assert(status == solver->get_status()); + throw "status is wrong"; + } + if (solver->get_status() == lp_status::OPTIMAL) { + std::cout << "cost = " << solver->get_current_cost() << std::endl; + if (solution != nullptr) { + for (auto it : *solution) { + if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { + std::cout << "expected:" << it.first << "=" << + it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; + } + lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); + } + } + if (reader.column_names().size() < 20) { + for (const auto & name : reader.column_names()) { + std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; + } + std::cout << std::endl; + } + } + delete solver; + } else { + std::cout << "cannot process " << file_name << std::endl; + } +} int get_random_rows() { return 5 + my_random() % 2; @@ -1548,6 +1686,55 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } +void add_random_row(lp_primal_simplex * solver, int cols, int row) { + solver->add_constraint(lp_relation::Greater_or_equal, 1, row); + for (int i = 0; i < cols; i++) { + solver->set_row_column_coefficient(row, i, get_random_int()); + } +} + +void add_random_cost(lp_primal_simplex * solver, int cols) { + for (int i = 0; i < cols; i++) { + solver->set_cost_for_column(i, get_random_int()); + } +} + +lp_primal_simplex * generate_random_solver() { + int rows = get_random_rows(); + int cols = get_random_columns(); + auto * solver = new lp_primal_simplex(); + for (int i = 0; i < rows; i++) { + add_random_row(solver, cols, i); + } + add_random_cost(solver, cols); + return solver; +} + + + +void random_test_on_i(unsigned i) { + if (i % 1000 == 0) { + std::cout << "."; + } + srand(i); + auto *solver = generate_random_solver(); + solver->find_maximal_solution(); + // std::cout << lp_status_to_string(solver->get_status()) << std::endl; + delete solver; +} + +void random_test() { + for (unsigned i = 0; i < std::numeric_limits::max(); i++) { + try { + random_test_on_i(i); + } + catch (const char * error) { + std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; + break; + } + } +} + #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); @@ -1709,9 +1896,140 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } +void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); +void solve_some_mps(argument_parser & args_parser) { + unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + unsigned successes = 0; + unsigned failures = 0; + unsigned inconclusives = 0; + std::set minimums; + vector file_names; + fill_file_names(file_names, minimums); + bool solve_for_rational = args_parser.option_is_used("--mpq"); + bool dual = args_parser.option_is_used("--dual"); + bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); + bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); + if (compare_with_glpk) { + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + test_out_dir(out_dir); + for (auto& a : file_names) { + try { + std::string file_dir; + std::string file_name; + find_dir_and_file_name(a, file_dir, file_name); + process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + return; + } + if (!solve_for_rational) { + solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); + solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) + std::unordered_map sol; + sol["X1"] = 0; + sol["X2"] = 6; + sol["X3"] = 0; + sol["X4"] = 15; + sol["X5"] = 2; + sol["X6"] = 1; + sol["X7"] = 1; + sol["X8"] = 0; + solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); + solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); + sol.clear(); + sol["X1"] = 25.0/14.0; + // sol["X2"] = 0; + // sol["X3"] = 0; + // sol["X4"] = 0; + // sol["X5"] = 0; + // sol["X6"] = 0; + // sol["X7"] = 9.0/14.0; + solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) + solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) + solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); + // return; + for (auto& s : file_names) { + try { + solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + } else { + // unsigned i = 0; + for (auto& s : file_names) { + // if (i++ > 9) return; + try { + solve_mps_in_rational(s, dual, args_parser); + } + catch(const char *s){ + std::cout<< "exception: "<< s << std::endl; + } + } + } +} #endif +void solve_rational() { + lp_primal_simplex solver; + solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); + solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); + + // setting the cost + int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; + std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; + + for (unsigned i = 0; i < 8; i++) { + solver.set_cost_for_column(i, lp::mpq(cost[i])); + solver.give_symbolic_name_to_column(var_names[i], i); + } + + int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; + for (unsigned i = 0; i < 8; i++) { + solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); + } + + int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; + for (unsigned i = 0; i < 8; i++) { + solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); + } + + int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; + for (unsigned i = 0; i < 8; i++) { + solver.set_lower_bound(i, lp::mpq(0)); + solver.set_upper_bound(i, lp::mpq(bounds[i])); + } + + std::unordered_map expected_sol; + expected_sol["x1"] = lp::mpq(0); + expected_sol["x2"] = lp::mpq(6); + expected_sol["x3"] = lp::mpq(0); + expected_sol["x4"] = lp::mpq(15); + expected_sol["x5"] = lp::mpq(2); + expected_sol["x6"] = lp::mpq(1); + expected_sol["x7"] = lp::mpq(1); + expected_sol["x8"] = lp::mpq(0); + solver.find_maximal_solution(); + lp_assert(solver.get_status() == lp_status::OPTIMAL); +#ifdef Z3DEBUG + for (const auto & it : expected_sol) { + (void)it; + lp_assert(it.second == solver.get_column_value_by_name(it.first)); + } +#endif +} std::string read_line(bool & end, std::ifstream & file) { @@ -1729,6 +2047,49 @@ bool contains(std::string const & s, char const * pattern) { } +std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { + std::ifstream file(file_name); + if (!file.is_open()){ + std::cerr << "cannot open " << file_name << std::endl; + return nullptr; + } + std::string s; + bool end; + do { + s = read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + if (contains(s, "Column name")){ + break; + } + } while (true); + + read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + + auto ret = new std::unordered_map(); + + do { + s = read_line(end, file); + if (end) { + std::cerr << "unexpected file end " << file_name << std::endl; + return nullptr; + } + auto split = string_split(s, " \t", false); + if (split.empty()) { + return ret; + } + + lp_assert(split.size() > 3); + (*ret)[split[1]] = atof(split[3].c_str()); + } while (true); +} + void test_init_U() { @@ -1828,6 +2189,7 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); + parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -1998,9 +2360,237 @@ std::string get_status(std::string file_name) { throw 0; } +// returns true if the costs should be compared too +bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { + std::string glpk_status = get_status(glpk_out_file_name); + std::string lp_tst_status = get_status(lp_out_file_name); + + if (glpk_status != lp_tst_status) { + if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { + successes++; + return false; + } else { + std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; + std::cout << " but lp_tst status is " << lp_tst_status << std::endl; + failures++; + return false; + } + } + return lp_tst_status == "OPTIMAL"; +} + +double get_glpk_cost(std::string file_name) { + std::ifstream f(file_name); + if (!f.is_open()) { + std::cout << "cannot open " << file_name << std::endl; + throw 0; + } + std::string str; + while (getline(f, str)) { + if (str.find("Objective") != std::string::npos) { + vector tokens = split_and_trim(str); + if (tokens.size() != 5) { + std::cout << "unexpected Objective std::string " << str << std::endl; + throw 0; + } + return atof(tokens[3].c_str()); + } + } + std::cout << "cannot find the Objective line in " << file_name << std::endl; + throw 0; +} + +double get_lp_tst_cost(std::string file_name) { + std::ifstream f(file_name); + if (!f.is_open()) { + std::cout << "cannot open " << file_name << std::endl; + throw 0; + } + std::string str; + std::string cost_string; + while (getline(f, str)) { + if (str.find("cost") != std::string::npos) { + cost_string = str; + } + } + if (cost_string.empty()) { + std::cout << "cannot find the cost line in " << file_name << std::endl; + throw 0; + } + + vector tokens = split_and_trim(cost_string); + if (tokens.size() != 3) { + std::cout << "unexpected cost string " << cost_string << std::endl; + throw 0; + } + return atof(tokens[2].c_str()); +} + +bool values_are_one_percent_close(double a, double b) { + double maxval = std::max(fabs(a), fabs(b)); + if (maxval < 0.000001) { + return true; + } + + double one_percent = maxval / 100; + return fabs(a - b) <= one_percent; +} + +// returns true if both are optimal +void compare_costs(std::string glpk_out_file_name, + std::string lp_out_file_name, + unsigned & successes, + unsigned & failures) { + double a = get_glpk_cost(glpk_out_file_name); + double b = get_lp_tst_cost(lp_out_file_name); + + if (values_are_one_percent_close(a, b)) { + successes++; + } else { + failures++; + std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; + } +} +void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { +#ifdef CHECK_GLPK_SOLUTION + std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); + if (solution_is_feasible(lp_file_name, *solution_table)) { + std::cout << "glpk solution is feasible" << std::endl; + } else { + std::cout << "glpk solution is infeasible" << std::endl; + } + delete solution_table; +#endif + if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { + compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); + } +} +void test_lar_on_file(std::string file_name, argument_parser & args_parser); + +void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { + bool use_mpq = args_parser.option_is_used("--mpq"); + bool minimize = args_parser.option_is_used("--min"); + std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); + + std::string input_file_name = test_dir + "/" + test_file_name; + if (input_file_name[input_file_name.size() - 1] == '~') { + // std::cout << "ignoring " << input_file_name << std::endl; + return; + } + std::cout <<"processing " << input_file_name << std::endl; + + std::ofstream out(full_lp_tst_out_name); + if (!out.is_open()) { + std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; + throw 0; + } + std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer + std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! + bool dual = args_parser.option_is_used("--dual"); + try { + if (args_parser.option_is_used("--lar")) + test_lar_on_file(input_file_name, args_parser); + else + solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); + } + catch(...) { + std::cout << "catching the failure" << std::endl; + failures++; + std::cout.rdbuf(coutbuf); // reset to standard output again + return; + } + std::cout.rdbuf(coutbuf); // reset to standard output again + + if (args_parser.option_is_used("--compare_with_glpk")) { + std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); + int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); + if (glpk_exit_code != 0) { + std::cout << "glpk failed" << std::endl; + inconclusives++; + } else { + compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); + } + } +} +/* + int my_readdir(DIR *dirp, struct dirent * + #ifndef LEAN_WINDOWS + entry + #endif + , struct dirent **result) { + #ifdef LEAN_WINDOWS + *result = readdir(dirp); // NOLINT + return *result != nullptr? 0 : 1; + #else + return readdir_r(dirp, entry, result); + #endif + } +*/ +/* + vector> get_file_list_of_dir(std::string test_file_dir) { + DIR *dir; + if ((dir = opendir(test_file_dir.c_str())) == nullptr) { + std::cout << "Cannot open directory " << test_file_dir << std::endl; + throw 0; + } + vector> ret; + struct dirent entry; + struct dirent* result; + int return_code; + for (return_code = my_readdir(dir, &entry, &result); + #ifndef LEAN_WINDOWS + result != nullptr && + #endif + return_code == 0; + return_code = my_readdir(dir, &entry, &result)) { + DIR *tmp_dp = opendir(result->d_name); + struct stat file_record; + if (tmp_dp == nullptr) { + std::string s = test_file_dir+ "/" + result->d_name; + int stat_ret = stat(s.c_str(), & file_record); + if (stat_ret!= -1) { + ret.push_back(make_pair(result->d_name, file_record.st_size)); + } else { + perror("stat"); + exit(1); + } + } else { + closedir(tmp_dp); + } + } + closedir(dir); + return ret; + } +*/ +/* + struct file_size_comp { + unordered_map& m_file_sizes; + file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} + int operator()(std::string a, std::string b) { + std::cout << m_file_sizes.size() << std::endl; + std::cout << a << std::endl; + std::cout << b << std::endl; + + auto ls = m_file_sizes.find(a); + std::cout << "fa" << std::endl; + auto rs = m_file_sizes.find(b); + std::cout << "fb" << std::endl; + if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { + std::cout << "fc " << std::endl; + int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); + std::cout << "calc r " << std::endl; + return r; + } else { + std::cout << "sc " << std::endl; + return 0; + } + } + }; + +*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2008,11 +2598,121 @@ struct sort_pred { }; +void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { + /* + std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + DIR *out_dir_p = opendir(out_dir.c_str()); + if (out_dir_p == nullptr) { + std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; + return; + } + closedir(out_dir_p); + vector> files = get_file_list_of_dir(test_file_dir); + std::sort(files.begin(), files.end(), sort_pred()); + unsigned max_iters, time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + unsigned successes = 0, failures = 0, inconclusives = 0; + for (auto & t : files) { + process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + */ +} +std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { + std::unordered_map ret; + for (const auto & it : reader.column_names()) { + ret[it] = lps->get_column_value_by_name(it); + } + return ret; +} +void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { + std::string maxng = args_parser.get_option_value("--maxng"); + if (!maxng.empty()) { + solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); + } + if (args_parser.option_is_used("-pd")){ + solver->settings().presolve_with_double_solver_for_lar = true; + } + + if (args_parser.option_is_used("--compare_with_primal")){ + if (reader == nullptr) { + std::cout << "cannot compare with primal, the reader is null " << std::endl; + return; + } + auto * lps = reader->create_solver(false); + lps->find_maximal_solution(); + std::unordered_map sol = get_solution_map(lps, *reader); + std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; + return; + } + stopwatch sw; + sw.start(); + lp_status status = solver->solve(); + std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; + if (solver->get_status() == lp_status::INFEASIBLE) { + explanation evidence; + solver->get_infeasibility_explanation(evidence); + } + if (args_parser.option_is_used("--randomize_lar")) { + if (solver->get_status() != lp_status::OPTIMAL) { + std::cout << "cannot check randomize on an infeazible problem" << std::endl; + return; + } + std::cout << "checking randomize" << std::endl; + vector all_vars; + for (unsigned j = 0; j < solver->number_of_vars(); j++) + all_vars.push_back(j); + + unsigned m = all_vars.size(); + if (m > 100) + m = 100; + + var_index *vars = new var_index[m]; + for (unsigned i = 0; i < m; i++) + vars[i]=all_vars[i]; + + solver->random_update(m, vars); + delete []vars; + } +} +lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { + if (args_parser.option_is_used("--smt")) { + smt_reader reader(file_name); + reader.read(); + if (!reader.is_ok()){ + std::cout << "cannot process " << file_name << std::endl; + return nullptr; + } + return reader.create_lar_solver(); + } + mps_reader reader(file_name); + reader.read(); + if (!reader.is_ok()) { + std::cout << "cannot process " << file_name << std::endl; + return nullptr; + } + return reader.create_lar_solver(); +} +void test_lar_on_file(std::string file_name, argument_parser & args_parser) { + lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); + mps_reader reader(file_name); + mps_reader * mps_reader = nullptr; + reader.read(); + if (reader.is_ok()) { + mps_reader = & reader; + run_lar_solver(args_parser, solver, mps_reader); + } + delete solver; +} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2033,6 +2733,23 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } +void test_lar_solver(argument_parser & args_parser) { + + std::string file_name = args_parser.get_option_value("--file"); + if (!file_name.empty()) { + test_lar_on_file(file_name, args_parser); + return; + } + + std::string file_list = args_parser.get_option_value("--filelist"); + if (!file_list.empty()) { + for (const std::string & fn : get_file_names_from_file_list(file_list)) + test_lar_on_file(fn, args_parser); + return; + } + + std::cout << "give option --file or --filelist to test_lar_solver\n"; +} void test_numeric_pair() { numeric_pair a; @@ -3217,6 +3934,22 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } + if (args_parser.option_is_used("--test_mpq")) { + test_rationals(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_mpq_np")) { + test_rationals_no_numeric_pairs(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_mpq_np_plus")) { + test_rationals_no_numeric_pairs_plus(); + return finalize(0); + } + + if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3234,8 +3967,29 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - - +#ifdef Z3DEBUG + if (args_parser.option_is_used("--test_swaps")) { + square_sparse_matrix m(10, 0); + fill_matrix(m); + test_swap_rows_with_permutation(m); + test_swap_cols_with_permutation(m); + return finalize(0); + } +#endif + if (args_parser.option_is_used("--test_perm")) { + test_permutations(); + return finalize(0); + } + if (args_parser.option_is_used("--test_file_directory")) { + test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); + return finalize(0); + } + std::string file_list = args_parser.get_option_value("--filelist"); + if (!file_list.empty()) { + for (const std::string & fn : get_file_names_from_file_list(file_list)) + solve_mps(fn, args_parser); + return finalize(0); + } if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3243,6 +3997,100 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } +#ifdef Z3DEBUG + lp_settings settings; + update_settings(args_parser, settings); + if (args_parser.option_is_used("--test_lu")) { + test_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_small_lu")) { + test_small_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--lar")){ + std::cout <<"calling test_lar_solver" << std::endl; + test_lar_solver(args_parser); + return finalize(0); + } + + + + if (args_parser.option_is_used("--test_larger_lu")) { + test_larger_lu(settings); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_larger_lu_with_holes")) { + test_larger_lu_with_holes(settings); + ret = 0; + return finalize(ret); + } +#endif + if (args_parser.option_is_used("--eti")) { + test_evidence_for_total_inf_simple(args_parser); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--maximize_term")) { + test_maximize_term(); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--test_lp_0")) { + test_lp_0(); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--smap")) { + test_stacked(); + ret = 0; + return finalize(ret); + } + if (args_parser.option_is_used("--term")) { + test_term(); + ret = 0; + return finalize(ret); + } + unsigned time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit); + bool dual = args_parser.option_is_used("--dual"); + bool solve_for_rational = args_parser.option_is_used("--mpq"); + std::string file_name = args_parser.get_option_value("--file"); + if (!file_name.empty()) { + solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); + ret = 0; + return finalize(ret); + } + + if (args_parser.option_is_used("--solve_some_mps")) { +#ifndef _WINDOWS + solve_some_mps(args_parser); +#endif + ret = 0; + return finalize(ret); + } + // lp::ccc = 0; + return finalize(0); + test_init_U(); + test_replace_column(); +#ifdef Z3DEBUG + square_sparse_matrix_with_permutations_test(); + test_dense_matrix(); + test_swap_operations(); + test_permutations(); + test_pivot_like_swaps_and_pivot(); +#endif + tst1(); + std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 75edb23b9..2ab0c1ea6 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,14 +20,18 @@ Revision History: #pragma once +// reads an MPS file representing a Mixed Integer Program #include #include #include +#include "math/lp/lp_primal_simplex.h" +#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include #include #include +#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 36b273740..8f461ea1c 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,6 +27,7 @@ Revision History: #include #include #include "math/lp/lp_utils.h" +#include "math/lp/lp_solver.h" namespace lp { From b9a87e493babf0d545614610942fda0988d3e8c0 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 5 Mar 2023 19:08:41 +0000 Subject: [PATCH 083/220] minor code simplifications --- src/ast/rewriter/hoist_rewriter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index d5b2042a2..72a764bfa 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -37,7 +37,7 @@ expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { continue; else negs.push_back(::mk_not(m, a)); - return expr_ref(::mk_not(m, mk_or(negs)), m); + return ::mk_not(mk_or(negs)); } else return ::mk_and(args); @@ -164,7 +164,6 @@ unsigned hoist_rewriter::mk_var(expr* e) { } expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsigned num_args, expr* const* es) { - expr_ref result(m); expr_ref_vector args(m), args1(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { VERIFY(is_and(es[i], &args1)); @@ -178,8 +177,7 @@ expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsi fmls.push_back(mk_or(args)); for (auto* p : preds) fmls.push_back(p); - result = mk_and(fmls); - return result; + return mk_and(fmls); } From 42076a3c13e67f89e6ab577f1de3f67e870905fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Mar 2023 22:26:27 -0800 Subject: [PATCH 084/220] bug fixes to new core, elim_predicates and elim_unconstrained --- src/ast/simplifiers/elim_unconstrained.cpp | 12 ++++---- src/ast/simplifiers/elim_unconstrained.h | 4 ++- src/ast/simplifiers/eliminate_predicates.cpp | 31 ++++++++++---------- src/ast/simplifiers/eliminate_predicates.h | 3 +- src/model/model_core.cpp | 2 +- src/sat/smt/euf_model.cpp | 5 ++-- src/sat/smt/euf_solver.cpp | 9 ++++-- src/sat/smt/euf_solver.h | 2 +- src/sat/smt/q_mbi.cpp | 2 +- src/sat/tactic/goal2sat.cpp | 2 +- 10 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 41877202a..3d2566193 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -52,9 +52,9 @@ monotonicity or reflexivity rules. #include "ast/simplifiers/elim_unconstrained.h" elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : - dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { + dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m), m_args(m) { std::function is_var = [&](expr* e) { - return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).m_refcount <= 1; + return is_uninterp_const(e) && !m_fmls.frozen(e) && is_node(e) && get_node(e).m_refcount <= 1; }; m_inverter.set_is_var(is_var); } @@ -114,7 +114,7 @@ void elim_unconstrained::eliminate() { gc(e); invalidate_parents(e); freeze_rec(r); - + m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_proof = pr; @@ -291,7 +291,7 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { unsigned sz0 = todo.size(); if (is_app(t)) { for (expr* arg : *to_app(t)) - if (get_node(arg).m_dirty) + if (get_node(arg).m_dirty || !get_node(arg).m_term) todo.push_back(arg); if (todo.size() != sz0) continue; @@ -300,18 +300,20 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); n.m_term = m.mk_app(to_app(t)->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + m_args.shrink(sz); } else if (is_quantifier(t)) { expr* body = to_quantifier(t)->get_expr(); node& n2 = get_node(body); - if (n2.m_dirty) { + if (n2.m_dirty || !n2.m_term) { todo.push_back(body); continue; } n.m_term = m.update_quantifier(to_quantifier(t), n2.m_term); } m_trail.push_back(n.m_term); + m_root.setx(n.m_term->get_id(), n.m_term->get_id(), UINT_MAX); todo.pop_back(); n.m_dirty = false; } diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 19af099d0..0fdde5af2 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -46,13 +46,15 @@ class elim_unconstrained : public dependent_expr_simplifier { var_lt m_lt; heap m_heap; expr_ref_vector m_trail; - ptr_vector m_args; + expr_ref_vector m_args; stats m_stats; unsigned_vector m_root; bool m_created_compound = false; bool m_enable_proofs = false; bool is_var_lt(int v1, int v2) const; + bool is_node(unsigned n) const { return m_nodes.size() > n; } + bool is_node(expr* t) const { return is_node(t->get_id()); } node& get_node(unsigned n) { return m_nodes[n]; } node const& get_node(unsigned n) const { return m_nodes[n]; } node& get_node(expr* t) { return m_nodes[root(t)]; } diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index b2cc5e25d..0dfb5a87e 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -174,7 +174,7 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bou // then replace (f x y z) by (if (= z (+ x y)) s (f' x y z)) // -void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause const& cl) { +void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause& cl) { expr_ref _body(body, m); uint_set indices; expr_ref_vector args(m), eqs(m); @@ -208,7 +208,7 @@ void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause cons lhs = m.mk_app(f, args); rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); - insert_macro(lhs, rhs, cl.m_dep); + insert_macro(lhs, rhs, cl); } @@ -311,6 +311,12 @@ bool eliminate_predicates::is_macro_safe(expr* e) { return true; } +void eliminate_predicates::insert_macro(app* head, expr* def, clause& cl) { + insert_macro(head, def, cl.m_dep); + TRACE("elim_predicates", tout << "remove " << cl << "\n"); + cl.m_alive = false; +} + void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* dep) { unsigned num = head->get_num_args(); ptr_buffer vars, subst_args; @@ -335,7 +341,7 @@ void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* d auto* info = alloc(macro_def, _head, _def, _dep); m_macros.insert(head->get_decl(), info); m_fmls.model_trail().push(head->get_decl(), _def, _dep, {}); // augment with definition for head - m_is_macro.mark(head->get_decl(), true); + m_is_macro.mark(head->get_decl(), true); TRACE("elim_predicates", tout << "insert " << _head << " " << _def << "\n"); ++m_stats.m_num_macros; } @@ -368,26 +374,22 @@ void eliminate_predicates::try_find_macro(clause& cl) { // (= (f x) t) if (cl.is_unit() && !cl.sign(0) && m.is_eq(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), y, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), y, cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), x, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), x, cl); return; } } // not (= (p x) t) -> (p x) = (not t) if (cl.is_unit() && cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), m.mk_not(y), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), m.mk_not(y), cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), m.mk_not(x), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), m.mk_not(x), cl); return; } } @@ -414,8 +416,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { m_fmls.model_trail().hide(fn); // hide definition of fn k = m.mk_app(fn, f->get_num_args(), f->get_args()); def = m.mk_ite(cond, t, k); - insert_macro(f, def, cl.m_dep); - cl.m_alive = false; + insert_macro(f, def, cl); fml = m.mk_not(m.mk_eq(k, t)); clause* new_cl = init_clause(fml, cl.m_dep, UINT_MAX); add_use_list(*new_cl); @@ -532,8 +533,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { expr_ref y1 = subtract(y, to_app(x), i); if (inv) y1 = uminus(y1); - insert_macro(to_app(arg), y1, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(arg), y1, cl); return true; } next: @@ -737,6 +737,7 @@ void eliminate_predicates::update_model(func_decl* p) { } rewrite(def); + TRACE("elim_predicates", tout << "insert " << p->get_name() << " " << def << "\n"); m_fmls.model_trail().push(p, def, dep, deleted); } diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index af0ede119..013c7cf65 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -111,9 +111,10 @@ private: bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); void try_resolve_definition(func_decl* p); void insert_macro(app* head, expr* def, expr_dependency* dep); + void insert_macro(app* head, expr* def, clause& cl); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); - void insert_quasi_macro(app* head, expr* body, clause const& cl); + void insert_quasi_macro(app* head, expr* body, clause& cl); bool can_be_quasi_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_find_macro(clause& cl); diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index a23fc9800..222247c07 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -81,7 +81,7 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { } func_interp* model_core::update_func_interp(func_decl* d, func_interp* fi) { - TRACE("model", tout << "register " << d->get_name() << "\n";); + TRACE("model_verbose", tout << "register " << d->get_name() << "\n";); SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m); diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index 0fd021d70..b117ac1e3 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -67,7 +67,7 @@ namespace euf { m_qmodel = mdl; } - void solver::update_model(model_ref& mdl) { + void solver::update_model(model_ref& mdl, bool validate) { TRACE("model", tout << "create model\n";); if (m_qmodel) { mdl = m_qmodel; @@ -87,7 +87,8 @@ namespace euf { for (auto* mb : m_solvers) mb->finalize_model(*mdl); TRACE("model", tout << "created model " << *mdl << "\n";); - validate_model(*mdl); + if (validate) + validate_model(*mdl); } bool solver::include_func_interp(func_decl* f) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8efb433ff..f4e9c2c64 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -478,8 +478,13 @@ namespace euf { m_ackerman->cg_conflict_eh(a, b); switch (s().value(lit)) { case l_true: - if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) - m_egraph.merge(n, ante, to_ptr(lit)); + if (!n->merge_tf()) + break; + if (m.is_value(n->get_root()->get_expr())) + break; + if (!ante) + ante = mk_true(); + m_egraph.merge(n, ante, to_ptr(lit)); break; case l_undef: case l_false: diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index e51b68b09..1a2cdf123 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -495,7 +495,7 @@ namespace euf { // model construction void save_model(model_ref& mdl); - void update_model(model_ref& mdl); + void update_model(model_ref& mdl, bool validate); obj_map const& values2root(); void model_updated(model_ref& mdl); expr* node2value(enode* n) const; diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 21842ec76..c66f1b3a2 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -635,7 +635,7 @@ namespace q { if (m_model) return; m_model = alloc(model, m); - ctx.update_model(m_model); + ctx.update_model(m_model, false); } void mbqi::init_solver() { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 865c5f15d..c107a74c5 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -987,7 +987,7 @@ struct goal2sat::imp : public sat::sat_internalizer { void update_model(model_ref& mdl) { auto* ext = dynamic_cast(m_solver.get_extension()); if (ext) - ext->update_model(mdl); + ext->update_model(mdl, true); } void user_push() { From f7c9c9ef72fde0e00ef9409e6c24456effb8b930 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Mon, 6 Mar 2023 22:28:22 -0500 Subject: [PATCH 085/220] fix unsound slice criteria (#6625) * rename for readability * bug fix #6617. Don't slice op args that are values --- src/muz/transforms/dl_mk_slice.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index 25888cb68..c13509224 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -467,15 +467,15 @@ namespace datalog { void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) { expr_ref_vector conjs = get_tail_conjs(r); for (expr * e : conjs) { - expr_ref r(m); + expr_ref rhs(m); unsigned v = 0; - if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) { + if (is_eq(e, v, rhs) && is_output(v) && m_var_is_sliceable[v]) { TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";); add_var(v); if (!m_solved_vars[v].get()) { TRACE("dl", tout << v << " is solved\n";); - add_free_vars(parameter_vars, r); - m_solved_vars[v] = r; + add_free_vars(parameter_vars, rhs); + m_solved_vars[v] = rhs; } else { TRACE("dl", tout << v << " is used\n";); @@ -666,10 +666,8 @@ namespace datalog { } else { SASSERT(m.is_value(arg)); - if (!is_output) { - TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";); - bv.unset(i); - } + TRACE("dl", tout << i << " in " << p->get_decl()->get_name() << " is a value, unable to slice\n";); + bv.unset(i); } } } From ea16f6608cafada28378e2707a3ee40a6f2f5f41 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 11:30:59 -0800 Subject: [PATCH 086/220] before rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/CMakeLists.txt | 3 - src/math/lp/indexed_value.h | 11 - src/math/lp/lar_core_solver.h | 30 +- src/math/lp/lp_dual_simplex.cpp | 24 - src/math/lp/lp_dual_simplex.h | 93 --- src/math/lp/lp_dual_simplex_def.h | 376 ----------- src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/lp_primal_simplex.cpp | 35 - src/math/lp/lp_primal_simplex.h | 106 --- src/math/lp/lp_primal_simplex_def.h | 367 ----------- src/math/lp/lp_solver.cpp | 55 -- src/math/lp/lp_solver.h | 260 -------- src/math/lp/lp_solver_def.h | 571 ---------------- src/math/lp/mps_reader.h | 891 ------------------------- src/math/lp/static_matrix.cpp | 1 - src/sat/smt/arith_sls.h | 3 - src/sat/smt/arith_solver.h | 4 +- src/shell/CMakeLists.txt | 1 - src/shell/lp_frontend.cpp | 109 --- src/shell/lp_frontend.h | 7 - src/shell/main.cpp | 10 +- src/smt/theory_lra.cpp | 3 - src/test/lp/lp.cpp | 982 ++-------------------------- src/test/lp/smt_reader.h | 4 - src/test/lp/test_file_reader.h | 1 - 25 files changed, 70 insertions(+), 3878 deletions(-) delete mode 100644 src/math/lp/lp_dual_simplex.cpp delete mode 100644 src/math/lp/lp_dual_simplex.h delete mode 100644 src/math/lp/lp_dual_simplex_def.h delete mode 100644 src/math/lp/lp_primal_simplex.cpp delete mode 100644 src/math/lp/lp_primal_simplex.h delete mode 100644 src/math/lp/lp_primal_simplex_def.h delete mode 100644 src/math/lp/lp_solver.cpp delete mode 100644 src/math/lp/lp_solver.h delete mode 100644 src/math/lp/lp_solver_def.h delete mode 100644 src/math/lp/mps_reader.h delete mode 100644 src/shell/lp_frontend.cpp delete mode 100644 src/shell/lp_frontend.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 9f0fae6bc..5719de44f 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,11 +20,8 @@ z3_add_component(lp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp - lp_dual_simplex.cpp lp_primal_core_solver.cpp - lp_primal_simplex.cpp lp_settings.cpp - lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index c48376470..92d8f2adf 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,15 +43,4 @@ public: m_value = val; } }; -#ifdef Z3DEBUG -template -bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - const X & v = w[i]; - if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) - return false; - } - return true; -} -#endif } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 8a6c64ef0..de8fe68ad 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -651,35 +651,7 @@ public: } } - void scale_problem_for_doubles( - static_matrix& A, - vector & lower_bounds, - vector & upper_bounds) { - vector column_scale_vector; - vector right_side_vector(A.column_count()); - settings().reps_in_scaler = 5; - scaler scaler(right_side_vector, - A, - settings().scaling_minimum, - settings().scaling_maximum, - column_scale_vector, - settings()); - if (! scaler.scale()) { - // the scale did not succeed, unscaling - A.clear(); - create_double_matrix(A); - } else { - for (unsigned j = 0; j < A.column_count(); j++) { - if (m_r_solver.column_has_upper_bound(j)) { - upper_bounds[j] /= column_scale_vector[j]; - } - if (m_r_solver.column_has_lower_bound(j)) { - lower_bounds[j] /= column_scale_vector[j]; - } - } - } - - } + // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp deleted file mode 100644 index aaf612f56..000000000 --- a/src/math/lp/lp_dual_simplex.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_dual_simplex_def.h" -template lp::mpq lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); -template double lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h deleted file mode 100644 index 75ef87492..000000000 --- a/src/math/lp/lp_dual_simplex.h +++ /dev/null @@ -1,93 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_dual_core_solver.h" -namespace lp { - -template -class lp_dual_simplex: public lp_solver { - lp_dual_core_solver * m_core_solver; - vector m_b_copy; - vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver - vector m_column_types_of_core_solver; - vector m_column_types_of_logicals; - vector m_can_enter_basis; -public: - ~lp_dual_simplex() override { - delete m_core_solver; - } - - lp_dual_simplex() : m_core_solver(nullptr) {} - - - void decide_on_status_after_stage1(); - - void fix_logical_for_stage2(unsigned j); - - void fix_structural_for_stage2(unsigned j); - - void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - - void restore_right_sides(); - - void solve_for_stage2(); - - void fill_x_with_zeros(); - - void stage1(); - - void stage2(); - - void fill_first_stage_solver_fields(); - - column_type get_column_type(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); - - void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); - - void set_type_for_logical(unsigned j, column_type col_type) { - this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; - } - - void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial); - - void augment_matrix_A_and_fill_x_and_allocate_some_fields(); - - - - void copy_m_b_aside_and_set_it_to_zeros(); - - void find_maximal_solution() override; - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; -}; -} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h deleted file mode 100644 index 8af9d87c1..000000000 --- a/src/math/lp/lp_dual_simplex_def.h +++ /dev/null @@ -1,376 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "math/lp/lp_dual_simplex.h" -namespace lp{ - -template void lp_dual_simplex::decide_on_status_after_stage1() { - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = lp_status::FEASIBLE; - } else { - this->m_status = lp_status::UNBOUNDED; - } - break; - case lp_status::DUAL_UNBOUNDED: - lp_unreachable(); - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - lp_assert(j >= this->number_of_core_structurals()); - switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::fixed; - m_can_enter_basis[j] = false; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - switch (ci->get_column_type()) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - case column_type::upper_bound: - lp_unreachable(); - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::boxed; - m_can_enter_basis[j] = true; - break; - case column_type::free_column: - m_can_enter_basis[j] = true; - m_column_types_of_core_solver[j] = column_type::free_column; - break; - default: - lp_unreachable(); - } - // T cost_was = this->m_costs[j]; - this->set_scaled_cost(j); -} - -template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { - fix_logical_for_stage2(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fix_structural_for_stage2(j); - } -} - -template void lp_dual_simplex::restore_right_sides() { - unsigned i = this->m_A->row_count(); - while (i--) { - this->m_b[i] = m_b_copy[i]; - } -} - -template void lp_dual_simplex::solve_for_stage2() { - m_core_solver->restore_non_basis(); - m_core_solver->solve_yB(m_core_solver->m_y); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(lp_status::FEASIBLE); - m_core_solver->solve(); - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - this->m_status = lp_status::OPTIMAL; - break; - case lp_status::DUAL_UNBOUNDED: - this->m_status = lp_status::INFEASIBLE; - break; - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } - this->m_second_stage_iterations = m_core_solver->total_iterations(); - this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); -} - -template void lp_dual_simplex::fill_x_with_zeros() { - unsigned j = this->m_A->column_count(); - while (j--) { - this->m_x[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::stage1() { - lp_assert(m_core_solver == nullptr); - this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - m_core_solver = new lp_dual_core_solver( - *this->m_A, - m_can_enter_basis, - this->m_b, // the right side vector - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types_of_core_solver, - this->m_lower_bounds, - this->m_upper_bounds, - this->m_settings, - *this); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - // skipping stage 1 - m_core_solver->set_status(lp_status::OPTIMAL); - m_core_solver->set_total_iterations(0); - } else { - m_core_solver->solve(); - } - decide_on_status_after_stage1(); - this->m_first_stage_iterations = m_core_solver->total_iterations(); -} - -template void lp_dual_simplex::stage2() { - unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - restore_right_sides(); - solve_for_stage2(); -} - -template void lp_dual_simplex::fill_first_stage_solver_fields() { - unsigned slack_var = this->number_of_core_structurals(); - unsigned artificial = this->number_of_core_structurals() + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); - } - fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); -} - -template column_type lp_dual_simplex::get_column_type(unsigned j) { - lp_assert(j < this->m_A->column_count()); - if (j >= this->number_of_core_structurals()) { - return m_column_types_of_logicals[j - this->number_of_core_structurals()]; - } - return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { - // see 4.7 in the dissertation of Achim Koberstein - lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != - this->m_core_solver_columns_to_external_columns.end()); - - T free_bound = T(1e4); // see 4.8 - unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = this->m_map_from_var_index_to_column_info[jj]; - switch (ci->get_column_type()) { - case column_type::upper_bound: { - std::stringstream s; - s << "unexpected bound type " << j << " " - << column_type_to_string(get_column_type(j)); - throw_exception(s.str()); - break; - } - case column_type::lower_bound: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - break; - } - case column_type::free_column: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; - break; - } - case column_type::boxed: - m_can_enter_basis[j] = false; - this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? - break; - default: - lp_unreachable(); - } - m_column_types_of_core_solver[j] = column_type::boxed; -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { - this->m_costs[j] = 0; - lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { - m_column_types_of_core_solver[j] = column_type::boxed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = column_type::fixed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { // go over logicals here - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); - } -} - -template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - switch (constraint.m_relation) { - case Equal: // no slack variable here - set_type_for_logical(artificial, column_type::fixed); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - (*this->m_A)(row, artificial) = numeric_traits::one(); - artificial++; - break; - - case Greater_or_equal: - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put a slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = numeric_traits::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - } -} - -template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { - this->count_slacks_and_artificials(); - this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); - unsigned n = this->m_A->column_count(); - this->m_column_types_of_core_solver.resize(n); - m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); - this->m_costs.resize(n); - this->m_upper_bounds.resize(n); - this->m_lower_bounds.resize(n); - m_can_enter_basis.resize(n); - this->m_basis.resize(this->m_A->row_count()); -} - - - -template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { - for (unsigned i = 0; i < this->m_b.size(); i++) { - m_b_copy.push_back(this->m_b[i]); - this->m_b[i] = numeric_traits::zero(); // preparing for the first stage - } -} - -template void lp_dual_simplex::find_maximal_solution(){ - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->flip_costs(); // do it for now, todo ( remove the flipping) - - this->cleanup(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->fill_matrix_A_and_init_right_side(); - this->fill_m_b(); - this->scale(); - augment_matrix_A_and_fill_x_and_allocate_some_fields(); - fill_first_stage_solver_fields(); - copy_m_b_aside_and_set_it_to_zeros(); - stage1(); - if (this->m_status == lp_status::FEASIBLE) { - stage2(); - } -} - - -template T lp_dual_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return -ret; // we flip costs for now -} -} diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 4b6163df8..a60395ab0 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -30,7 +30,6 @@ Revision History: #include #include #include "math/lp/lu.h" -#include "math/lp/lp_solver.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp deleted file mode 100644 index 634f52900..000000000 --- a/src/math/lp/lp_primal_simplex.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_primal_simplex_def.h" -template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template double lp::lp_primal_simplex::get_current_cost() const; -template double lp::lp_primal_simplex::get_column_value(unsigned int) const; -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::mpq lp::lp_primal_simplex::get_current_cost() const; -template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; -template void lp::lp_primal_simplex::find_maximal_solution(); -template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h deleted file mode 100644 index 77e12d088..000000000 --- a/src/math/lp/lp_primal_simplex.h +++ /dev/null @@ -1,106 +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_utils.h" -#include "math/lp/column_info.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/lp_solver.h" -namespace lp { -template -class lp_primal_simplex: public lp_solver { - lp_primal_core_solver * m_core_solver; - vector m_lower_bounds; -private: - unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } - - void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); - - void init_buffer(unsigned k, vector & r); - - void refactor(); - - void set_scaled_costs(); -public: - lp_primal_simplex(): m_core_solver(nullptr) {} - - column_info * get_or_create_column_info(unsigned column); - - void set_status(lp_status status) { - this->m_status = status; - } - - lp_status get_status() { - return this->m_status; - } - - void fill_acceptable_values_for_x(); - - - void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); - - void fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial); - - - - - void set_core_solver_bounds(); - - void find_maximal_solution() override; - - void fill_A_x_and_basis_for_stage_one_total_inf(); - - void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); - - void solve_with_total_inf(); - - - ~lp_primal_simplex() override; - - bool bounds_hold(std::unordered_map const & solution); - - T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraints_hold(std::unordered_map const & solution); - - - T * get_array_from_map(std::unordered_map const & solution); - - bool solution_is_feasible(std::unordered_map const & solution) { - return bounds_hold(solution) && row_constraints_hold(solution); - } - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; - - -}; -} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h deleted file mode 100644 index 7ffe819b2..000000000 --- a/src/math/lp/lp_primal_simplex_def.h +++ /dev/null @@ -1,367 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "util/vector.h" -#include "math/lp/lp_primal_simplex.h" - -namespace lp { -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { - unsigned slack_var = original_number_of_columns; - unsigned artificial = original_number_of_columns + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); - } -} - -template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { - for (unsigned i = 0; i < k; i++) { - r[i] = 0; - } - r[k] = 1; - for (unsigned i = this->row_count() -1; i > k; i--) { - r[i] = 0; - } -} - -template void lp_primal_simplex::refactor() { - m_core_solver->init_lu(); - if (m_core_solver->factorization()->get_status() != LU_status::OK) { - throw_exception("cannot refactor"); - } -} - -template void lp_primal_simplex::set_scaled_costs() { - unsigned j = this->number_of_core_structurals(); - while (j-- > 0) { - this->set_scaled_cost(j); - } -} - -template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { - auto it = this->m_columns.find(column); - return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; -} - -template void lp_primal_simplex::fill_acceptable_values_for_x() { - for (auto t : this->m_core_solver_columns_to_external_columns) { - this->m_x[t.first] = numeric_traits::zero(); - } -} - - -template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { - bound_is_set[i] = true; - bounds[i] = numeric_traits::zero(); -} - -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row >= 0 && row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - T artificial_cost = - numeric_traits::one(); - switch (constraint.m_relation) { - case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::lower_bound; - this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero - this->m_basis[row] = artificial; - if (rs >= 0) { - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_x[artificial] = - rs; - } - artificial++; - break; - - case Greater_or_equal: - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - - if (rs > 0) { - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_basis[row] = artificial; - this->m_x[artificial] = rs; - artificial++; - } else { - // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = - rs; - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = numeric_traits::one(); - - if (rs < 0) { - // adding one artificial - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_x[artificial] = - rs; - this->m_basis[row] = artificial++; - } else { - // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = rs; - } - slack_var++; - break; - } -} - - - - - -template void lp_primal_simplex::set_core_solver_bounds() { - unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; - this->m_column_types.resize(total_vars); - this->m_upper_bounds.resize(total_vars); - for (auto cit : this->m_map_from_var_index_to_column_info) { - column_info * ci = cit.second; - unsigned j = ci->get_column_index(); - if (!is_valid(j)) - continue; // the variable is not mapped to a column - switch (this->m_column_types[j] = ci->get_column_type()){ - case column_type::fixed: - this->m_upper_bounds[j] = numeric_traits::zero(); - break; - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - break; - - default: break; // do nothing - } - } -} - - -template void lp_primal_simplex::find_maximal_solution() { - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->cleanup(); - this->fill_matrix_A_and_init_right_side(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->m_x.resize(this->m_A->column_count()); - this->fill_m_b(); - this->scale(); - fill_acceptable_values_for_x(); - this->count_slacks_and_artificials(); - set_core_solver_bounds(); - solve_with_total_inf(); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { - for (unsigned row = 0; row < this->row_count(); row++) - fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - lp_assert(row < this->row_count()); - auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); - unsigned ext_row = ext_row_it->second; - auto constr_it = this->m_constraints.find(ext_row); - lp_assert(constr_it != this->m_constraints.end()); - auto & constraint = constr_it->second; - unsigned j = this->m_A->column_count(); // j is a slack variable - this->m_A->add_column(); - // we need to bring the program to the form Ax = b - this->m_basis[row] = j; - switch (constraint.m_relation) { - case Equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - default: - lp_unreachable(); - } -} - -template void lp_primal_simplex::solve_with_total_inf() { - int total_vars = this->m_A->column_count() + this->row_count(); - if (total_vars == 0) { - this->m_status = lp_status::OPTIMAL; - return; - } - m_lower_bounds.clear(); - m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - fill_A_x_and_basis_for_stage_one_total_inf(); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - set_scaled_costs(); - - m_core_solver = new lp_primal_core_solver(*this->m_A, - this->m_b, - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types, - m_lower_bounds, - this->m_upper_bounds, - this->m_settings, *this); - m_core_solver->solve(); - this->set_status(m_core_solver->get_status()); - this->m_total_iterations = m_core_solver->total_iterations(); -} - - -template lp_primal_simplex::~lp_primal_simplex() { - delete m_core_solver; -} - -template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { - for (auto it : this->m_map_from_var_index_to_column_info) { - auto sol_it = solution.find(it.second->get_name()); - if (sol_it == solution.end()) { - std::stringstream s; - s << "cannot find column " << it.first << " in solution"; - throw_exception(s.str() ); - } - - if (!it.second->bounds_hold(sol_it->second)) { - it.second->bounds_hold(sol_it->second); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { - auto it = this->m_A_values.find(i); - if (it == this->m_A_values.end()) { - std::stringstream s; - s << "cannot find row " << i; - throw_exception(s.str() ); - } - T ret = numeric_traits::zero(); - for (auto & pair : it->second) { - auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = cit->second; - auto sol_it = solution.find(ci->get_name()); - lp_assert(sol_it != solution.end()); - T column_val = sol_it->second; - if (out != nullptr) { - (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; - } - ret += pair.second * column_val; - } - if (out != nullptr) { - (*out) << " = " << ret << std::endl; - } - return ret; -} - -template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { - T row_val = get_row_value(i, solution, out); - auto & constraint = this->m_constraints[i]; - T rs = constraint.m_rs; - bool print = out != nullptr; - switch (constraint.m_relation) { - case Equal: - if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { - if (print) { - (*out) << "should be = " << rs << std::endl; - } - return false; - } - return true; - case Greater_or_equal: - if (numeric_traits::get_double(row_val - rs) < -0.00001) { - if (print) { - (*out) << "should be >= " << rs << std::endl; - } - return false; - } - return true;; - - case Less_or_equal: - if (numeric_traits::get_double(row_val - rs) > 0.00001) { - if (print) { - (*out) << "should be <= " << rs << std::endl; - } - return false; - } - return true;; - } - lp_unreachable(); - return false; // it is unreachable -} - -template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { - for (auto it : this->m_A_values) { - if (!row_constraint_holds(it.first, solution, nullptr)) { - row_constraint_holds(it.first, solution, nullptr); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; -} -} diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp deleted file mode 100644 index fc9514098..000000000 --- a/src/math/lp/lp_solver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "math/lp/lp_solver_def.h" -template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h deleted file mode 100644 index ab16a686f..000000000 --- a/src/math/lp/lp_solver.h +++ /dev/null @@ -1,260 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/column_info.h" -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include "math/lp/scaler.h" -#include "math/lp/bound_analyzer_on_row.h" -namespace lp { -enum lp_relation { - Less_or_equal, - Equal, - Greater_or_equal -}; - -template -struct lp_constraint { - X m_rs; // right side of the constraint - lp_relation m_relation; - lp_constraint() {} // empty constructor - lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} -}; - - -template -class lp_solver : public column_namer { - column_info * get_or_create_column_info(unsigned column); - -protected: - T get_column_cost_value(unsigned j, column_info * ci) const; -public: - unsigned m_total_iterations; - static_matrix* m_A; // this is the matrix of constraints - vector m_b; // the right side vector - unsigned m_first_stage_iterations; - unsigned m_second_stage_iterations; - std::unordered_map> m_constraints; - std::unordered_map*> m_map_from_var_index_to_column_info; - std::unordered_map > m_A_values; - std::unordered_map m_names_to_columns; // don't have to use it - std::unordered_map m_external_rows_to_core_solver_rows; - std::unordered_map m_core_solver_rows_to_external_rows; - std::unordered_map m_core_solver_columns_to_external_columns; - vector m_column_scale; - std::unordered_map m_name_map; - unsigned m_artificials; - unsigned m_slacks; - vector m_column_types; - vector m_costs; - vector m_x; - vector m_upper_bounds; - vector m_basis; - vector m_nbasis; - vector m_heading; - - - lp_status m_status; - - lp_settings m_settings; - lp_solver(): - m_A(nullptr), // this is the matrix of constraints - m_first_stage_iterations (0), - m_second_stage_iterations (0), - m_artificials (0), - m_slacks (0), - m_status(lp_status::UNKNOWN) - {} - - unsigned row_count() const { return this->m_A->row_count(); } - - void add_constraint(lp_relation relation, T right_side, unsigned row_index); - - void set_cost_for_column(unsigned column, T column_cost) { - get_or_create_column_info(column)->set_cost(column_cost); - } - std::string get_variable_name(unsigned j) const override; - - void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { - m_A_values[row][column] = val; - } - // returns the current cost - virtual T get_current_cost() const = 0; - // do not have to call it - void give_symbolic_name_to_column(std::string name, unsigned column); - - virtual T get_column_value(unsigned column) const = 0; - - T get_column_value_by_name(std::string name) const; - - // returns -1 if not found - virtual int get_column_index_by_name(std::string name) const; - - void set_lower_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_lower_bound(bound); - } - - void set_upper_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_upper_bound(bound); - } - - void unset_lower_bound(unsigned i) { - get_or_create_column_info(i)->unset_lower_bound(); - } - - void unset_upper_bound(unsigned i) { - get_or_create_column_info(i)->unset_upper_bound(); - } - - void set_fixed_value(unsigned i, T val) { - column_info *ci = get_or_create_column_info(i); - ci->set_fixed_value(val); - } - - void unset_fixed_value(unsigned i) { - get_or_create_column_info(i)->unset_fixed(); - } - - lp_status get_status() const { - return m_status; - } - - void set_status(lp_status st) { - m_status = st; - } - - - ~lp_solver() override; - - void flip_costs(); - - virtual void find_maximal_solution() = 0; - void set_time_limit(unsigned time_limit_in_seconds) { - m_settings.time_limit = time_limit_in_seconds; - } - - -protected: - bool problem_is_empty(); - - void scale(); - - - void print_rows_scale_stats(std::ostream & out); - - void print_columns_scale_stats(std::ostream & out); - - void print_row_scale_stats(unsigned i, std::ostream & out); - - void print_column_scale_stats(unsigned j, std::ostream & out); - - void print_scale_stats(std::ostream & out); - - void get_max_abs_in_row(std::unordered_map & row_map); - - void pin_vars_down_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, - numeric_traits::one()); - } - - void pin_vars_up_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, numeric_traits::one()); - } - - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - - bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - - bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); - - bool row_is_zero(std::unordered_map & row); - - bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); - - // analyse possible max and min values that are derived from var boundaries - // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. - // Then we know what values of the variables are. For each positive coeff of the row it has to be - // the low boundary of the var and for a negative - the upper. - - // this routing also pins the variables to the boundaries - bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); - - void remove_fixed_or_zero_columns(); - - void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); - - unsigned try_to_remove_some_rows(); - - void cleanup(); - - void map_external_rows_to_core_solver_rows(); - - void map_external_columns_to_core_solver_columns(); - - unsigned number_of_core_structurals() { - return static_cast(m_core_solver_columns_to_external_columns.size()); - } - - void restore_column_scales_to_one() { - for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); - } - - void unscale(); - - void fill_A_from_A_values(); - - void fill_matrix_A_and_init_right_side(); - - void count_slacks_and_artificials(); - - void count_slacks_and_artificials_for_row(unsigned i); - - T lower_bound_shift_for_row(unsigned i); - - void fill_m_b(); - - T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; - void set_scaled_cost(unsigned j); - void print_statistics_on_A(std::ostream & out) { - out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; - } - -public: - lp_settings & settings() { return m_settings;} - void print_model(std::ostream & s) const { - s << "objective = " << get_current_cost() << std::endl; - s << "column values\n"; - for (auto & it : m_names_to_columns) { - s << it.first << " = " << get_column_value(it.second) << std::endl; - } - } -}; -} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h deleted file mode 100644 index 191832a24..000000000 --- a/src/math/lp/lp_solver_def.h +++ /dev/null @@ -1,571 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_solver.h" -namespace lp { -template column_info * lp_solver::get_or_create_column_info(unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; -} - -template -std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index - if (!m_settings.print_external_var_name()) - return std::string("j")+T_to_string(j); - auto it = this->m_core_solver_columns_to_external_columns.find(j); - if (it == this->m_core_solver_columns_to_external_columns.end()) - return std::string("x")+T_to_string(j); - unsigned external_j = it->second; - auto t = this->m_map_from_var_index_to_column_info.find(external_j); - if (t == this->m_map_from_var_index_to_column_info.end()) { - return std::string("x") +T_to_string(external_j); - } - return t->second->get_name(); -} - -template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { - if (ci->is_fixed()) { - return ci->get_cost() * ci->get_fixed_value(); - } - return ci->get_cost() * get_column_value(j); -} -template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - lp_assert(m_constraints.find(row_index) == m_constraints.end()); - lp_constraint cs(right_side, relation); - m_constraints[row_index] = cs; -} - -template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - column_info *ci; - if (it == m_map_from_var_index_to_column_info.end()){ - m_map_from_var_index_to_column_info[column] = ci = new column_info; - } else { - ci = it->second; - } - ci->set_name(name); - m_names_to_columns[name] = column; -} - - -template T lp_solver::get_column_value_by_name(std::string name) const { - auto it = m_names_to_columns.find(name); - if (it == m_names_to_columns.end()) { - std::stringstream s; - s << "get_column_value_by_name " << name; - throw_exception(s.str()); - } - return get_column_value(it -> second); -} - -// returns -1 if not found -template int lp_solver::get_column_index_by_name(std::string name) const { - auto t = m_names_to_columns.find(name); - if (t == m_names_to_columns.end()) { - return -1; - } - return t->second; -} - - -template lp_solver::~lp_solver(){ - delete m_A; - for (auto t : m_map_from_var_index_to_column_info) { - delete t.second; - } -} - -template void lp_solver::flip_costs() { - for (auto t : m_map_from_var_index_to_column_info) { - column_info *ci = t.second; - ci->set_cost(-ci->get_cost()); - } -} - -template bool lp_solver::problem_is_empty() { - for (auto & c : m_A_values) - if (!c.second.empty()) - return false; - return true; -} - -template void lp_solver::scale() { - if (numeric_traits::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); - if (!scaler.scale()) { - unscale(); - } -} - - -template void lp_solver::print_rows_scale_stats(std::ostream & out) { - out << "rows max" << std::endl; - for (unsigned i = 0; i < m_A->row_count(); i++) { - print_row_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_columns_scale_stats(std::ostream & out) { - out << "columns max" << std::endl; - for (unsigned i = 0; i < m_A->column_count(); i++) { - print_column_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { - out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; - out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; -} - -template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { - out << "(" << m_A->get_min_abs_in_row(j) << " "; - out << m_A->get_max_abs_in_column(j) << ")"; -} - -template void lp_solver::print_scale_stats(std::ostream & out) { - print_rows_scale_stats(out); - print_columns_scale_stats(out); -} - -template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; -} - -template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_map_from_var_index_to_column_info[j]; - T a = t.second; - if (a * sign > numeric_traits::zero()) { - lp_assert(ci->upper_bound_is_set()); - ci->set_fixed_value(ci->get_upper_bound()); - } else { - lp_assert(ci->lower_bound_is_set()); - ci->set_fixed_value(ci->get_lower_bound()); - } - } -} - -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a > numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a < numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; -} - -template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (!is_zero(rs)) - m_status = lp_status::INFEASIBLE; - return true; - } - - T lower_bound; - bool lb = get_minimal_row_value(row, lower_bound); - if (lb) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_epsilon - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - T upper_bound; - bool ub = get_maximal_row_value(row, upper_bound); - if (ub) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - T upper_bound; - if (get_maximal_row_value(row, upper_bound)) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T lower_bound; - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs < zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - if (get_minimal_row_value(row, lower_bound)) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - return false; -} - -// analyse possible max and min values that are derived from var boundaries -// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. -// Then we know what values of the variables are. For each positive coeff of the row it has to be -// the low boundary of the var and for a negative - the upper. - -// this routing also pins the variables to the boundaries -template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { - auto & constraint = m_constraints[row_index]; - switch (constraint.m_relation) { - case lp_relation::Equal: - return row_e_is_obsolete(row, row_index); - - case lp_relation::Greater_or_equal: - return row_ge_is_obsolete(row, row_index); - - case lp_relation::Less_or_equal: - return row_le_is_obsolete(row, row_index); - } - lp_unreachable(); - return false; // it is unreachable -} - -template void lp_solver::remove_fixed_or_zero_columns() { - for (auto & i_row : m_A_values) { - remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); - } -} - -template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { - auto & constraint = m_constraints[i]; - vector removed; - for (auto & col : row) { - unsigned j = col.first; - lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); - column_info * ci = m_map_from_var_index_to_column_info[j]; - if (ci->is_fixed()) { - removed.push_back(j); - T aj = col.second; - constraint.m_rs -= aj * ci->get_fixed_value(); - } else { - if (numeric_traits::is_zero(col.second)){ - removed.push_back(j); - } - } - } - - for (auto j : removed) { - row.erase(j); - } -} - -template unsigned lp_solver::try_to_remove_some_rows() { - vector rows_to_delete; - for (auto & t : m_A_values) { - if (row_is_obsolete(t.second, t.first)) { - rows_to_delete.push_back(t.first); - } - - if (m_status == lp_status::INFEASIBLE) { - return 0; - } - } - if (!rows_to_delete.empty()) { - for (unsigned k : rows_to_delete) { - m_A_values.erase(k); - } - } - remove_fixed_or_zero_columns(); - return static_cast(rows_to_delete.size()); -} - -template void lp_solver::cleanup() { - int n = 0; // number of deleted rows - int d; - while ((d = try_to_remove_some_rows()) > 0) - n += d; - - if (n == 1) { - LP_OUT(m_settings, "deleted one row" << std::endl); - } else if (n) { - LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); - } -} - -template void lp_solver::map_external_rows_to_core_solver_rows() { - unsigned size = 0; - for (auto & row : m_A_values) { - m_external_rows_to_core_solver_rows[row.first] = size; - m_core_solver_rows_to_external_rows[size] = row.first; - size++; - } -} - -template void lp_solver::map_external_columns_to_core_solver_columns() { - unsigned size = 0; - for (auto & row : m_A_values) { - for (auto & col : row.second) { - if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { - throw_exception("found fixed column"); - } - unsigned j = col.first; - auto column_info_it = m_map_from_var_index_to_column_info.find(j); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - - auto j_column = column_info_it->second->get_column_index(); - if (!is_valid(j_column)) { // j is a newcomer - m_map_from_var_index_to_column_info[j]->set_column_index(size); - m_core_solver_columns_to_external_columns[size++] = j; - } - } - } -} - -template void lp_solver::unscale() { - delete m_A; - m_A = nullptr; - fill_A_from_A_values(); - restore_column_scales_to_one(); - fill_m_b(); -} - -template void lp_solver::fill_A_from_A_values() { - m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); - for (auto & t : m_A_values) { - auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); - unsigned row = row_it->second; - for (auto k : t.second) { - auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - column_info *ci = column_info_it->second; - unsigned col = ci->get_column_index(); - lp_assert(is_valid(col)); - bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); - if (!col_is_flipped) { - (*m_A)(row, col) = k.second; - } else { - (*m_A)(row, col) = - k.second; - } - } - } -} - -template void lp_solver::fill_matrix_A_and_init_right_side() { - map_external_rows_to_core_solver_rows(); - map_external_columns_to_core_solver_columns(); - lp_assert(m_A == nullptr); - fill_A_from_A_values(); - m_b.resize(m_A->row_count()); -} - -template void lp_solver::count_slacks_and_artificials() { - for (int i = row_count() - 1; i >= 0; i--) { - count_slacks_and_artificials_for_row(i); - } -} - -template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; - switch (constraint.m_relation) { - case Equal: - m_artificials++; - break; - case Greater_or_equal: - m_slacks++; - if (this->m_b[i] > 0) { - m_artificials++; - } - break; - case Less_or_equal: - m_slacks++; - if (this->m_b[i] < 0) { - m_artificials++; - } - break; - } -} - -template T lp_solver::lower_bound_shift_for_row(unsigned i) { - T ret = numeric_traits::zero(); - - auto row = this->m_A_values.find(i); - if (row == this->m_A_values.end()) { - throw_exception("cannot find row"); - } - for (auto col : row->second) { - ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); - } - return ret; -} - -template void lp_solver::fill_m_b() { - for (int i = this->row_count() - 1; i >= 0; i--) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; - auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); - } -} - -template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { - auto cit = this->m_map_from_var_index_to_column_info.find(column); - if (cit == this->m_map_from_var_index_to_column_info.end()) { - return numeric_traits::zero(); - } - - column_info * ci = cit->second; - - if (ci->is_fixed()) { - return ci->get_fixed_value(); - } - - unsigned cj = ci->get_column_index(); - if (cj != static_cast(-1)) { - T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; - if (ci->is_free()) { - return v; - } - if (!ci->is_flipped()) { - return v + ci->get_lower_bound(); - } - - // the flipped case when there is only upper bound - return -v + ci->get_upper_bound(); // - } - - return numeric_traits::zero(); // returns zero for out of boundary columns -} - -template void lp_solver::set_scaled_cost(unsigned j) { - // grab original costs but modify it with the column scales - lp_assert(j < this->m_column_scale.size()); - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - T cost = ci->get_cost(); - if (ci->is_flipped()){ - cost *= T(-1); - } - lp_assert(ci->is_fixed() == false); - this->m_costs[j] = cost * this->m_column_scale[j]; -} -} diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h deleted file mode 100644 index 8093954b1..000000000 --- a/src/math/lp/mps_reader.h +++ /dev/null @@ -1,891 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -// reads an MPS file representing a Mixed Integer Program -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" -#include "math/lp/lar_solver.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -namespace lp { -inline bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -inline size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -inline size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - - // trim from start -inline std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - -inline std::string trim(std::string const &r) { - std::string s = r; - return ltrim(rtrim(s)); -} - - -inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -inline vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - -template -class mps_reader { - enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; - struct bound { - T m_low; - T m_upper; - bool m_low_is_set; - bool m_upper_is_set; - bool m_value_is_fixed; - T m_fixed_value; - bool m_free; - // constructor - bound() : m_low(numeric_traits::zero()), - m_low_is_set(true), - m_upper_is_set(false), - m_value_is_fixed(false), - m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable - }; - - struct column { - std::string m_name; - bound * m_bound; - unsigned m_index; - column(const std::string &name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { - } - }; - - struct row { - row_type m_type; - std::string m_name; - std::unordered_map m_row_columns; - unsigned m_index; - T m_right_side; - T m_range; - row(row_type type, const std::string &name, unsigned index) : - m_type(type), - m_name(name), - m_index(index), - m_right_side(zero_of_type()), - m_range(zero_of_type()) - { - } - }; - - bool m_is_OK; - std::string m_file_name; - std::unordered_map m_rows; - std::unordered_map m_columns; - std::unordered_map m_names_to_var_index; - std::string m_line; - std::string m_name; - std::string m_cost_row_name; - std::ifstream m_file_stream; - // needed to adjust the index row - unsigned m_cost_line_count; - unsigned m_line_number; - std::ostream * m_message_stream; - - void set_m_ok_to_false() { - *m_message_stream << "setting m_is_OK to false" << std::endl; - m_is_OK = false; - } - - std::string get_string_from_position(unsigned offset) { - unsigned i = offset; - for (; i < m_line.size(); i++){ - if (m_line[i] == ' ') - break; - } - lp_assert(m_line.size() >= offset); - lp_assert(m_line.size() >> i); - lp_assert(i >= offset); - return m_line.substr(offset, i - offset); - } - - void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ - if (b == nullptr) { - solver->set_lower_bound(col, numeric_traits::zero()); - return; - } - - if (b->m_free) { - return; - } - if (b->m_low_is_set) { - solver->set_lower_bound(col, b->m_low); - } - if (b->m_upper_is_set) { - solver->set_upper_bound(col, b->m_upper); - } - - if (b->m_value_is_fixed) { - solver->set_fixed_value(col, b->m_fixed_value); - } - } - - bool all_white_space() { - for (unsigned i = 0; i < m_line.size(); i++) { - char c = m_line[i]; - if (c != ' ' && c != '\t') { - return false; - } - } - return true; - } - - void read_line() { - while (m_is_OK) { - if (!getline(m_file_stream, m_line)) { - m_line_number++; - set_m_ok_to_false(); - *m_message_stream << "cannot read from file" << std::endl; - } - m_line_number++; - if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) - break; - } - } - - void read_name() { - do { - read_line(); - if (m_line.find("NAME") != 0) { - continue; - } - m_line = m_line.substr(4); - m_name = trim(m_line); - break; - } while (m_is_OK); - } - - void read_rows() { - // look for start of the rows - read_line(); - do { - if (static_cast(m_line.find("ROWS")) >= 0) { - break; - } - } while (m_is_OK); - do { - read_line(); - if (m_line.find("COLUMNS") == 0) { - break; - } - add_row(); - } while (m_is_OK); - } - - void read_column_by_columns(const std::string & column_name, std::string column_data) { - // uph, let us try to work with columns - if (column_data.size() >= 22) { - std::string ss = column_data.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - *m_message_stream << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); - if (column_data.size() > 24) { - column_data = column_data.substr(25); - if (column_data.size() >= 22) { - read_column_by_columns(column_name, column_data); - } - } - } - } else { - fail: - set_m_ok_to_false(); - *m_message_stream << "cannot understand this line\n" - "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void read_column(const std::string & column_name, const std::string & column_data){ - auto tokens = split_and_trim(column_data); - for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { - auto row_name = tokens[i]; - if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here - auto t = m_rows.find(row_name); - if (t == m_rows.end()) { - read_column_by_columns(column_name, column_data); - return; - } - row *r = t->second; - r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); - } - } - - void read_columns(){ - std::string column_name; - do { - read_line(); - if (m_line.find("RHS") == 0) { - break; - } - if (m_line.size() < 22) { - (*m_message_stream) << "line is too short for a column" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string column_name_tmp = trim(m_line.substr(4, 8)); - if (!column_name_tmp.empty()) { - column_name = column_name_tmp; - } - auto col_it = m_columns.find(column_name); - mps_reader::column * col; - if (col_it == m_columns.end()) { - col = new mps_reader::column(column_name, static_cast(m_columns.size())); - m_columns[column_name] = col; - // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; - } else { - col = col_it->second; - } - read_column(column_name, m_line.substr(14)); - } while (m_is_OK); - } - - void read_rhs() { - do { - read_line(); - if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { - break; - } - fill_rhs(); - } while (m_is_OK); - } - - - void fill_rhs_by_columns(std::string rhsides) { - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - fill_rhs_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void fill_rhs() { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string rhsides = m_line.substr(14); - vector splitted_line = split_and_trim(rhsides); - - for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { - auto t = m_rows.find(splitted_line[i]); - if (t == m_rows.end()) { - fill_rhs_by_columns(rhsides); - return; - } - row * row = t->second; - row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); - } - } - - void read_bounds() { - if (m_line.find("BOUNDS") != 0) { - return; - } - - do { - read_line(); - if (m_line[0] != ' ') { - break; - } - create_or_update_bound(); - } while (m_is_OK); - } - - void read_ranges() { - if (m_line.find("RANGES") != 0) { - return; - } - do { - read_line(); - auto sl = split_and_trim(m_line); - if (sl.size() < 2) { - break; - } - read_range(sl); - } while (m_is_OK); - } - - - void read_bound_by_columns(const std::string & colstr) { - if (colstr.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (colstr.size() >= 22) { - std::string ss = colstr.substr(0, 8); - std::string column_name = trim(ss); - auto t = m_columns.find(column_name); - - if (t == m_columns.end()) { - (*m_message_stream) << "cannot find " << column_name << std::endl; - goto fail; - } else { - vector bound_string; - bound_string.push_back(column_name); - if (colstr.size() > 14) { - bound_string.push_back(colstr.substr(14)); - } - mps_reader::column * col = t->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void update_bound(bound * b, vector bound_string) { - /* - UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. - */ - - std::string bound_type = get_string_from_position(1); - if (bound_type == "BV") { - b->m_upper_is_set = true; - b->m_upper = 1; - return; - } - - if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - b->m_upper_is_set = true; - b->m_upper= numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "LO" || bound_type == "LI") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_low_is_set = true; - b->m_low = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "FR") { - b->m_free = true; - } else if (bound_type == "FX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_value_is_fixed = true; - b->m_fixed_value = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "PL") { - b->m_low_is_set = true; - b->m_low = 0; - } else if (bound_type == "MI") { - b->m_upper_is_set = true; - b->m_upper = 0; - } else { - (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - } - - void create_or_update_bound() { - const unsigned name_offset = 14; - lp_assert(m_line.size() >= 14); - vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - - if (bound_string.empty()) { - set_m_ok_to_false(); - (*m_message_stream) << "error at line " << m_line_number << std::endl; - throw m_line; - } - - std::string name = bound_string[0]; - auto it = m_columns.find(name); - if (it == m_columns.end()){ - read_bound_by_columns(m_line.substr(14)); - return; - } - mps_reader::column * col = it->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - - - - void read_range_by_columns(std::string rhsides) { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_range = numeric_traits::from_string(rhsides.substr(8)); - maybe_modify_current_row_and_add_row_for_range(row); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - read_range_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - - void read_range(vector & splitted_line){ - for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { - auto it = m_rows.find(splitted_line[i]); - if (it == m_rows.end()) { - read_range_by_columns(m_line.substr(14)); - return; - } - row * row = it->second; - row->m_range = numeric_traits::from_string(splitted_line[i + 1]); - maybe_modify_current_row_and_add_row_for_range(row); - } - } - - void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - std::string row_name = row_with_range->m_name + "_range"; - row * other_bound_range_row; - switch (row_with_range->m_type) { - case row_type::Greater_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); - break; - case row_type::Less_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); - break; - case row_type::Equal: - if (row_with_range->m_range > 0) { - row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - } else { // row->m_range < 0; - row_with_range->m_type = row_type::Less_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - } - other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; - break; - default: - (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - - for (auto s : row_with_range->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - other_bound_range_row->m_row_columns[s.first] = s.second; - } - } - - void add_row() { - if (m_line.length() < 2) { - return; - } - - m_line = trim(m_line); - char c = m_line[0]; - m_line = m_line.substr(1); - m_line = trim(m_line); - add_row(c); - } - - void add_row(char c) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - switch (c) { - case 'E': - m_rows[m_line] = new row(row_type::Equal, m_line, index); - break; - case 'L': - m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); - break; - case 'G': - m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); - break; - case 'N': - m_rows[m_line] = new row(row_type::Cost, m_line, index); - m_cost_row_name = m_line; - m_cost_line_count++; - break; - } - } - unsigned range_count() { - unsigned ret = 0; - for (auto s : m_rows) { - if (s.second->m_range != 0) { - ret++; - } - } - return ret; - } - - /* - If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: - sense interval - G [rhs, rhs + |range|] - L [rhs - |range|, rhs] - E [rhs, rhs + |range|] if range > 0, - [rhs - |range|, rhs] if range < 0 - where |range| is range's absolute value. - */ - - lp_relation get_relation_from_row(row_type rt) { - switch (rt) { - case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; - case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; - case mps_reader::Equal: return lp_relation::Equal; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned solver_row_count() { - return m_rows.size() - m_cost_line_count + range_count(); - } - - void fill_solver_on_row(row * row, lp_solver *solver) { - if (row->m_name != m_cost_row_name) { - solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); - for (auto s : row->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); - } - } else { - set_solver_cost(row, solver); - } - } - - T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } - - void fill_solver_on_rows(lp_solver * solver) { - for (auto row_it : m_rows) { - fill_solver_on_row(row_it.second, solver); - } - } - - - void fill_solver_on_columns(lp_solver * solver){ - for (auto s : m_columns) { - mps_reader::column * col = s.second; - unsigned index = col->m_index; - set_boundary_for_column(index, col->m_bound, solver); - // optional call - solver->give_symbolic_name_to_column(col->m_name, col->m_index); - } - } - - void fill_solver(lp_solver *solver) { - fill_solver_on_rows(solver); - fill_solver_on_columns(solver); - } - - void set_solver_cost(row * row, lp_solver *solver) { - for (auto s : row->m_row_columns) { - std::string name = s.first; - lp_assert(m_columns.find(name) != m_columns.end()); - mps_reader::column * col = m_columns[name]; - solver->set_cost_for_column(col->m_index, s.second); - } - } - -public: - - void set_message_stream(std::ostream * o) { - lp_assert(o != nullptr); - m_message_stream = o; - } - vector column_names() { - vector v; - for (auto s : m_columns) { - v.push_back(s.first); - } - return v; - } - - ~mps_reader() { - for (auto s : m_rows) { - delete s.second; - } - for (auto s : m_columns) { - auto col = s.second; - delete col->m_bound; - delete col; - } - } - - mps_reader(const std::string & file_name): - m_is_OK(true), - m_file_name(file_name), - m_file_stream(file_name), - m_cost_line_count(0), - m_line_number(0), - m_message_stream(& std::cout) {} - void read() { - if (!m_file_stream.is_open()){ - set_m_ok_to_false(); - return; - } - - read_name(); - read_rows(); - read_columns(); - read_rhs(); - if (m_line.find("BOUNDS") == 0) { - read_bounds(); - read_ranges(); - } else if (m_line.find("RANGES") == 0) { - read_ranges(); - read_bounds(); - } - } - - bool is_ok() { - return m_is_OK; - } - - lp_solver * create_solver(bool dual) { - lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); - fill_solver(solver); - return solver; - } - - lconstraint_kind get_lar_relation_from_row(row_type rt) { - switch (rt) { - case Less_or_equal: return LE; - case Greater_or_equal: return GE; - case Equal: return EQ; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned get_var_index(std::string s) { - auto it = m_names_to_var_index.find(s); - if (it != m_names_to_var_index.end()) - return it->second; - unsigned ret = static_cast(m_names_to_var_index.size()); - m_names_to_var_index[s] = ret; - return ret; - } - - void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { - if (row->m_name != m_cost_row_name) { - auto kind = get_lar_relation_from_row(row->m_type); - vector> ls; - for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first), false); - ls.push_back(std::make_pair(s.second, i)); - } - unsigned j = solver->add_term(ls, row_index); - solver->add_var_bound(j, kind, row->m_right_side); - } else { - // ignore the cost row - } - } - - - void fill_lar_solver_on_rows(lar_solver * solver) { - int row_index = 0; - for (auto row_it : m_rows) { - fill_lar_solver_on_row(row_it.second, solver, row_index++); - } - } - - void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, GE, b->m_low); - } - - void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_upper); - } - - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_fixed_value); - solver->add_var_bound(i, GE, b->m_fixed_value); - } - - void fill_lar_solver_on_columns(lar_solver * solver) { - for (auto s : m_columns) { - mps_reader::column * col = s.second; - solver->add_var(col->m_index, false); - auto b = col->m_bound; - if (b == nullptr) return; - - if (b->m_free) continue; - - if (b->m_low_is_set) { - create_low_constraint_for_var(col, b, solver); - } - if (b->m_upper_is_set) { - create_upper_constraint_for_var(col, b, solver); - } - if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); - } - } - } - - - void fill_lar_solver(lar_solver * solver) { - fill_lar_solver_on_columns(solver); - fill_lar_solver_on_rows(solver); - } - - lar_solver * create_lar_solver() { - lar_solver * solver = new lar_solver(); - fill_lar_solver(solver); - return solver; - } -}; -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 571e9b1d0..cde96277b 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_dual_core_solver.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h index af3a46234..09a56c84e 100644 --- a/src/sat/smt/arith_sls.h +++ b/src/sat/smt/arith_sls.h @@ -19,9 +19,6 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 9ae671c16..68d5f8025 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,9 +19,7 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" + #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index d9b74f162..c0e9c8505 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp - lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp deleted file mode 100644 index 8d6425533..000000000 --- a/src/shell/lp_frontend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Author: - - Lev Nachmanson 2016-10-27 - ---*/ - -#include "math/lp/lp_settings.h" -#include "math/lp/mps_reader.h" -#include "util/timeout.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "util/rlimit.h" -#include "util/gparams.h" -#include "util/mutex.h" -#include -#include -#include "smt/params/smt_params_helper.hpp" - -namespace { -static mutex *display_stats_mux = new mutex; - -static lp::lp_solver* g_solver = nullptr; - -static void display_statistics() { - lock_guard lock(*display_stats_mux); - if (g_solver && g_solver->settings().print_statistics) { - // TBD display relevant information about statistics - } -} - -static void STD_CALL on_ctrl_c(int) { - signal (SIGINT, SIG_DFL); - display_statistics(); - raise(SIGINT); -} - -static void on_timeout() { - display_statistics(); - _Exit(0); -} - -struct front_end_resource_limit : public lp::lp_resource_limit { - reslimit& m_reslim; - - front_end_resource_limit(reslimit& lim): - m_reslim(lim) - {} - - bool get_cancel_flag() override { return !m_reslim.inc(); } -}; - -void run_solver(smt_params_helper & params, char const * mps_file_name) { - - reslimit rlim; - unsigned timeout = gparams::get_ref().get_uint("timeout", 0); - unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); - front_end_resource_limit lp_limit(rlim); - - scoped_rlimit _rlimit(rlim, rlimit); - cancel_eh eh(rlim); - scoped_timer timer(timeout, &eh); - - std::string fn(mps_file_name); - lp::mps_reader reader(fn); - reader.set_message_stream(&std::cout); // can be redirected - reader.read(); - if (!reader.is_ok()) { - std::cerr << "cannot process " << mps_file_name << std::endl; - return; - } - lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver - solver->settings().set_resource_limit(lp_limit); - g_solver = solver; - if (params.arith_min()) { - solver->flip_costs(); - } - solver->settings().set_message_ostream(&std::cout); - solver->settings().report_frequency = params.arith_rep_freq(); - solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); - - solver->find_maximal_solution(); - - *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::lp_status::OPTIMAL) { - if (params.arith_min()) { - solver->flip_costs(); - } - solver->print_model(std::cout); - } - - display_statistics(); - g_solver = nullptr; - delete solver; -} -} - -unsigned read_mps_file(char const * mps_file_name) { - signal(SIGINT, on_ctrl_c); - register_on_timeout_proc(on_timeout); - smt_params_helper p; - param_descrs r; - p.collect_param_descrs(r); - run_solver(p, mps_file_name); - return 0; -} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h deleted file mode 100644 index b24be811f..000000000 --- a/src/shell/lp_frontend.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (c) 2013 Microsoft Corporation. All rights reserved. - - Author: Lev Nachmanson -*/ -#pragma once -unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 4c26d91d9..26325d3d0 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,14 +37,13 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" -#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -377,10 +376,6 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } - else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || - strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { - g_input_kind = IN_MPS; - } } } switch (g_input_kind) { @@ -406,9 +401,6 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; - case IN_MPS: - return_value = read_mps_file(g_input_file); - break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d61910ff2..2aa988282 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,9 +19,6 @@ --*/ #include "util/stopwatch.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c82cdd0a4..78abf1a6f 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,8 +33,6 @@ #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" @@ -59,6 +57,71 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" + +bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + +std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + + +vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + + namespace nla { void test_horner(); void test_monics(); @@ -1395,169 +1458,15 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { } } -template -void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { - if (time_limit > 0) - solver->set_time_limit(time_limit); - - if (look_for_min) - solver->flip_costs(); - - update_settings(args_parser, solver->settings()); -} bool values_are_one_percent_close(double a, double b); -void print_x(mps_reader & reader, lp_solver * solver) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; -} - -void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (const auto & name : reader.column_names()) { - double a = solver->get_column_value_by_name(name); - double b = solver0->get_column_value_by_name(name); - if (!values_are_one_percent_close(a, b)) { - std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; - } - } -} -void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return; - } - - lp_solver * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - if (dual) { - std::cout << "solving for dual" << std::endl; - } - solver->find_maximal_solution(); - sw.stop(); - double span = sw.get_seconds(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - print_x(reader, solver); - } - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost << std::endl; - } - std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; - if (compare_with_primal) { - auto * primal_solver = reader.create_solver(false); - setup_solver(time_limit, look_for_min, args_parser, primal_solver); - primal_solver->find_maximal_solution(); - if (solver->get_status() != primal_solver->get_status()) { - std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; - } else { - if (solver->get_status() == lp_status::OPTIMAL) { - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - double primal_cost = primal_solver->get_current_cost(); - if (look_for_min) { - primal_cost = -primal_cost; - } - std::cout << "primal cost = " << primal_cost << std::endl; - if (!values_are_one_percent_close(cost, primal_cost)) { - compare_solutions(reader, primal_solver, solver); - print_x(reader, primal_solver); - std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - lp_assert(false); - } - } - } - delete primal_solver; - } - delete solver; -} -void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - solver->find_maximal_solution(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - - if (solver->get_status() == lp_status::OPTIMAL) { - // for (auto name: reader.column_names()) { - // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - // } - lp::mpq cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost.get_double() << std::endl; - } - std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition -void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { - if (!solve_for_rational) { - std::cout << "solving " << file_name << std::endl; - solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); - } - else { - std::cout << "solving " << file_name << " in rationals " << std::endl; - solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); - } -} -void solve_mps(std::string file_name, argument_parser & args_parser) { - bool look_for_min = args_parser.option_is_used("--min"); - unsigned time_limit; - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); -} - -void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { - std::cout << "solving " << file_name << std::endl; - - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; - } - } - std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void test_upair_queue() { int n = 10; @@ -1626,53 +1535,6 @@ void test_binary_priority_queue() { std::cout << " done" << std::endl; } -bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); - return solver->solution_is_feasible(solution); - } - return false; -} - - -void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { - std::cout << "solving " << file_name << std::endl; - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (status != solver->get_status()){ - std::cout << "status should be " << lp_status_to_string(status) << std::endl; - lp_assert(status == solver->get_status()); - throw "status is wrong"; - } - if (solver->get_status() == lp_status::OPTIMAL) { - std::cout << "cost = " << solver->get_current_cost() << std::endl; - if (solution != nullptr) { - for (auto it : *solution) { - if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { - std::cout << "expected:" << it.first << "=" << - it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; - } - lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); - } - } - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; - } - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} int get_random_rows() { return 5 + my_random() % 2; @@ -1686,55 +1548,6 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -void add_random_row(lp_primal_simplex * solver, int cols, int row) { - solver->add_constraint(lp_relation::Greater_or_equal, 1, row); - for (int i = 0; i < cols; i++) { - solver->set_row_column_coefficient(row, i, get_random_int()); - } -} - -void add_random_cost(lp_primal_simplex * solver, int cols) { - for (int i = 0; i < cols; i++) { - solver->set_cost_for_column(i, get_random_int()); - } -} - -lp_primal_simplex * generate_random_solver() { - int rows = get_random_rows(); - int cols = get_random_columns(); - auto * solver = new lp_primal_simplex(); - for (int i = 0; i < rows; i++) { - add_random_row(solver, cols, i); - } - add_random_cost(solver, cols); - return solver; -} - - - -void random_test_on_i(unsigned i) { - if (i % 1000 == 0) { - std::cout << "."; - } - srand(i); - auto *solver = generate_random_solver(); - solver->find_maximal_solution(); - // std::cout << lp_status_to_string(solver->get_status()) << std::endl; - delete solver; -} - -void random_test() { - for (unsigned i = 0; i < std::numeric_limits::max(); i++) { - try { - random_test_on_i(i); - } - catch (const char * error) { - std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; - break; - } - } -} - #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); @@ -1896,140 +1709,9 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); -void solve_some_mps(argument_parser & args_parser) { - unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0; - unsigned failures = 0; - unsigned inconclusives = 0; - std::set minimums; - vector file_names; - fill_file_names(file_names, minimums); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); - if (compare_with_glpk) { - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - test_out_dir(out_dir); - for (auto& a : file_names) { - try { - std::string file_dir; - std::string file_name; - find_dir_and_file_name(a, file_dir, file_name); - process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - return; - } - if (!solve_for_rational) { - solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) - std::unordered_map sol; - sol["X1"] = 0; - sol["X2"] = 6; - sol["X3"] = 0; - sol["X4"] = 15; - sol["X5"] = 2; - sol["X6"] = 1; - sol["X7"] = 1; - sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); - sol.clear(); - sol["X1"] = 25.0/14.0; - // sol["X2"] = 0; - // sol["X3"] = 0; - // sol["X4"] = 0; - // sol["X5"] = 0; - // sol["X6"] = 0; - // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) - solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); - // return; - for (auto& s : file_names) { - try { - solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } else { - // unsigned i = 0; - for (auto& s : file_names) { - // if (i++ > 9) return; - try { - solve_mps_in_rational(s, dual, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } -} #endif -void solve_rational() { - lp_primal_simplex solver; - solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); - solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); - - // setting the cost - int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; - std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; - - for (unsigned i = 0; i < 8; i++) { - solver.set_cost_for_column(i, lp::mpq(cost[i])); - solver.give_symbolic_name_to_column(var_names[i], i); - } - - int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); - } - - int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); - } - - int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; - for (unsigned i = 0; i < 8; i++) { - solver.set_lower_bound(i, lp::mpq(0)); - solver.set_upper_bound(i, lp::mpq(bounds[i])); - } - - std::unordered_map expected_sol; - expected_sol["x1"] = lp::mpq(0); - expected_sol["x2"] = lp::mpq(6); - expected_sol["x3"] = lp::mpq(0); - expected_sol["x4"] = lp::mpq(15); - expected_sol["x5"] = lp::mpq(2); - expected_sol["x6"] = lp::mpq(1); - expected_sol["x7"] = lp::mpq(1); - expected_sol["x8"] = lp::mpq(0); - solver.find_maximal_solution(); - lp_assert(solver.get_status() == lp_status::OPTIMAL); -#ifdef Z3DEBUG - for (const auto & it : expected_sol) { - (void)it; - lp_assert(it.second == solver.get_column_value_by_name(it.first)); - } -#endif -} std::string read_line(bool & end, std::ifstream & file) { @@ -2047,49 +1729,6 @@ bool contains(std::string const & s, char const * pattern) { } -std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { - std::ifstream file(file_name); - if (!file.is_open()){ - std::cerr << "cannot open " << file_name << std::endl; - return nullptr; - } - std::string s; - bool end; - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - if (contains(s, "Column name")){ - break; - } - } while (true); - - read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - - auto ret = new std::unordered_map(); - - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - auto split = string_split(s, " \t", false); - if (split.empty()) { - return ret; - } - - lp_assert(split.size() > 3); - (*ret)[split[1]] = atof(split[3].c_str()); - } while (true); -} - void test_init_U() { @@ -2189,7 +1828,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); - parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); @@ -2360,237 +1998,9 @@ std::string get_status(std::string file_name) { throw 0; } -// returns true if the costs should be compared too -bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { - std::string glpk_status = get_status(glpk_out_file_name); - std::string lp_tst_status = get_status(lp_out_file_name); - - if (glpk_status != lp_tst_status) { - if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { - successes++; - return false; - } else { - std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; - std::cout << " but lp_tst status is " << lp_tst_status << std::endl; - failures++; - return false; - } - } - return lp_tst_status == "OPTIMAL"; -} - -double get_glpk_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - while (getline(f, str)) { - if (str.find("Objective") != std::string::npos) { - vector tokens = split_and_trim(str); - if (tokens.size() != 5) { - std::cout << "unexpected Objective std::string " << str << std::endl; - throw 0; - } - return atof(tokens[3].c_str()); - } - } - std::cout << "cannot find the Objective line in " << file_name << std::endl; - throw 0; -} - -double get_lp_tst_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - std::string cost_string; - while (getline(f, str)) { - if (str.find("cost") != std::string::npos) { - cost_string = str; - } - } - if (cost_string.empty()) { - std::cout << "cannot find the cost line in " << file_name << std::endl; - throw 0; - } - - vector tokens = split_and_trim(cost_string); - if (tokens.size() != 3) { - std::cout << "unexpected cost string " << cost_string << std::endl; - throw 0; - } - return atof(tokens[2].c_str()); -} - -bool values_are_one_percent_close(double a, double b) { - double maxval = std::max(fabs(a), fabs(b)); - if (maxval < 0.000001) { - return true; - } - - double one_percent = maxval / 100; - return fabs(a - b) <= one_percent; -} - -// returns true if both are optimal -void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, - unsigned & successes, - unsigned & failures) { - double a = get_glpk_cost(glpk_out_file_name); - double b = get_lp_tst_cost(lp_out_file_name); - - if (values_are_one_percent_close(a, b)) { - successes++; - } else { - failures++; - std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; - } -} -void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { -#ifdef CHECK_GLPK_SOLUTION - std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); - if (solution_is_feasible(lp_file_name, *solution_table)) { - std::cout << "glpk solution is feasible" << std::endl; - } else { - std::cout << "glpk solution is infeasible" << std::endl; - } - delete solution_table; -#endif - if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { - compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); - } -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser); - -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { - bool use_mpq = args_parser.option_is_used("--mpq"); - bool minimize = args_parser.option_is_used("--min"); - std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); - - std::string input_file_name = test_dir + "/" + test_file_name; - if (input_file_name[input_file_name.size() - 1] == '~') { - // std::cout << "ignoring " << input_file_name << std::endl; - return; - } - std::cout <<"processing " << input_file_name << std::endl; - - std::ofstream out(full_lp_tst_out_name); - if (!out.is_open()) { - std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; - throw 0; - } - std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer - std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! - bool dual = args_parser.option_is_used("--dual"); - try { - if (args_parser.option_is_used("--lar")) - test_lar_on_file(input_file_name, args_parser); - else - solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); - } - catch(...) { - std::cout << "catching the failure" << std::endl; - failures++; - std::cout.rdbuf(coutbuf); // reset to standard output again - return; - } - std::cout.rdbuf(coutbuf); // reset to standard output again - - if (args_parser.option_is_used("--compare_with_glpk")) { - std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); - int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); - if (glpk_exit_code != 0) { - std::cout << "glpk failed" << std::endl; - inconclusives++; - } else { - compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); - } - } -} -/* - int my_readdir(DIR *dirp, struct dirent * - #ifndef LEAN_WINDOWS - entry - #endif - , struct dirent **result) { - #ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; - #else - return readdir_r(dirp, entry, result); - #endif - } -*/ -/* - vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); - #ifndef LEAN_WINDOWS - result != nullptr && - #endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; - } -*/ -/* - struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; - - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } - }; - -*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2598,121 +2008,11 @@ struct sort_pred { }; -void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { - /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - */ -} -std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { - std::unordered_map ret; - for (const auto & it : reader.column_names()) { - ret[it] = lps->get_column_value_by_name(it); - } - return ret; -} -void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { - std::string maxng = args_parser.get_option_value("--maxng"); - if (!maxng.empty()) { - solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); - } - if (args_parser.option_is_used("-pd")){ - solver->settings().presolve_with_double_solver_for_lar = true; - } - - if (args_parser.option_is_used("--compare_with_primal")){ - if (reader == nullptr) { - std::cout << "cannot compare with primal, the reader is null " << std::endl; - return; - } - auto * lps = reader->create_solver(false); - lps->find_maximal_solution(); - std::unordered_map sol = get_solution_map(lps, *reader); - std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; - return; - } - stopwatch sw; - sw.start(); - lp_status status = solver->solve(); - std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == lp_status::INFEASIBLE) { - explanation evidence; - solver->get_infeasibility_explanation(evidence); - } - if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != lp_status::OPTIMAL) { - std::cout << "cannot check randomize on an infeazible problem" << std::endl; - return; - } - std::cout << "checking randomize" << std::endl; - vector all_vars; - for (unsigned j = 0; j < solver->number_of_vars(); j++) - all_vars.push_back(j); - - unsigned m = all_vars.size(); - if (m > 100) - m = 100; - - var_index *vars = new var_index[m]; - for (unsigned i = 0; i < m; i++) - vars[i]=all_vars[i]; - - solver->random_update(m, vars); - delete []vars; - } -} -lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { - if (args_parser.option_is_used("--smt")) { - smt_reader reader(file_name); - reader.read(); - if (!reader.is_ok()){ - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); - } - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser) { - lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); - mps_reader reader(file_name); - mps_reader * mps_reader = nullptr; - reader.read(); - if (reader.is_ok()) { - mps_reader = & reader; - run_lar_solver(args_parser, solver, mps_reader); - } - delete solver; -} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2733,23 +2033,6 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } -void test_lar_solver(argument_parser & args_parser) { - - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - test_lar_on_file(file_name, args_parser); - return; - } - - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - test_lar_on_file(fn, args_parser); - return; - } - - std::cout << "give option --file or --filelist to test_lar_solver\n"; -} void test_numeric_pair() { numeric_pair a; @@ -3934,22 +3217,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq")) { - test_rationals(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np")) { - test_rationals_no_numeric_pairs(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np_plus")) { - test_rationals_no_numeric_pairs_plus(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3967,29 +3234,8 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } -#ifdef Z3DEBUG - if (args_parser.option_is_used("--test_swaps")) { - square_sparse_matrix m(10, 0); - fill_matrix(m); - test_swap_rows_with_permutation(m); - test_swap_cols_with_permutation(m); - return finalize(0); - } -#endif - if (args_parser.option_is_used("--test_perm")) { - test_permutations(); - return finalize(0); - } - if (args_parser.option_is_used("--test_file_directory")) { - test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); - return finalize(0); - } - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - solve_mps(fn, args_parser); - return finalize(0); - } + + if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); @@ -3997,100 +3243,6 @@ void test_lp_local(int argn, char**argv) { return finalize(ret); } -#ifdef Z3DEBUG - lp_settings settings; - update_settings(args_parser, settings); - if (args_parser.option_is_used("--test_lu")) { - test_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_small_lu")) { - test_small_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--lar")){ - std::cout <<"calling test_lar_solver" << std::endl; - test_lar_solver(args_parser); - return finalize(0); - } - - - - if (args_parser.option_is_used("--test_larger_lu")) { - test_larger_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_larger_lu_with_holes")) { - test_larger_lu_with_holes(settings); - ret = 0; - return finalize(ret); - } -#endif - if (args_parser.option_is_used("--eti")) { - test_evidence_for_total_inf_simple(args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--maximize_term")) { - test_maximize_term(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_lp_0")) { - test_lp_0(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--smap")) { - test_stacked(); - ret = 0; - return finalize(ret); - } - if (args_parser.option_is_used("--term")) { - test_term(); - ret = 0; - return finalize(ret); - } - unsigned time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - bool dual = args_parser.option_is_used("--dual"); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--solve_some_mps")) { -#ifndef _WINDOWS - solve_some_mps(args_parser); -#endif - ret = 0; - return finalize(ret); - } - // lp::ccc = 0; - return finalize(0); - test_init_U(); - test_replace_column(); -#ifdef Z3DEBUG - square_sparse_matrix_with_permutations_test(); - test_dense_matrix(); - test_swap_operations(); - test_permutations(); - test_pivot_like_swaps_and_pivot(); -#endif - tst1(); - std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 2ab0c1ea6..75edb23b9 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,18 +20,14 @@ Revision History: #pragma once -// reads an MPS file representing a Mixed Integer Program #include #include #include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include #include #include -#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 8f461ea1c..36b273740 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,7 +27,6 @@ Revision History: #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" namespace lp { From 97c1ba4641138b3990cb93a8aa10c3a1ea6d3f4e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 11:56:23 -0800 Subject: [PATCH 087/220] rm get_column_in_lu_mode --- src/math/lp/lar_solver.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index f13231610..20556deab 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -267,13 +267,7 @@ class lar_solver : public column_namer { void shrink_explanation_to_minimum(vector> & explanation) const; inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; - inline - indexed_vector & get_column_in_lu_mode(unsigned j) { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - return m_column_buffer; - } + bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } void catch_up_in_updating_int_solver(); From a38be432642598b2afcaccb050d0f29652d1ea4e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 12:15:48 -0800 Subject: [PATCH 088/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 2 +- src/math/lp/lar_solver.cpp | 19 ++++---------- src/math/lp/lp_core_solver_base_def.h | 20 ++------------- src/math/lp/lp_settings.h | 8 +++--- src/test/lp/lp.cpp | 36 --------------------------- 5 files changed, 11 insertions(+), 74 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index de8fe68ad..b6c975139 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -229,7 +229,7 @@ public: } bool need_to_presolve_with_double_solver() const { - return settings().simplex_strategy() == simplex_strategy_enum::lu; + return false; } template diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 86ecb02b9..c2a332ad3 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -45,7 +45,7 @@ namespace lp { delete t; } - bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + bool lar_solver::use_lu() const { return false; } bool lar_solver::sizes_are_correct() const { lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); @@ -478,10 +478,7 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; - case simplex_strategy_enum::lu: - lp_assert(false); // not implemented - return false; - + default: lp_unreachable(); // wrong mode } @@ -1999,20 +1996,14 @@ namespace lp { void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); - if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { - m_settings.set_simplex_strategy(simplex_strategy_enum::lu); - } - else { - m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? - } + + m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? + adjust_initial_state(); } void lar_solver::adjust_initial_state() { switch (m_settings.simplex_strategy()) { - case simplex_strategy_enum::lu: - adjust_initial_state_for_lu(); - break; case simplex_strategy_enum::tableau_rows: adjust_initial_state_for_tableau_rows(); break; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 4d29234a8..40e0a527d 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -894,27 +894,11 @@ template bool lp_core_solver_base::pivot_column_g lp_assert(m_basis_heading[j] < 0); lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; - if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { - if (m_factorization->need_to_refactor()) { - init_lu(); - } - else { - m_factorization->prepare_entering(j, w); // to init vector w - m_factorization->replace_column(zero_of_type(), w, row_index); - } - if (m_factorization->get_status() != LU_status::OK) { - init_lu(); - return false; - } - else { - change_basis(j, j_basic); - } - } - else { // the tableau case + // the tableau case if (pivot_column_tableau(j, row_index)) change_basis(j, j_basic); else return false; - } + return true; } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 2245f6f4e..790354e55 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -55,8 +55,7 @@ inline std::ostream& operator<<(std::ostream& out, column_type const& t) { enum class simplex_strategy_enum { undecided = 3, tableau_rows = 0, - tableau_costs = 1, - lu = 2 + tableau_costs = 1 }; std::string column_type_to_string(column_type t); @@ -341,12 +340,11 @@ public: } bool use_lu() const { - return m_simplex_strategy == simplex_strategy_enum::lu; + return false; } bool use_tableau() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows || - m_simplex_strategy == simplex_strategy_enum::tableau_costs; + return true; } bool use_tableau_rows() const { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 78abf1a6f..fe589835d 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1422,42 +1422,6 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par } -void update_settings(argument_parser & args_parser, lp_settings& settings) { - unsigned n; - settings.m_simplex_strategy = simplex_strategy_enum::lu; - if (get_int_from_args_parser("--rep_frq", args_parser, n)) - settings.report_frequency = n; - else - settings.report_frequency = args_parser.option_is_used("--mpq")? 80: 1000; - - settings.print_statistics = true; - - if (get_int_from_args_parser("--percent_for_enter", args_parser, n)) - settings.percent_of_entering_to_check = n; - if (get_int_from_args_parser("--partial_pivot", args_parser, n)) { - std::cout << "setting partial pivot constant to " << n << std::endl; - settings.c_partial_pivoting = n; - } - if (get_int_from_args_parser("--density", args_parser, n)) { - double density = static_cast(n) / 100.0; - std::cout << "setting density to " << density << std::endl; - settings.density_threshold = density; - } - if (get_int_from_args_parser("--maxng", args_parser, n)) - settings.max_number_of_iterations_with_no_improvements = n; - double d; - if (get_double_from_args_parser("--harris_toler", args_parser, d)) { - std::cout << "setting harris_feasibility_tolerance to " << d << std::endl; - settings.harris_feasibility_tolerance = d; - } - if (get_int_from_args_parser("--random_seed", args_parser, n)) { - settings.set_random_seed(n); - } - if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { - settings.set_simplex_strategy(static_cast(n)); - } -} - bool values_are_one_percent_close(double a, double b); From 527f0d124280c59641177087b9c5dd58d9af4c9d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 12:34:15 -0800 Subject: [PATCH 089/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 13 +++---------- src/math/lp/lar_solver.h | 1 - src/math/lp/lp_core_solver_base_def.h | 3 +-- src/math/lp/lp_settings.h | 5 +---- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index c2a332ad3..3339b55a5 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -45,8 +45,7 @@ namespace lp { delete t; } - bool lar_solver::use_lu() const { return false; } - + bool lar_solver::sizes_are_correct() const { lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); @@ -1622,8 +1621,7 @@ namespace lp { m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); add_new_var_to_core_fields_for_mpq(false); // false for not adding a row - if (use_lu()) - add_new_var_to_core_fields_for_doubles(false); + } void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { @@ -1785,8 +1783,6 @@ namespace lp { fill_last_row_of_A_r(A_r(), term); } m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); - if (use_lu()) - fill_last_row_of_A_d(A_d(), term); for (lar_term::ival c : *term) { unsigned j = c.column(); while (m_usage_in_terms.size() <= j) { @@ -1798,15 +1794,12 @@ namespace lp { } void lar_solver::add_basic_var_to_core_fields() { - bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); - lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); m_incorrect_columns.increase_size_by_one(); m_rows_with_changed_bounds.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); - if (use_lu) - add_new_var_to_core_fields_for_doubles(true); + } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 20556deab..c1deb2fb1 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -166,7 +166,6 @@ class lar_solver : public column_namer { void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); - bool use_lu() const; bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 40e0a527d..93d5f4302 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -82,8 +82,7 @@ allocate_basis_heading() { // the rest of initialization will be handled by the template void lp_core_solver_base:: init() { allocate_basis_heading(); - if (m_settings.use_lu()) - init_factorization(m_factorization, m_A, m_basis, m_settings); + } // i is the pivot row, and j is the pivot column diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 790354e55..d6a78564d 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -339,10 +339,7 @@ public: m_simplex_strategy = s; } - bool use_lu() const { - return false; - } - + bool use_tableau() const { return true; } From 25f103db1abc5d76412f85ddeb74d24bcb453f5e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 14:58:49 -0800 Subject: [PATCH 090/220] rm_lp Signed-off-by: Lev Nachmanson --- src/math/lp/core_solver_pretty_printer.h | 1 - src/math/lp/core_solver_pretty_printer_def.h | 67 +---- src/math/lp/int_solver.cpp | 1 - src/math/lp/lar_core_solver.h | 68 +---- src/math/lp/lar_core_solver_def.h | 37 +-- src/math/lp/lar_solver.cpp | 57 ++-- src/math/lp/lar_solver.h | 22 +- src/math/lp/lp_core_solver_base.cpp | 14 +- src/math/lp/lp_core_solver_base.h | 12 +- src/math/lp/lp_core_solver_base_def.h | 68 +---- src/math/lp/lp_dual_core_solver_def.h | 22 +- src/math/lp/lp_primal_core_solver.h | 3 +- src/math/lp/lp_primal_core_solver_def.h | 244 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 34 +-- src/math/lp/lp_settings.h | 6 +- src/math/lp/lu.cpp | 3 - src/math/lp/lu.h | 2 - src/math/lp/lu_def.h | 15 +- src/test/lp/lp.cpp | 30 +-- 19 files changed, 65 insertions(+), 641 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 3c0563c32..353212dcd 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -110,7 +110,6 @@ public: return T_to_string(m_exact_column_norms[col]); } - void print_exact_norms(); void print_approx_norms(); diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 23417b691..697106183 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -59,22 +59,13 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve } template void core_solver_pretty_printer::init_costs() { - if (!m_core_solver.use_tableau()) { - vector local_y(m_core_solver.m_m()); - m_core_solver.solve_yB(local_y); - for (unsigned i = 0; i < ncols(); i++) { - if (m_core_solver.m_basis_heading[i] < 0) { - T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i); - set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i)); - } - } - } else { + for (unsigned i = 0; i < ncols(); i++) { if (m_core_solver.m_basis_heading[i] < 0) { set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i)); } } - } + } template core_solver_pretty_printer::~core_solver_pretty_printer() { @@ -97,7 +88,7 @@ template T core_solver_pretty_printer::current_co } template void core_solver_pretty_printer::init_m_A_and_signs() { - if (numeric_traits::precise() && m_core_solver.m_settings.use_tableau()) { + if (numeric_traits::precise() ) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ @@ -125,23 +116,7 @@ template void core_solver_pretty_printer::init_m_ m_rs[row] += t[row] * m_core_solver.m_x[column]; } } - } else { - for (unsigned column = 0; column < ncols(); column++) { - m_core_solver.solve_Bd(column, m_ed_buff, m_w_buff); // puts the result into m_core_solver.m_ed - string name = m_core_solver.column_name(column); - for (unsigned row = 0; row < nrows(); row ++) { - set_coeff( - m_A[row], - m_signs[row], - column, - m_ed_buff[row], - name); - m_rs[row] += m_ed_buff[row] * m_core_solver.m_x[column]; - } - if (!m_core_solver.use_tableau()) - m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T - } - } + } } template void core_solver_pretty_printer::init_column_widths() { @@ -190,11 +165,7 @@ template unsigned core_solver_pretty_printer:: ge w = cellw; } } - if (!m_core_solver.use_tableau()) { - w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size()); - if (!m_core_solver.m_column_norms.empty()) - w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); - } + return w; } @@ -315,41 +286,15 @@ template void core_solver_pretty_printer::print_u m_out << std::endl; } -template void core_solver_pretty_printer::print_exact_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_exact_norm_title.size()); - m_out << m_exact_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = get_exact_column_norm_string(i); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; -} template void core_solver_pretty_printer::print_approx_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_approx_norm_title.size()); - m_out << m_approx_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = T_to_string(m_core_solver.m_column_norms[i]); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; + return; } template void core_solver_pretty_printer::print() { for (unsigned i = 0; i < nrows(); i++) { print_row(i); } - print_exact_norms(); - if (!m_core_solver.m_column_norms.empty()) - print_approx_norms(); m_out << std::endl; if (m_core_solver.inf_set().size()) { m_out << "inf columns: "; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 3aaf8f29e..e338e222a 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -344,7 +344,6 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq set_upper(u, inf_u, upper_bound(j) - xj); - lp_assert(settings().use_tableau()); const auto & A = lra.A_r(); TRACE("random_update", tout << "m = " << m << "\n";); diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index b6c975139..9678edd6b 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -80,8 +80,7 @@ public: column_type get_column_type(unsigned j) { return m_column_types[j];} - void calculate_pivot_row(unsigned i); - + void print_pivot_row(std::ostream & out, unsigned row_index) const { for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) @@ -138,18 +137,11 @@ public: void fill_not_improvable_zero_sum(); void pop_basis(unsigned k) { - if (!settings().use_tableau()) { - m_r_pushed_basis.pop(k); - m_r_basis = m_r_pushed_basis(); - m_r_solver.init_basis_heading_and_non_basic_columns_vector(); - m_d_pushed_basis.pop(k); - m_d_basis = m_d_pushed_basis(); - m_d_solver.init_basis_heading_and_non_basic_columns_vector(); - } else { + m_d_basis = m_r_basis; m_d_nbasis = m_r_nbasis; m_d_heading = m_r_heading; - } + } void push() { @@ -160,19 +152,11 @@ public: m_stacked_simplex_strategy.push(); m_column_types.push(); // rational - if (!settings().use_tableau()) - m_r_A.push(); m_r_lower_bounds.push(); m_r_upper_bounds.push(); - if (!settings().use_tableau()) { - push_vector(m_r_pushed_basis, m_r_basis); - push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); - push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); - } m_d_A.push(); - if (!settings().use_tableau()) - push_vector(m_d_pushed_basis, m_d_basis); + } template @@ -202,8 +186,6 @@ public: void pop(unsigned k) { // rationals - if (!settings().use_tableau()) - m_r_A.pop(k); m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); @@ -213,8 +195,7 @@ public: m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - if(!settings().use_tableau()) - pop_markowitz_counts(k); + m_d_A.pop(k); // doubles delete m_d_solver.m_factorization; @@ -454,7 +435,6 @@ public: void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); - lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis @@ -553,8 +533,7 @@ public: bool r_basis_is_OK() const { #ifdef Z3DEBUG - if (!m_r_solver.m_settings.use_tableau()) - return true; + for (unsigned j : m_r_solver.m_basis) { lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); } @@ -568,40 +547,7 @@ public: return true; } - void solve_on_signature(const lar_solution_signature & signature, const vector & changes_of_basis) { - SASSERT(!settings().use_tableau()); - if (m_r_solver.m_factorization == nullptr) { - for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { - unsigned entering = changes_of_basis[j]; - unsigned leaving = changes_of_basis[j + 1]; - m_r_solver.change_basis_unconditionally(entering, leaving); - } - init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); - } else { - catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); - } - - if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back - catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } else { - prepare_solver_x_with_signature(signature, m_r_solver); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - } - + void create_double_matrix(static_matrix & A) { for (unsigned i = 0; i < m_r_A.row_count(); i++) { auto & row = m_r_A.m_rows[i]; diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 75fff64fd..182029e3e 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,23 +46,9 @@ lar_core_solver::lar_core_solver( column_names) { } -void lar_core_solver::calculate_pivot_row(unsigned i) { - m_r_solver.calculate_pivot_row(i); -} void lar_core_solver::prefix_r() { - if (!m_r_solver.m_settings.use_tableau()) { - m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); - m_r_solver.m_ed.resize(m_r_solver.m_m()); - m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); - m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); - m_r_solver.m_w.resize(m_r_solver.m_m()); - m_r_solver.m_y.resize(m_r_solver.m_m()); - m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0); - m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0); - init_column_row_nz_for_r_solver(); - } - + // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) @@ -142,7 +128,7 @@ void lar_core_solver::solve() { } ++settings().stats().m_need_to_solve_inf; CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); - lp_assert((!settings().use_tableau()) || r_basis_is_OK()); + lp_assert( r_basis_is_OK()); if (need_to_presolve_with_double_solver()) { TRACE("lar_solver", tout << "presolving\n";); prefix_d(); @@ -152,26 +138,17 @@ void lar_core_solver::solve() { m_r_solver.set_status(lp_status::TIME_EXHAUSTED); return; } - if (settings().use_tableau()) - solve_on_signature_tableau(solution_signature, changes_of_basis); - else - solve_on_signature(solution_signature, changes_of_basis); - - lp_assert(!settings().use_tableau() || r_basis_is_OK()); + solve_on_signature_tableau(solution_signature, changes_of_basis); + + lp_assert( r_basis_is_OK()); } else { - if (!settings().use_tableau()) { - TRACE("lar_solver", tout << "no tablau\n";); - bool snapped = m_r_solver.snap_non_basic_x_to_bound(); - lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); - if (snapped) - m_r_solver.solve_Ax_eq_b(); - } + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? m_r_solver.find_feasible_solution(); else { m_r_solver.solve(); } - lp_assert(!settings().use_tableau() || r_basis_is_OK()); + lp_assert(r_basis_is_OK()); } switch (m_r_solver.get_status()) { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 3339b55a5..2e9541525 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -261,8 +261,7 @@ namespace lp { m_crossed_bounds_column.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); - if (m_settings.use_tableau()) - pop_tableau(); + pop_tableau(); lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) { @@ -284,7 +283,7 @@ namespace lp { clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + ( m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau())); m_constraints.pop(k); @@ -299,7 +298,7 @@ namespace lp { m_simplex_strategy.pop(k); m_settings.set_simplex_strategy(m_simplex_strategy); lp_assert(sizes_are_correct()); - lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); set_status(lp_status::UNKNOWN); } @@ -630,15 +629,7 @@ namespace lp { } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - if (A_r().row_count() != m_column_buffer.data_size()) - m_column_buffer.resize(A_r().row_count()); - else - m_column_buffer.clear(); - lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); - - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) - insert_row_with_changed_bounds(i); + lp_assert(false); } @@ -648,8 +639,7 @@ namespace lp { insert_row_with_changed_bounds(rc.var()); } - bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } - + bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } @@ -692,7 +682,7 @@ namespace lp { } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta) { - if (use_tableau()) { + { for (const auto& c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { @@ -705,15 +695,7 @@ namespace lp { } } - else { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); - } - } + } void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { @@ -742,10 +724,8 @@ namespace lp { return; } - if (use_tableau()) - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - else - detect_rows_of_bound_change_column_for_nbasic_column(j); + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + } void lar_solver::detect_rows_with_changed_bounds() { @@ -773,8 +753,6 @@ namespace lp { void lar_solver::solve_with_core_solver() { - if (!use_tableau()) - add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); } @@ -782,10 +760,7 @@ namespace lp { if (costs_are_used()) { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); } - if (use_tableau()) - update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - else - update_x_and_inf_costs_for_columns_with_changed_bounds(); + update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); @@ -1753,7 +1728,7 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if (use_tableau() && !coeffs.empty()) { + if ( !coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) insert_row_with_changed_bounds(A_r().row_count() - 1); @@ -1774,14 +1749,12 @@ namespace lp { ul_pair ul(true); // to mark this column as associated_with_row m_columns_to_ul_pairs.push_back(ul); add_basic_var_to_core_fields(); - if (use_tableau()) { - A_r().fill_last_row_with_pivoting(*term, + + A_r().fill_last_row_with_pivoting(*term, j, m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - } - else { - fill_last_row_of_A_r(A_r(), term); - } + + m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); for (lar_term::ival c : *term) { unsigned j = c.column(); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index c1deb2fb1..513659368 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -177,7 +177,6 @@ class lar_solver : public column_namer { if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) return; - lp_assert(use_tableau()); bound_analyzer_on_row, lp_bound_propagator>::analyze_row(A_r().m_rows[row_index], null_ci, @@ -190,7 +189,6 @@ class lar_solver : public column_namer { void substitute_basis_var_in_terms_for_row(unsigned i); template void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - SASSERT(use_tableau()); analyze_new_bounds_on_row_tableau(i, bp); } static void clean_popped_elements(unsigned n, u_set& set); @@ -210,7 +208,6 @@ class lar_solver : public column_namer { vector> &left_side) const; void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); - bool use_tableau() const; bool use_tableau_costs() const; void detect_rows_of_column_with_bound_change(unsigned j); void adjust_x_of_column(unsigned j); @@ -376,7 +373,6 @@ public: void mark_rows_for_bound_prop(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { - SASSERT(use_tableau()); for (unsigned i : m_rows_with_changed_bounds) { calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) @@ -429,8 +425,8 @@ public: void change_basic_columns_dependend_on_a_given_nb_column_report(unsigned j, const numeric_pair & delta, const ChangeReport& after) { - if (use_tableau()) { - for (const auto & c : A_r().m_columns[j]) { + + for (const auto & c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { m_basic_columns_with_changed_cost.insert(bj); @@ -440,20 +436,8 @@ public: TRACE("change_x_del", tout << "changed basis column " << bj << ", it is " << ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); - - } - } else { - NOT_IMPLEMENTED_YET(); - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); - } - } - } + } template void set_value_for_nbasic_column_report(unsigned j, diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index c8b1692d2..5dc8fb9e2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -26,7 +26,6 @@ Revision History: template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -52,16 +51,12 @@ template void lp::lp_core_solver_base::set_non_basic_x_to_correc template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base>::solve_Bd(unsigned int, indexed_vector&); template void lp::lp_core_solver_base::solve_yB(vector&) const; template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -71,11 +66,9 @@ template bool lp::lp_core_solver_base::print_statistics_with_i template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); template void lp::lp_core_solver_base::solve_yB(vector&) const; template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); -template void lp::lp_core_solver_base >::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -88,7 +81,7 @@ template lp::lp_core_solver_base >::lp_core_s template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); -template void lp::lp_core_solver_base >::solve_Bd(unsigned int); + template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( @@ -145,8 +138,7 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base >::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; + + diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index b7010aa54..631f68781 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -156,11 +156,6 @@ public: void solve_yB(vector & y) const; - void solve_Bd(unsigned entering, indexed_vector & d_buff, indexed_vector& w_buff) const; - - void solve_Bd(unsigned entering); - - void solve_Bd(unsigned entering, indexed_vector & column); void pretty_print(std::ostream & out); @@ -184,9 +179,7 @@ public: bool A_mult_x_is_off() const; bool A_mult_x_is_off_on_index(const vector & index) const; - // from page 182 of Istvan Maros's book - void calculate_pivot_row_of_B_1(unsigned pivot_row); - + void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); void add_delta_to_entering(unsigned entering, const X & delta); @@ -670,7 +663,6 @@ public: m_settings.simplex_strategy(); } - bool use_tableau() const { return m_settings.use_tableau(); } template static void swap(vector &v, unsigned i, unsigned j) { @@ -760,7 +752,7 @@ public: return m_iters_with_no_cost_growing; } - void calculate_pivot_row(unsigned i); + unsigned get_base_column_in_row(unsigned row_index) const { return m_basis[row_index]; } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 93d5f4302..0e8342430 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -131,35 +131,9 @@ solve_yB(vector & y) const { // m_index_of_ed.push_back(i); // } // } -template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { - lp_assert(!m_settings.use_tableau()); - if (m_factorization == nullptr) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - } - m_factorization->solve_Bd_faster(entering, column); -} -template void lp_core_solver_base::solve_Bd(unsigned , indexed_vector& , indexed_vector &) const { - NOT_IMPLEMENTED_YET(); -} -template void lp_core_solver_base:: -solve_Bd(unsigned entering) { - lp_assert(m_ed.is_OK()); - m_factorization->solve_Bd(entering, m_ed, m_w); - if (this->precise()) - m_columns_nz[entering] = m_ed.m_index.size(); - lp_assert(m_ed.is_OK()); - lp_assert(m_w.is_OK()); -#ifdef Z3DEBUG - // auto B = get_B(*m_factorization, m_basis); - // vector a(m_m()); - // m_A.copy_column_to_vector(entering, a); - // vector cd(m_ed.m_data); - // B.apply_from_left(cd, m_settings); - // lp_assert(vectors_are_equal(cd , a)); -#endif -} + template void lp_core_solver_base:: pretty_print(std::ostream & out) { @@ -279,17 +253,6 @@ A_mult_x_is_off_on_index(const vector & index) const { return false; } -// from page 182 of Istvan Maros's book -template void lp_core_solver_base:: -calculate_pivot_row_of_B_1(unsigned pivot_row) { - lp_assert(! use_tableau()); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); - lp_assert(m_pivot_row_of_B_1.is_OK()); -} template void lp_core_solver_base:: @@ -316,13 +279,7 @@ calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { m_x[entering] += delta; - if (!use_tableau()) - for (unsigned i : m_ed.m_index) { - if (!numeric_traits::precise()) - m_copy_of_xB[i] = m_x[m_basis[i]]; - m_x[m_basis[i]] -= delta * m_ed[i]; - } - else + for (const auto & c : m_A.m_columns[entering]) { unsigned i = c.var(); m_x[m_basis[i]] -= delta * m_A.get_val(c); @@ -1000,26 +957,5 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) } } -template -void lp_core_solver_base::calculate_pivot_row(unsigned i) { - lp_assert(!use_tableau()); - lp_assert(m_pivot_row.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.resize(m_m()); - m_pivot_row.clear(); - m_pivot_row.resize(m_n()); - if (m_settings.use_tableau()) { - unsigned basic_j = m_basis[i]; - for (auto & c : m_A.m_rows[i]) { - if (c.var() != basic_j) - m_pivot_row.set_value(c.coeff(), c.var()); - } - return; - } - - calculate_pivot_row_of_B_1(i); - calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); -} - } diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index b42d644af..df70e64f1 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -210,26 +210,8 @@ template void lp_dual_core_solver::DSE_FTran() { } template bool lp_dual_core_solver::advance_on_known_p() { - if (done()) { - return true; - } - this->calculate_pivot_row_of_B_1(m_r); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r); - if (!ratio_test()) { - return true; - } - calculate_beta_r_precisely(); - this->solve_Bd(m_q); // FTRAN - int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - lp_unreachable(); // not implemented yet - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - } - DSE_FTran(); - return basis_change_and_update(); + + return false; } template int lp_dual_core_solver::define_sign_of_alpha_r() { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index a60395ab0..dc6cb2900 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -468,7 +468,6 @@ public: } void update_basis_and_x_tableau_rows(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau_rows(entering, leaving, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -804,7 +803,7 @@ public: return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - void init_reduced_costs(); + bool lower_bounds_are_set() const override { return true; } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 3818b589a..0a58b0fdf 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -33,21 +33,14 @@ namespace lp { template void lp_primal_core_solver::sort_non_basis_rational() { lp_assert(numeric_traits::precise()); - if (this->m_settings.use_tableau()) { + std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; return ca < cb; }); - } else { - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - unsigned ca = this->m_columns_nz[a]; - unsigned cb = this->m_columns_nz[b]; - if (ca == 0 && cb != 0) return false; - return ca < cb; - });} - + m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { @@ -644,25 +637,7 @@ template void lp_primal_core_solver::backup_an } template void lp_primal_core_solver::init_run() { - this->m_basis_sort_counter = 0; // to initiate the sort of the basis - // this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - init_inf_set(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) - return; - this->set_using_infeas_costs(false); - if (this->m_settings.backup_costs) - backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise()? zero_of_type(): T(1)/T(10000000); - m_breakpoint_indices_queue.resize(this->m_n()); - init_reduced_costs(); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } else { - if (this->m_columns_nz.size() != this->m_n()) - init_column_row_non_zeroes(); - } + } @@ -676,166 +651,20 @@ template void lp_primal_core_solver::calc_work template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); - this->add_delta_to_entering(entering, t * m_sign_of_entering_delta); - if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { - this->init_lu(); - if (!this->find_x_by_solving()) { - this->restore_x(entering, t * m_sign_of_entering_delta); - this->iters_with_no_cost_growing()++; - LP_OUT(this->m_settings, "failing in advance_on_entering_equal_leaving for entering = " << entering << std::endl); - return; - } - } - if (this->using_infeas_costs()) { - lp_assert(is_zero(this->m_costs[entering])); - init_infeasibility_costs_for_changed_basis_only(); - } - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - return; - if (need_to_switch_costs() ||!this->current_x_is_feasible()) { - init_reduced_costs(); - } - this->iters_with_no_cost_growing() = 0; } template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || t >= zero_of_type()); - lp_assert(leaving >= 0 && entering >= 0); - lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes - if (entering == leaving) { - advance_on_entering_equal_leaving(entering, t); - return; - } - unsigned pivot_row = this->m_basis_heading[leaving]; - this->calculate_pivot_row_of_B_1(pivot_row); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(pivot_row); - - int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } - } - if (!numeric_traits::precise()) - calc_working_vector_beta_for_column_norms(); - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search) { - if (m_sign_of_entering_delta == -1) - t = -t; - } - if (!this->update_basis_and_x(entering, leaving, t)) { - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) - return; - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_reduced_costs(); - return; - } - - if (!is_zero(t)) { - this->iters_with_no_cost_growing() = 0; - init_infeasibility_after_update_x_if_inf(leaving); - } - - if (this->current_x_is_feasible()) { - this->set_status(lp_status::FEASIBLE); - if (this->m_look_for_feasible_solution_only) - return; - } - if (numeric_traits::precise() == false) - update_or_init_column_norms(entering, leaving); - - - if (need_to_switch_costs()) { - init_reduced_costs(); - } else { - update_reduced_costs_from_pivot_row(entering, leaving); - } - lp_assert(!need_to_switch_costs()); - std::list::iterator it = m_non_basis_list.end(); - it--; - * it = static_cast(leaving); + } template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - lp_assert(numeric_traits::precise()); - lp_assert(entering > -1); - this->solve_Bd(entering); - X t; - int leaving = find_leaving_and_t_precise(entering, t); - if (leaving == -1) { - TRACE("lar_solver", tout << "non-leaving\n";); - this->set_status(lp_status::UNBOUNDED); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); + lp_assert(false); } template void lp_primal_core_solver::advance_on_entering(int entering) { - if (numeric_traits::precise()) { - advance_on_entering_precise(entering); - return; - } - lp_assert(entering > -1); - this->solve_Bd(entering); - int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); - if (refresh_result) { - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - - this->init_lu(); - init_reduced_costs(); - if (refresh_result == 2) { - this->iters_with_no_cost_growing()++; - return; - } - } - X t; - int leaving = find_leaving_and_t(entering, t); - if (leaving == -1){ - if (!this->current_x_is_feasible()) { - lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs - - // if (m_look_for_feasible_solution_only) { - // this->m_status = INFEASIBLE; - // return; - // } - - - if (this->get_status() == lp_status::UNSTABLE) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_infeasibility_costs(); - this->set_status(lp_status::UNSTABLE); - - return; - } - if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { - this->set_status(lp_status::UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_UNBOUNDED); - } - TRACE("lar_solver", tout << this->get_status() << "\n";); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); + lp_assert(false); } template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { @@ -867,7 +696,7 @@ template void lp_primal_core_solver::print_column // returns the number of iterations template unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - if (numeric_traits::precise() && this->m_settings.use_tableau()) + if (numeric_traits::precise()) return solve_with_tableau(); init_run(); @@ -893,56 +722,19 @@ template unsigned lp_primal_core_solver::solve() case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); + { // precise case - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status (lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case - if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } } break; case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); + lp_assert(false); break; case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs(); - this->set_status(lp_status::UNKNOWN); - } + lp_assert(false); break; case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); + lp_assert(false); break; default: @@ -1292,20 +1084,6 @@ template void lp_primal_core_solver::print_breakp print_bound_info_and_x(b->m_j, out); } -template -void lp_primal_core_solver::init_reduced_costs() { - lp_assert(!this->use_tableau()); - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } - - this->init_reduced_costs_for_one_iteration(); -} template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { if (b->m_j == entering) { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 46297a63e..fa25694ad 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -126,22 +126,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); - - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case + { // precise case if ((!this->infeasibility_costs_are_correct())) { init_reduced_costs_tableau(); // forcing recalc if (choose_entering_column_tableau() == -1) { @@ -153,13 +138,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } break; case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); + lp_assert(false); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { @@ -169,13 +148,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { break; case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); + lp_assert(false); break; default: @@ -348,7 +321,6 @@ template void lp_primal_core_solver::init_run_tab template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index d6a78564d..360ef99bf 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -339,11 +339,7 @@ public: m_simplex_strategy = s; } - - bool use_tableau() const { - return true; - } - + bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } diff --git a/src/math/lp/lu.cpp b/src/math/lp/lu.cpp index 6c9bcc5f6..313fa9901 100644 --- a/src/math/lp/lu.cpp +++ b/src/math/lp/lu.cpp @@ -28,13 +28,10 @@ template double dot_product(vector const&, vector>::lu(static_matrix const&, vector&, lp_settings&); template void lu>::push_matrix_to_tail(tail_matrix*); template void lu>::replace_column(double, indexed_vector&, unsigned); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template mpq dot_product(vector const&, vector const&); template void init_factorization> diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index aca59065d..191018100 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -171,8 +171,6 @@ public: void print_matrix_compact(std::ostream & f); void print(indexed_vector & w, const vector& basis); - void solve_Bd(unsigned a_column, vector & d, indexed_vector & w); - void solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w); void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit void solve_yB(vector& y); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index 80c9cdf0e..059a43012 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -117,7 +117,7 @@ lu::lu(const M& A, m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { - lp_assert(!(numeric_traits::precise() && settings.use_tableau())); + lp_assert(!(numeric_traits::precise() )); #ifdef Z3DEBUG debug_test_of_basis(A, basis); #endif @@ -256,19 +256,6 @@ void lu< M>::print(indexed_vector & w, const vector& basis) { print_indexed_vector(w, f); f.close(); } -template -void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { - init_vector_w(a_column, w); - - if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning - d = w; - solve_By_for_T_indexed_only(d, m_settings); - } else { - d.m_data = w.m_data; - d.m_index.clear(); - solve_By_when_y_is_ready_for_T(d.m_data, d.m_index); - } -} template void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index fe589835d..189bd604b 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -2095,35 +2095,7 @@ void read_indexed_vector(indexed_vector & v, std::ifstream & f) { } void check_lu_from_file(std::string lufile_name) { - std::ifstream f(lufile_name); - if (!f.is_open()) { - std::cout << "cannot open file " << lufile_name << std::endl; - } - unsigned m, n; - get_matrix_dimensions(f, m, n); - std::cout << "init matrix " << m << " by " << n << std::endl; - static_matrix A(m, n); - read_rows(A, f); - vector basis; - read_basis(basis, f); - indexed_vector v(m); - // read_indexed_vector(v, f); - f.close(); - vector basis_heading; - lp_settings settings; - vector non_basic_columns; - lu> lsuhl(A, basis, settings); - indexed_vector d(A.row_count()); - unsigned entering = 26; - lsuhl.solve_Bd(entering, d, v); -#ifdef Z3DEBUG - auto B = get_B(lsuhl, basis); - vector a(m); - A.copy_column_to_vector(entering, a); - indexed_vector cd(d); - B.apply_from_left(cd.m_data, settings); - lp_assert(vectors_are_equal(cd.m_data , a)); -#endif + lp_assert(false); } void test_square_dense_submatrix() { From c251151d6631bfaf11f9d8eae6cb20979d96ffc0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 15:15:08 -0800 Subject: [PATCH 091/220] rm_lu --- src/math/lp/lar_solver.cpp | 19 +----- src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/lu.h | 59 ++---------------- src/test/lp/lp.cpp | 92 +---------------------------- 4 files changed, 9 insertions(+), 163 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 2e9541525..d9961eaa8 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -795,24 +795,7 @@ namespace lp { template void lar_solver::add_last_rows_to_lu(lp_primal_core_solver& s) { - auto& f = s.m_factorization; - if (f != nullptr) { - auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); - if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { - delete f; - f = nullptr; - } - else { - f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); - } - } - if (f == nullptr) { - init_factorization(f, s.m_A, s.m_basis, m_settings); - if (f->get_status() != LU_status::OK) { - delete f; - f = nullptr; - } - } + lp_assert(false); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index dc6cb2900..5332b4001 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -418,7 +418,7 @@ public: // returns the number of iterations unsigned solve(); - lu> * factorization() {return this->m_factorization;} + lu> * factorization() {return nullptr;} void delete_factorization(); diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index 191018100..bcbb85043 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -305,66 +305,19 @@ public: void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); void prepare_entering(unsigned entering, indexed_vector & w) { - init_vector_w(entering, w); + lp_assert(false); } - bool need_to_refactor() { return m_refactor_counter >= 200; } + bool need_to_refactor() { lp_assert(false); + return m_refactor_counter >= 200; } void adjust_dimension_with_matrix_A() { - lp_assert(m_A.row_count() >= m_dim); - m_dim = m_A.row_count(); - m_U.resize(m_dim); - m_Q.resize(m_dim); - m_R.resize(m_dim); - m_row_eta_work_vector.resize(m_dim); + lp_assert(false); } - std::unordered_set get_set_of_columns_to_replace_for_add_last_rows(const vector & heading) const { - std::unordered_set columns_to_replace; - unsigned m = m_A.row_count(); - unsigned m_prev = m_U.dimension(); - - lp_assert(m_A.column_count() == heading.size()); - - for (unsigned i = m_prev; i < m; i++) { - for (const row_cell & c : m_A.m_rows[i]) { - int h = heading[c.var()]; - if (h < 0) { - continue; - } - columns_to_replace.insert(c.var()); - } - } - return columns_to_replace; - } - void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { - unsigned m = m_A.row_count(); - lp_assert(m_A.column_count() == heading.size()); - adjust_dimension_with_matrix_A(); - m_w_for_extension.resize(m); - // At this moment the LU is correct - // for B extended by only by ones at the diagonal in the lower right corner - - for (unsigned j :columns_to_replace) { - lp_assert(heading[j] >= 0); - replace_column_with_only_change_at_last_rows(j, heading[j]); - if (get_status() == LU_status::Degenerated) - break; - } - } - // column j is a basis column, and there is a change in the last rows - void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) { - init_vector_w(j, m_w_for_extension); - replace_column(zero_of_type(), m_w_for_extension, column_to_change_in_U); - } - - bool has_dense_submatrix() const { - for (auto m : m_tail) - if (m->is_dense()) - return true; - return false; - } + + }; // end of lu diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 189bd604b..ceca92b4e 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -551,90 +551,7 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, #ifdef Z3DEBUG void test_small_lu(lp_settings & settings) { - std::cout << " test_small_lu" << std::endl; - static_matrix m(3, 6); - vector basis(3); - basis[0] = 0; - basis[1] = 1; - basis[2] = 3; - - m(0, 0) = 1; m(0, 2)= 3.9; m(2, 3) = 11; m(0, 5) = -3; - m(1, 1) = 4; m(1, 4) = 7; - m(2, 0) = 1.8; m(2, 2) = 5; m(2, 4) = 2; m(2, 5) = 8; - -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - lp_assert(l.is_correct(basis)); - indexed_vector w(m.row_count()); - std::cout << "entering 2, leaving 0" << std::endl; - l.prepare_entering(2, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(2, 0, basis, non_basic_columns, heading); - // #ifdef Z3DEBUG - // std::cout << "we were factoring " << std::endl; - // print_matrix(get_B(l)); - // #endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 4, leaving 3" << std::endl; - l.prepare_entering(4, w); // to init vector w - l.replace_column(0, w, heading[3]); - change_basis(4, 3, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - std::cout << "entering 5, leaving 1" << std::endl; - l.prepare_entering(5, w); // to init vector w - l.replace_column(0, w, heading[1]); - change_basis(5, 1, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 3, leaving 2" << std::endl; - l.prepare_entering(3, w); // to init vector w - l.replace_column(0, w, heading[2]); - change_basis(3, 2, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - m.add_row(); - m.add_column(); - m.add_row(); - m.add_column(); - for (unsigned i = 0; i < m.column_count(); i++) { - m(3, i) = i; - m(4, i) = i * i; // to make the rows linearly independent - } - unsigned j = m.column_count() ; - basis.push_back(j-2); - heading.push_back(basis.size() - 1); - basis.push_back(j-1); - heading.push_back(basis.size() - 1); - auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); - l.add_last_rows_to_B(heading, columns_to_replace); - lp_assert(l.is_correct(basis)); } #endif @@ -827,10 +744,7 @@ void test_larger_lu(lp_settings& settings) { void test_lu(lp_settings & settings) { - test_small_lu(settings); - test_larger_lu(settings); - test_larger_lu_with_holes(settings); - test_larger_lu_exp(settings); + } #endif @@ -1785,10 +1699,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_after_string_with_help("--time_limit", "time limit in seconds"); parser.add_option_with_help_string("--mpq", "solve for rational numbers"); parser.add_option_with_after_string_with_help("--simplex_strategy", "sets simplex strategy for rational number"); - parser.add_option_with_help_string("--test_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_small_lu", "test the work of the factorization on a smallish matrix"); - parser.add_option_with_help_string("--test_larger_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_larger_lu_with_holes", "test the work of the factorization"); parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); From 9a7c99da332fae795dc52ea82e4ccba000e2b778 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 15:26:55 -0800 Subject: [PATCH 092/220] rm lu --- src/math/lp/lu.h | 10 +- src/math/lp/lu_def.h | 235 +++---------------------------------------- src/test/lp/lp.cpp | 136 ------------------------- 3 files changed, 16 insertions(+), 365 deletions(-) diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index bcbb85043..b3e2f0c22 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -266,13 +266,6 @@ public: bool is_correct(); -#ifdef Z3DEBUG - dense_matrix tail_product(); - dense_matrix get_left_side(const vector& basis); - dense_matrix get_left_side(); - - dense_matrix get_right_side(); -#endif // needed for debugging purposes void copy_w(T *buffer, indexed_vector & w); @@ -296,8 +289,7 @@ public: void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time - row_eta_matrix *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking); - + void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index 059a43012..d7c0c0c3a 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -626,161 +626,41 @@ void lu::process_column(int j) { } template bool lu::is_correct(const vector& basis) { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(basis); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else - return true; -#endif +return true; } template bool lu::is_correct() { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else return true; -#endif } -#ifdef Z3DEBUG -template -dense_matrix lu::tail_product() { - lp_assert(tail_size() > 0); - dense_matrix left_side = permutation_matrix(m_dim); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side(const vector& basis) { - dense_matrix left_side = get_B(*this, basis); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side() { - dense_matrix left_side = get_B(*this); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_right_side() { - auto ret = U() * R(); - ret = Q() * ret; - return ret; -} -#endif - // needed for debugging purposes template void lu::copy_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - buffer[i] = w[i]; - } + } // needed for debugging purposes template void lu::restore_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - w[i] = buffer[i]; - } + } template bool lu::all_columns_and_rows_are_active() { - unsigned i = m_dim; - while (i--) { - lp_assert(m_U.col_is_active(i)); - lp_assert(m_U.row_is_active(i)); - } return true; } template bool lu::too_dense(unsigned j) const { - unsigned r = m_dim - j; - if (r < 5) - return false; - // if (j * 5 < m_dim * 4) // start looking for dense only at the bottom of the rows - // return false; - // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); - return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); + return false; } template void lu::pivot_in_dense_mode(unsigned i) { - int j = m_dense_LU->find_pivot_column_in_row(i); - if (j == -1) { - m_failure = true; - return; - } - if (i != static_cast(j)) { - swap_columns(i, j); - m_dense_LU->swap_columns(i, j); - } - m_dense_LU->pivot(i, m_settings); + } template void lu::create_initial_factorization(){ - m_U.prepare_for_factorization(); - unsigned j; - for (j = 0; j < m_dim; j++) { - process_column(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - if (too_dense(j)) { - break; - } - } - if (j == m_dim) { - // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - return; - } - j++; - m_dense_LU = new square_dense_submatrix(&m_U, j); - for (; j < m_dim; j++) { - pivot_in_dense_mode(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - } - m_dense_LU->update_parent_matrix(m_settings); - lp_assert(m_dense_LU->is_L_matrix()); - m_dense_LU->conjugate_by_permutation(m_Q); - push_matrix_to_tail(m_dense_LU); - m_refactor_counter = 0; - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + } template @@ -856,123 +736,38 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest } } } -// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last -// row at the same time -template -row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { - if (replaced_column == lowest_row_of_the_bump) return nullptr; - scan_last_row_to_work_vector(lowest_row_of_the_bump); - pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); - if (numeric_traits::precise() == false && !is_zero(pivot_elem_for_checking)) { - T denom = std::max(T(1), abs(pivot_elem_for_checking)); - if ( - !m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) { - set_status(LU_status::Degenerated); - // LP_OUT(m_settings, "diagonal element is off" << std::endl); - return nullptr; - } - } -#ifdef Z3DEBUG - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); -#else - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); -#endif - - for (auto j : m_row_eta_work_vector.m_index) { - if (j < lowest_row_of_the_bump) { - auto & v = m_row_eta_work_vector[j]; - if (!is_zero(v)) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){ - ret->push_back(j, v); - } - v = numeric_traits::zero(); - } - } - } // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values - return ret; -} template void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ - m_refactor_counter++; - unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); - unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); - m_r_wave.init(m_dim); - calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave); - auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking); - - if (get_status() == LU_status::Degenerated) { - m_row_eta_work_vector.clear_all(); - return; - } - m_Q.multiply_by_permutation_from_right(m_r_wave); - m_R.multiply_by_permutation_reverse_from_left(m_r_wave); - if (row_eta != nullptr) { - row_eta->conjugate_by_permutation(m_Q); - push_matrix_to_tail(row_eta); - } - calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); + lp_assert(false); } template void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ - T diagonal_elem; - if (replaced_column < lowest_row_of_the_bump) { - diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; - // lp_assert(m_row_eta_work_vector.is_OK()); - m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); - } else { - diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently - } - if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) { - set_status(LU_status::Degenerated); - return; - } - - calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + lp_assert(false);// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } template void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { - auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); -#ifdef Z3DEBUG - l->set_number_of_columns(m_dim); -#endif - push_matrix_to_tail(l); - m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings); - l->conjugate_by_permutation(m_Q); + lp_assert(false); } template void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { - if (factorization != nullptr) - delete factorization; - factorization = new lu(m_A, m_basis, m_settings); - // if (factorization->get_status() != LU_status::OK) - // LP_OUT(m_settings, "failing in init_factorization" << std::endl); + lp_assert(false); } #ifdef Z3DEBUG template dense_matrix get_B(lu& f, const vector& basis) { - lp_assert(basis.size() == f.dimension()); - lp_assert(basis.size() == f.m_U.dimension()); - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.B_(i, j, basis)); - + lp_assert(false); + + dense_matrix B(0, 0); return B; } template dense_matrix get_B(lu& f) { - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.m_A[i][j]); - + lp_assert(false); + dense_matrix B(0,0); return B; } #endif diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index ceca92b4e..1a2ee2338 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -611,142 +611,6 @@ void fill_larger_square_sparse_matrix(static_matrix & m){ int perm_id = 0; -#ifdef Z3DEBUG -void test_larger_lu_exp(lp_settings & settings) { - std::cout << " test_larger_lu_exp" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix_exp(m); - // print_matrix(m); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - lp_assert(left_side == right_side); - int leaving = 3; - int entering = 8; - for (unsigned i = 0; i < m.row_count(); i++) { - std::cout << static_cast(m(i, entering)) << std::endl; - } - - indexed_vector w(m.row_count()); - - l.prepare_entering(entering, w); - l.replace_column(0, w, heading[leaving]); - change_basis(entering, leaving, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); - - l.prepare_entering(11, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(11, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - -void test_larger_lu_with_holes(lp_settings & settings) { - std::cout << " test_larger_lu_with_holes" << std::endl; - static_matrix m(8, 9); - vector basis(8); - for (unsigned i = 0; i < m.row_count(); i++) { - basis[i] = i; - } - m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; m(0, 3) = 4; m(0, 4) = 5; m(0, 8) = 99; - /* */ m(1, 1) =- 6; m(1, 2) = 7; m(1, 3) = 8; m(1, 4) = 9; - /* */ m(2, 2) = 10; - /* */ m(3, 2) = 11; m(3, 3) = -12; - /* */ m(4, 2) = 13; m(4, 3) = 14; m(4, 4) = 15; - // the rest of the matrix is denser - m(5, 4) = 28; m(5, 5) = -18; m(5, 6) = 19; m(5, 7) = 25; - /* */ m(6, 5) = 20; m(6, 6) = -21; - /* */ m(7, 5) = 22; m(7, 6) = 23; m(7, 7) = 24; m(7, 8) = 88; - print_matrix(m, std::cout); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - std::cout << "printing factorization" << std::endl; - for (int i = l.tail_size() - 1; i >=0; i--) { - auto lp = l.get_lp_matrix(i); - lp->set_number_of_columns(m.row_count()); - lp->set_number_of_rows(m.row_count()); - print_matrix( *lp, std::cout); - } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "different sides" << std::endl; - } - - indexed_vector w(m.row_count()); - l.prepare_entering(8, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(8, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_larger_lu(lp_settings& settings) { - std::cout << " test_larger_lu" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix(m); - print_matrix(m, std::cout); - - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - auto l = lu> (m, basis, settings); - // std::cout << "printing factorization" << std::endl; - // for (int i = lu.tail_size() - 1; i >=0; i--) { - // auto lp = lu.get_lp_matrix(i); - // lp->set_number_of_columns(m.row_count()); - // lp->set_number_of_rows(m.row_count()); - // print_matrix(* lp); - // } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "left side" << std::endl; - print_matrix(&left_side, std::cout); - std::cout << "right side" << std::endl; - print_matrix(&right_side, std::cout); - - std::cout << "different sides" << std::endl; - std::cout << "initial factorization is incorrect" << std::endl; - exit(1); - } - indexed_vector w(m.row_count()); - l.prepare_entering(9, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(9, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_lu(lp_settings & settings) { - -} -#endif From 5f03c93270b35e2cf43a3063bb6accd548ecf7d4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 4 Mar 2023 16:02:46 -0800 Subject: [PATCH 093/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lp_core_solver_base.cpp | 3 -- src/math/lp/lp_core_solver_base.h | 3 +- src/math/lp/lp_core_solver_base_def.h | 60 ------------------------- src/math/lp/lp_dual_core_solver_def.h | 28 +----------- src/math/lp/lp_primal_core_solver.h | 4 +- src/math/lp/lp_primal_core_solver_def.h | 6 --- 6 files changed, 4 insertions(+), 100 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 5dc8fb9e2..9f6f2534f 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -52,7 +52,6 @@ template void lp::lp_core_solver_base::snap_xN_to_bounds_and_fre template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; @@ -67,7 +66,6 @@ template void lp::lp_core_solver_base::restore_x(unsigned int, template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); @@ -82,7 +80,6 @@ template bool lp::lp_core_solver_base >::prin template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); -template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 631f68781..dce805b62 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -307,8 +307,7 @@ public: bool find_x_by_solving(); - bool update_basis_and_x(int entering, int leaving, X const & tt); - + bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 0e8342430..51b24128f 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -457,66 +457,6 @@ template bool lp_core_solver_base::inf_set_is_cor return true; } -template bool lp_core_solver_base:: -update_basis_and_x(int entering, int leaving, X const & tt) { - - if (!is_zero(tt)) { - add_delta_to_entering(entering, tt); - if ((!numeric_traits::precise()) && A_mult_x_is_off_on_index(m_ed.m_index) && !find_x_by_solving()) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (!find_x_by_solving()) { - restore_x(entering, tt); - if(A_mult_x_is_off()) { - m_status = lp_status::FLOATING_POINT_ERROR; - m_iters_with_no_cost_growing++; - return false; - } - - init_factorization(m_factorization, m_A, m_basis, m_settings); - m_iters_with_no_cost_growing++; - if (m_factorization->get_status() != LU_status::OK) { - std::stringstream s; - // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); - m_status = lp_status::FLOATING_POINT_ERROR; - return false; - } - return false; - } - } - } - - bool refactor = m_factorization->need_to_refactor(); - if (!refactor) { - const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise - m_factorization->replace_column(pivot, m_w, m_basis_heading[leaving]); - if (m_factorization->get_status() == LU_status::OK) { - change_basis(entering, leaving); - return true; - } - } - // need to refactor == true - change_basis(entering, leaving); - init_lu(); - if (m_factorization->get_status() != LU_status::OK) { - if (m_look_for_feasible_solution_only && !precise()) { - m_status = lp_status::UNSTABLE; - delete m_factorization; - m_factorization = nullptr; - return false; - } - // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); - restore_x_and_refactor(entering, leaving, tt); - if (m_status == lp_status::FLOATING_POINT_ERROR) - return false; - CASSERT("A_off", !A_mult_x_is_off()); - m_iters_with_no_cost_growing++; - // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); - m_status = lp_status::UNSTABLE; - return false; - } - return true; -} - template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index df70e64f1..fd8fc8071 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -411,33 +411,7 @@ template void lp_dual_core_solver::init_betas_pre // step 7 of the algorithm from Progress template bool lp_dual_core_solver::basis_change_and_update() { - update_betas(); - update_d_and_xB(); - // m_theta_P = m_delta / this->m_ed[m_r]; - m_theta_P = m_delta / this->m_pivot_row[m_q]; - // xb_minus_delta_p_pivot_column(); - apply_flips(); - if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) { - init_betas_precisely(); - return false; - } - - if (snap_runaway_nonbasic_column(m_p)) { - if (!this->find_x_by_solving()) { - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - } - - if (!problem_is_dual_feasible()) { - // todo : shift the costs!!!! - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - - lp_assert(d_is_correct()); + return true; } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 5332b4001..abee0cc6e 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -552,9 +552,9 @@ public: void clear_breakpoints(); void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); - void advance_on_sorted_breakpoints(unsigned entering); + - void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); + void decide_on_status_when_cannot_find_entering() { lp_assert(!need_to_switch_costs()); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 0a58b0fdf..7d522f933 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -855,12 +855,6 @@ template void lp_primal_core_solver::one_iteratio } } -template void lp_primal_core_solver::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) { - if (entering != leaving) - this->update_basis_and_x(entering, leaving, delta); - else - this->update_x(entering, delta); -} template void lp_primal_core_solver::clear_breakpoints() { From 62bd3bd1e6f7145564dc444453918b34757e09cd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:35:10 -0800 Subject: [PATCH 094/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 241 +----------------- src/math/lp/lar_core_solver_def.h | 28 +- src/math/lp/lp_core_solver_base.cpp | 11 - src/math/lp/lp_core_solver_base.h | 13 +- src/math/lp/lp_core_solver_base_def.h | 97 +------ src/math/lp/lp_dual_core_solver.h | 2 +- src/math/lp/lp_dual_core_solver_def.h | 14 +- src/math/lp/lp_primal_core_solver_def.h | 5 +- .../lp/lp_primal_core_solver_tableau_def.h | 9 - src/math/lp/lu.h | 3 +- src/math/lp/lu_def.h | 60 +---- 11 files changed, 22 insertions(+), 461 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 9678edd6b..999eef13b 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -190,16 +190,12 @@ public: m_r_upper_bounds.pop(k); m_column_types.pop(k); - delete m_r_solver.m_factorization; - m_r_solver.m_factorization = nullptr; m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); m_d_A.pop(k); // doubles - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; m_d_x.resize(m_d_A.column_count()); pop_basis(k); @@ -294,172 +290,13 @@ public: unsigned jb = m_r_solver.m_basis[i]; m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); + } lp_assert(m_r_solver.inf_set_is_correct()); } - template - void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { - for (auto &t : signature) { - unsigned j = t.first; - lp_assert(m_r_heading[j] < 0); - auto pos_type = t.second; - switch (pos_type) { - case at_lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case free_of_bounds: { - s.m_x[j] = zero_of_type(); - continue; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - lp_assert(false); // unreachable - break; - case column_type::upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case column_type::lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case column_type::boxed: - if (settings().random_next() % 2) { - s.m_x[j] = s.m_lower_bounds[j]; - } else { - s.m_x[j] = s.m_upper_bounds[j]; - } - break; - case column_type::fixed: - s.m_x[j] = s.m_lower_bounds[j]; - break; - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - } - - // lp_assert(is_zero_vector(s.m_b)); - s.solve_Ax_eq_b(); - } - - template - void catch_up_in_lu_in_reverse(const vector & trace_of_basis_change, lp_primal_core_solver & cs) { - // recover the previous working basis - for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) { - unsigned entering = trace_of_basis_change[i-1]; - unsigned leaving = trace_of_basis_change[i-2]; - cs.change_basis_unconditionally(entering, leaving); - } - cs.init_lu(); - } - - //basis_heading is the basis heading of the solver owning trace_of_basis_change - // here we compact the trace as we go to avoid unnecessary column changes - template - void catch_up_in_lu(const vector & trace_of_basis_change, const vector & basis_heading, lp_primal_core_solver & cs) { - if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - cs.change_basis_unconditionally(entering, leaving); - } - if (cs.m_factorization != nullptr) { - delete cs.m_factorization; - cs.m_factorization = nullptr; - } - } else { - indexed_vector w(cs.m_A.row_count()); - // the queues of delayed indices - std::queue entr_q, leav_q; - auto * l = cs.m_factorization; - lp_assert(l->get_status() == LU_status::OK); - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(cs.m_basis_heading[entering] < 0); - lp_assert(cs.m_basis_heading[leaving] >= 0); - if (l->get_status() == LU_status::OK) { - l->prepare_entering(entering, w); // to init vector w - l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); - } - cs.change_basis_unconditionally(entering, leaving); - } - if (l->get_status() != LU_status::OK) { - delete l; - cs.m_factorization = nullptr; - } - } - if (cs.m_factorization == nullptr) { - if (numeric_traits::precise()) - init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); - } - } - - bool no_r_lu() const { - return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; - } - - void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { - r_basis_is_OK(); - bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); - - if (!r) { // it is the case where m_d_solver gives a degenerated basis - prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } - else { - prepare_solver_x_with_signature_tableau(signature); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - lp_assert(r_basis_is_OK()); - } - + bool adjust_x_of_column(unsigned j) { /* if (m_r_solver.m_basis_heading[j] >= 0) { @@ -478,58 +315,6 @@ public: return true; } - - bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { - lp_assert(r_basis_is_OK()); - // the queues of delayed indices - std::queue entr_q, leav_q; - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(m_r_solver.m_basis_heading[entering] < 0); - lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); - m_r_solver.change_basis_unconditionally(entering, leaving); - if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { - // unroll the last step - m_r_solver.change_basis_unconditionally(leaving, entering); -#ifdef Z3DEBUG - bool t = -#endif - m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); -#ifdef Z3DEBUG - lp_assert(t); -#endif - return false; - } - } - lp_assert(r_basis_is_OK()); - return true; - } - bool r_basis_is_OK() const { #ifdef Z3DEBUG @@ -598,27 +383,7 @@ public: } - // returns the trace of basis changes - vector find_solution_signature_with_doubles(lar_solution_signature & signature) { - if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { - vector ret; - return ret; - } - get_bounds_for_double_solver(); - - extract_signature_from_lp_core_solver(m_r_solver, signature); - prepare_solver_x_with_signature(signature, m_d_solver); - m_d_solver.start_tracing_basis_changes(); - m_d_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return vector(); - - m_d_solver.stop_tracing_basis_changes(); - extract_signature_from_lp_core_solver(m_d_solver, signature); - return m_d_solver.m_trace_of_basis_change_vector; - } - - + bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 182029e3e..22dc23bd5 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -78,7 +78,6 @@ void lar_core_solver::prefix_d() { } void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); @@ -127,29 +126,16 @@ void lar_core_solver::solve() { return; } ++settings().stats().m_need_to_solve_inf; - CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); lp_assert( r_basis_is_OK()); - if (need_to_presolve_with_double_solver()) { - TRACE("lar_solver", tout << "presolving\n";); - prefix_d(); - lar_solution_signature solution_signature; - vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); - if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { - m_r_solver.set_status(lp_status::TIME_EXHAUSTED); - return; - } - solve_on_signature_tableau(solution_signature, changes_of_basis); + - lp_assert( r_basis_is_OK()); - } else { - - if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? - m_r_solver.find_feasible_solution(); - else { - m_r_solver.solve(); - } - lp_assert(r_basis_is_OK()); + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? + m_r_solver.find_feasible_solution(); + else { + m_r_solver.solve(); } + lp_assert(r_basis_is_OK()); + switch (m_r_solver.get_status()) { case lp_status::INFEASIBLE: diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 9f6f2534f..508a06d73 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,8 +23,6 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -33,7 +31,6 @@ template bool lp::lp_core_solver_base::find_x_by_solving(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, // vector&, vector&, @@ -51,26 +48,20 @@ template void lp::lp_core_solver_base::set_non_basic_x_to_correc template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_yB(vector&) const; template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::find_x_by_solving(); -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_yB(vector&) const; template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); -template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, // vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, @@ -106,7 +97,6 @@ template std::string lp::lp_core_solver_base template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::solve_yB(vector&) const; template void lp::lp_core_solver_base::init_lu(); template void lp::lp_core_solver_base::init_lu(); template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; @@ -122,7 +112,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::init_lu(); -template bool lp::lp_core_solver_base >::A_mult_x_is_off_on_index(vector const&) const; template bool lp::lp_core_solver_base >::find_x_by_solving(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index dce805b62..edff349d2 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -154,9 +154,6 @@ public: void fill_cb(vector & y) const; - void solve_yB(vector & y) const; - - void pretty_print(std::ostream & out); void save_state(T * w_buffer, T * d_buffer); @@ -175,10 +172,6 @@ public: void copy_m_ed(T * buffer); void restore_m_ed(T * buffer); - - bool A_mult_x_is_off() const; - - bool A_mult_x_is_off_on_index(const vector & index) const; void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); @@ -317,8 +310,6 @@ public: bool basis_heading_is_correct() const; - void restore_x_and_refactor(int entering, int leaving, X const & t); - void restore_x(unsigned entering, X const & t); void fill_reduced_costs_from_m_y_by_rows(); @@ -435,9 +426,7 @@ public: void snap_xN_to_bounds_and_fill_xB(); void snap_xN_to_bounds_and_free_columns_to_zeroes(); - - void init_reduced_costs_for_one_iteration(); - + non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; void init_lu(); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 51b24128f..ae04bd5ee 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -116,11 +116,6 @@ fill_cb(vector & y) const { } } -template void lp_core_solver_base:: -solve_yB(vector & y) const { - fill_cb(y); // now y = cB, that is the projection of costs to basis - m_factorization->solve_yB_with_error_check(y, m_basis); -} // template void lp_core_solver_base:: // update_index_of_ed() { @@ -188,71 +183,6 @@ restore_m_ed(T * buffer) { } } -template bool lp_core_solver_base:: -A_mult_x_is_off() const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_m(); i++) { - X delta = /*m_b[i] */- m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; - } - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i = 0; i < m_m(); i++) { - X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); - auto eps = feps /* * (one + T(0.1) * abs(m_b[i])) */; - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} -template bool lp_core_solver_base:: -A_mult_x_is_off_on_index(const vector & index) const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) return false; -#if RUN_A_MULT_X_IS_OFF_FOR_PRECESE - for (unsigned i : index) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; -#endif - // todo(levnach) run on m_ed.m_index only !!!!! - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i : index) { - X delta = abs(/*m_b[i] -*/ m_A.dot_product_with_row(i, m_x)); - auto eps = feps /* *(one + T(0.1) * abs(m_b[i])) */; - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} - template void lp_core_solver_base:: @@ -292,7 +222,7 @@ print_statistics(char const* str, X cost, std::ostream & out) { if (str!= nullptr) out << str << " "; out << "iterations = " << (total_iterations() - 1) << ", cost = " << T_to_string(cost) - << ", nonzeros = " << (m_factorization != nullptr? m_factorization->get_number_of_nonzeroes() : m_A.number_of_non_zeroes()) << std::endl; + << ", nonzeros = " << m_A.number_of_non_zeroes() << std::endl; } template bool lp_core_solver_base:: @@ -411,7 +341,7 @@ rs_minus_Anx(vector & rs) { template bool lp_core_solver_base:: find_x_by_solving() { solve_Ax_eq_b(); - return !A_mult_x_is_off(); + return true; } template bool lp_core_solver_base::column_is_feasible(unsigned j) const { @@ -591,24 +521,6 @@ template bool lp_core_solver_base:: return true; } -template void lp_core_solver_base:: -restore_x_and_refactor(int entering, int leaving, X const & t) { - this->restore_basis_change(entering, leaving); - restore_x(entering, t); - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (m_factorization->get_status() == LU_status::Degenerated) { - LP_OUT(m_settings, "cannot refactor" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } - // solve_Ax_eq_b(); - if (A_mult_x_is_off()) { - LP_OUT(m_settings, "cannot restore solution" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } -} - template void lp_core_solver_base:: restore_x(unsigned entering, X const & t) { if (is_zero(t)) return; @@ -731,11 +643,6 @@ snap_xN_to_bounds_and_free_columns_to_zeroes() { solve_Ax_eq_b(); } -template void lp_core_solver_base:: -init_reduced_costs_for_one_iteration() { - solve_yB(m_y); - fill_reduced_costs_from_m_y_by_rows(); -} template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h index 804879c3d..b78a22147 100644 --- a/src/math/lp/lp_dual_core_solver.h +++ b/src/math/lp/lp_dual_core_solver.h @@ -76,7 +76,7 @@ public: m_a_wave(this->m_m()), m_betas(this->m_m()) { m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); - this->solve_yB(this->m_y); + lp_assert(false); this->init_basic_part_of_basis_heading(); fill_non_basis_with_only_able_to_enter_columns(); } diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h index fd8fc8071..9c39fe195 100644 --- a/src/math/lp/lp_dual_core_solver_def.h +++ b/src/math/lp/lp_dual_core_solver_def.h @@ -74,8 +74,7 @@ template void lp_dual_core_solver::recalculate_xB } template void lp_dual_core_solver::recalculate_d() { - this->solve_yB(this->m_y); - this->fill_reduced_costs_from_m_y_by_rows(); +lp_assert(false) } template void lp_dual_core_solver::init_betas() { @@ -316,15 +315,8 @@ template void lp_dual_core_solver::restore_d() { } template bool lp_dual_core_solver::d_is_correct() { - this->solve_yB(this->m_y); - for (auto j : this->non_basis()) { - T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j); - if (numeric_traits::get_double(abs(d - this->m_d[j])) >= 0.001) { - LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl - << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl); - return false; - } - } + + lp_assert(false); return true; } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7d522f933..19ffaa1ea 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -705,10 +705,7 @@ template unsigned lp_primal_core_solver::solve() return 0; } - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } + do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf" : "feas"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index fa25694ad..736b79db5 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -107,10 +107,6 @@ unsigned lp_primal_core_solver::solve_with_tableau() { return 0; } - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); @@ -183,7 +179,6 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(leaving >= 0 && entering >= 0); lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || @@ -200,7 +195,6 @@ template void lp_primal_core_solver::advance_on_en t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); - CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -224,7 +218,6 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; @@ -296,7 +289,6 @@ template int lp_primal_core_solver::find_leaving_ return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis // this->set_total_iterations(0); @@ -350,7 +342,6 @@ update_x_tableau(unsigned entering, const X& delta) { this->insert_column_into_inf_set(j); } } - CASSERT("A_off", this->A_mult_x_is_off() == false); } template void lp_primal_core_solver:: diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h index b3e2f0c22..18251e3ad 100644 --- a/src/math/lp/lu.h +++ b/src/math/lp/lu.h @@ -173,8 +173,7 @@ public: void print(indexed_vector & w, const vector& basis); void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit - void solve_yB(vector& y); - + void solve_yB_indexed(indexed_vector& y); void add_delta_to_solution_indexed(indexed_vector& y); diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h index d7c0c0c3a..ca34481cc 100644 --- a/src/math/lp/lu_def.h +++ b/src/math/lp/lu_def.h @@ -263,20 +263,6 @@ void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts solve_By_for_T_indexed_only(d, m_settings); } -template -void lu< M>::solve_yB(vector& y) { - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - m_U.solve_y_U(y); // got y*U=cb*R(-1) - m_Q.apply_reverse_from_right_to_T(y); // - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - } -} - template void lu< M>::solve_yB_indexed(indexed_vector& y) { lp_assert(y.is_OK()); @@ -412,55 +398,15 @@ void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { - if (numeric_traits::precise()) { - if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { - solve_yB_indexed(y); - } else { - solve_yB(y.m_data); - y.restore_index_and_clean_from_data(); - } - return; - } - lp_assert(m_y_copy.is_OK()); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { - m_y_copy = y; - solve_yB_indexed(y); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { - find_error_of_yB(m_y_copy.m_data, y.m_data, basis); - solve_yB(m_y_copy.m_data); - add_delta_to_solution(m_y_copy.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - m_y_copy.clear_all(); - } else { - find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy - solve_yB_indexed(m_y_copy); - add_delta_to_solution_indexed(y); - } - lp_assert(m_y_copy.is_OK()); - } else { - solve_yB_with_error_check(y.m_data, basis); - y.restore_index_and_clean_from_data(); - } -} + lp_assert(false); + } // solves y*B = y // y is the input template void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { - if (numeric_traits::precise()) { - solve_yB(y); - return; - } - auto & yc = m_y_copy.m_data; - yc =y; // copy y aside - solve_yB(y); - find_error_of_yB(yc, y, basis); - solve_yB(yc); - add_delta_to_solution(yc, y); - m_y_copy.clear_all(); + lp_assert(false); } template void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { From 1da4c018e458919f012f83057026f4f8913aae4f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:44:22 -0800 Subject: [PATCH 095/220] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 3 - src/math/lp/lp_core_solver_base_def.h | 6 - src/math/lp/lp_dual_core_solver.cpp | 44 -- src/math/lp/lp_dual_core_solver.h | 212 -------- src/math/lp/lp_dual_core_solver_def.h | 699 -------------------------- src/math/lp/static_matrix.cpp | 1 - 8 files changed, 969 deletions(-) delete mode 100644 src/math/lp/lp_dual_core_solver.cpp delete mode 100644 src/math/lp/lp_dual_core_solver.h delete mode 100644 src/math/lp/lp_dual_core_solver_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5719de44f..cc58c9610 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -19,7 +19,6 @@ z3_add_component(lp lar_solver.cpp lar_core_solver.cpp lp_core_solver_base.cpp - lp_dual_core_solver.cpp lp_primal_core_solver.cpp lp_settings.cpp lu.cpp diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 508a06d73..0d9f42974 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -27,7 +27,6 @@ template bool lp::lp_core_solver_base::basis_heading_is_correct( template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; @@ -53,7 +52,6 @@ template bool lp::lp_core_solver_base::basis_heading_is_correc template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); @@ -112,7 +110,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::init_lu(); -template bool lp::lp_core_solver_base >::find_x_by_solving(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index edff349d2..729792c12 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -298,9 +298,6 @@ public: void rs_minus_Anx(vector & rs); - bool find_x_by_solving(); - - bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index ae04bd5ee..e470072e0 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -338,12 +338,6 @@ rs_minus_Anx(vector & rs) { } } -template bool lp_core_solver_base:: -find_x_by_solving() { - solve_Ax_eq_b(); - return true; -} - template bool lp_core_solver_base::column_is_feasible(unsigned j) const { const X& x = this->m_x[j]; switch (this->m_column_types[j]) { diff --git a/src/math/lp/lp_dual_core_solver.cpp b/src/math/lp/lp_dual_core_solver.cpp deleted file mode 100644 index 8cf45f7c6..000000000 --- a/src/math/lp/lp_dual_core_solver.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_dual_core_solver_def.h" -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, - vector&, - vector&, - vector&, - vector &, - vector &, - vector&, - vector&, - vector&, - vector&, - lp::lp_settings&, const lp::column_namer&); -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h deleted file mode 100644 index b78a22147..000000000 --- a/src/math/lp/lp_dual_core_solver.h +++ /dev/null @@ -1,212 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include -#include -#include -#include -#include "util/vector.h" - -namespace lp { -template -class lp_dual_core_solver:public lp_core_solver_base { -public: - vector & m_can_enter_basis; - int m_r; // the row of the leaving column - int m_p; // leaving column; that is m_p = m_basis[m_r] - T m_delta; // the offset of the leaving basis variable - int m_sign_of_alpha_r; // see page 27 - T m_theta_D; - T m_theta_P; - int m_q; - // todo : replace by a vector later - std::set m_breakpoint_set; // it is F in "Progress in the dual simplex method ..." - std::set m_flipped_boxed; - std::set m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight - vector m_a_wave; - vector m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B - T m_harris_tolerance; - std::set m_forbidden_rows; - - lp_dual_core_solver(static_matrix & A, - vector & can_enter_basis, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - vector & column_type_array, - vector & lower_bound_values, - vector & upper_bound_values, - lp_settings & settings, - const column_namer & column_names): - lp_core_solver_base(A, - // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), - m_can_enter_basis(can_enter_basis), - m_a_wave(this->m_m()), - m_betas(this->m_m()) { - m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); - lp_assert(false); - this->init_basic_part_of_basis_heading(); - fill_non_basis_with_only_able_to_enter_columns(); - } - - void init_a_wave_by_zeros(); - - void fill_non_basis_with_only_able_to_enter_columns() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue; - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } - - void restore_non_basis(); - - bool update_basis(int entering, int leaving); - - void recalculate_xB_and_d(); - - void recalculate_d(); - - void init_betas(); - - void adjust_xb_for_changed_xn_and_init_betas(); - - void start_with_initial_basis_and_make_it_dual_feasible(); - - bool done(); - - T get_edge_steepness_for_lower_bound(unsigned p); - - T get_edge_steepness_for_upper_bound(unsigned p); - - T pricing_for_row(unsigned i); - - void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows); - - bool advance_on_known_p(); - - int define_sign_of_alpha_r(); - - bool can_be_breakpoint(unsigned j); - - void fill_breakpoint_set(); - - void DSE_FTran(); - T get_delta(); - - void restore_d(); - - bool d_is_correct(); - - void xb_minus_delta_p_pivot_column(); - - void update_betas(); - - void apply_flips(); - - void snap_xN_column_to_bounds(unsigned j); - - void snap_xN_to_bounds(); - - void init_beta_precisely(unsigned i); - - void init_betas_precisely(); - - // step 7 of the algorithm from Progress - bool basis_change_and_update(); - - void revert_to_previous_basis(); - - non_basic_column_value_position m_entering_boundary_position; - bool update_basis_and_x_local(int entering, int leaving, X const & tt); - void recover_leaving(); - - bool problem_is_dual_feasible() const; - - bool snap_runaway_nonbasic_column(unsigned); - - bool snap_runaway_nonbasic_columns(); - - unsigned get_number_of_rows_to_try_for_leaving(); - - void update_a_wave(const T & del, unsigned j) { - this->m_A.add_column_to_vector(del, j, & m_a_wave[0]); - } - - bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); - - void set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - - // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound - T signed_span_of_boxed(unsigned j) { - return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); - } - - void add_tight_breakpoints_and_q_to_flipped_set(); - - T delta_lost_on_flips_of_tight_breakpoints(); - - bool tight_breakpoinst_are_all_boxed(); - - T calculate_harris_delta_on_breakpoint_set(); - - void fill_tight_set_on_harris_delta(const T & harris_delta ); - - void find_q_on_tight_set(); - - void find_q_and_tight_set(); - - void erase_tight_breakpoints_and_q_from_breakpoint_set(); - - bool ratio_test(); - - void process_flipped(); - void update_d_and_xB(); - - void calculate_beta_r_precisely(); - // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - - void update_xb_after_bound_flips(); - - void one_iteration(); - - void solve(); - - bool lower_bounds_are_set() const override { return true; } -}; -} diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h deleted file mode 100644 index 9c39fe195..000000000 --- a/src/math/lp/lp_dual_core_solver_def.h +++ /dev/null @@ -1,699 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_dual_core_solver.h" - -namespace lp { - -template void lp_dual_core_solver::init_a_wave_by_zeros() { - unsigned j = this->m_m(); - while (j--) { - m_a_wave[j] = numeric_traits::zero(); - } -} - -template void lp_dual_core_solver::restore_non_basis() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 ) continue; - if (m_can_enter_basis[j]) { - lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } -} - -template bool lp_dual_core_solver::update_basis(int entering, int leaving) { - // the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done - if (this->m_refactor_counter++ < 200) { - this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w); - if (this->m_factorization->get_status() == LU_status::OK) { - this->m_factorization->change_basis(entering, leaving); - return true; - } - } - // need to refactor - this->m_factorization->change_basis(entering, leaving); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings); - this->m_refactor_counter = 0; - if (this->m_factorization->get_status() != LU_status::OK) { - LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl); - this->m_iters_with_no_cost_growing++; - return false; - } - return true; -} - -template void lp_dual_core_solver::recalculate_xB_and_d() { - this->solve_Ax_eq_b(); - recalculate_d(); -} - -template void lp_dual_core_solver::recalculate_d() { -lp_assert(false) -} - -template void lp_dual_core_solver::init_betas() { - // todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation - // the current implementation is not good enough: todo - unsigned i = this->m_m(); - while (i--) { - m_betas[i] = 1; - } -} - -template void lp_dual_core_solver::adjust_xb_for_changed_xn_and_init_betas() { - this->solve_Ax_eq_b(); - init_betas(); -} - -template void lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible() { - this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29, - // however this version does not require that m_x is the solution of Ax = 0 beforehand - adjust_xb_for_changed_xn_and_init_betas(); -} - -template bool lp_dual_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL) { - return true; - } - - return false; // todo, need to be more cases -} - -template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_lower_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_upper_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::pricing_for_row(unsigned i) { - unsigned p = this->m_basis[i]; - switch (this->m_column_types[p]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - case column_type::lower_bound: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::free_column: - lp_assert(numeric_traits::is_zero(this->m_d[p])); - return numeric_traits::zero(); - default: - lp_unreachable(); - } - lp_unreachable(); - return numeric_traits::zero(); -} - -template void lp_dual_core_solver::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { - m_r = -1; - T steepest_edge_max = numeric_traits::zero(); - unsigned initial_offset_in_rows = offset_in_rows; - unsigned i = offset_in_rows; - unsigned rows_left = number_of_rows_to_try; - do { - if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) { - if (++i == this->m_m()) { - i = 0; - } - continue; - } - T se = pricing_for_row(i); - if (se > steepest_edge_max) { - steepest_edge_max = se; - m_r = i; - if (rows_left > 0) { - rows_left--; - } - } - if (++i == this->m_m()) { - i = 0; - } - } while (i != initial_offset_in_rows && rows_left); - if (m_r == -1) { - if (this->get_status() != lp_status::UNSTABLE) { - this->set_status(lp_status::OPTIMAL); - } - } else { - m_p = this->m_basis[m_r]; - m_delta = get_delta(); - if (advance_on_known_p()){ - m_forbidden_rows.clear(); - return; - } - // failure in advance_on_known_p - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { - return; - } - this->set_status(lp_status::UNSTABLE); - m_forbidden_rows.insert(m_r); - } -} - - // this calculation is needed for the steepest edge update, - // it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle -template void lp_dual_core_solver::DSE_FTran() { // todo, see algorithm 7 from page 35 - this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings); -} - -template bool lp_dual_core_solver::advance_on_known_p() { - - return false; -} - -template int lp_dual_core_solver::define_sign_of_alpha_r() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - case column_type::fixed: - if (this->x_below_low_bound(m_p)) { - return -1; - } - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return -1; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - default: - lp_unreachable(); - } - lp_unreachable(); - return 0; -} - -template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { - if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - case column_type::upper_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - return lower_bound == grawing; - } - case column_type::fixed: // is always dual feasible so we ignore it - return false; - case column_type::free_column: - return true; - default: - return false; - } -} - -template void lp_dual_core_solver::fill_breakpoint_set() { - m_breakpoint_set.clear(); - for (unsigned j : this->non_basis()) { - if (can_be_breakpoint(j)) { - m_breakpoint_set.insert(j); - } - } -} - -// template void lp_dual_core_solver::FTran() { -// this->solve_Bd(m_q); -// } - -template T lp_dual_core_solver::get_delta() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - if (this->x_above_upper_bound(m_p)) { - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return get_edge_steepness_for_upper_bound(m_p); - } - lp_unreachable(); - case column_type::fixed: - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - default: - lp_unreachable(); - } - lp_unreachable(); - return zero_of_type(); -} - -template void lp_dual_core_solver::restore_d() { - this->m_d[m_p] = numeric_traits::zero(); - for (auto j : this->non_basis()) { - this->m_d[j] += m_theta_D * this->m_pivot_row[j]; - } -} - -template bool lp_dual_core_solver::d_is_correct() { - - lp_assert(false); - return true; -} - -template void lp_dual_core_solver::xb_minus_delta_p_pivot_column() { - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i]; - } -} - -template void lp_dual_core_solver::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized - T one_over_arq = numeric_traits::one() / this->m_pivot_row[m_q]; - T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq); - T k = -2 * one_over_arq; - unsigned i = this->m_m(); - while (i--) { - if (static_cast(i) == m_r) continue; - T a = this->m_ed[i]; - m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]); - if (m_betas[i] < T(0.0001)) - m_betas[i] = T(0.0001); - } -} - -template void lp_dual_core_solver::apply_flips() { - for (unsigned j : m_flipped_boxed) { - lp_assert(this->x_is_at_bound(j)); - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - } else { - this->m_x[j] = this->m_lower_bounds[j]; - } - } -} - -template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { - switch (this->m_column_type[j]) { - case column_type::fixed: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - } else { - this->m_x[j] = this->m_upper_bounds[j]; - } - break; - case column_type::lower_bound: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::upper_bound: - this->m_x[j] = this->m_upper_bounds[j]; - break; - case column_type::free_column: - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::snap_xN_to_bounds() { - for (auto j : this->non_basis()) { - snap_xN_column_to_bounds(j); - } -} - -template void lp_dual_core_solver::init_beta_precisely(unsigned i) { - vector vec(this->m_m(), numeric_traits::zero()); - vec[i] = numeric_traits::one(); - this->m_factorization->solve_yB_with_error_check(vec, this->m_basis); - T beta = numeric_traits::zero(); - for (T & v : vec) { - beta += v * v; - } - this->m_betas[i] =beta; -} - -template void lp_dual_core_solver::init_betas_precisely() { - unsigned i = this->m_m(); - while (i--) { - init_beta_precisely(i); - } -} - -// step 7 of the algorithm from Progress -template bool lp_dual_core_solver::basis_change_and_update() { - - return true; -} - -template void lp_dual_core_solver::recover_leaving() { - switch (m_entering_boundary_position) { - case at_lower_bound: - case at_fixed: - this->m_x[m_q] = this->m_lower_bounds[m_q]; - break; - case at_upper_bound: - this->m_x[m_q] = this->m_upper_bounds[m_q]; - break; - case free_of_bounds: - this->m_x[m_q] = zero_of_type(); - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::revert_to_previous_basis() { - LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl); - this->change_basis_unconditionally(m_p, m_q); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure - return; - } - recover_leaving(); - if (!this->find_x_by_solving()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - recalculate_xB_and_d(); - init_betas_precisely(); -} - -// returns true if the column has been snapped -template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - break; - case column_type::boxed: - { - bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); - if (closer_to_lower_bound) { - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } else { - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } - } - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - return true; - } - break; - default: - break; - } - return false; -} - - -template bool lp_dual_core_solver::problem_is_dual_feasible() const { - for (unsigned j : this->non_basis()){ - if (!this->column_is_dual_feasible(j)) { - return false; - } - } - return true; -} - -template unsigned lp_dual_core_solver::get_number_of_rows_to_try_for_leaving() { - unsigned s = this->m_m(); - if (this->m_m() > 300) { - s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check); - } - return this->m_settings.random_next() % s + 1; -} - -template bool lp_dual_core_solver::delta_keeps_the_sign(int initial_delta_sign, const T & delta) { - if (numeric_traits::precise()) - return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || - ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); - - double del = numeric_traits::get_double(delta); - return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) || - ((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1)); -} - -template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - this->set_status(lp_status::DUAL_UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); - } -} - -template void lp_dual_core_solver::add_tight_breakpoints_and_q_to_flipped_set() { - m_flipped_boxed.insert(m_q); - for (auto j : m_tight_set) { - m_flipped_boxed.insert(j); - } -} - -template T lp_dual_core_solver::delta_lost_on_flips_of_tight_breakpoints() { - T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]); - for (auto j : m_tight_set) { - ret += abs(this->bound_span(j) * this->m_pivot_row[j]); - } - return ret; -} - -template bool lp_dual_core_solver::tight_breakpoinst_are_all_boxed() { - if (this->m_column_types[m_q] != column_type::boxed) return false; - for (auto j : m_tight_set) { - if (this->m_column_types[j] != column_type::boxed) return false; - } - return true; -} - -template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { - bool first_time = true; - T ret = zero_of_type(); - lp_assert(m_breakpoint_set.size() > 0); - for (auto j : m_breakpoint_set) { - T t; - if (this->x_is_at_lower_bound(j)) { - t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); - } else { - t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); - } - if (first_time) { - ret = t; - first_time = false; - } else if (t < ret) { - ret = t; - } - } - return ret; -} - -template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ - m_tight_set.clear(); - for (auto j : m_breakpoint_set) { - if (this->x_is_at_lower_bound(j)) { - if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } else { - if (abs(std::min(this->m_d[j], numeric_traits::zero() ) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } - } -} - -template void lp_dual_core_solver::find_q_on_tight_set() { - m_q = -1; - T max_pivot; - for (auto j : m_tight_set) { - T r = abs(this->m_pivot_row[j]); - if (m_q != -1) { - if (r > max_pivot) { - max_pivot = r; - m_q = j; - } - } else { - max_pivot = r; - m_q = j; - } - } - m_tight_set.erase(m_q); - lp_assert(m_q != -1); -} - -template void lp_dual_core_solver::find_q_and_tight_set() { - T harris_del = calculate_harris_delta_on_breakpoint_set(); - fill_tight_set_on_harris_delta(harris_del); - find_q_on_tight_set(); - m_entering_boundary_position = this->get_non_basic_column_value_position(m_q); -} - -template void lp_dual_core_solver::erase_tight_breakpoints_and_q_from_breakpoint_set() { - m_breakpoint_set.erase(m_q); - for (auto j : m_tight_set) { - m_breakpoint_set.erase(j); - } -} - -template bool lp_dual_core_solver::ratio_test() { - m_sign_of_alpha_r = define_sign_of_alpha_r(); - fill_breakpoint_set(); - m_flipped_boxed.clear(); - int initial_delta_sign = m_delta >= numeric_traits::zero()? 1: -1; - do { - if (m_breakpoint_set.empty()) { - set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - return false; - } - this->set_status(lp_status::FEASIBLE); - find_q_and_tight_set(); - if (!tight_breakpoinst_are_all_boxed()) break; - T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; - if (!delta_keeps_the_sign(initial_delta_sign, del)) break; - if (m_tight_set.size() + 1 == m_breakpoint_set.size()) { - break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis - } - // we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set - add_tight_breakpoints_and_q_to_flipped_set(); - m_delta = del; - erase_tight_breakpoints_and_q_from_breakpoint_set(); - } while (true); - m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q]; - return true; -} - -template void lp_dual_core_solver::process_flipped() { - init_a_wave_by_zeros(); - for (auto j : m_flipped_boxed) { - update_a_wave(signed_span_of_boxed(j), j); - } -} -template void lp_dual_core_solver::update_d_and_xB() { - for (auto j : this->non_basis()) { - this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; - } - this->m_d[m_p] = - m_theta_D; - if (!m_flipped_boxed.empty()) { - process_flipped(); - update_xb_after_bound_flips(); - } -} - -template void lp_dual_core_solver::calculate_beta_r_precisely() { - T t = numeric_traits::zero(); - unsigned i = this->m_m(); - while (i--) { - T b = this->m_pivot_row_of_B_1[i]; - t += b * b; - } - m_betas[m_r] = t; -} -// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - -template void lp_dual_core_solver::update_xb_after_bound_flips() { - this->m_factorization->solve_By(m_a_wave); - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_a_wave[i]; - } -} - -template void lp_dual_core_solver::one_iteration() { - unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); - unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - number_of_rows_to_try = this->m_m(); - } else { - this->set_status(lp_status::FEASIBLE); - } - pricing_loop(number_of_rows_to_try, offset_in_rows); - lp_assert(problem_is_dual_feasible()); -} - -template void lp_dual_core_solver::solve() { // see the page 35 - lp_assert(d_is_correct()); - lp_assert(problem_is_dual_feasible()); - lp_assert(this->basis_heading_is_correct()); - //this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){ - return; - } - one_iteration(); - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - ); -} -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index cde96277b..93360b914 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -23,7 +23,6 @@ Revision History: #include #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/lp_dual_core_solver.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/scaler.h" #include "math/lp/lar_solver.h" From bfe73c01a6e3fbc309213832c051eeb093391b9e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 07:56:04 -0800 Subject: [PATCH 096/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 41 --------------------------- src/math/lp/lar_solver.cpp | 5 ---- src/math/lp/lar_solver.h | 1 - src/math/lp/lp_core_solver_base.cpp | 14 --------- src/math/lp/lp_core_solver_base.h | 10 +------ src/math/lp/lp_core_solver_base_def.h | 40 -------------------------- 6 files changed, 1 insertion(+), 110 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 999eef13b..9168862c6 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -274,47 +274,6 @@ public: } - - void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { - lp_assert(m_r_solver.inf_set_is_correct()); - for (auto &t : signature) { - unsigned j = t.first; - if (m_r_heading[j] >= 0) - continue; - auto pos_type = t.second; - numeric_pair delta; - if (!update_xj_and_get_delta(j, pos_type, delta)) - continue; - for (const auto & cc : m_r_solver.m_A.m_columns[j]){ - unsigned i = cc.var(); - unsigned jb = m_r_solver.m_basis[i]; - m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); - } - - } - lp_assert(m_r_solver.inf_set_is_correct()); - } - - - - bool adjust_x_of_column(unsigned j) { - /* - if (m_r_solver.m_basis_heading[j] >= 0) { - return false; - } - - if (m_r_solver.column_is_feasible(j)) { - return false; - } - - m_r_solver.snap_column_to_bound_tableau(j); - lp_assert(m_r_solver.column_is_feasible(j)); - m_r_solver.m_inf_set.erase(j); - */ - lp_assert(false); - return true; - } - bool r_basis_is_OK() const { #ifdef Z3DEBUG diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index d9961eaa8..48c575184 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -643,11 +643,6 @@ namespace lp { bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } - - void lar_solver::adjust_x_of_column(unsigned j) { - lp_assert(false); - } - bool lar_solver::row_is_correct(unsigned i) const { numeric_pair r = zero_of_type>(); for (const auto& c : A_r().m_rows[i]) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 513659368..09612d905 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -210,7 +210,6 @@ class lar_solver : public column_namer { void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau_costs() const; void detect_rows_of_column_with_bound_change(unsigned j); - void adjust_x_of_column(unsigned j); bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 0d9f42974..90101e998 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -24,7 +24,6 @@ Revision History: #include #include "math/lp/lp_core_solver_base_def.h" template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; @@ -44,12 +43,9 @@ template bool lp::lp_core_solver_base::print_statistics_with_ite template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); @@ -57,7 +53,6 @@ template void lp::lp_core_solver_base::restore_x(unsigned int, template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); -template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, @@ -85,18 +80,10 @@ template lp::lp_core_solver_base::lp_core_solver_base( template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(double*, double*); -template void lp::lp_core_solver_base::save_state(double*, double*); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base::save_state(lp::mpq*, lp::mpq*); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base::init_lu(); -template void lp::lp_core_solver_base::init_lu(); template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; @@ -109,7 +96,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); -template void lp::lp_core_solver_base >::init_lu(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 729792c12..e54c88489 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -156,10 +156,6 @@ public: void pretty_print(std::ostream & out); - void save_state(T * w_buffer, T * d_buffer); - - void restore_state(T * w_buffer, T * d_buffer); - X get_cost() const { return dot_product(m_costs, m_x); } @@ -173,8 +169,6 @@ public: void restore_m_ed(T * buffer); - void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { @@ -422,11 +416,9 @@ public: void snap_non_basic_x_to_bound_and_free_to_zeroes(); void snap_xN_to_bounds_and_fill_xB(); - void snap_xN_to_bounds_and_free_columns_to_zeroes(); - + non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - void init_lu(); int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index e470072e0..bc902e8b0 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -136,17 +136,6 @@ pretty_print(std::ostream & out) { pp.print(); } -template void lp_core_solver_base:: -save_state(T * w_buffer, T * d_buffer) { - copy_m_w(w_buffer); - copy_m_ed(d_buffer); -} - -template void lp_core_solver_base:: -restore_state(T * w_buffer, T * d_buffer) { - restore_m_w(w_buffer); - restore_m_ed(d_buffer); -} template void lp_core_solver_base:: copy_m_w(T * buffer) { @@ -185,26 +174,6 @@ restore_m_ed(T * buffer) { -template void lp_core_solver_base:: -calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { - m_pivot_row.clear(); - - for (unsigned i : m_pivot_row_of_B_1.m_index) { - const T & pi_1 = m_pivot_row_of_B_1[i]; - if (numeric_traits::is_zero(pi_1)) { - continue; - } - for (auto & c : m_A.m_rows[i]) { - unsigned j = c.var(); - if (m_basis_heading[j] < 0) { - m_pivot_row.add_value_at_index_with_drop_tolerance(j, c.coeff() * pi_1); - } - } - } - if (precise()) { - m_rows_nz[pivot_row] = m_pivot_row.m_index.size(); - } -} template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { @@ -631,11 +600,6 @@ snap_xN_to_bounds_and_fill_xB() { solve_Ax_eq_b(); } -template void lp_core_solver_base:: -snap_xN_to_bounds_and_free_columns_to_zeroes() { - snap_non_basic_x_to_bound_and_free_to_zeroes(); - solve_Ax_eq_b(); -} template non_basic_column_value_position lp_core_solver_base:: @@ -661,10 +625,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template void lp_core_solver_base::init_lu() { - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); -} - template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; const T & row_p = this->m_pivot_row[entering]; From 6132bf93f7b4adddc039b00da4ebe1f606e2b2e2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:11:04 -0800 Subject: [PATCH 097/220] rm lu Signed-off-by: Lev Nachmanson --- src/math/lp/lp_core_solver_base.cpp | 1 - src/math/lp/lp_core_solver_base.h | 7 ++-- src/math/lp/lp_core_solver_base_def.h | 46 +------------------------ src/math/lp/lp_primal_core_solver_def.h | 21 ++--------- 4 files changed, 6 insertions(+), 69 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 90101e998..b76976d37 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -61,7 +61,6 @@ template lp::lp_core_solver_base >::lp_core_s const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index e54c88489..c9b42f28b 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -74,7 +74,7 @@ public: vector & m_x; // a feasible solution, the fist time set in the constructor vector & m_costs; lp_settings & m_settings; - lu> * m_factorization = nullptr; + vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis const column_namer & m_column_names; @@ -134,7 +134,7 @@ public: void init(); virtual ~lp_core_solver_base() { - delete m_factorization; + } vector & non_basis() { @@ -413,9 +413,6 @@ public: } - void snap_non_basic_x_to_bound_and_free_to_zeroes(); - void snap_xN_to_bounds_and_fill_xB(); - non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index bc902e8b0..01aa9802e 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -554,54 +554,10 @@ find_error_in_BxB(vector& rs){ // recalculates the projection of x to B, such that Ax = b template void lp_core_solver_base:: solve_Ax_eq_b() { - if (numeric_traits::precise()) { - vector rs(m_m()); - rs_minus_Anx(rs); - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - } - else { - vector rs(m_m()); - rs_minus_Anx(rs); - vector rrs = rs; // another copy of rs - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - find_error_in_BxB(rrs); - m_factorization->solve_By(rrs); - add_delta_to_xB(rrs); - } + lp_assert(false); } - - -template void lp_core_solver_base:: -snap_non_basic_x_to_bound_and_free_to_zeroes() { - for (unsigned j : non_basis()) { - lp_assert(j < m_x.size()); - switch (m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - break; - default: - m_x[j] = zero_of_type(); - break; - } - } -} -template void lp_core_solver_base:: -snap_xN_to_bounds_and_fill_xB() { - snap_non_basic_x_to_bound(); - solve_Ax_eq_b(); -} - - - template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 19ffaa1ea..7764ce6d2 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -642,11 +642,7 @@ template void lp_primal_core_solver::init_run( template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - lp_assert(numeric_traits::precise() == false); - lp_assert(this->m_ed.is_OK()); - lp_assert(m_beta.is_OK()); - m_beta = this->m_ed; - this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); + lp_assert(false); } template @@ -758,10 +754,7 @@ template unsigned lp_primal_core_solver::solve() } template void lp_primal_core_solver::delete_factorization() { - if (this->m_factorization != nullptr) { - delete this->m_factorization; - this->m_factorization = nullptr; - } + lp_assert(false); } // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" @@ -776,15 +769,7 @@ template void lp_primal_core_solver::init_column_ // debug only template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(numeric_traits::precise() == false); - indexed_vector w(this->m_m()); - this->m_A.copy_column_to_vector(j, w); - vector d(this->m_m()); - this->m_factorization->solve_Bd_when_w_is_ready(d, w); - T ret = zero_of_type(); - for (auto v : d) - ret += v*v; - return ret+1; + lp_assert(false); } template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { From 377ceba6d58e436a05092b2f57ebe5f068cc78eb Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:33:53 -0800 Subject: [PATCH 098/220] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_core_solver_base.h | 14 +- src/math/lp/lp_primal_core_solver.h | 4 - src/math/lp/lp_primal_core_solver_def.h | 13 +- .../lp/lp_primal_core_solver_tableau_def.h | 6 +- src/math/lp/lp_settings.h | 1 - src/math/lp/lp_settings_def.h | 2 - src/math/lp/lu.cpp | 81 -- src/math/lp/lu.h | 325 -------- src/math/lp/lu_def.h | 720 ------------------ src/math/lp/row_eta_matrix.cpp | 1 - src/math/lp/square_sparse_matrix.cpp | 1 - src/math/lp/square_sparse_matrix_def.h | 2 + src/smt/theory_lra.cpp | 2 +- 14 files changed, 22 insertions(+), 1151 deletions(-) delete mode 100644 src/math/lp/lu.cpp delete mode 100644 src/math/lp/lu.h delete mode 100644 src/math/lp/lu_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index cc58c9610..970737367 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -21,7 +21,6 @@ z3_add_component(lp lp_core_solver_base.cpp lp_primal_core_solver.cpp lp_settings.cpp - lu.cpp lp_utils.cpp matrix.cpp mon_eq.cpp diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index c9b42f28b..0e8a2abdf 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -25,11 +25,21 @@ Revision History: #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/numeric_pair.h" #include "math/lp/static_matrix.h" -#include "math/lp/lu.h" #include "math/lp/permutation_matrix.h" #include "math/lp/column_namer.h" +#include "math/lp/u_set.h" + namespace lp { +template +X dot_product(const vector & a, const vector & b) { + lp_assert(a.size() == b.size()); + auto r = zero_of_type(); + for (unsigned i = 0; i < a.size(); i++) { + r += a[i] * b[i]; + } + return r; +} template // X represents the type of the x variable and the bounds class lp_core_solver_base { @@ -156,6 +166,8 @@ public: void pretty_print(std::ostream & out); + + X get_cost() const { return dot_product(m_costs, m_x); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index abee0cc6e..38a2aa5f5 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -29,7 +29,6 @@ Revision History: #include #include #include -#include "math/lp/lu.h" #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" @@ -418,9 +417,6 @@ public: // returns the number of iterations unsigned solve(); - lu> * factorization() {return nullptr;} - - void delete_factorization(); // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" void init_column_norms(); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7764ce6d2..3967b6e66 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -26,6 +26,7 @@ Revision History: #include #include #include "math/lp/lp_primal_core_solver.h" +#include "math/lp/dense_matrix.h" namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis @@ -733,8 +734,7 @@ template unsigned lp_primal_core_solver::solve() default: break; // do nothing } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && + } while ( this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL @@ -745,18 +745,13 @@ template unsigned lp_primal_core_solver::solve() && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || + lp_assert( this->current_x_is_feasible() == false || this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } -template void lp_primal_core_solver::delete_factorization() { - lp_assert(false); -} - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" template void lp_primal_core_solver::init_column_norms() { lp_assert(numeric_traits::precise() == false); @@ -860,7 +855,7 @@ template void lp_primal_core_solver::fill_breakpo template bool lp_primal_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; + if (this->get_status() == lp_status::OPTIMAL) return true; if (this->get_status() == lp_status::INFEASIBLE) { return true; } diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 736b79db5..e742b0cd0 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -157,8 +157,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->set_status(lp_status::CANCELLED); break; // from the loop } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && + } while ( this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL @@ -168,8 +167,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) ); - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || + lp_assert( this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 360ef99bf..cb60eebc9 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -69,7 +69,6 @@ enum class lp_status { DUAL_UNBOUNDED, OPTIMAL, FEASIBLE, - FLOATING_POINT_ERROR, TIME_EXHAUSTED, EMPTY, UNSTABLE, diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index 58b37a19d..bb87109f6 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -45,7 +45,6 @@ const char* lp_status_to_string(lp_status status) { case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; case lp_status::OPTIMAL: return "OPTIMAL"; case lp_status::FEASIBLE: return "FEASIBLE"; - case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; case lp_status::EMPTY: return "EMPTY"; case lp_status::UNSTABLE: return "UNSTABLE"; @@ -62,7 +61,6 @@ lp_status lp_status_from_string(std::string status) { if (status == "UNBOUNDED") return lp_status::UNBOUNDED; if (status == "OPTIMAL") return lp_status::OPTIMAL; if (status == "FEASIBLE") return lp_status::FEASIBLE; - if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; lp_unreachable(); diff --git a/src/math/lp/lu.cpp b/src/math/lp/lu.cpp deleted file mode 100644 index 313fa9901..000000000 --- a/src/math/lp/lu.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lu_def.h" -namespace lp { -template double dot_product(vector const&, vector const&); -template lu>::lu(static_matrix const&, vector&, lp_settings&); -template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::replace_column(double, indexed_vector&, unsigned); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template lu>::~lu(); -template mpq dot_product(vector const&, vector const&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); -template void print_matrix>(square_sparse_matrix&, std::ostream & out); -template void print_matrix>(static_matrix&, std::ostream&); -template void print_matrix >(static_matrix&, std::ostream&); -template void print_matrix>(static_matrix&, std::ostream & out); -#ifdef Z3DEBUG -template bool lu>::is_correct(const vector& basis); -template bool lu>::is_correct( vector const &); -template dense_matrix get_B>(lu>&, const vector& basis); -template dense_matrix get_B>(lu>&, vector const&); - -#endif - -template bool lu>::pivot_the_row(int); // NOLINT -template void lu>::init_vector_w(unsigned int, indexed_vector&); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); -template void lu>::replace_column(mpq, indexed_vector&, unsigned); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::init_vector_w(unsigned int, indexed_vector&); -template void lu >::replace_column(mpq, indexed_vector&, unsigned); -template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); -template void lu >::solve_By(vector&); -template void lu >::solve_By_when_y_is_ready_for_X(vector&); -template void lu >::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_yB_indexed(indexed_vector&); -template void lu >::solve_yB_indexed(indexed_vector&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -#ifdef Z3DEBUG -template void print_matrix>(tail_matrix&, std::ostream&); -#endif -} diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h deleted file mode 100644 index 18251e3ad..000000000 --- a/src/math/lp/lu.h +++ /dev/null @@ -1,325 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - for matrix B we have - t0*...*tn-1*B = Q*U*R - here ti are matrices corresponding to pivot operations, - including columns and rows swaps, - or a multiplication matrix row by a number - Q, R - permutations and U is an upper triangular matrix -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -#include "util/vector.h" -#include "util/debug.h" -#include -#include -#include "math/lp/square_sparse_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include "math/lp/numeric_pair.h" -#include -#include -#include "math/lp/row_eta_matrix.h" -#include "math/lp/square_dense_submatrix.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); - -template -void print_matrix(M &m, std::ostream & out); - -template -X dot_product(const vector & a, const vector & b) { - lp_assert(a.size() == b.size()); - auto r = zero_of_type(); - for (unsigned i = 0; i < a.size(); i++) { - r += a[i] * b[i]; - } - return r; -} - - -template -class one_elem_on_diag: public tail_matrix { - unsigned m_i; - T m_val; -public: - one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) { -#ifdef Z3DEBUG - m_one_over_val = numeric_traits::one() / m_val; -#endif - } - - bool is_dense() const override { return false; } - - one_elem_on_diag(const one_elem_on_diag & o); - -#ifdef Z3DEBUG - unsigned m_m; - unsigned m_n; - void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; } - void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; } - T m_one_over_val; - - T get_elem (unsigned i, unsigned j) const override; - - unsigned row_count() const override { return m_m; } // not defined } - unsigned column_count() const override { return m_m; } // not defined } -#endif - void apply_from_left(vector & w, lp_settings &) override { - w[m_i] /= m_val; - } - - void apply_from_right(vector & w) override { - w[m_i] /= m_val; - } - - void apply_from_right(indexed_vector & w) override { - if (is_zero(w.m_data[m_i])) - return; - auto & v = w.m_data[m_i] /= m_val; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - w.erase_from_index(m_i); - v = zero_of_type(); - } - } - - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_i = p.apply_reverse(m_i); - -#ifdef Z3DEBUG - // lp_assert(*this == deb); -#endif - } -}; // end of one_elem_on_diag - -enum class LU_status { OK, Degenerated}; - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -class lu { - LU_status m_status; -public: - typedef typename M::coefftype T; - typedef typename M::argtype X; - - // the fields - unsigned m_dim; - const M & m_A; - permutation_matrix m_Q; - permutation_matrix m_R; - permutation_matrix m_r_wave; - square_sparse_matrix m_U; - square_dense_submatrix* m_dense_LU; - - vector *> m_tail; - lp_settings & m_settings; - bool m_failure; - indexed_vector m_row_eta_work_vector; - indexed_vector m_w_for_extension; - indexed_vector m_y_copy; - indexed_vector m_ii; //to optimize the work with the m_index fields - unsigned m_refactor_counter; - // constructor - // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different - // they represent the set of m columns - lu(const M & A, - vector& basis, - lp_settings & settings); - lu(const M & A, lp_settings&); - void debug_test_of_basis(const M & A, vector & basis); - void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); - void solve_By(indexed_vector & y); - - void solve_By(vector & y); - - void solve_By_for_T_indexed_only(indexed_vector& y, const lp_settings &); - - template - void solve_By_when_y_is_ready(indexed_vector & y); - void solve_By_when_y_is_ready_for_X(vector & y); - void solve_By_when_y_is_ready_for_T(vector & y, vector & index); - void print_indexed_vector(indexed_vector & w, std::ofstream & f); - - void print_matrix_compact(std::ostream & f); - - void print(indexed_vector & w, const vector& basis); - void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit - - - void solve_yB_indexed(indexed_vector& y); - - void add_delta_to_solution_indexed(indexed_vector& y); - - void add_delta_to_solution(const vector& yc, vector& y); - - - void find_error_of_yB(vector& yc, const vector& y, - const vector& basis); - - void find_error_of_yB_indexed(const indexed_vector& y, - const vector& heading, const lp_settings& settings); - - - void solve_yB_with_error_check(vector & y, const vector& basis); - - void solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings &); - - void apply_Q_R_to_U(permutation_matrix & r_wave); - - - LU_status get_status() { return m_status; } - - void set_status(LU_status status) { - m_status = status; - } - - ~lu(); - - void init_vector_y(vector & y); - - void perform_transformations_on_w(indexed_vector& w); - - void init_vector_w(unsigned entering, indexed_vector & w); - void apply_lp_list_to_w(indexed_vector & w); - void apply_lp_list_to_y(vector& y); - - void swap_rows(int j, int k); - - void swap_columns(int j, int pivot_column); - - void push_matrix_to_tail(tail_matrix* tm) { - m_tail.push_back(tm); - } - - bool pivot_the_row(int row); - - eta_matrix * get_eta_matrix_for_pivot(unsigned j); - // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); - - // see page 407 of Chvatal - unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); - -#ifdef Z3DEBUG - void check_vector_w(unsigned entering); - - void check_apply_matrix_to_vector(matrix *lp, T *w); - - void check_apply_lp_lists_to_w(T * w); - - // provide some access operators for testing - permutation_matrix & Q() { return m_Q; } - permutation_matrix & R() { return m_R; } - matrix & U() { return m_U; } - unsigned tail_size() { return m_tail.size(); } - - tail_matrix * get_lp_matrix(unsigned i) { - return m_tail[i]; - } - - T B_(unsigned i, unsigned j, const vector& basis) { - return m_A[i][basis[j]]; - } - - unsigned dimension() { return m_dim; } - -#endif - - - unsigned get_number_of_nonzeroes() { - return m_U.get_number_of_nonzeroes(); - } - - - void process_column(int j); - - bool is_correct(const vector& basis); - bool is_correct(); - - - - // needed for debugging purposes - void copy_w(T *buffer, indexed_vector & w); - - // needed for debugging purposes - void restore_w(T *buffer, indexed_vector & w); - bool all_columns_and_rows_are_active(); - - bool too_dense(unsigned j) const; - - void pivot_in_dense_mode(unsigned i); - - void create_initial_factorization(); - - void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave); - - void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump); - - bool diagonal_element_is_off(T /* diag_element */) { return false; } - - void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); - // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last - // row at the same time - - void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); - - void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); - - void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); - - void prepare_entering(unsigned entering, indexed_vector & w) { - lp_assert(false); - } - bool need_to_refactor() { lp_assert(false); - return m_refactor_counter >= 200; } - - void adjust_dimension_with_matrix_A() { - lp_assert(false); - } - - - - - - -}; // end of lu - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis); - -template -dense_matrix get_B(lu& f); -#endif -} diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h deleted file mode 100644 index ca34481cc..000000000 --- a/src/math/lp/lu_def.h +++ /dev/null @@ -1,720 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lu.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); - } - } - - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -void print_matrix(M &m, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(m[i][j])); - } - } - - for (unsigned j = 0; j < m.column_count(); j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -one_elem_on_diag::one_elem_on_diag(const one_elem_on_diag & o) { - m_i = o.m_i; - m_val = o.m_val; -#ifdef Z3DEBUG - m_m = m_n = o.m_m; - m_one_over_val = numeric_traits::one() / o.m_val; -#endif -} - -#ifdef Z3DEBUG -template -T one_elem_on_diag::get_elem(unsigned i, unsigned j) const { - if (i == j){ - if (j == m_i) { - return m_one_over_val; - } - return numeric_traits::one(); - } - - return numeric_traits::zero(); -} -#endif -template -void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { - T & t = w[m_i]; - if (numeric_traits::is_zero(t)) { - return; - } - t /= m_val; - if (numeric_traits::precise()) return; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - w.erase_from_index(m_i); - t = numeric_traits::zero(); - } -} - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -lu::lu(const M& A, - vector& basis, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A, basis), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(!(numeric_traits::precise() )); -#ifdef Z3DEBUG - debug_test_of_basis(A, basis); -#endif - ++m_settings.stats().m_num_factorizations; - create_initial_factorization(); -#ifdef Z3DEBUG - // lp_assert(check_correctness()); -#endif -} -template -lu::lu(const M& A, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(A.row_count() == A.column_count()); - create_initial_factorization(); -#ifdef Z3DEBUG - lp_assert(is_correct()); -#endif -} -template -void lu::debug_test_of_basis( M const & A, vector & basis) { - std::set set; - for (unsigned i = 0; i < A.row_count(); i++) { - lp_assert(basis[i]< A.column_count()); - set.insert(basis[i]); - } - lp_assert(set.size() == A.row_count()); -} - -template -void lu::solve_By(indexed_vector & y) { - lp_assert(false); // not implemented - // init_vector_y(y); - // solve_By_when_y_is_ready(y); - } - - -template -void lu::solve_By(vector & y) { - init_vector_y(y); - solve_By_when_y_is_ready_for_X(y); -} - -template -void lu::solve_By_when_y_is_ready_for_X(vector & y) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } - } -} - -template -void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned j = m_dim; - while (j--) { - if (!is_zero(y[j])) - index.push_back(j); - } - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } else { - index.push_back(i); - } - } -} - -template -void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { - if (numeric_traits::precise()) { - vector active_rows; - m_U.solve_U_y_indexed_only(y, settings, active_rows); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y, m_settings); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal -} - -template -void lu::print_matrix_compact(std::ostream & f) { - f << "matrix_start" << std::endl; - f << "nrows " << m_A.row_count() << std::endl; - f << "ncolumns " << m_A.column_count() << std::endl; - for (unsigned i = 0; i < m_A.row_count(); i++) { - auto & row = m_A.m_rows[i]; - f << "row " << i << std::endl; - for (auto & t : row) { - f << "column " << t.m_j << " value " << t.m_value << std::endl; - } - f << "row_end" << std::endl; - } - f << "matrix_end" << std::endl; -} -template -void lu< M>::print(indexed_vector & w, const vector& basis) { - std::string dump_file_name("/tmp/lu"); - remove(dump_file_name.c_str()); - std::ofstream f(dump_file_name); - if (!f.is_open()) { - LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl); - return; - } - LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl); - print_matrix_compact(f); - print_vector(basis, f); - print_indexed_vector(w, f); - f.close(); -} - -template -void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d - init_vector_w(a_column, d); - solve_By_for_T_indexed_only(d, m_settings); -} - -template -void lu< M>::solve_yB_indexed(indexed_vector& y) { - lp_assert(y.is_OK()); - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - lp_assert(y.is_OK()); - m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) - lp_assert(y.is_OK()); - m_Q.apply_reverse_from_right_to_T(y); - lp_assert(y.is_OK()); - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - lp_assert(y.is_OK()); - } -} - -template -void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ - unsigned i = static_cast(y.size()); - while (i--) - y[i]+=yc[i]; -} - -template -void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { - // the delta sits in m_y_copy, put result into y - lp_assert(y.is_OK()); - lp_assert(m_y_copy.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - for (unsigned i : y.m_index) - m_ii.set_value(1, i); - for (unsigned i : m_y_copy.m_index) { - y.m_data[i] += m_y_copy[i]; - if (m_ii[i] == 0) - m_ii.set_value(1, i); - } - lp_assert(m_ii.is_OK()); - y.m_index.clear(); - - for (unsigned i : m_ii.m_index) { - T & v = y.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) - y.m_index.push_back(i); - else if (!numeric_traits::is_zero(v)) - v = zero_of_type(); - } - - lp_assert(y.is_OK()); -} - -template -void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { - unsigned i = m_dim; - while (i--) { - yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); - } -} - -template -void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { -#if 0 == 1 - // it is a non efficient version - indexed_vector yc = m_y_copy; - yc.m_index.clear(); - lp_assert(!numeric_traits::precise()); - { - - vector d_basis(y.m_data.size()); - for (unsigned j = 0; j < heading.size(); j++) { - if (heading[j] >= 0) { - d_basis[heading[j]] = j; - } - } - - - unsigned i = m_dim; - while (i--) { - T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]); - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else - yc.m_index.push_back(i); - } - } -#endif - lp_assert(m_ii.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - lp_assert(m_y_copy.is_OK()); - // put the error into m_y_copy - for (auto k : y.m_index) { - auto & row = m_A.m_rows[k]; - const T & y_k = y.m_data[k]; - for (auto & c : row) { - unsigned j = c.var(); - int hj = heading[j]; - if (hj < 0) continue; - if (m_ii.m_data[hj] == 0) - m_ii.set_value(1, hj); - m_y_copy.m_data[hj] -= c.coeff() * y_k; - } - } - // add the index of m_y_copy to m_ii - for (unsigned i : m_y_copy.m_index) { - if (m_ii.m_data[i] == 0) - m_ii.set_value(1, i); - } - - // there is no guarantee that m_y_copy is OK here, but its index - // is contained in m_ii index - m_y_copy.m_index.clear(); - // setup the index of m_y_copy - for (auto k : m_ii.m_index) { - T& v = m_y_copy.m_data[k]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else { - m_y_copy.set_value(v, k); - } - } - lp_assert(m_y_copy.is_OK()); - -} - - - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { - lp_assert(false); - } - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { - lp_assert(false); -} -template -void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} - - -// Solving yB = cb to find the entering variable, -// where cb is the cost vector projected to B. -// The result is stored in cb. - -// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering -// variable -template -lu< M>::~lu(){ - for (auto t : m_tail) { - delete t; - } -} -template -void lu< M>::init_vector_y(vector & y) { - apply_lp_list_to_y(y); - m_Q.apply_reverse_from_left_to_X(y); -} - -template -void lu< M>::perform_transformations_on_w(indexed_vector& w) { - apply_lp_list_to_w(w); - m_Q.apply_reverse_from_left(w); - // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); -} - -// see Chvatal 24.3 -template -void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { - w.clear(); - m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column - perform_transformations_on_w(w); -} -template -void lu< M>::apply_lp_list_to_w(indexed_vector & w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left_to_T(w, m_settings); - // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); - } -} -template -void lu< M>::apply_lp_list_to_y(vector& y) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left(y, m_settings); - } -} -template -void lu< M>::swap_rows(int j, int k) { - if (j != k) { - m_Q.transpose_from_left(j, k); - m_U.swap_rows(j, k); - } -} - -template -void lu< M>::swap_columns(int j, int pivot_column) { - if (j == pivot_column) - return; - m_R.transpose_from_right(j, pivot_column); - m_U.swap_columns(j, pivot_column); -} -template -bool lu< M>::pivot_the_row(int row) { - eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); - if (get_status() != LU_status::OK) { - return false; - } - - if (eta_matrix == nullptr) { - m_U.shorten_active_matrix(row, nullptr); - return true; - } - if (!m_U.pivot_with_eta(row, eta_matrix, m_settings)) - return false; - eta_matrix->conjugate_by_permutation(m_Q); - push_matrix_to_tail(eta_matrix); - return true; -} -// we're processing the column j now -template -eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { - eta_matrix *ret; - if(!m_U.fill_eta_matrix(j, &ret)) { - set_status(LU_status::Degenerated); - } - return ret; -} -// we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { - eta_matrix *ret; - copy_of_U.fill_eta_matrix(j, &ret); - return ret; -} - -// see page 407 of Chvatal -template -unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, - unsigned leaving_column) { - unsigned column_to_replace = m_R.apply_reverse(leaving_column); - m_U.replace_column(column_to_replace, w, m_settings); - return column_to_replace; -} - -#ifdef Z3DEBUG -template -void lu::check_vector_w(unsigned entering) { - T * w = new T[m_dim]; - m_A.copy_column_to_vector(entering, w); - check_apply_lp_lists_to_w(w); - delete [] w; -} -template -void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { - if (lp != nullptr) { - lp -> set_number_of_rows(m_dim); - lp -> set_number_of_columns(m_dim); - apply_to_vector(*lp, w); - } -} - -template -void lu::check_apply_lp_lists_to_w(T * w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - check_apply_matrix_to_vector(m_tail[i], w); - } - permutation_matrix qr = m_Q.get_reverse(); - apply_to_vector(qr, w); - for (int i = m_dim - 1; i >= 0; i--) { - lp_assert(abs(w[i] - w[i]) < 0.0000001); - } -} - -#endif -template -void lu::process_column(int j) { - unsigned pi, pj; - bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); - if (!success) { - // LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - - if (static_cast(pi) == -1) { - // LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - swap_columns(j, pj); - swap_rows(j, pi); - if (!pivot_the_row(j)) { - // LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl); - m_failure = true; - } -} -template -bool lu::is_correct(const vector& basis) { -return true; -} - -template -bool lu::is_correct() { - return true; -} - - -// needed for debugging purposes -template -void lu::copy_w(T *buffer, indexed_vector & w) { - -} - -// needed for debugging purposes -template -void lu::restore_w(T *buffer, indexed_vector & w) { - -} -template -bool lu::all_columns_and_rows_are_active() { - return true; -} -template -bool lu::too_dense(unsigned j) const { - return false; -} -template -void lu::pivot_in_dense_mode(unsigned i) { - -} -template -void lu::create_initial_factorization(){ - -} - -template -void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { - if (bump_start > bump_end) { - set_status(LU_status::Degenerated); - return; - } - if (bump_start == bump_end) { - return; - } - - r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump - - for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) { - r_wave[i] = i - 1; - } - - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} -template -void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { - vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); - for (auto & iv : last_row_vec) { - if (is_zero(iv.m_value)) continue; - lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); - unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); - if (adjusted_col < lowest_row_of_the_bump) { - m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); - } else { - m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix - } - } -} - -template -void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { - // we have the system right side at m_row_eta_work_vector now - // solve the system column wise - for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { - T v = m_row_eta_work_vector[j]; - if (numeric_traits::is_zero(v)) continue; // this column does not contribute to the solution - unsigned aj = m_U.adjust_row(j); - vector> & row = m_U.get_row_values(aj); - for (auto & iv : row) { - unsigned col = m_U.adjust_column_inverse(iv.m_index); - lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); - if (col == j) continue; - if (numeric_traits::is_zero(iv.m_value)) { - continue; - } - // the -v is for solving the system ( to zero the last row), and +v is for pivoting - T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; - lp_assert(numeric_traits::is_zero(delta) == false); - - - - // m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta); - if (numeric_traits::is_zero(m_row_eta_work_vector[col])) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){ - m_row_eta_work_vector.set_value(delta, col); - } - } else { - T t = (m_row_eta_work_vector[col] += delta); - if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){ - m_row_eta_work_vector[col] = numeric_traits::zero(); - auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col); - if (it != m_row_eta_work_vector.m_index.end()) - m_row_eta_work_vector.m_index.erase(it); - } - } - } - } -} - -template -void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ - lp_assert(false); -} -template -void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ - lp_assert(false);// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); -} - -template -void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { - lp_assert(false); -} - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { - lp_assert(false); -} - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis) { - lp_assert(false); - - dense_matrix B(0, 0); - return B; -} -template -dense_matrix get_B(lu& f) { - lp_assert(false); - dense_matrix B(0,0); - return B; -} -#endif -} diff --git a/src/math/lp/row_eta_matrix.cpp b/src/math/lp/row_eta_matrix.cpp index 6fafb83ed..356f80b3c 100644 --- a/src/math/lp/row_eta_matrix.cpp +++ b/src/math/lp/row_eta_matrix.cpp @@ -20,7 +20,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/row_eta_matrix_def.h" -#include "math/lp/lu.h" namespace lp { template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); diff --git a/src/math/lp/square_sparse_matrix.cpp b/src/math/lp/square_sparse_matrix.cpp index 35d38e529..3ec88f47d 100644 --- a/src/math/lp/square_sparse_matrix.cpp +++ b/src/math/lp/square_sparse_matrix.cpp @@ -20,7 +20,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/lp_settings.h" -#include "math/lp/lu.h" #include "math/lp/square_sparse_matrix_def.h" #include "math/lp/dense_matrix.h" namespace lp { diff --git a/src/math/lp/square_sparse_matrix_def.h b/src/math/lp/square_sparse_matrix_def.h index 3533ba066..192634627 100644 --- a/src/math/lp/square_sparse_matrix_def.h +++ b/src/math/lp/square_sparse_matrix_def.h @@ -21,6 +21,8 @@ Revision History: #include "util/vector.h" #include "math/lp/square_sparse_matrix.h" +#include "math/lp/dense_matrix.h" + #include #include namespace lp { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2aa988282..3eda1ddff 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3126,7 +3126,7 @@ public: return l_false; TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, - // FLOATING_POINT_ERROR, TIME_EXAUSTED, EMPTY, UNSTABLE + // TIME_EXAUSTED, EMPTY, UNSTABLE return l_undef; } From 73224adc48b6468d8aa48b082919fab1b90d7139 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:51:10 -0800 Subject: [PATCH 099/220] cleanup --- src/math/lp/lar_core_solver_def.h | 5 +- src/math/lp/lp_core_solver_base.h | 12 ---- src/math/lp/lp_core_solver_base_def.h | 34 +---------- src/math/lp/lp_primal_core_solver.cpp | 1 - src/math/lp/lp_primal_core_solver.h | 4 -- src/math/lp/lp_primal_core_solver_def.h | 58 ++----------------- .../lp/lp_primal_core_solver_tableau_def.h | 6 +- src/math/lp/lp_settings.h | 1 - 8 files changed, 9 insertions(+), 112 deletions(-) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 22dc23bd5..fe99a202f 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -51,8 +51,6 @@ void lar_core_solver::prefix_r() { // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) - m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); m_r_solver.set_using_infeas_costs(true); @@ -61,7 +59,6 @@ void lar_core_solver::prefix_r() { void lar_core_solver::prefix_d() { // m_d_solver.m_b.resize(m_d_solver.m_m()); - m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); m_d_solver.m_costs.resize(m_d_solver.m_n()); m_d_solver.m_d.resize(m_d_solver.m_n()); @@ -91,7 +88,7 @@ void lar_core_solver::fill_not_improvable_zero_sum() { return; } // reusing the existing mechanism for row_feasibility_loop - m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1; + m_infeasible_sum_sign = 1; m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 0e8a2abdf..5f723c118 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -159,15 +159,8 @@ public: lp_status get_status() const{ return m_status; } - - void fill_cb(T * y) const; - - void fill_cb(vector & y) const; - void pretty_print(std::ostream & out); - - X get_cost() const { return dot_product(m_costs, m_x); } @@ -176,11 +169,6 @@ public: void restore_m_w(T * buffer); - // needed for debugging - void copy_m_ed(T * buffer); - - void restore_m_ed(T * buffer); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 01aa9802e..8b87728e0 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -101,21 +101,8 @@ pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { } -template void lp_core_solver_base:: -fill_cb(T * y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} -template void lp_core_solver_base:: -fill_cb(vector & y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} - // template void lp_core_solver_base:: // update_index_of_ed() { @@ -155,25 +142,6 @@ restore_m_w(T * buffer) { } } -// needed for debugging -template void lp_core_solver_base:: -copy_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_ed[i]; - } -} - -template void lp_core_solver_base:: -restore_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - m_ed[i] = buffer[i]; - } -} - - - template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { @@ -682,7 +650,7 @@ lp_core_solver_base::infeasibility_costs_are_correct() const { template bool lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { - T r = (!this->m_settings.use_breakpoints_in_feasibility_search)? -one_of_type(): one_of_type(); + T r = -one_of_type(); switch (this->m_column_types[j]) { case column_type::fixed: diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index 8a4359806..f4597da76 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -34,7 +34,6 @@ template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); -template void lp::lp_primal_core_solver::clear_breakpoints(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 38a2aa5f5..0a182ad91 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -47,8 +47,6 @@ public: unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; - vector> m_breakpoints; - binary_heap_priority_queue m_breakpoint_indices_queue; indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; @@ -545,8 +543,6 @@ public: void try_add_breakpoint_in_row(unsigned i); - void clear_breakpoints(); - void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 3967b6e66..5d4a662cf 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -112,8 +112,6 @@ template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { if (numeric_traits::precise()) return column_is_benefitial_for_entering_basis_precise(j); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; switch (this->m_column_types[j]) { case column_type::fixed: break; @@ -146,8 +144,6 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { lp_assert (numeric_traits::precise()); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -219,8 +215,6 @@ int lp_primal_core_solver::choose_entering_column_presize(unsigned number_ return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -259,8 +253,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef if (entering_iter != m_non_basis_list.end()) { unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = - m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -268,28 +260,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef return -1; } -template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { - T slope_at_entering = this->m_d[entering]; - breakpoint * last_bp = nullptr; - lp_assert(m_breakpoint_indices_queue.is_empty()==false); - while (m_breakpoint_indices_queue.is_empty() == false) { - unsigned bi = m_breakpoint_indices_queue.dequeue(); - breakpoint *b = &m_breakpoints[bi]; - change_slope_on_breakpoint(entering, b, slope_at_entering); - last_bp = b; - if (slope_at_entering * m_sign_of_entering_delta > - m_epsilon_of_reduced_cost) { // the slope started to increase infeasibility - break; - } else { - if ((numeric_traits::precise() == false) || ( numeric_traits::is_zero(slope_at_entering) && this->m_settings.random_next() % 2 == 0)) { - // it is not cost beneficial to advance the delta more, so just break to increase the randomness - break; - } - } - } - lp_assert (last_bp != nullptr); - t = last_bp->m_delta; - return last_bp->m_j; -} template int @@ -406,8 +376,6 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { } template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); bool unlimited = true; unsigned steps = this->m_ed.m_index.size(); unsigned k = this->m_settings.random_next() % steps; @@ -472,8 +440,6 @@ template int lp_primal_core_solver::find_leaving_ template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); X theta = zero_of_type(); bool unlimited = get_harris_theta(theta); lp_assert(unlimited || theta >= zero_of_type()); @@ -584,7 +550,7 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e this->m_d[j] -= dq * this->m_pivot_row[j]; } this->m_d[leaving] = -dq; - if (this->current_x_is_infeasible() && !this->m_settings.use_breakpoints_in_feasibility_search) { + if (this->current_x_is_infeasible()) { this->m_d[leaving] -= this->m_costs[leaving]; this->m_costs[leaving] = zero_of_type(); } @@ -833,14 +799,7 @@ template void lp_primal_core_solver::one_iteratio } - -template void lp_primal_core_solver::clear_breakpoints() { - m_breakpoints.clear(); - m_breakpoint_indices_queue.clear(); -} - template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { - clear_breakpoints(); for (unsigned i : this->m_ed.m_index) try_add_breakpoint_in_row(i); @@ -924,9 +883,8 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const break; } - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - ret = - ret; - } + ret = - ret; + return ret; } @@ -982,9 +940,8 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { } else { this->insert_column_into_inf_set(j); } - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - this->m_costs[j] = - this->m_costs[j]; - } + this->m_costs[j] = - this->m_costs[j]; + } @@ -1009,10 +966,7 @@ template void lp_primal_core_solver::print_column } } -template void lp_primal_core_solver::add_breakpoint(unsigned j, X delta, breakpoint_type type) { - m_breakpoints.push_back(breakpoint(j, delta, type)); - m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); -} + // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index e742b0cd0..b63c54bfd 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -88,8 +88,6 @@ template int lp_primal_core_solver::choose_enteri return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; @@ -188,7 +186,7 @@ template void lp_primal_core_solver::advance_on_en return; } if (!is_zero(t)) { - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) { + if (this->current_x_is_feasible() ) { if (m_sign_of_entering_delta == -1) t = -t; } @@ -297,8 +295,6 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (this->m_settings.use_breakpoints_in_feasibility_search) - m_breakpoint_indices_queue.resize(this->m_n()); if (!numeric_traits::precise()) { this->m_column_norm_update_counter = 0; init_column_norms(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index cb60eebc9..38270230e 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -223,7 +223,6 @@ public: unsigned column_norms_update_frequency { 12000 }; bool scale_with_ratio { true }; double density_threshold { 0.7 }; - bool use_breakpoints_in_feasibility_search { false }; unsigned max_row_length_for_bound_propagation { 300 }; bool backup_costs { true }; unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; From 6201eda05531dc641e37c2f8812580441eaf5d37 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 09:59:32 -0800 Subject: [PATCH 100/220] rm breakpoints --- src/math/lp/breakpoint.h | 35 ------ src/math/lp/lar_core_solver.h | 6 - src/math/lp/lp_primal_core_solver.h | 66 +--------- src/math/lp/lp_primal_core_solver_def.h | 161 ------------------------ 4 files changed, 1 insertion(+), 267 deletions(-) delete mode 100644 src/math/lp/breakpoint.h diff --git a/src/math/lp/breakpoint.h b/src/math/lp/breakpoint.h deleted file mode 100644 index 40fab293f..000000000 --- a/src/math/lp/breakpoint.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -namespace lp { -enum breakpoint_type { - low_break, upper_break, fixed_break -}; -template -struct breakpoint { - unsigned m_j; // the basic column - breakpoint_type m_type; - X m_delta; - breakpoint(){} - breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {} -}; -} diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 9168862c6..d9379cb9d 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -13,7 +13,6 @@ Author: #include #include "math/lp/indexed_vector.h" #include "math/lp/binary_heap_priority_queue.h" -#include "math/lp/breakpoint.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" #include "math/lp/lar_solution_signature.h" @@ -96,11 +95,6 @@ public: m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } - - void advance_on_sorted_breakpoints(unsigned entering); - - void change_slope_on_breakpoint(unsigned entering, breakpoint> * b, mpq & slope_at_entering); - bool row_is_infeasible(unsigned row); bool row_is_evidence(unsigned row); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 0a182ad91..2c7360712 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -32,7 +32,6 @@ Revision History: #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/breakpoint.h" #include "math/lp/binary_heap_priority_queue.h" #include "math/lp/u_set.h" namespace lp { @@ -64,47 +63,7 @@ public: int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); int choose_entering_column_tableau(); int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - int find_leaving_and_t_with_breakpoints(unsigned entering, X & t); - // int find_inf_row() { - // // mimicing CLP : todo : use a heap - // int j = -1; - // for (unsigned k : this->m_inf_set.m_index) { - // if (k < static_cast(j)) - // j = static_cast(k); - // } - // if (j == -1) - // return -1; - // return this->m_basis_heading[j]; - // #if 0 - // vector choices; - // unsigned len = 100000000; - // for (unsigned j : this->m_inf_set.m_index) { - // int i = this->m_basis_heading[j]; - // lp_assert(i >= 0); - // unsigned row_len = this->m_A.m_rows[i].size(); - // if (row_len < len) { - // choices.clear(); - // choices.push_back(i); - // len = row_len; - // if (m_settings.random_next() % 10) break; - // } else if (row_len == len) { - // choices.push_back(i); - // if (m_settings.random_next() % 10) break; - // } - // } - - // if (choices.size() == 0) - // return -1; - - // if (choices.size() == 1) - // return choices[0]; - - // unsigned k = this->m_settings.random_next() % choices.size(); - // return choices[k]; - // #endif - // } - - + bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos // we have xbj = -aj * xj @@ -538,16 +497,7 @@ public: if (this->current_x_is_feasible()) this->set_status(lp_status::OPTIMAL); } - - void fill_breakpoints_array(unsigned entering); - - void try_add_breakpoint_in_row(unsigned i); - - void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); - - - void decide_on_status_when_cannot_find_entering() { lp_assert(!need_to_switch_costs()); this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); @@ -772,10 +722,6 @@ public: bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - - bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const; - - bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); @@ -785,26 +731,16 @@ public: void init_infeasibility_costs_for_changed_basis_only(); void print_column(unsigned j, std::ostream & out); - void add_breakpoint(unsigned j, X delta, breakpoint_type type); - // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column - void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value); template bool same_sign_with_entering_delta(const L & a) { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - bool lower_bounds_are_set() const override { return true; } - int advance_on_sorted_breakpoints(unsigned entering, X & t); - - std::string break_type_to_string(breakpoint_type type); - - void print_breakpoint(const breakpoint * b, std::ostream & out); - void print_bound_info_and_x(unsigned j, std::ostream & out); void init_infeasibility_after_update_x_if_inf(unsigned leaving) { diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 5d4a662cf..7a027f8d3 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -75,39 +75,6 @@ void lp_primal_core_solver::sort_non_basis() { } } -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoints(unsigned j) const { - bool ret; - const T & d = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->x_is_at_lower_bound(j)); - ret = d < -m_epsilon_of_reduced_cost; - break; - case column_type::upper_bound: - lp_assert(this->x_is_at_upper_bound(j)); - ret = d > m_epsilon_of_reduced_cost; - break; - case column_type::fixed: - ret = false; - break; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - lp_assert(lower_bound || this->x_is_at_upper_bound(j)); - ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); - } - break; - case column_type::free_column: - ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; - break; - default: - lp_unreachable(); - ret = false; - break; - } - return ret; -} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { if (numeric_traits::precise()) @@ -261,14 +228,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } - -template int -lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ - lp_assert(this->precise() == false); - fill_breakpoints_array(entering); - return advance_on_sorted_breakpoints(entering, t); -} - template bool lp_primal_core_solver::get_harris_theta(X & theta) { lp_assert(this->m_ed.is_OK()); bool unlimited = true; @@ -799,19 +758,6 @@ template void lp_primal_core_solver::one_iteratio } -template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { - for (unsigned i : this->m_ed.m_index) - try_add_breakpoint_in_row(i); - - if (this->m_column_types[entering] == column_type::boxed) { - if (m_sign_of_entering_delta < 0) - add_breakpoint(entering, - this->bound_span(entering), low_break); - else - add_breakpoint(entering, this->bound_span(entering), upper_break); - } -} - - template bool lp_primal_core_solver::done() { if (this->get_status() == lp_status::OPTIMAL) return true; @@ -893,9 +839,6 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const template void lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { - // If j is a breakpoint column, then we set the cost zero. - // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function - // set zero cost for each non-basis column if (this->m_basis_heading[j] < 0) { this->m_costs[j] = numeric_traits::zero(); this->remove_column_from_inf_set(j); @@ -966,110 +909,6 @@ template void lp_primal_core_solver::print_column } } - - -// j is the basic column, x is the value at x[j] -// d is the coefficient before m_entering in the row with j as the basis column -template void lp_primal_core_solver::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) { - X diff = x - break_value; - if (is_zero(diff)) { - switch (break_type) { - case low_break: - if (!same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - case upper_break: - if (same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - default: break; - } - add_breakpoint(j, zero_of_type(), break_type); - return; - } - auto delta_j = diff / d; - if (same_sign_with_entering_delta(delta_j)) - add_breakpoint(j, delta_j, break_type); -} - -template std::string lp_primal_core_solver::break_type_to_string(breakpoint_type type) { - switch (type){ - case low_break: return "low_break"; - case upper_break: return "upper_break"; - case fixed_break: return "fixed_break"; - default: - lp_assert(false); - break; - } - return "type is not found"; -} - -template void lp_primal_core_solver::print_breakpoint(const breakpoint * b, std::ostream & out) { - out << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl; - print_bound_info_and_x(b->m_j, out); -} - - -template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { - if (b->m_j == entering) { - lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); - slope_at_entering += m_sign_of_entering_delta; - return; - } - - lp_assert(this->m_basis_heading[b->m_j] >= 0); - unsigned i_row = this->m_basis_heading[b->m_j]; - const T & d = - this->m_ed[i_row]; - if (numeric_traits::is_zero(d)) return; - - T delta = m_sign_of_entering_delta * abs(d); - switch (b->m_type) { - case fixed_break: - if (is_zero(b->m_delta)) { - slope_at_entering += delta; - } else { - slope_at_entering += 2 * delta; - } - break; - case low_break: - case upper_break: - slope_at_entering += delta; - break; - default: - lp_assert(false); - } -} - - -template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { - lp_assert(i < this->m_m()); - const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row - if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x - unsigned j = this->m_basis[i]; - const X & x = this->m_x[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: - try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); - break; - case column_type::boxed: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::lower_bound: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - break; - case column_type::upper_bound: - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } -} - - template void lp_primal_core_solver::print_bound_info_and_x(unsigned j, std::ostream & out) { out << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_types[j]) << std::endl; out << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl; From a4189186cc5b3b9786ae8415f5faaa9874502097 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 10:23:52 -0800 Subject: [PATCH 101/220] rm dealing with doubles --- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 1 - src/math/lp/lp_core_solver_base_def.h | 17 --- src/math/lp/lp_primal_core_solver.h | 137 +++--------------- src/math/lp/lp_primal_core_solver_def.h | 103 +------------ .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 6 files changed, 21 insertions(+), 245 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index b76976d37..e87dc5dd2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -83,9 +83,6 @@ template std::string lp::lp_core_solver_base::column_name(unsi template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5f723c118..fff500905 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -416,7 +416,6 @@ public: non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 8b87728e0..c5910f427 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -549,23 +549,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; -} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 2c7360712..568eefa98 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -43,20 +43,16 @@ class lp_primal_core_solver:public lp_core_solver_base { public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise - unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; - indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; - T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; - // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -277,14 +273,9 @@ public: return convert_struct::convert(std::numeric_limits::max()); } - bool get_harris_theta(X & theta); - - void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } - void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - int find_leaving_on_harris_theta(X const & harris_theta, X & t); + bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -318,10 +309,7 @@ public: lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - - X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } - + void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -514,10 +502,7 @@ public: // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); - limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); - if (theta < zero_of_type()) theta = zero_of_type(); + lp_assert(false); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -531,16 +516,7 @@ public: theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } + } return true; } @@ -555,16 +531,7 @@ public: theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } + } return true; } @@ -576,82 +543,32 @@ public: limit_theta((bound - x) / m, theta, unlimited); } } - else { - // x gets larger - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } - } + } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } + lp_assert(false); + } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & lbound = this->m_lower_bounds[j]; - - if (this->below_bound(x, lbound)) { - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta((lbound - x + eps) / m, theta, unlimited); - } else { - const X & ubound = this->m_upper_bounds[j]; - if (this->below_bound(x, ubound)){ - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x + eps) / m, theta, unlimited); - } else if (!this->above_bound(x, ubound)) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & ubound = this->m_upper_bounds[j]; - if (this->above_bound(x, ubound)) { - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x - eps) / m, theta, unlimited); - } else { - const X & lbound = this->m_lower_bounds[j]; - if (this->above_bound(x, lbound)){ - const X& eps = harris_eps_for_bound(lbound); - limit_theta((lbound - x - eps) / m, theta, unlimited); - } else if (!this->below_bound(x, lbound)) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0); - const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - unlimited = false; - } - } + lp_assert(false); + } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - } + lp_assert(false); + } // j is a basic column or the entering, in any case x[j] has to stay feasible. @@ -722,14 +639,12 @@ public: bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - + void print_column(unsigned j, std::ostream & out); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column @@ -743,13 +658,6 @@ public: void print_bound_info_and_x(unsigned j, std::ostream & out); - void init_infeasibility_after_update_x_if_inf(unsigned leaving) { - if (this->using_infeas_costs()) { - init_infeasibility_costs_for_changed_basis_only(); - this->m_costs[leaving] = zero_of_type(); - this->remove_column_from_inf_set(leaving); - } - } void init_inf_set() { this->clear_inf_set(); @@ -885,15 +793,10 @@ public: column_type_array, lower_bound_values, upper_bound_values), - m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - if (!(numeric_traits::precise())) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } else { - m_converted_harris_eps = zero_of_type(); - } + this->set_status(lp_status::UNKNOWN); } @@ -919,9 +822,7 @@ public: column_names, column_type_array, m_lower_bounds_dummy, - upper_bound_values), - m_beta(A.row_count()), - m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { + upper_bound_values) { lp_assert(initial_x_is_correct()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7a027f8d3..22bd4db52 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -228,54 +228,8 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } -template bool lp_primal_core_solver::get_harris_theta(X & theta) { - lp_assert(this->m_ed.is_OK()); - bool unlimited = true; - for (unsigned i : this->m_ed.m_index) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); - if (!unlimited && is_zero(theta)) break; - } - return unlimited; -} -template int lp_primal_core_solver:: -find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max = zero_of_type(); - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { - if (++k == steps) - k = 0; - continue; - } - X ratio; - unsigned j = this->m_basis[i]; - bool unlimited = true; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); - if ((!unlimited) && ratio <= harris_theta) { - if (leaving == -1 || abs(ed) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(ed); - } - } - if (++k == steps) k = 0; - } while (k != initial_k); - if (!this->precise()) - restore_harris_eps(); - return leaving; -} - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, @@ -398,17 +352,6 @@ template int lp_primal_core_solver::find_leaving_ } -template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - X theta = zero_of_type(); - bool unlimited = get_harris_theta(theta); - lp_assert(unlimited || theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; - if (unlimited) - return -1; - return find_leaving_on_harris_theta(theta, t); -} - - // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) @@ -687,47 +630,10 @@ template void lp_primal_core_solver::init_column_ } } -// debug only -template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(false); -} -template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); - if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - m_column_norm_update_counter = 0; - init_column_norms(); - } else { - m_column_norm_update_counter++; - update_column_norms(entering, leaving); - } -} + // following Swietanowski - A new steepest ... -template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - T pivot = this->m_pivot_row[entering]; - T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; - if (!numeric_traits::precise()) { - if (g_ent < T(0.000001)) - g_ent = T(0.000001); - } - this->m_column_norms[leaving] = g_ent; - - for (unsigned j : this->m_pivot_row.m_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta.m_data, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 - 1 + tp * tp); - } - } -} template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { T r = numeric_traits::one(); @@ -771,13 +677,6 @@ template bool lp_primal_core_solver::done() { return false; } -template -void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { - for (unsigned i : this->m_ed.m_index) - init_infeasibility_cost_for_column(this->m_basis[i]); - this->set_using_infeas_costs(true); -} - template void lp_primal_core_solver::init_infeasibility_costs() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index b63c54bfd..a63a6be17 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,10 +295,7 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } + if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From d00fcc87c9a822d3a995f527e5bca69c400f60b0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 10:56:44 -0800 Subject: [PATCH 102/220] Revert "rm dealing with doubles" This reverts commit 547254abe786c80231ca78bcd245e6ddb5a15c47. --- src/math/lp/lp_core_solver_base.cpp | 3 + src/math/lp/lp_core_solver_base.h | 1 + src/math/lp/lp_core_solver_base_def.h | 17 +++ src/math/lp/lp_primal_core_solver.h | 137 +++++++++++++++--- src/math/lp/lp_primal_core_solver_def.h | 103 ++++++++++++- .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 6 files changed, 245 insertions(+), 21 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index e87dc5dd2..b76976d37 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -83,6 +83,9 @@ template std::string lp::lp_core_solver_base::column_name(unsi template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); +template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; +template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; +template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index fff500905..5f723c118 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -416,6 +416,7 @@ public: non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; + int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index c5910f427..8b87728e0 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -549,6 +549,23 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } +template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { + const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; + const T & row_p = this->m_pivot_row[entering]; + if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero + // the pivots have to have the same sign + if (column_p < 0) { + if (row_p > 0) + return 2; + } else { // column_p > 0 + if (row_p < 0) + return 2; + } + T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); + if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) + return 1; + return 0; +} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 568eefa98..2c7360712 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -43,16 +43,20 @@ class lp_primal_core_solver:public lp_core_solver_base { public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise + unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; + indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; + T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; + // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -273,9 +277,14 @@ public: return convert_struct::convert(std::numeric_limits::max()); } - + bool get_harris_theta(X & theta); + + void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } + void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } + int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); + int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -309,7 +318,10 @@ public: lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - + + X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; + } + void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -502,7 +514,10 @@ public: // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); + lp_assert(m < 0); + const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); + limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + if (theta < zero_of_type()) theta = zero_of_type(); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -516,7 +531,16 @@ public: theta = zero_of_type(); unlimited = false; } - } + } else { + const X& eps = harris_eps_for_bound(bound); + if (this->below_bound(x, bound)) return false; + if (this->above_bound(x, bound)) { + limit_theta((bound - x - eps) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + } return true; } @@ -531,7 +555,16 @@ public: theta = zero_of_type(); unlimited = false; } - } + } else { + const X& eps = harris_eps_for_bound(bound); + if (this->above_bound(x, bound)) return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x + eps) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + } return true; } @@ -543,32 +576,82 @@ public: limit_theta((bound - x) / m, theta, unlimited); } } - + else { + // x gets larger + lp_assert(m > 0); + const X& eps = harris_eps_for_bound(bound); + if (this->below_bound(x, bound)) { + limit_theta((bound - x + eps) / m, theta, unlimited); + } + } } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - lp_assert(false); - + lp_assert(m < 0); + const X& eps = harris_eps_for_bound(bound); + if (this->above_bound(x, bound)) { + limit_theta((bound - x - eps) / m, theta, unlimited); + } } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); + const X & x = this->m_x[j]; + const X & lbound = this->m_lower_bounds[j]; + + if (this->below_bound(x, lbound)) { + const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + limit_theta((lbound - x + eps) / m, theta, unlimited); + } else { + const X & ubound = this->m_upper_bounds[j]; + if (this->below_bound(x, ubound)){ + const X& eps = harris_eps_for_bound(ubound); + limit_theta((ubound - x + eps) / m, theta, unlimited); + } else if (!this->above_bound(x, ubound)) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); + const X & x = this->m_x[j]; + const X & ubound = this->m_upper_bounds[j]; + if (this->above_bound(x, ubound)) { + const X& eps = harris_eps_for_bound(ubound); + limit_theta((ubound - x - eps) / m, theta, unlimited); + } else { + const X & lbound = this->m_lower_bounds[j]; + if (this->above_bound(x, lbound)){ + const X& eps = harris_eps_for_bound(lbound); + limit_theta((lbound - x - eps) / m, theta, unlimited); + } else if (!this->below_bound(x, lbound)) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(false); - + lp_assert(m > 0); + const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { + limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + unlimited = false; + } + } } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(false); - + lp_assert(m > 0); + const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); + limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + } } // j is a basic column or the entering, in any case x[j] has to stay feasible. @@ -639,12 +722,14 @@ public: bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; + bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); T get_infeasibility_cost_for_column(unsigned j) const; - + void init_infeasibility_costs_for_changed_basis_only(); + void print_column(unsigned j, std::ostream & out); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column @@ -658,6 +743,13 @@ public: void print_bound_info_and_x(unsigned j, std::ostream & out); + void init_infeasibility_after_update_x_if_inf(unsigned leaving) { + if (this->using_infeas_costs()) { + init_infeasibility_costs_for_changed_basis_only(); + this->m_costs[leaving] = zero_of_type(); + this->remove_column_from_inf_set(leaving); + } + } void init_inf_set() { this->clear_inf_set(); @@ -793,10 +885,15 @@ public: column_type_array, lower_bound_values, upper_bound_values), + m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - + if (!(numeric_traits::precise())) { + m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); + } else { + m_converted_harris_eps = zero_of_type(); + } this->set_status(lp_status::UNKNOWN); } @@ -822,7 +919,9 @@ public: column_names, column_type_array, m_lower_bounds_dummy, - upper_bound_values) { + upper_bound_values), + m_beta(A.row_count()), + m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { lp_assert(initial_x_is_correct()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 22bd4db52..7a027f8d3 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -228,8 +228,54 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef } +template bool lp_primal_core_solver::get_harris_theta(X & theta) { + lp_assert(this->m_ed.is_OK()); + bool unlimited = true; + for (unsigned i : this->m_ed.m_index) { + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; + limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); + if (!unlimited && is_zero(theta)) break; + } + return unlimited; +} +template int lp_primal_core_solver:: +find_leaving_on_harris_theta(X const & harris_theta, X & t) { + int leaving = -1; + T pivot_abs_max = zero_of_type(); + // we know already that there is no bound flip on entering + // we also know that harris_theta is limited, so we will find a leaving + zero_harris_eps(); + unsigned steps = this->m_ed.m_index.size(); + unsigned k = this->m_settings.random_next() % steps; + unsigned initial_k = k; + do { + unsigned i = this->m_ed.m_index[k]; + const T & ed = this->m_ed[i]; + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { + if (++k == steps) + k = 0; + continue; + } + X ratio; + unsigned j = this->m_basis[i]; + bool unlimited = true; + limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); + if ((!unlimited) && ratio <= harris_theta) { + if (leaving == -1 || abs(ed) > pivot_abs_max) { + t = ratio; + leaving = j; + pivot_abs_max = abs(ed); + } + } + if (++k == steps) k = 0; + } while (k != initial_k); + if (!this->precise()) + restore_harris_eps(); + return leaving; +} + template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, @@ -352,6 +398,17 @@ template int lp_primal_core_solver::find_leaving_ } +template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { + X theta = zero_of_type(); + bool unlimited = get_harris_theta(theta); + lp_assert(unlimited || theta >= zero_of_type()); + if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; + if (unlimited) + return -1; + return find_leaving_on_harris_theta(theta, t); +} + + // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) @@ -630,10 +687,47 @@ template void lp_primal_core_solver::init_column_ } } +// debug only +template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { + lp_assert(false); +} - +template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { + lp_assert(numeric_traits::precise() == false); + lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); + if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { + m_column_norm_update_counter = 0; + init_column_norms(); + } else { + m_column_norm_update_counter++; + update_column_norms(entering, leaving); + } +} // following Swietanowski - A new steepest ... +template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { + lp_assert(numeric_traits::precise() == false); + T pivot = this->m_pivot_row[entering]; + T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; + if (!numeric_traits::precise()) { + if (g_ent < T(0.000001)) + g_ent = T(0.000001); + } + this->m_column_norms[leaving] = g_ent; + + for (unsigned j : this->m_pivot_row.m_index) { + if (j == leaving) + continue; + const T & t = this->m_pivot_row[j]; + T s = this->m_A.dot_product_with_column(m_beta.m_data, j); + T k = -2 / pivot; + T tp = t/pivot; + if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column + this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 + 1 + tp * tp); + } + } +} template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { T r = numeric_traits::one(); @@ -677,6 +771,13 @@ template bool lp_primal_core_solver::done() { return false; } +template +void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { + for (unsigned i : this->m_ed.m_index) + init_infeasibility_cost_for_column(this->m_basis[i]); + this->set_using_infeas_costs(true); +} + template void lp_primal_core_solver::init_infeasibility_costs() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index a63a6be17..b63c54bfd 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,7 +295,10 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - + if (!numeric_traits::precise()) { + this->m_column_norm_update_counter = 0; + init_column_norms(); + } if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From 2e9dc3d090ba3b91c3996cb9742d2b9696a08ddb Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 11:01:39 -0800 Subject: [PATCH 103/220] rm lu Signed-off-by: Lev Nachmanson --- src/test/lp/lp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 1a2ee2338..c386cf002 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -49,7 +49,6 @@ #include "math/lp/matrix.h" #include "math/lp/hnf.h" #include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/lu_def.h" #include "math/lp/general_matrix.h" #include "math/lp/lp_bound_propagator.h" #include "math/lp/nla_solver.h" From e04e726f45c375c9b4efd0c621812e5782ec71d2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 11:38:45 -0800 Subject: [PATCH 104/220] rm lu --- src/math/lp/lar_core_solver.h | 9 +-------- src/math/lp/lar_core_solver_def.h | 29 +---------------------------- src/math/lp/lar_solver.cpp | 4 ---- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index d9379cb9d..d40572b9e 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -56,8 +56,6 @@ public: lp_primal_core_solver> m_r_solver; // solver in rational numbers - - lp_primal_core_solver m_d_solver; // solver in doubles lar_core_solver( lp_settings & settings, @@ -140,7 +138,6 @@ public: void push() { lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); @@ -196,13 +193,9 @@ public: m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); - } - - bool need_to_presolve_with_double_solver() const { - return false; } + template bool is_zero_vector(const vector & b) { for (const L & m: b) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index fe99a202f..419345f0c 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -31,18 +31,6 @@ lar_core_solver::lar_core_solver( m_r_lower_bounds(), m_r_upper_bounds(), settings, - column_names), - m_d_solver(m_d_A, - m_d_right_sides_dummy, - m_d_x, - m_d_basis, - m_d_nbasis, - m_d_heading, - m_d_costs_dummy, - m_column_types(), - m_d_lower_bounds, - m_d_upper_bounds, - settings, column_names) { } @@ -57,22 +45,7 @@ void lar_core_solver::prefix_r() { } } -void lar_core_solver::prefix_d() { - // m_d_solver.m_b.resize(m_d_solver.m_m()); - m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); - m_d_solver.m_costs.resize(m_d_solver.m_n()); - m_d_solver.m_d.resize(m_d_solver.m_n()); - m_d_solver.m_ed.resize(m_d_solver.m_m()); - m_d_solver.m_pivot_row.resize(m_d_solver.m_n()); - m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m()); - m_d_solver.m_w.resize(m_d_solver.m_m()); - m_d_solver.m_y.resize(m_d_solver.m_m()); - m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n()); - m_d_solver.m_column_norms.clear(); - m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2); - m_d_solver.clear_inf_set(); - m_d_solver.resize_inf_set(m_d_solver.m_n()); -} + void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 48c575184..f890b27fa 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -47,7 +47,6 @@ namespace lp { bool lar_solver::sizes_are_correct() const { - lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); @@ -748,9 +747,6 @@ namespace lp { void lar_solver::solve_with_core_solver() { - if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { - add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); - } m_mpq_lar_core_solver.prefix_r(); if (costs_are_used()) { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); From 6eedbd4f35d6cee4c2c50ebc0cca88135ac22a38 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 13:40:21 -0800 Subject: [PATCH 105/220] rm lu --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/square_dense_submatrix.cpp | 48 -- src/math/lp/square_dense_submatrix.h | 225 --------- src/math/lp/square_dense_submatrix_def.h | 370 -------------- src/test/lp/lp.cpp | 592 +---------------------- 5 files changed, 4 insertions(+), 1232 deletions(-) delete mode 100644 src/math/lp/square_dense_submatrix.cpp delete mode 100644 src/math/lp/square_dense_submatrix.h delete mode 100644 src/math/lp/square_dense_submatrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 970737367..5d21bba70 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -42,7 +42,6 @@ z3_add_component(lp random_updater.cpp row_eta_matrix.cpp scaler.cpp - square_dense_submatrix.cpp square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES diff --git a/src/math/lp/square_dense_submatrix.cpp b/src/math/lp/square_dense_submatrix.cpp deleted file mode 100644 index 4d9fcec13..000000000 --- a/src/math/lp/square_dense_submatrix.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/square_dense_submatrix_def.h" -template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); -template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); -template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix >::is_L_matrix() const; -template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template int lp::square_dense_submatrix >::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix >::pivot(unsigned int, lp::lp_settings&); -#ifdef Z3DEBUG -template double lp::square_dense_submatrix::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::square_dense_submatrix::apply_from_right(vector&); - -template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); diff --git a/src/math/lp/square_dense_submatrix.h b/src/math/lp/square_dense_submatrix.h deleted file mode 100644 index 308f9eadc..000000000 --- a/src/math/lp/square_dense_submatrix.h +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/square_sparse_matrix.h" -namespace lp { -template -class square_dense_submatrix : public tail_matrix { - // the submatrix uses the permutations of the parent matrix to access the elements - struct ref { - unsigned m_i_offset; - square_dense_submatrix & m_s; - ref(unsigned i, square_dense_submatrix & s) : - m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} - T & operator[] (unsigned j) { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - const T & operator[] (unsigned j) const { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - }; -public: - unsigned m_index_start; - unsigned m_dim; - vector m_v; - square_sparse_matrix * m_parent; - permutation_matrix m_row_permutation; - indexed_vector m_work_vector; -public: - permutation_matrix m_column_permutation; - bool is_active() const { return m_parent != nullptr; } - - square_dense_submatrix() {} - - square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); - - void init(square_sparse_matrix *parent_matrix, unsigned index_start); - - bool is_dense() const override { return true; } - - ref operator[] (unsigned i) { - lp_assert(i >= m_index_start); - lp_assert(i < m_parent->dimension()); - return ref(i, *this); - } - - int find_pivot_column_in_row(unsigned i) const; - - void swap_columns(unsigned i, unsigned j) { - if (i != j) - m_column_permutation.transpose_from_left(i, j); - } - - unsigned adjust_column(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_column_inverse(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation[col]; - } - unsigned adjust_row(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation[row]; - } - - unsigned adjust_row_inverse(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation.apply_reverse(row); - } - - void pivot(unsigned i, lp_settings & settings); - - void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);; - - void divide_row_by_pivot(unsigned i); - - void update_parent_matrix(lp_settings & settings); - - void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings); - - void push_new_elements_to_parent_matrix(lp_settings & settings); - - template - L row_by_vector_product(unsigned i, const vector & v); - - template - L column_by_vector_product(unsigned j, const vector & v); - - template - L row_by_indexed_vector_product(unsigned i, const indexed_vector & v); - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - template - void apply_from_left_to_vector(vector & w); - - bool is_L_matrix() const; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - - void apply_from_right(indexed_vector & w) override { -#if 1==0 - indexed_vector wcopy = w; - apply_from_right(wcopy.m_data); - wcopy.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(wcopy.m_data[i])) - wcopy.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = wcopy.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - wcopy.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } - lp_assert(wcopy.is_OK()); - apply_from_right(w.m_data); - w.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(w.m_data[i])) - w.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = w.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - w.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } -#else - lp_assert(w.is_OK()); - lp_assert(m_work_vector.is_OK()); - m_work_vector.resize(w.data_size()); - m_work_vector.clear(); - lp_assert(m_work_vector.is_OK()); - unsigned end = m_index_start + m_dim; - for (unsigned k : w.m_index) { - // find j such that k = adjust_row_inverse(j) - unsigned j = adjust_row(k); - if (j < m_index_start || j >= end) { - m_work_vector.set_value(w[k], adjust_column_inverse(j)); - } else { // j >= m_index_start and j < end - unsigned offset = (j - m_index_start) * m_dim; // this is the row start - const T& wv = w[k]; - for (unsigned col = m_index_start; col < end; col++, offset ++) { - unsigned adj_col = adjust_column_inverse(col); - m_work_vector.add_value_at_index(adj_col, m_v[offset] * wv); - } - } - } - m_work_vector.clean_up(); - lp_assert(m_work_vector.is_OK()); - w = m_work_vector; -#endif - } - void apply_from_left(vector & w, lp_settings & /*settings*/) override { - apply_from_left_to_vector(w);// , settings); - } - - void apply_from_right(vector & w) override; - -#ifdef Z3DEBUG - T get_elem (unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_parent->row_count();} - unsigned column_count() const override { return row_count();} - void set_number_of_rows(unsigned) override {} - void set_number_of_columns(unsigned) override {} -#endif - void conjugate_by_permutation(permutation_matrix & q); -}; -} diff --git a/src/math/lp/square_dense_submatrix_def.h b/src/math/lp/square_dense_submatrix_def.h deleted file mode 100644 index 3a9006b4d..000000000 --- a/src/math/lp/square_dense_submatrix_def.h +++ /dev/null @@ -1,370 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_dense_submatrix.h" -namespace lp { -template -square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : - m_index_start(index_start), - m_dim(parent_matrix->dimension() - index_start), - m_v(m_dim * m_dim), - m_parent(parent_matrix), - m_row_permutation(m_parent->dimension()), - m_column_permutation(m_parent->dimension()) { - int row_offset = - static_cast(m_index_start); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - lp_assert(j>= m_index_start); - m_v[row_offset + j] = iv.m_value; - } - row_offset += m_dim; - } -} - -template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { - m_index_start = index_start; - m_dim = parent_matrix->dimension() - index_start; - m_v.resize(m_dim * m_dim); - m_parent = parent_matrix; - m_column_permutation.init(m_parent->dimension()); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - (*this)[i][j] = iv.m_value; - } - } -} - -template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { - int j = -1; - T max = zero_of_type(); - lp_assert(i >= m_index_start); - unsigned row_start = (i - m_index_start) * m_dim; - for (unsigned k = i; k < m_parent->dimension(); k++) { - unsigned col = adjust_column(k); // this is where the column is in the row - unsigned offs = row_start + col - m_index_start; - T t = abs(m_v[offs]); - if (t > max) { - j = k; - max = t; - } - } - return j; -} - -template void square_dense_submatrix::pivot(unsigned i, lp_settings & settings) { - divide_row_by_pivot(i); - for (unsigned k = i + 1; k < m_parent->dimension(); k++) - pivot_row_to_row(i, k, settings); -} - -template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { - lp_assert(i < row); - unsigned pj = adjust_column(i); // the pivot column - unsigned pjd = pj - m_index_start; - unsigned pivot_row_offset = (i-m_index_start)*m_dim; - T pivot = m_v[pivot_row_offset + pjd]; - unsigned row_offset= (row-m_index_start)*m_dim; - T m = m_v[row_offset + pjd]; - lp_assert(!is_zero(pivot)); - m_v[row_offset + pjd] = -m * pivot; // creating L matrix - for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { - if (j == pj) { - pivot_row_offset++; - row_offset++; - continue; - } - auto t = m_v[row_offset] - m_v[pivot_row_offset] * m; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - m_v[row_offset] = zero_of_type(); - } else { - m_v[row_offset] = t; - } - row_offset++; pivot_row_offset++; - // at the same time we pivot the L too - } -} - -template void square_dense_submatrix::divide_row_by_pivot(unsigned i) { - unsigned pj = adjust_column(i); // the pivot column - unsigned irow_offset = (i - m_index_start) * m_dim; - T pivot = m_v[irow_offset + pj - m_index_start]; - lp_assert(!is_zero(pivot)); - for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { - if (k == pj){ - m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal - continue; - } - m_v[irow_offset++] /= pivot; - } -} - -template void square_dense_submatrix::update_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - update_existing_or_delete_in_parent_matrix_for_row(i, settings); - push_new_elements_to_parent_matrix(settings); - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - m_parent->set_max_in_row(m_parent->adjust_row(i)); -} - -template void square_dense_submatrix::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) { - bool diag_updated = false; - unsigned ai = m_parent->adjust_row(i); - auto & row_vals = m_parent->get_row_values(ai); - for (unsigned k = 0; k < row_vals.size(); k++) { - auto & iv = row_vals[k]; - unsigned j = m_parent->adjust_column_inverse(iv.m_index); - if (j < i) { - m_parent->remove_element(row_vals, iv); - k--; - } else if (i == j) { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type()); - diag_updated = true; - } else { // j > i - T & v = (*this)[i][j]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - m_parent->remove_element(row_vals, iv); - k--; - } else { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); - v = zero_of_type(); // only new elements are left above the diagonal - } - } - } - if (!diag_updated) { - unsigned aj = m_parent->adjust_column(i); - m_parent->add_new_element(ai, aj, one_of_type()); - } -} - -template void square_dense_submatrix::push_new_elements_to_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) { - unsigned ai = m_parent->adjust_row(i); - for (unsigned j = i + 1; j < m_parent->dimension(); j++) { - T & v = (*this)[i][j]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) { - unsigned aj = m_parent->adjust_column(j); - m_parent->add_new_element(ai, aj, v); - } - v = zero_of_type(); // leave only L elements now - } - } -} -template -template -L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} - -template -template -L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { - lp_assert(j >= m_index_start); - - unsigned offset = j - m_index_start; - L r = zero_of_type(); - for (unsigned i = 0; i < m_dim; i++, offset += m_dim) - r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)]; - return r; -} -template -template -L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} -template -template -void square_dense_submatrix::apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w.m_data.size()); - // for (unsigned i = 0; i < w.m_data.size(); i++) - // deb_w[i] = w[i]; - - // deb.apply_from_left(deb_w); -#endif // use indexed vector here - -#ifndef DO_NOT_USE_INDEX - vector t(m_parent->dimension(), zero_of_type()); - for (auto k : w.m_index) { - unsigned j = adjust_column(k); // k-th element will contribute only to column j - if (j < m_index_start || j >= this->m_index_start + this->m_dim) { // it is a unit matrix outside - t[adjust_row_inverse(j)] = w[k]; - } else { - const L & v = w[k]; - for (unsigned i = 0; i < m_dim; i++) { - unsigned row = adjust_row_inverse(m_index_start + i); - unsigned offs = i * m_dim + j - m_index_start; - t[row] += m_v[offs] * v; - } - } - } - w.m_index.clear(); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){ - w.m_index.push_back(i); - w.m_data[i] = v; - } else { - w.m_data[i] = zero_of_type(); - } - } -#else - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w.set_value(t[i], i); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!is_zero(v)) - w.m_index.push_back(i); - w.m_data[i] = v; - } -#endif -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w.m_data)); - // lp_assert(w.is_OK()); -#endif -} - -template -template -void square_dense_submatrix::apply_from_left_to_vector(vector & w) { - // lp_settings & settings) { - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_left_to_X(deb_w, settings); - // // cout << "deb" << endl; - // // print_matrix(deb); - // // cout << "w" << endl; - // // print_vector(w.m_data); - // // cout << "deb_w" << endl; - // // print_vector(deb_w); - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w[i] = t[i]; - } -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w)); -#endif -} - -template bool square_dense_submatrix::is_L_matrix() const { -#ifdef Z3DEBUG - lp_assert(m_row_permutation.is_identity()); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (i < m_index_start) { - lp_assert(m_column_permutation[i] == i); - continue; - } - unsigned row_offs = (i-m_index_start)*m_dim; - for (unsigned k = 0; k < m_dim; k++) { - unsigned j = m_index_start + k; - unsigned jex = adjust_column_inverse(j); - if (jex > i) { - lp_assert(is_zero(m_v[row_offs + k])); - } else if (jex == i) { - lp_assert(!is_zero(m_v[row_offs + k])); - } - } - } -#endif - return true; -} - -template void square_dense_submatrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_right(deb_w); -#endif - vector t(w.size()); - - for (unsigned j = 0; j < m_index_start; j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - unsigned end = m_index_start + m_dim; - for (unsigned j = end; j < m_parent->dimension(); j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - for (unsigned j = m_index_start; j < end; j++) { - t[adjust_column_inverse(j)] = column_by_vector_product(j, w); - } - w = t; -#ifdef Z3DEBUG - // lp_assert(vector_are_equal(deb_w, w)); -#endif -} - - - - -#ifdef Z3DEBUG - -template T square_dense_submatrix::get_elem (unsigned i, unsigned j) const { - i = adjust_row(i); - j = adjust_column(j); - if (i < m_index_start || j < m_index_start) - return i == j? one_of_type() : zero_of_type(); - unsigned offs = (i - m_index_start)* m_dim + j - m_index_start; - return m_v[offs]; -} - -#endif -template void square_dense_submatrix::conjugate_by_permutation(permutation_matrix & q) { - m_row_permutation.multiply_by_permutation_from_left(q); - m_column_permutation.multiply_by_reverse_from_right(q); -} -} diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c386cf002..2e8faeaed 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -48,7 +48,6 @@ #include "test/lp/gomory_test.h" #include "math/lp/matrix.h" #include "math/lp/hnf.h" -#include "math/lp/square_sparse_matrix_def.h" #include "math/lp/general_matrix.h" #include "math/lp/lp_bound_propagator.h" #include "math/lp/nla_solver.h" @@ -56,6 +55,8 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" +#include "math/lp/static_matrix.h" +#include "math/lp/dense_matrix.h" bool my_white_space(const char & a) { return a == ' ' || a == '\t'; @@ -388,122 +389,6 @@ struct simple_column_namer:public column_namer }; -template -void test_matrix(square_sparse_matrix & a) { - auto m = a.dimension(); - - // copy a to b in the reversed order - square_sparse_matrix b(m, m); - std::cout << "copy b to a"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) { - b(row, col) = (T const&) a(row, col); - } - - - std::cout << "zeroing b in the reverse order"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) - b.set(row, col, T(0)); - - - - for (unsigned row = 0; row < m; row ++) - for (unsigned col = 0; col < m; col ++) - a.set(row, col, T(0)); - - - unsigned i = my_random() % m; - unsigned j = my_random() % m; - - auto t = T(1); - - a.set(i, j, t); - - lp_assert(a.get(i, j) == t); - - unsigned j1; - if (j < m - 1) { - j1 = m - 1; - a.set(i, j1, T(2)); - } -} - -void tst1() { - std::cout << "testing the minimal matrix with 1 row and 1 column" << std::endl; - square_sparse_matrix m0(1, 1); - m0.set(0, 0, 1); - // print_matrix(m0); - m0.set(0, 0, 0); - // print_matrix(m0); - test_matrix(m0); - - unsigned rows = 2; - square_sparse_matrix m(rows, rows); - std::cout << "setting m(0,1)=" << std::endl; - - m.set(0, 1, 11); - m.set(0, 0, 12); - - // print_matrix(m); - - test_matrix(m); - - square_sparse_matrix m1(2, 2); - m1.set(0, 0, 2); - m1.set(1, 0, 3); - // print_matrix(m1); - std::cout << " zeroing matrix 2 by 2" << std::endl; - m1.set(0, 0, 0); - m1.set(1, 0, 0); - // print_matrix(m1); - - test_matrix(m1); - - - std::cout << "printing zero matrix 3 by 1" << std::endl; - square_sparse_matrix m2(3, 3); - // print_matrix(m2); - - m2.set(0, 0, 1); - m2.set(2, 0, 2); - std::cout << "printing matrix 3 by 1 with a gap" << std::endl; - // print_matrix(m2); - - test_matrix(m2); - - square_sparse_matrix m10by9(10, 10); - m10by9.set(0, 1, 1); - - m10by9(0, 1) = 4; - - double test = m10by9(0, 1); - - std::cout << "got " << test << std::endl; - - - m10by9.set(0, 8, 8); - m10by9.set(3, 4, 7); - m10by9.set(3, 2, 5); - m10by9.set(3, 8, 99); - m10by9.set(3, 2, 6); - m10by9.set(1, 8, 9); - m10by9.set(4, 0, 40); - m10by9.set(0, 0, 10); - - std::cout << "printing matrix 10 by 9" << std::endl; - // print_matrix(m10by9); - - - test_matrix(m10by9); - std::cout <<"zeroing m10by9\n"; -#ifdef Z3DEBUG - for (unsigned int i = 0; i < m10by9.dimension(); i++) - for (unsigned int j = 0; j < m10by9.column_count(); j++) - m10by9.set(i, j, 0); -#endif - // print_matrix(m10by9); -} vector allocate_basis_heading(unsigned count) { // the rest of initialization will be handled by lu_QR vector basis_heading(count, -1); @@ -555,12 +440,6 @@ void test_small_lu(lp_settings & settings) { #endif -void fill_long_row(square_sparse_matrix &m, int i) { - int n = m.dimension(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} void fill_long_row(static_matrix &m, int i) { int n = m.column_count(); @@ -570,13 +449,6 @@ void fill_long_row(static_matrix &m, int i) { } -void fill_long_row_exp(square_sparse_matrix &m, int i) { - int n = m.dimension(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} void fill_long_row_exp(static_matrix &m, int i) { int n = m.column_count(); @@ -586,26 +458,9 @@ void fill_long_row_exp(static_matrix &m, int i) { } } -void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row_exp(m, i); -} - -void fill_larger_square_sparse_matrix_exp(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row_exp(m, i); -} -void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row(m, i); -} -void fill_larger_square_sparse_matrix(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row(m, i); -} int perm_id = 0; @@ -616,11 +471,6 @@ int perm_id = 0; -void init_b(vector & b, square_sparse_matrix & m, vector& x) { - for (unsigned i = 0; i < m.dimension(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} void init_b(vector & b, static_matrix & m, vector & x) { for (unsigned i = 0; i < m.row_count(); i++) { @@ -671,7 +521,7 @@ void test_lp_1() { m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; #ifdef Z3DEBUG - print_matrix(m, std::cout); + //print_matrix(m, std::cout); #endif vector x_star(7); x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; @@ -724,213 +574,9 @@ void test_lp_primal_core_solver() { } -#ifdef Z3DEBUG -template -void test_swap_rows_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_left(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} -#endif -template -void fill_matrix(square_sparse_matrix& m); // forward definition -#ifdef Z3DEBUG -template -void test_swap_cols_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_right(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} -template -void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; - m.swap_rows(i0, i1); - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(i0, j) == m(i1, j)); - lp_assert(mcopy(i1, j) == m(i0, j)); - } -} -template -void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - m.swap_columns(i0, i1); - - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i0) == m(j, i1)); - lp_assert(mcopy(j, i1) == m(j, i0)); - } - - for (unsigned i = 0; i < m.dimension(); i++) { - if (i == i0 || i == i1) - continue; - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i)== m(j, i)); - } - } -} -#endif - -template -void fill_matrix(square_sparse_matrix& m){ - int v = 0; - for (int i = m.dimension() - 1; i >= 0; i--) { - for (int j = m.dimension() - 1; j >=0; j--){ - m(i, j) = v++; - } - } -} - -void test_pivot_like_swaps_and_pivot(){ - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - // pivot at 2,7 - m.swap_columns(0, 7); - // print_matrix(m); - m.swap_rows(2, 0); - // print_matrix(m); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - // print_matrix(m); - - // say pivot at 3,4 - m.swap_columns(1, 4); - // print_matrix(m); - m.swap_rows(1, 3); - // print_matrix(m); - - vector row; - double alpha = 2.33; - unsigned pivot_row = 1; - unsigned target_row = 2; - unsigned pivot_row_0 = 3; - double beta = 3.1; - m(target_row, 3) = 0; - m(target_row, 5) = 0; - m(pivot_row, 6) = 0; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - - for (unsigned j = 0; j < m.dimension(); j++) { - row.push_back(m(target_row, j) + alpha * m(pivot_row, j) + beta * m(pivot_row_0, j)); - } - - for (auto & t : row) { - std::cout << t << ","; - } - std::cout << std::endl; - lp_settings settings; - m.pivot_row_to_row(pivot_row, alpha, target_row, settings); - m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); - // print_matrix(m); - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); - } -} - -#ifdef Z3DEBUG -void test_swap_rows() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - test_swap_rows(m, 3, 5); - - test_swap_rows(m, 1, 3); - - - test_swap_rows(m, 1, 3); - - test_swap_rows(m, 1, 7); - - test_swap_rows(m, 3, 7); - - test_swap_rows(m, 0, 7); - - m(0, 4) = 1; - // print_matrix(m); - test_swap_rows(m, 0, 7); - - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_rows(m0, 0, 1); - m0(0, 0) = 3; - test_swap_rows(m0, 0, 1); - m0(1, 0) = 3; - test_swap_rows(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_rows(m1, 0, 1); - m1(0, 0) = 3; - test_swap_rows(m1, 0, 1); - m1(1, 0) = 3; - m1(0, 3) = 5; - m1(1, 3) = 4; - m1(1, 8) = 8; - m1(1, 9) = 8; - - test_swap_rows(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_rows(m2, 0, 1); - m2(0, 0) = 3; - test_swap_rows(m2, 0, 1); - m2(2, 0) = 3; - test_swap_rows(m2, 0, 2); -} - -void fill_uniformly(square_sparse_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m(i, j) = v++; - } - } -} void fill_uniformly(dense_matrix & m, unsigned dim) { int v = 0; @@ -941,146 +587,8 @@ void fill_uniformly(dense_matrix & m, unsigned dim) { } } -void square_sparse_matrix_with_permutations_test() { - unsigned dim = 4; - square_sparse_matrix m(dim, dim); - fill_uniformly(m, dim); - dense_matrix dm(dim, dim); - fill_uniformly(dm, dim); - dense_matrix dm0(dim, dim); - fill_uniformly(dm0, dim); - permutation_matrix q0(dim); - q0[0] = 1; - q0[1] = 0; - q0[2] = 3; - q0[3] = 2; - permutation_matrix q1(dim); - q1[0] = 1; - q1[1] = 2; - q1[2] = 3; - q1[3] = 0; - permutation_matrix p0(dim); - p0[0] = 1; - p0[1] = 0; - p0[2] = 3; - p0[3] = 2; - permutation_matrix p1(dim); - p1[0] = 1; - p1[1] = 2; - p1[2] = 3; - p1[3] = 0; - - m.multiply_from_left(q0); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); - } - } - - auto q0_dm = q0 * dm; - lp_assert(m == q0_dm); - - m.multiply_from_left(q1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); - } - } - auto q1_q0_dm = q1 * q0_dm; - - lp_assert(m == q1_q0_dm); - - m.multiply_from_right(p0); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); - } - } - - auto q1_q0_dm_p0 = q1_q0_dm * p0; - - lp_assert(m == q1_q0_dm_p0); - - m.multiply_from_right(p1); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); - } - } - - auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; - lp_assert(m == q1_q0_dm_p0_p1); - - m.multiply_from_right(p1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); - } - } - auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; - - lp_assert(m == q1_q0_dm_p0_p1_p1); -} - -void test_swap_columns() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - - test_swap_columns(m, 3, 5); - - test_swap_columns(m, 1, 3); - - test_swap_columns(m, 1, 3); - - // print_matrix(m); - test_swap_columns(m, 1, 7); - - test_swap_columns(m, 3, 7); - - test_swap_columns(m, 0, 7); - - test_swap_columns(m, 0, 7); - - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_columns(m0, 0, 1); - m0(0, 0) = 3; - test_swap_columns(m0, 0, 1); - m0(0, 1) = 3; - test_swap_columns(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_columns(m1, 0, 1); - m1(0, 0) = 3; - test_swap_columns(m1, 0, 1); - m1(0, 1) = 3; - m1(3, 0) = 5; - m1(3, 1) = 4; - m1(8, 1) = 8; - m1(9, 1) = 8; - - test_swap_columns(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_columns(m2, 0, 1); - m2(0, 0) = 3; - test_swap_columns(m2, 0, 1); - m2(0, 2) = 3; - test_swap_columns(m2, 0, 2); -} - - - -void test_swap_operations() { - test_swap_rows(); - test_swap_columns(); -} void test_dense_matrix() { dense_matrix d(3, 2); @@ -1110,7 +618,7 @@ void test_dense_matrix() { p2.set_elem(1, 0, 1); auto c2 = d * p2; } -#endif + @@ -1471,59 +979,6 @@ bool contains(std::string const & s, char const * pattern) { - -void test_init_U() { - static_matrix m(3, 7); - m(0, 0) = 10; m(0, 1) = 11; m(0, 2) = 12; m(0, 3) = 13; m(0, 4) = 14; - m(1, 0) = 20; m(1, 1) = 21; m(1, 2) = 22; m(1, 3) = 23; m(1, 5) = 24; - m(2, 0) = 30; m(2, 1) = 31; m(2, 2) = 32; m(2, 3) = 33; m(2, 6) = 34; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector basis(3); - basis[0] = 1; - basis[1] = 2; - basis[2] = 4; - - square_sparse_matrix u(m, basis); - - for (unsigned i = 0; i < 3; i++) { - for (unsigned j = 0; j < 3; j ++) { - lp_assert(m(i, basis[j]) == u(i, j)); - } - } - - // print_matrix(m); - // print_matrix(u); -} - -void test_replace_column() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - m.swap_columns(0, 7); - m.swap_columns(6, 3); - m.swap_rows(2, 0); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - - indexed_vector w(m.dimension()); - for (unsigned i = 0; i < m.dimension(); i++) { - w.set_value(i % 3, i); - } - - lp_settings settings; - - for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { - m.replace_column(column_to_replace, w, settings); - for (unsigned i = 0; i < m.dimension(); i++) { - lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); - } - } -} - - - void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-monics", "test emonics"); parser.add_option_with_help_string("-nex_order", "test nex order"); @@ -1544,8 +999,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); - parser.add_option_with_help_string("--test_swaps", "test row swaps with a permutation"); - parser.add_option_with_help_string("--test_perm", "test permutations"); parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); @@ -1871,38 +1324,6 @@ void check_lu_from_file(std::string lufile_name) { lp_assert(false); } -void test_square_dense_submatrix() { - std::cout << "testing square_dense_submatrix" << std::endl; - unsigned parent_dim = 7; - square_sparse_matrix parent(parent_dim, 0); - fill_matrix(parent); - unsigned index_start = 3; - square_dense_submatrix d; - d.init(&parent, index_start); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = i*3+j*2; -#ifdef Z3DEBUG - unsigned dim = parent_dim - index_start; - dense_matrix m(dim, dim); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - print_matrix(&m, std::cout); -#endif - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = d[j][i]; -#ifdef Z3DEBUG - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - - print_matrix(&m, std::cout); - std::cout << std::endl; -#endif -} - void print_st(lp_status status) { @@ -2958,8 +2379,3 @@ void test_lp_local(int argn, char**argv) { void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } -#ifdef Z3DEBUG -namespace lp { -template void print_matrix(general_matrix&, std::ostream&); -} -#endif From 178135486c5fb8f20bd433604208ffa54a79f4fd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 13:56:04 -0800 Subject: [PATCH 106/220] rm scaler --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lar_solver.h | 1 - src/math/lp/scaler.cpp | 22 --- src/math/lp/scaler.h | 94 ------------ src/math/lp/scaler_def.h | 270 ---------------------------------- src/math/lp/static_matrix.cpp | 1 - 6 files changed, 389 deletions(-) delete mode 100644 src/math/lp/scaler.cpp delete mode 100644 src/math/lp/scaler.h delete mode 100644 src/math/lp/scaler_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5d21bba70..5ccc023ab 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -41,7 +41,6 @@ z3_add_component(lp permutation_matrix.cpp random_updater.cpp row_eta_matrix.cpp - scaler.cpp square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 09612d905..be255dd65 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -31,7 +31,6 @@ #include "math/lp/lar_constraints.h" #include "math/lp/lar_core_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/scaler.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/random_updater.h" #include "util/stacked_value.h" diff --git a/src/math/lp/scaler.cpp b/src/math/lp/scaler.cpp deleted file mode 100644 index 46330e2a1..000000000 --- a/src/math/lp/scaler.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/scaler_def.h" -template bool lp::scaler::scale(); -template bool lp::scaler::scale(); diff --git a/src/math/lp/scaler.h b/src/math/lp/scaler.h deleted file mode 100644 index dd21b5c28..000000000 --- a/src/math/lp/scaler.h +++ /dev/null @@ -1,94 +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 /* printf, fopen */ -#include /* exit, EXIT_FAILURE */ -#include "math/lp/lp_utils.h" -#include "math/lp/static_matrix.h" -namespace lp { -// for scaling an LP -template -class scaler { - vector & m_b; // right side - static_matrix &m_A; // the constraint matrix - const T & m_scaling_minimum; - const T & m_scaling_maximum; - vector& m_column_scale; - lp_settings & m_settings; -public: - // constructor - scaler(vector & b, static_matrix &A, const T & scaling_minimum, const T & scaling_maximum, vector & column_scale, - lp_settings & settings): - m_b(b), - m_A(A), - m_scaling_minimum(scaling_minimum), - m_scaling_maximum(scaling_maximum), - m_column_scale(column_scale), - m_settings(settings) { - lp_assert(m_column_scale.size() == 0); - m_column_scale.resize(m_A.column_count(), numeric_traits::one()); - } - - T right_side_balance(); - - T get_balance() { return m_A.get_balance(); } - - T A_min() const; - - T A_max() const; - - T get_A_ratio() const; - - T get_max_ratio_on_rows() const; - - T get_max_ratio_on_columns() const; - - void scale_rows_with_geometric_mean(); - - void scale_columns_with_geometric_mean(); - - void scale_once_for_ratio(); - - bool scale_with_ratio(); - - void bring_row_maximums_to_one(); - - void bring_column_maximums_to_one(); - - void bring_rows_and_columns_maximums_to_one(); - - bool scale_with_log_balance(); - // Returns true if and only if the scaling was successful. - // It is the caller responsibility to restore the matrix - bool scale(); - - void scale_rows(); - - void scale_row(unsigned i); - - void scale_column(unsigned i); - - void scale_columns(); -}; -} diff --git a/src/math/lp/scaler_def.h b/src/math/lp/scaler_def.h deleted file mode 100644 index 8604a67a1..000000000 --- a/src/math/lp/scaler_def.h +++ /dev/null @@ -1,270 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/scaler.h" -#include "math/lp/numeric_pair.h" -namespace lp { -// for scaling an LP -template T scaler::right_side_balance() { - T ret = zero_of_type(); - unsigned i = m_A.row_count(); - while (i--) { - T rs = abs(convert_struct::convert(m_b[i])); - if (!is_zero(rs)) { - numeric_traits::log(rs); - ret += rs * rs; - } - } - return ret; -} - -template T scaler::A_min() const { - T min = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_min_abs_in_row(i); - min = i == 0 ? t : std::min(t, min); - } - return min; -} - -template T scaler::A_max() const { - T max = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_max_abs_in_row(i); - max = i == 0? t : std::max(t, max); - } - return max; -} - -template T scaler::get_A_ratio() const { - T min = A_min(); - T max = A_max(); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); - T ratio = max / min; - return ratio; -} - -template T scaler::get_max_ratio_on_rows() const { - T ret = T(1); - unsigned i = m_A.row_count(); - while (i--) { - T den = m_A.get_min_abs_in_row(i); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); - T t = m_A.get_max_abs_in_row(i)/ den; - if (t > ret) - ret = t; - } - return ret; -} - -template T scaler::get_max_ratio_on_columns() const { - T ret = T(1); - unsigned i = m_A.column_count(); - while (i--) { - T den = m_A.get_min_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(den)) - continue; // got a zero column - T t = m_A.get_max_abs_in_column(i)/den; - if (t > ret) - ret = t; - } - return ret; -} - -template void scaler::scale_rows_with_geometric_mean() { - unsigned i = m_A.row_count(); - while (i--) { - T max = m_A.get_max_abs_in_row(i); - T min = m_A.get_min_abs_in_row(i); - lp_assert(max > zero_of_type() && min > zero_of_type()); - if (is_zero(max) || is_zero(min)) - continue; - T gm = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(gm, 0.01)) { - continue; - } - m_A.multiply_row(i, one_of_type() / gm); - m_b[i] /= gm; - } -} - -template void scaler::scale_columns_with_geometric_mean() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - T min = m_A.get_min_abs_in_column(i); - T den = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(den, 0.01)) - continue; // got a zero column - T gm = T(1)/ den; - T cs = m_column_scale[i] * gm; - if (m_settings.is_eps_small_general(cs, 0.1)) - continue; - m_A.multiply_column(i, gm); - m_column_scale[i] = cs; - } -} - -template void scaler::scale_once_for_ratio() { - T max_ratio_on_rows = get_max_ratio_on_rows(); - T max_ratio_on_columns = get_max_ratio_on_columns(); - bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns; - // if max_ratio_on_columns is the largest then the rows are in worse shape than columns - if (scale_rows_first) { - scale_rows_with_geometric_mean(); - scale_columns_with_geometric_mean(); - } else { - scale_columns_with_geometric_mean(); - scale_rows_with_geometric_mean(); - } -} - -template bool scaler::scale_with_ratio() { - T ratio = get_A_ratio(); - // The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible - unsigned reps = m_settings.reps_in_scaler; - do { - scale_once_for_ratio(); - T new_r = get_A_ratio(); - if (new_r >= T(0.9) * ratio) - break; - } while (reps--); - - bring_rows_and_columns_maximums_to_one(); - return true; -} - -template void scaler::bring_row_maximums_to_one() { - unsigned i = m_A.row_count(); - while (i--) { - T t = m_A.get_max_abs_in_row(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(t)) continue; - m_A.multiply_row(i, one_of_type() / t); - m_b[i] /= t; - } -} - -template void scaler::bring_column_maximums_to_one() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(max)) continue; - T t = T(1) / max; - m_A.multiply_column(i, t); - m_column_scale[i] *= t; - } -} - -template void scaler::bring_rows_and_columns_maximums_to_one() { - if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) { - bring_row_maximums_to_one(); - bring_column_maximums_to_one(); - } else { - bring_column_maximums_to_one(); - bring_row_maximums_to_one(); - } -} - -template bool scaler::scale_with_log_balance() { - T balance = get_balance(); - T balance_before_scaling = balance; - // todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed - for (int i = 0; i < 10; i++) { - scale_rows(); - scale_columns(); - T nb = get_balance(); - if (nb < T(0.9) * balance) { - balance = nb; - } else { - balance = nb; - break; - } - } - return balance <= balance_before_scaling; -} -// Returns true if and only if the scaling was successful. -// It is the caller responsibility to restore the matrix -template bool scaler::scale() { - if (numeric_traits::precise()) return true; - if (m_settings.scale_with_ratio) - return scale_with_ratio(); - return scale_with_log_balance(); -} - -template void scaler::scale_rows() { - for (unsigned i = 0; i < m_A.row_count(); i++) - scale_row(i); -} - -template void scaler::scale_row(unsigned i) { - T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); - T alpha = numeric_traits::one(); - if (numeric_traits::is_zero(row_max)) { - return; - } - if (row_max < m_scaling_minimum) { - do { - alpha *= T(2); - row_max *= T(2); - } while (row_max < m_scaling_minimum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } else if (row_max > m_scaling_maximum) { - do { - alpha /= T(2); - row_max /= T(2); - } while (row_max > m_scaling_maximum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } -} - -template void scaler::scale_column(unsigned i) { - T column_max = m_A.get_max_abs_in_column(i); - T alpha = numeric_traits::one(); - - if (numeric_traits::is_zero(column_max)){ - return; // the column has zeros only - } - if (column_max < m_scaling_minimum) { - do { - alpha *= T(2); - column_max *= T(2); - } while (column_max < m_scaling_minimum); - } else if (column_max > m_scaling_maximum) { - do { - alpha /= T(2); - column_max /= T(2); - } while (column_max > m_scaling_maximum); - } else { - return; - } - m_A.multiply_column(i, alpha); - m_column_scale[i] = alpha; -} - -template void scaler::scale_columns() { - for (unsigned i = 0; i < m_A.column_count(); i++) { - scale_column(i); - } -} -} diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 93360b914..12d2f9f0d 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -24,7 +24,6 @@ Revision History: #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/lp_primal_core_solver.h" -#include "math/lp/scaler.h" #include "math/lp/lar_solver.h" namespace lp { template void static_matrix::add_columns_at_the_end(unsigned int); From 0fb65dea3f68e16d9c9dd53f365d27cda71a3a57 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 14:17:04 -0800 Subject: [PATCH 107/220] rm square_sparse_matrix --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lar_solver.cpp | 7 - src/math/lp/lar_solver.h | 2 - src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/square_sparse_matrix.cpp | 118 --- src/math/lp/square_sparse_matrix.h | 433 -------- src/math/lp/square_sparse_matrix_def.h | 1303 ------------------------ src/test/lp/lp.cpp | 4 +- 8 files changed, 3 insertions(+), 1867 deletions(-) delete mode 100644 src/math/lp/square_sparse_matrix.cpp delete mode 100644 src/math/lp/square_sparse_matrix.h delete mode 100644 src/math/lp/square_sparse_matrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 5ccc023ab..ef363509f 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -41,7 +41,6 @@ z3_add_component(lp permutation_matrix.cpp random_updater.cpp row_eta_matrix.cpp - square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index f890b27fa..9bc850848 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -783,13 +783,6 @@ namespace lp { return r; } - - template - void lar_solver::add_last_rows_to_lu(lp_primal_core_solver& s) { - lp_assert(false); - - } - bool lar_solver::x_is_correct() const { if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { return false; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index be255dd65..b925b5ef0 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -222,8 +222,6 @@ class lar_solver : public column_namer { void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - template - void add_last_rows_to_lu(lp_primal_core_solver & s); bool x_is_correct() const; void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); template diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 2c7360712..10de624f5 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -926,7 +926,7 @@ public: m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); #ifdef Z3DEBUG - // check_correctness(); + lp_assert(false); #endif } diff --git a/src/math/lp/square_sparse_matrix.cpp b/src/math/lp/square_sparse_matrix.cpp deleted file mode 100644 index 3ec88f47d..000000000 --- a/src/math/lp/square_sparse_matrix.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector >&, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set(unsigned int, unsigned int, double); -template void square_sparse_matrix::set_max_in_row(vector >&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector> &, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set_max_in_row(vector>&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); -template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; -template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); -template void square_sparse_matrix>::prepare_for_factorization(); -template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); -template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix>::set_max_in_row(vector>&); -template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); -template void square_sparse_matrix>::solve_y_U(vector&) const; -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -#ifdef Z3DEBUG -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -#endif - -template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix >::solve_U_y >(vector >&); -template void square_sparse_matrix >::double_solve_U_y >(vector >&); -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); -template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; -template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::solve_U_y(vector&); -template void square_sparse_matrix >::double_solve_U_y(vector&); -template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); -template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); - -template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); -template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); -template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); -} -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); diff --git a/src/math/lp/square_sparse_matrix.h b/src/math/lp/square_sparse_matrix.h deleted file mode 100644 index 5637af99f..000000000 --- a/src/math/lp/square_sparse_matrix.h +++ /dev/null @@ -1,433 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/u_set.h" -namespace lp { -// it is a square matrix -template -class square_sparse_matrix - : public matrix -{ - struct col_header { - unsigned m_shortened_markovitz; - vector> m_values; // the actual column values - - col_header(): m_shortened_markovitz(0) {} - - void shorten_markovich_by_one() { - m_shortened_markovitz++; - } - - void zero_shortened_markovitz() { - m_shortened_markovitz = 0; - } - }; - - unsigned m_n_of_active_elems; - binary_heap_upair_queue m_pivot_queue; -public: - vector>> m_rows; - vector m_columns; - permutation_matrix m_row_permutation; - permutation_matrix m_column_permutation; - // m_work_pivot_vector[j] = offset of elementh of j-th column in the row we are pivoting to - // if the column is not present then m_work_pivot_vector[j] is -1 - vector m_work_pivot_vector; - vector m_processed; - unsigned get_n_of_active_elems() const { return m_n_of_active_elems; } - -#ifdef Z3DEBUG - // dense_matrix m_dense; -#endif - /* - the rule is: row i is mapped to m_row_permutation[i] and - column j is mapped to m_column_permutation.apply_reverse(j) - */ - - unsigned adjust_row(unsigned row) const{ - return m_row_permutation[row]; - } - - unsigned adjust_column(unsigned col) const{ - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_row_inverse(unsigned row) const{ - return m_row_permutation.apply_reverse(row); - } - - unsigned adjust_column_inverse(unsigned col) const{ - return m_column_permutation[col]; - } - - template - void copy_column_from_input(unsigned input_column, const M& A, unsigned j); - template - void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); - - template - void copy_from_input(const M& A); - template - void copy_from_input_on_basis(const M& A, vector & basis); - -public: - - // constructors - template - square_sparse_matrix(const M &A, vector& basis); - - template - square_sparse_matrix(const M &A); - - square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this - // constructor from the one above - - - - class ref_matrix_element { - square_sparse_matrix & m_matrix; - unsigned m_row; - unsigned m_col; - public: - ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} - ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } - operator T () const { return m_matrix.get(m_row, m_col); } - }; - - class ref_row { - square_sparse_matrix & m_matrix; - unsigned m_row; - public: - ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} - ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } - }; - - void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val); - void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val); - - void set_with_no_adjusting(unsigned row, unsigned col, T val); - - void set(unsigned row, unsigned col, T val); - - T const & get_not_adjusted(unsigned row, unsigned col) const; - T const & get(unsigned row, unsigned col) const; - - ref_row operator[](unsigned row) { return ref_row(*this, row); } - - ref_matrix_element operator()(unsigned row, unsigned col) { return ref_matrix_element(*this, row, col); } - - T operator() (unsigned row, unsigned col) const { return get(row, col); } - - vector> & get_row_values(unsigned row) { - return m_rows[row]; - } - - vector> const & get_row_values(unsigned row) const { - return m_rows[row]; - } - - vector> & get_column_values(unsigned col) { - return m_columns[col].m_values; - } - - vector> const & get_column_values(unsigned col) const { - return m_columns[col].m_values; - } - - unsigned dimension() const {return static_cast(m_row_permutation.size());} - - unsigned row_count() const override {return dimension();} - unsigned column_count() const override {return dimension();} - - void init_row_headers(); - - void init_column_headers(); - - unsigned lowest_row_in_column(unsigned j); - - indexed_value & column_iv_other(indexed_value & iv) { - return m_rows[iv.m_index][iv.m_other]; - } - - indexed_value & row_iv_other(indexed_value & iv) { - return m_columns[iv.m_index].m_values[iv.m_other]; - } - - void remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset); - - void remove_element(vector> & row_chunk, indexed_value & row_el_iv); - - void put_max_index_to_0(vector> & row_vals, unsigned max_index); - - void set_max_in_row(unsigned row) { - set_max_in_row(m_rows[row]); - } - - - void set_max_in_row(vector> & row_vals); - - bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings); - - void scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column); - - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, - // Returns false if the resulting row is all zeroes, and true otherwise - bool pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ); - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings); - - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector(unsigned i0); - - void remove_zero_elements_and_set_data_on_existing_elements(unsigned row); - - // work_vec here has not adjusted column indices - void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings); - - void multiply_from_right(permutation_matrix& p) { - // m_dense = m_dense * p; - m_column_permutation.multiply_by_permutation_from_right(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_from_left(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left_with_reverse(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_reverse_from_left(p); - // lp_assert(*this == m_dense); - } - - // adding delta columns at the end of the matrix - void add_columns_at_the_end(unsigned delta); - - void delete_column(int i); - - void swap_columns(unsigned a, unsigned b) { - m_column_permutation.transpose_from_left(a, b); - } - - void swap_rows(unsigned a, unsigned b) { - m_row_permutation.transpose_from_right(a, b); - // m_dense.swap_rows(a, b); - // lp_assert(*this == m_dense); - } - - void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); - - bool close(T a, T b) { - return // (numeric_traits::precise() && numeric_traits::is_zero(a - b)) - // || - fabs(numeric_traits::get_double(a - b)) < 0.0000001; - } - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U(vector & y) const; - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U_indexed(indexed_vector & y, const lp_settings &); - - // fills the indices for such that y[i] can be not a zero - // sort them so the smaller indices come first - void fill_reachable_indices(std::set & rset, T *y); - - template - void find_error_in_solution_U_y(vector& y_orig, vector & y); - - template - void find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows); - - template - void add_delta_to_solution(const vector& del, vector & y); - - template - void add_delta_to_solution(const indexed_vector& del, indexed_vector & y); - - template - void double_solve_U_y(indexed_vector& y, const lp_settings & settings); - - template - void double_solve_U_y(vector& y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y(vector & y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y_indexed_only(indexed_vector & y, const lp_settings&, vector & sorted_active_rows ); - - T get_elem(unsigned i, unsigned j) const override { return get(i, j); } - unsigned get_number_of_rows() const { return dimension(); } - unsigned get_number_of_columns() const { return dimension(); } - void set_number_of_rows(unsigned /*m*/) override { } - void set_number_of_columns(unsigned /*n*/) override { } - template - L dot_product_with_row (unsigned row, const vector & y) const; - - template - L dot_product_with_row (unsigned row, const indexed_vector & y) const; - - unsigned get_number_of_nonzeroes() const; - - bool get_non_zero_column_in_row(unsigned i, unsigned *j) const; - - void remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv); - - - // w contains the new column - // the old column inside of the matrix has not been changed yet - void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w); - - void add_new_element(unsigned row, unsigned col, const T& val); - - // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed - // the old column inside of the matrix has not been changed yet - void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings); - - void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings); - - unsigned pivot_score(unsigned i, unsigned j); - - void enqueue_domain_into_pivot_queue(); - - void set_max_in_rows(); - - void zero_shortened_markovitz_numbers(); - - void prepare_for_factorization(); - - void recover_pivot_queue(vector & rejected_pivots); - - int elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting); - - bool remove_row_from_active_pivots_and_shorten_columns(unsigned row); - - void remove_pivot_column(unsigned row); - - void update_active_pivots(unsigned row); - - bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix); - - unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k); -#ifdef Z3DEBUG - bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k); - bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k); - void print_active_matrix(unsigned k, std::ostream & out); -#endif - bool pivot_queue_is_correct_for_row(unsigned i, unsigned k); - - bool pivot_queue_is_correct_after_pivoting(int k); - - bool get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k); - - bool elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting); - - unsigned number_of_non_zeroes_in_row(unsigned row) const { - return static_cast(m_rows[row].size()); - } - - unsigned number_of_non_zeroes_in_column(unsigned col) const { - return m_columns[col].m_values.size(); - } - - bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column); - - bool col_is_active(unsigned j, unsigned pivot) { - return adjust_column_inverse(j) > pivot; - } - - bool row_is_active(unsigned i, unsigned pivot) { - return adjust_row_inverse(i) > pivot; - } - - bool fill_eta_matrix(unsigned j, eta_matrix ** eta); -#ifdef Z3DEBUG - bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const; - - bool is_upper_triangular_until(unsigned k) const; - void check_column_vs_rows(unsigned col); - - void check_row_vs_columns(unsigned row); - - void check_rows_vs_columns(); - - void check_columns_vs_rows(); - - void check_matrix(); -#endif - void create_graph_G(const vector & active_rows, vector & sorted_active_rows); - void process_column_recursively(unsigned i, vector & sorted_rows); - void extend_and_sort_active_rows(const vector & active_rows, vector & sorted_active_rows); - void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); - void resize(unsigned new_dim) { - unsigned old_dim = dimension(); - lp_assert(new_dim >= old_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - m_rows.push_back(vector>()); - m_columns.push_back(col_header()); - } - m_pivot_queue.resize(new_dim); - m_row_permutation.resize(new_dim); - m_column_permutation.resize(new_dim); - m_work_pivot_vector.resize(new_dim); - m_processed.resize(new_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - add_new_element(j, j, numeric_traits::one()); - } - } -#ifdef Z3DEBUG -vector get_full_row(unsigned i) const; -#endif - unsigned pivot_queue_size() const { return m_pivot_queue.size(); } -}; -}; - - diff --git a/src/math/lp/square_sparse_matrix_def.h b/src/math/lp/square_sparse_matrix_def.h deleted file mode 100644 index 192634627..000000000 --- a/src/math/lp/square_sparse_matrix_def.h +++ /dev/null @@ -1,1303 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_sparse_matrix.h" -#include "math/lp/dense_matrix.h" - -#include -#include -namespace lp { -template -template -void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(input_column)) { - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(j)) { - if (is_zero(c.coeff())) - continue; - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input(basis[j], A, j); - } -} -template -template -void square_sparse_matrix::copy_from_input(const M & A) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input_with_possible_zeros(A, j); - } -} - -// constructor that copies columns of the basis from A -template -template -square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input_on_basis(A, basis); -} - -template -template -square_sparse_matrix::square_sparse_matrix(const M &A) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input(A); -} - - -template -void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & row_vec = m_rows[row]; - for (auto & iv : row_vec) { - if (iv.m_index == col) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - row_vec.push_back(indexed_value(val, col, -1)); -} - -template -void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & col_vec = m_columns[col].m_values; - for (auto & iv : col_vec) { - if (iv.m_index == row) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - col_vec.push_back(indexed_value(val, row, -1)); -} - - -template -void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code - set_with_no_adjusting_for_row(row, col, val); - set_with_no_adjusting_for_col(row, col, val); -} - -template -void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code - lp_assert(row < dimension() && col < dimension()); - // m_dense.set_elem(row, col, val); - row = adjust_row(row); - col = adjust_column(col); - set_with_no_adjusting(row, col, val); - // lp_assert(*this == m_dense); -} - -template -T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { - for (indexed_value const & iv : m_rows[row]) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -template -T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code - row = adjust_row(row); - auto & row_chunk = m_rows[row]; - col = adjust_column(col); - for (indexed_value const & iv : row_chunk) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -// constructor creating a zero matrix of dim*dim -template -square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : - m_pivot_queue(dim), // dim will be the initial size of the queue - m_row_permutation(dim), - m_column_permutation(dim), - m_work_pivot_vector(dim, -1), - m_processed(dim) { - init_row_headers(); - init_column_headers(); - } - -template -void square_sparse_matrix::init_row_headers() { - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_rows.push_back(vector>()); - } -} - -template -void square_sparse_matrix::init_column_headers() { // we always have only square square_sparse_matrix - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_columns.push_back(col_header()); - } -} - -template -unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { - auto & mc = get_column_values(adjust_column(j)); - unsigned ret = 0; - for (auto & iv : mc) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > ret) { - ret = row; - } - } - return ret; -} - -template -void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { - if (column_offset != column_vals.size() - 1) { - auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail - column_iv_other(column_iv).m_other = column_offset; - if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - } else if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - // do nothing - just decrease the sizes - column_vals.pop_back(); - row_vals.pop_back(); - m_n_of_active_elems--; // the value is correct only when refactoring -} - -template -void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { - auto & column_chunk = get_column_values(row_el_iv.m_index); - indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; - remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); -} - -template -void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { - if (max_index == 0) return; - indexed_value * max_iv = & row_vals[max_index]; - indexed_value * start_iv = & row_vals[0]; - // update the "other" columns elements which are bound to the start_iv and max_iv - m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; - m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; - - // swap the elements - indexed_value t = * max_iv; - * max_iv = * start_iv; - * start_iv = t; -} - -template -void square_sparse_matrix::set_max_in_row(vector> & row_vals) { - if (row_vals.empty()) - return; - T max_val = abs(row_vals[0].m_value); - unsigned max_index = 0; - for (unsigned i = 1; i < row_vals.size(); i++) { - T iabs = abs(row_vals[i].m_value); - if (iabs > max_val) { - max_val = iabs; - max_index = i; - } - } - put_max_index_to_0(row_vals, max_index); -} - -template -bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { - const T& pivot = eta_matrix->get_diagonal_element(); - for (auto & it : eta_matrix->m_column_vector.m_data) { - if (!pivot_row_to_row(i, it.second, it.first, settings)) { - return false; - } - } - - divide_row_by_constant(i, pivot, settings); - if (!shorten_active_matrix(i, eta_matrix)) { - return false; - } - - return true; -} - -// returns the offset of the pivot column in the row -template -void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { - auto & rvals = m_rows[row]; - unsigned size = rvals.size(); - for (unsigned j = 0; j < size; j++) { - auto & iv = rvals[j]; - if (iv.m_index != pivot_column) { - m_work_pivot_vector[iv.m_index] = j; - } else { - remove_element(rvals, iv); - j--; - size--; - } - } -} - -#ifdef Z3DEBUG -template -vector square_sparse_matrix::get_full_row(unsigned i) const { - vector r; - for (unsigned j = 0; j < column_count(); j++) - r.push_back(get(i, j)); - return r; -} -#endif - - - -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -// After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, -// Returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { - lp_assert(i < dimension() && i0 < dimension()); - lp_assert(i != i0); - unsigned pivot_col = adjust_column(i); - i = adjust_row(i); - i0 = adjust_row(i0); - vector became_zeros; - // the offset of element of the pivot column in row i0 - scan_row_to_work_vector_and_remove_pivot_column(i0, pivot_col); - auto & i0_row_vals = m_rows[i0]; - // run over the pivot row and update row i0 - unsigned prev_size_i0 = i0_row_vals.size(); - for (const auto & iv : m_rows[i]) { - unsigned j = iv.m_index; - if (j == pivot_col) continue; - T alv = alpha * iv.m_value; - int j_offs = m_work_pivot_vector[j]; - if (j_offs == -1) { // it is a new element - if (!settings.abs_val_is_smaller_than_drop_tolerance(alv)) { - add_new_element(i0, j, alv); - } - } - else { - auto & row_el_iv = i0_row_vals[j_offs]; - row_el_iv.m_value += alv; - if (settings.abs_val_is_smaller_than_drop_tolerance(row_el_iv.m_value)) { - became_zeros.push_back(j_offs); - ensure_increasing(became_zeros); - } - else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value); - } - } - } - - - // clean the work vector - for (unsigned k = 0; k < prev_size_i0; k++) { - m_work_pivot_vector[i0_row_vals[k].m_index] = -1; - } - - for (unsigned k = became_zeros.size(); k-- > 0; ) { - unsigned j = became_zeros[k]; - remove_element(i0_row_vals, i0_row_vals[j]); - if (i0_row_vals.empty()) - return false; - } - - if (numeric_traits::precise() == false) - set_max_in_row(i0_row_vals); - - return !i0_row_vals.empty(); -} - - - -// set the max val as well -// returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings) { - remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); - // all non-zero elements in m_work_pivot_vector are new - for (unsigned j : work_vec.m_index) { - if (numeric_traits::is_zero(work_vec[j])) { - continue; - } - lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); - add_new_element(i0, adjust_column(j), work_vec[j]); - work_vec[j] = numeric_traits::zero(); - } - work_vec.m_index.clear(); - auto & row_vals = m_rows[i0]; - if (row_vals.empty()) { - return false; - } - set_max_in_row(row_vals); // it helps to find larger pivots - return true; -} - - - -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - T & wj = m_work_pivot_vector[j]; - if (is_zero(wj)) { - remove_element(row_vals, row_el_iv); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(wj); - row_el_iv.set_value(wj); - wj = zero_of_type(); - } - } -} - -// work_vec here has not adjusted column indices -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - unsigned rj = adjust_column_inverse(j); - T val = work_vec[rj]; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - remove_element(row_vals, row_el_iv); - lp_assert(numeric_traits::is_zero(val)); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); - work_vec[rj] = numeric_traits::zero(); - } - } -} - - -// adding delta columns at the end of the matrix -template -void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { - for (unsigned i = 0; i < delta; i++) { - col_header col_head; - m_columns.push_back(col_head); - } - m_column_permutation.enlarge(delta); -} - -template -void square_sparse_matrix::delete_column(int i) { - lp_assert(i < dimension()); - for (auto cell = m_columns[i].m_head; cell != nullptr;) { - auto next_cell = cell->m_down; - kill_cell(cell); - cell = next_cell; - } -} - -template -void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { - lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); - i = adjust_row(i); - for (auto & iv : m_rows[i]) { - T &v = iv.m_value; - v /= t; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ - v = numeric_traits::zero(); - } - m_columns[iv.m_index].m_values[iv.m_other].set_value(v); - } -} - - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - unsigned end = dimension(); - for (unsigned i = 0; i + 1 < end; i++) { - // all y[i] has correct values already - const T & yv = y[i]; - if (numeric_traits::is_zero(yv)) continue; - auto & mc = get_row_values(adjust_row(i)); - for (auto & c : mc) { - unsigned col = adjust_column_inverse(c.m_index); - if (col != i) { - y[col] -= c.m_value * yv; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_right(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); - // delete [] clone_y; - // delete [] rs; -#endif -} - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { -#if 0 && Z3DEBUG - vector ycopy(y.m_data); - if (numeric_traits::precise() == false) - solve_y_U(ycopy); -#endif - vector sorted_active_columns; - extend_and_sort_active_rows(y.m_index, sorted_active_columns); - for (unsigned k = sorted_active_columns.size(); k-- > 0; ) { - unsigned j = sorted_active_columns[k]; - auto & yj = y[j]; - for (auto & c: m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(c.m_index); - if (i == j) continue; - yj -= y[i] * c.m_value; - } - } - y.m_index.clear(); - for (auto j : sorted_active_columns) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y.m_data[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#if 0 && Z3DEBUG - if (numeric_traits::precise() == false) - lp_assert(vectors_are_equal(ycopy, y.m_data)); -#endif -} - - -// fills the indices for such that y[i] can be not a zero -// sort them so the smaller indices come first -// void fill_reachable_indices(std::set & rset, T *y) { -// std::queue q; -// int m = dimension(); -// for (int i = m - 1; i >= 0; i--) { -// if (!numeric_traits::is_zero(y[i])){ -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// q.push(row); -// } -// } -// } -// while (!q.empty()) { -// unsigned i = q.front(); -// q.pop(); -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// if (rset.find(row) == rset.end()){ -// rset.insert(row); -// q.push(row); -// } -// } -// } -// } - -template -template -void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { - unsigned i = dimension(); - while (i--) { - y_orig[i] -= dot_product_with_row(i, y); - } -} - -template -template -void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { - for (unsigned i: sorted_active_rows) - y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! - // y_orig can contain very small values -} - - -template -template -void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { - unsigned i = dimension(); - while (i--) { - y[i] += del[i]; - } -} -template -template -void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { -// lp_assert(del.is_OK()); - // lp_assert(y.is_OK()); - for (auto i : del.m_index) { - y.add_value_at_index(i, del[i]); - } -} -template -template -void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ - lp_assert(y.is_OK()); - indexed_vector y_orig(y); // copy y aside - vector active_rows; - solve_U_y_indexed_only(y, settings, active_rows); - lp_assert(y.is_OK()); - find_error_in_solution_U_y_indexed(y_orig, y, active_rows); - // y_orig contains the error now - if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { - active_rows.clear(); - solve_U_y_indexed_only(y_orig, settings, active_rows); - add_delta_to_solution(y_orig, y); - y.clean_up(); - } else { // the dense version - solve_U_y(y_orig.m_data); - add_delta_to_solution(y_orig.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - } - lp_assert(y.is_OK()); -} -template -template -void square_sparse_matrix::double_solve_U_y(vector& y){ - vector y_orig(y); // copy y aside - solve_U_y(y); - find_error_in_solution_U_y(y_orig, y); - // y_orig contains the error now - solve_U_y(y_orig); - add_delta_to_solution(y_orig, y); -} - -// solving this * x = y, and putting the answer into y -// the matrix here has to be upper triangular -template -template -void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - - for (unsigned j = dimension(); j--; ) { - const L & yj = y[j]; - if (is_zero(yj)) continue; - for (const auto & iv : m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); -#endif -} -template -void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - m_processed[j]=true; - auto & row = m_rows[adjust_row(j)]; - for (auto & c : row) { - unsigned i = adjust_column_inverse(c.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_index_recursively_for_y_U(i, sorted_active_rows); - } - } - sorted_active_rows.push_back(j); -} - -template -void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_column_recursively(i, sorted_active_rows); - } - } - m_processed[j]=true; - sorted_active_rows.push_back(j); -} - - -template -void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_column_recursively(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_index_recursively_for_y_U(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -template -void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version - create_graph_G(y.m_index, sorted_active_rows); - - for (auto k = sorted_active_rows.size(); k-- > 0;) { - unsigned j = sorted_active_rows[k]; - const L & yj = y[j]; - if (is_zero(yj)) continue; - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } - y.m_index.clear(); - for (auto j : sorted_active_rows) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#ifdef Z3DEBUG - // dense_matrix deb(this); - // vector clone_y(y.m_data); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y)); -#endif -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - - -template -unsigned square_sparse_matrix::get_number_of_nonzeroes() const { - unsigned ret = 0; - for (unsigned i = dimension(); i--; ) { - ret += number_of_non_zeroes_in_row(i); - } - return ret; -} - -template -bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { - // go over the i-th row - auto & mc = get_row_values(adjust_row(i)); - if (mc.size() > 0) { - *j = m_column_permutation[mc[0].m_index]; - return true; - } - return false; -} - -template -void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { - auto & row_chunk = m_rows[col_el_iv.m_index]; - indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; - unsigned index_in_row = col_el_iv.m_other; - remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); - if (index_in_row == 0) - set_max_in_row(row_chunk); -} - - -// w contains the new column -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { - // -------------------------------- - // column_vals represents the old column - auto & column_vals = m_columns[column_to_replace].m_values; - for (unsigned k = static_cast(column_vals.size()); k-- > 0;) { - indexed_value & col_el_iv = column_vals[k]; - unsigned i = col_el_iv.m_index; - T &w_data_at_i = w[adjust_row_inverse(i)]; - if (numeric_traits::is_zero(w_data_at_i)) { - remove_element_that_is_not_in_w(column_vals, col_el_iv); - } else { - auto& row_chunk = m_rows[i]; - unsigned index_in_row = col_el_iv.m_other; - if (index_in_row == 0) { - bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); - row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); - if (look_for_max) - set_max_in_row(i); - } else { - row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); - if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, index_in_row); - } - w_data_at_i = numeric_traits::zero(); - } - } -} - -template -void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { - auto & row_vals = m_rows[row]; - auto & col_vals = m_columns[col].m_values; - unsigned row_el_offs = static_cast(row_vals.size()); - unsigned col_el_offs = static_cast(col_vals.size()); - row_vals.push_back(indexed_value(val, col, col_el_offs)); - col_vals.push_back(indexed_value(val, row, row_el_offs)); - m_n_of_active_elems++; -} - -// w contains the "rest" of the new column; all common elements of w and the old column has been zeroed -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - T w_at_i = w[i]; - if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { - unsigned ai = adjust_row(i); - add_new_element(ai, column_to_replace, w_at_i); - auto & row_chunk = m_rows[ai]; - lp_assert(row_chunk.size() > 0); - if (abs(w_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); - } - w[i] = numeric_traits::zero(); - } - w.m_index.clear(); -} - -template -void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { - column_to_replace = adjust_column(column_to_replace); - remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); - add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); -} - -template -unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { - // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of - // new non zeroes we can obtain after the pivoting. - // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, - // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for - // a row singleton ( which is not a column singleton). - - auto col_header = m_columns[j]; - - return static_cast(get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1)); -} - -template -void square_sparse_matrix::enqueue_domain_into_pivot_queue() { - lp_assert(m_pivot_queue.size() == 0); - for (unsigned i = 0; i < dimension(); i++) { - auto & rh = m_rows[i]; - unsigned rnz = static_cast(rh.size()); - for (auto iv : rh) { - unsigned j = iv.m_index; - m_pivot_queue.enqueue(i, j, rnz * (static_cast(m_columns[j].m_values.size()) - 1)); - } - } -} - -template -void square_sparse_matrix::set_max_in_rows() { - unsigned i = dimension(); - while (i--) - set_max_in_row(i); -} - - -template -void square_sparse_matrix::zero_shortened_markovitz_numbers() { - for (auto & ch : m_columns) - ch.zero_shortened_markovitz(); -} - -template -void square_sparse_matrix::prepare_for_factorization() { - zero_shortened_markovitz_numbers(); - set_max_in_rows(); - enqueue_domain_into_pivot_queue(); -} - -template -void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { - for (auto p : rejected_pivots) { - m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); - } -} - -template -int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { - auto & row_chunk = m_rows[i]; - - if (j == row_chunk[0].m_index) { - return 0; // the max element is at the head - } - T max = abs(row_chunk[0].m_value); - for (unsigned k = 1; k < row_chunk.size(); k++) { - auto &iv = row_chunk[k]; - if (iv.m_index == j) - return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; - } - return 2; // the element became zero but it still sits in the active pivots? -} - -template -bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { - unsigned arow = adjust_row(row); - for (auto & iv : m_rows[arow]) { - m_pivot_queue.remove(arow, iv.m_index); - m_n_of_active_elems--; // the value is correct only when refactoring - if (adjust_column_inverse(iv.m_index) <= row) - continue; // this column will be removed anyway - auto & col = m_columns[iv.m_index]; - - col.shorten_markovich_by_one(); - if (col.m_values.size() <= col.m_shortened_markovitz) - return false; // got a zero column - } - return true; -} - -template -void square_sparse_matrix::remove_pivot_column(unsigned row) { - unsigned acol = adjust_column(row); - for (const auto & iv : m_columns[acol].m_values) - if (adjust_row_inverse(iv.m_index) >= row) - m_pivot_queue.remove(iv.m_index, acol); -} - -template -void square_sparse_matrix::update_active_pivots(unsigned row) { - unsigned arow = adjust_row(row); - for (const auto & iv : m_rows[arow]) { - col_header & ch = m_columns[iv.m_index]; - int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cols >= 0); - for (const auto &ivc : ch.m_values) { - unsigned i = ivc.m_index; - if (adjust_row_inverse(i) <= row) continue; // the i is not an active row - m_pivot_queue.enqueue(i, iv.m_index, static_cast(m_rows[i].size())*cols); - } - } -} - -template -bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { - if (!remove_row_from_active_pivots_and_shorten_columns(row)) - return false; - remove_pivot_column(row); - // need to know the max priority of the queue here - update_active_pivots(row); - if (eta_matrix == nullptr) return true; - // it looks like double work, but the pivot scores have changed for all rows - // touched by eta_matrix - for (auto & it : eta_matrix->m_column_vector.m_data) { - unsigned row = adjust_row(it.first); - const auto & row_values = m_rows[row]; - unsigned rnz = static_cast(row_values.size()); - for (auto & iv : row_values) { - const col_header& ch = m_columns[iv.m_index]; - int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cnz >= 0); - m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); - } - } - - return true; -} - -template -unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { - auto &cols = m_columns[j].m_values; - unsigned cnz = cols.size(); - for (auto & iv : cols) { - if (adjust_row_inverse(iv.m_index) < k) - cnz--; - } - lp_assert(cnz > 0); - return m_rows[i].m_values.size() * (cnz - 1); -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { - unsigned arow = adjust_row(row); - auto & row_vals = m_rows[arow].m_values; - auto & begin_iv = row_vals[0]; - T row_max = abs(begin_iv.m_value); - lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); - if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - for (unsigned jj = 1; jj < row_vals.size(); jj++) { - auto & iv = row_vals[jj]; - lp_assert(adjust_column_inverse(iv.m_index) >= k); - lp_assert(abs(iv.m_value) <= row_max); - if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; - if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - } - return false; -} - -template -bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { - unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); - for (unsigned ii = k; ii < dimension(); ii++) { - lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); - } - return true; -} -template -void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { - out << "active matrix for k = " << k << std::endl; - if (k >= dimension()) { - out << "empty" << std::endl; - return; - } - unsigned dim = dimension() - k; - dense_matrix b(dim, dim); - for (unsigned i = 0; i < dim; i++) - for (unsigned j = 0; j < dim; j++ ) - b.set_elem(i, j, zero_of_type()); - for (int i = k; i < dimension(); i++) { - unsigned col = adjust_column(i); - for (auto &iv : get_column_values(col)) { - unsigned row = iv.m_index; - unsigned row_ex = this->adjust_row_inverse(row); - if (row_ex < k) continue; - auto v = this->get_not_adjusted(row, col); - b.set_elem(row_ex - k, i -k, v); - } - } - print_matrix(b, out); -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { - unsigned arow = adjust_row(i); - for (auto & iv : m_rows[arow].m_values) { - lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == - m_pivot_queue.get_priority(arow, iv.m_index)); - } - return true; -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { - for (unsigned i = k + 1; i < dimension(); i++ ) - lp_assert(pivot_queue_is_correct_for_row(i, k)); - lp_assert(m_pivot_queue.is_correct()); - return true; -} -#endif - -template -bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { - vector pivots_candidates_that_are_too_small; - while (!m_pivot_queue.is_empty()) { - m_pivot_queue.dequeue(i, j); - unsigned i_inv = adjust_row_inverse(i); - if (i_inv < k) continue; - unsigned j_inv = adjust_column_inverse(j); - if (j_inv < k) continue; - int _small = elem_is_too_small(i, j, c_partial_pivoting); - if (!_small) { -#ifdef Z3DEBUG - // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { - // print_active_matrix(k); - // lp_assert(false); - // } -#endif - recover_pivot_queue(pivots_candidates_that_are_too_small); - i = i_inv; - j = j_inv; - return true; - } - if (_small != 2) { // 2 means that the pair is not in the matrix - pivots_candidates_that_are_too_small.push_back(std::make_pair(i, j)); - } - } - recover_pivot_queue(pivots_candidates_that_are_too_small); - return false; -} - -template -bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { - if (&iv == &row_chunk[0]) { - return false; // the max element is at the head - } - T val = abs(iv.m_value); - T max = abs(row_chunk[0].m_value); - return val * c_partial_pivoting < max; -} - -template -bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { - vector> & row_chunk = get_row_values(i); - - for (indexed_value & iv : row_chunk) { - unsigned j = iv.m_index; - if (j == pivot_column) { - lp_assert(!col_is_active(j)); - continue; - } - m_columns[j].shorten_markovich_by_one(); - - if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { - const vector> & col_chunk = get_column_values(adjust_column(j)); - bool is_unit = true; - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i > j) { - is_unit = false; - break; - } - if (i == j && iv.m_value != 1) { - is_unit = false; - break; - } - } - - if (is_unit) { - *eta = nullptr; - return true; - } - -#ifdef Z3DEBUG - *eta = new eta_matrix(j, dimension()); -#else - *eta = new eta_matrix(j); -#endif - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i < j) { - continue; - } - if (i > j) { - (*eta)->push_back(i, - iv.m_value); - } else { // i == j - if ( !(*eta)->set_diagonal_element(iv.m_value)) { - delete *eta; - *eta = nullptr; - return false; - } - - } - } - - (*eta)->divide_by_diagonal_element(); - return true; -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { - for (unsigned i = 0; i < dimension(); i++) { - vector> const & row_chunk = get_row_values(i); - lp_assert(row_chunk.size()); - T const & max = abs(row_chunk[0].m_value); - unsigned ai = adjust_row_inverse(i); - for (auto & iv : row_chunk) { - lp_assert(abs(iv.m_value) <= max); - unsigned aj = adjust_column_inverse(iv.m_index); - if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) - return false; - if (aj == ai) { - if (iv.m_value != 1) { - return false; - } - } - if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { - for (unsigned j = 0; j < dimension() && j < k; j++) { - unsigned aj = adjust_column(j); - auto & col = get_column_values(aj); - for (auto & iv : col) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > j) - return false; - } - } - return true; -} - -template -void square_sparse_matrix::check_column_vs_rows(unsigned col) { - auto & mc = get_column_values(col); - for (indexed_value & column_iv : mc) { - indexed_value & row_iv = column_iv_other(column_iv); - if (row_iv.m_index != col) { - lp_assert(false); - } - - if (& row_iv_other(row_iv) != &column_iv) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_row_vs_columns(unsigned row) { - auto & mc = get_row_values(row); - for (indexed_value & row_iv : mc) { - indexed_value & column_iv = row_iv_other(row_iv); - - if (column_iv.m_index != row) { - lp_assert(false); - } - - if (& row_iv != & column_iv_other(column_iv)) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_rows_vs_columns() { - for (unsigned i = 0; i < dimension(); i++) { - check_row_vs_columns(i); - } -} - -template -void square_sparse_matrix::check_columns_vs_rows() { - for (unsigned i = 0; i < dimension(); i++) { - check_column_vs_rows(i); - } -} -template -void square_sparse_matrix::check_matrix() { - check_rows_vs_columns(); - check_columns_vs_rows(); -} -#endif -} - diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 2e8faeaed..b997b38f7 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -576,7 +576,7 @@ void test_lp_primal_core_solver() { - +#ifdef Z3DEBUG void fill_uniformly(dense_matrix & m, unsigned dim) { int v = 0; @@ -619,7 +619,7 @@ void test_dense_matrix() { auto c2 = d * p2; } - +#endif vector> vector_of_permutations() { From f33f8c265ee4ec52d37ec6c27c0e608fddaf6741 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 14:38:59 -0800 Subject: [PATCH 108/220] more cleanup --- src/math/lp/CMakeLists.txt | 2 - src/math/lp/eta_matrix.cpp | 43 ------- src/math/lp/eta_matrix.h | 98 ---------------- src/math/lp/eta_matrix_def.h | 151 ------------------------- src/math/lp/row_eta_matrix.cpp | 47 -------- src/math/lp/row_eta_matrix.h | 89 --------------- src/math/lp/row_eta_matrix_def.h | 188 ------------------------------- src/test/lp/lp.cpp | 58 ---------- 8 files changed, 676 deletions(-) delete mode 100644 src/math/lp/eta_matrix.cpp delete mode 100644 src/math/lp/eta_matrix.h delete mode 100644 src/math/lp/eta_matrix_def.h delete mode 100644 src/math/lp/row_eta_matrix.cpp delete mode 100644 src/math/lp/row_eta_matrix.h delete mode 100644 src/math/lp/row_eta_matrix_def.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index ef363509f..98241bf60 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -4,7 +4,6 @@ z3_add_component(lp binary_heap_upair_queue.cpp core_solver_pretty_printer.cpp dense_matrix.cpp - eta_matrix.cpp emonics.cpp factorization.cpp factorization_factory_imp.cpp @@ -40,7 +39,6 @@ z3_add_component(lp nra_solver.cpp permutation_matrix.cpp random_updater.cpp - row_eta_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util diff --git a/src/math/lp/eta_matrix.cpp b/src/math/lp/eta_matrix.cpp deleted file mode 100644 index 77f538426..000000000 --- a/src/math/lp/eta_matrix.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/eta_matrix_def.h" -#ifdef Z3DEBUG -template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix >::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix >::apply_from_left(vector >&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(vector&); -template void lp::eta_matrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); diff --git a/src/math/lp/eta_matrix.h b/src/math/lp/eta_matrix.h deleted file mode 100644 index 14fe3ffea..000000000 --- a/src/math/lp/eta_matrix.h +++ /dev/null @@ -1,98 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/tail_matrix.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -class eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_length; -#endif - unsigned m_column_index; -public: - sparse_vector m_column_vector; - T m_diagonal_element; -#ifdef Z3DEBUG - eta_matrix(unsigned column_index, unsigned length): -#else - eta_matrix(unsigned column_index): -#endif - -#ifdef Z3DEBUG - m_length(length), -#endif - m_column_index(column_index) {} - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - bool is_unit() { - return m_column_vector.size() == 0 && m_diagonal_element == 1; - } - - bool set_diagonal_element(T const & diagonal_element) { - m_diagonal_element = diagonal_element; - return !lp_settings::is_eps_small_general(diagonal_element, 1e-12); - } - - const T & get_diagonal_element() const { - return m_diagonal_element; - } - - void apply_from_left(vector & w, lp_settings & ) override; - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_column_index); - m_column_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - -#ifdef Z3DEBUG - T get_elem(unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_length; } - unsigned column_count() const override { return m_length; } - void set_number_of_rows(unsigned m) override { m_length = m; } - void set_number_of_columns(unsigned n) override { m_length = n; } -#endif - void divide_by_diagonal_element() { - m_column_vector.divide(m_diagonal_element); - } - void conjugate_by_permutation(permutation_matrix & p); -}; -} diff --git a/src/math/lp/eta_matrix_def.h b/src/math/lp/eta_matrix_def.h deleted file mode 100644 index a6c908572..000000000 --- a/src/math/lp/eta_matrix_def.h +++ /dev/null @@ -1,151 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/eta_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -void eta_matrix::apply_from_left(vector & w, lp_settings & ) { - auto & w_at_column_index = w[m_column_index]; - for (auto & it : m_column_vector.m_data) { - w[it.first] += w_at_column_index * it.second; - } - w_at_column_index /= m_diagonal_element; -} -template -template -void eta_matrix:: -apply_from_left_local(indexed_vector & w, lp_settings & settings) { - const L w_at_column_index = w[m_column_index]; - if (is_zero(w_at_column_index)) return; - - if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) { - w[m_column_index] = zero_of_type(); - w.erase_from_index(m_column_index); - } - - for (auto & it : m_column_vector.m_data) { - unsigned i = it.first; - if (is_zero(w[i])) { - L v = w[i] = w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - continue; - } - w.m_index.push_back(i); - } else { - L v = w[i] += w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - w.erase_from_index(i); - } - } - } -} -template -void eta_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, get_number_of_rows()); - // deb.apply_from_right(clone_w); -#endif - T t = w[m_column_index] / m_diagonal_element; - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - w[m_column_index] = t; -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); - // delete clone_w; -#endif -} -template -void eta_matrix::apply_from_right(indexed_vector & w) { - if (w.m_index.empty()) - return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - T & t = w[m_column_index]; - t /= m_diagonal_element; - bool was_in_index = (!numeric_traits::is_zero(t)); - - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - - if (numeric_traits::precise() ) { - if (!numeric_traits::is_zero(t)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - } - } else { - if (!lp_settings::is_eps_small_general(t, 1e-14)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - t = zero_of_type(); - } - } - -#ifdef Z3DEBUG - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} -#ifdef Z3DEBUG -template -T eta_matrix::get_elem(unsigned i, unsigned j) const { - if (j == m_column_index){ - if (i == j) { - return 1 / m_diagonal_element; - } - return m_column_vector[i]; - } - - return i == j ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -template -void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_column_index = p.get_rev(m_column_index); - for (auto & pair : m_column_vector.m_data) { - pair.first = p.get_rev(pair.first); - } -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -} diff --git a/src/math/lp/row_eta_matrix.cpp b/src/math/lp/row_eta_matrix.cpp deleted file mode 100644 index 356f80b3c..000000000 --- a/src/math/lp/row_eta_matrix.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/row_eta_matrix_def.h" -namespace lp { -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -#ifdef Z3DEBUG -template mpq row_eta_matrix::get_elem(unsigned int, unsigned int) const; -template mpq row_eta_matrix >::get_elem(unsigned int, unsigned int) const; -template double row_eta_matrix::get_elem(unsigned int, unsigned int) const; -#endif -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix >::apply_from_left(vector>&, lp_settings&); -template void row_eta_matrix >::apply_from_right(vector&); -template void row_eta_matrix >::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -} diff --git a/src/math/lp/row_eta_matrix.h b/src/math/lp/row_eta_matrix.h deleted file mode 100644 index 50c500f00..000000000 --- a/src/math/lp/row_eta_matrix.h +++ /dev/null @@ -1,89 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include -#include "math/lp/sparse_vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - // This is the sum of a unit matrix and a lower triangular matrix - // with non-zero elements only in one row -template -class row_eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_dimension; -#endif - unsigned m_row_start; - unsigned m_row; - sparse_vector m_row_vector; -public: -#ifdef Z3DEBUG - row_eta_matrix(unsigned row_start, unsigned row, unsigned dim): -#else - row_eta_matrix(unsigned row_start, unsigned row): -#endif - -#ifdef Z3DEBUG - m_dimension(dim), -#endif - m_row_start(row_start), m_row(row) { - } - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - const T & get_diagonal_element() const { - return m_row_vector.m_data[m_row]; - } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); - void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local_to_T(w, settings); - } - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_row); - m_row_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - - void conjugate_by_permutation(permutation_matrix & p); -#ifdef Z3DEBUG - T get_elem(unsigned row, unsigned col) const override; - unsigned row_count() const override { return m_dimension; } - unsigned column_count() const override { return m_dimension; } - void set_number_of_rows(unsigned m) override { m_dimension = m; } - void set_number_of_columns(unsigned n) override { m_dimension = n; } -#endif -}; // end of row_eta_matrix -} diff --git a/src/math/lp/row_eta_matrix_def.h b/src/math/lp/row_eta_matrix_def.h deleted file mode 100644 index faac5c6fe..000000000 --- a/src/math/lp/row_eta_matrix_def.h +++ /dev/null @@ -1,188 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/row_eta_matrix.h" -namespace lp { -template -void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { - // #ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_left(clone_w, settings); - // #endif - - auto & w_at_row = w[m_row]; - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - // w[m_row] = w_at_row; - // #ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete [] clone_w; - // #endif -} - -template -void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_right(vector & w) { - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_right(clone_w); -#endif - for (auto & it : m_row_vector.m_data) { - w[it.first] += w_row * it.second; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete clone_w; -#endif -} - -template -void row_eta_matrix::apply_from_right(indexed_vector & w) { - lp_assert(w.is_OK()); - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - if (numeric_traits::precise()) { - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - const T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!numeric_traits::is_zero(v)) - w.m_index.push_back(j); - } else { - if (numeric_traits::is_zero(v)) - w.erase_from_index(j); - } - } - } else { // the non precise version - const double drop_eps = 1e-14; - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!lp_settings::is_eps_small_general(v, drop_eps)) - w.m_index.push_back(j); - else - v = zero_of_type(); - } else { - if (lp_settings::is_eps_small_general(v, drop_eps)) { - w.erase_from_index(j); - v = zero_of_type(); - } - } - } - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(wcopy, w.m_data)); - -#endif -} - -template -void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_row = p.apply_reverse(m_row); - // copy aside the column indices - vector columns; - for (auto & it : m_row_vector.m_data) - columns.push_back(it.first); - for (unsigned i = static_cast(columns.size()); i-- > 0;) - m_row_vector.m_data[i].first = p.get_rev(columns[i]); -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -#ifdef Z3DEBUG -template -T row_eta_matrix::get_elem(unsigned row, unsigned col) const { - if (row == m_row){ - if (col == row) { - return numeric_traits::one(); - } - return m_row_vector[col]; - } - - return col == row ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -} - diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index b997b38f7..c993dbd7d 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -622,66 +622,8 @@ void test_dense_matrix() { #endif -vector> vector_of_permutations() { - vector> ret; - { - permutation_matrix p0(5); - p0[0] = 1; p0[1] = 2; p0[2] = 3; p0[3] = 4; - p0[4] = 0; - ret.push_back(p0); - } - { - permutation_matrix p0(5); - p0[0] = 2; p0[1] = 0; p0[2] = 1; p0[3] = 4; - p0[4] = 3; - ret.push_back(p0); - } - return ret; -} -void test_apply_reverse_from_right_to_perm(permutation_matrix & l) { - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - permutation_matrix pclone(5); - pclone[0] = 4; pclone[1] = 2; pclone[2] = 0; pclone[3] = 3; - pclone[4] = 1; - - p.multiply_by_reverse_from_right(l); -#ifdef Z3DEBUG - auto rev = l.get_inverse(); - auto rs = pclone * rev; - lp_assert(p == rs) -#endif - } - -void test_apply_reverse_from_right() { - auto vec = vector_of_permutations(); - for (unsigned i = 0; i < vec.size(); i++) { - test_apply_reverse_from_right_to_perm(vec[i]); - } -} - -void test_permutations() { - std::cout << "test permutations" << std::endl; - test_apply_reverse_from_right(); - vector v; v.resize(5, 0); - v[1] = 1; - v[3] = 3; - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - - indexed_vector vi(5); - vi.set_value(1, 1); - vi.set_value(3, 3); - - p.apply_reverse_from_right_to_T(v); - p.apply_reverse_from_right_to_T(vi); - lp_assert(vectors_are_equal(v, vi.m_data)); - lp_assert(vi.is_OK()); -} void lp_solver_test() { // lp_revised_solver lp_revised; From 569f5be91f4101eaa5cce897f84501b165f9c2b4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 6 Mar 2023 15:35:54 -0800 Subject: [PATCH 109/220] rm dead code --- src/math/lp/lp_core_solver_base.h | 7 +- src/math/lp/lp_core_solver_base_def.h | 2 - src/math/lp/lp_primal_core_solver.h | 14 +-- src/math/lp/lp_primal_core_solver_def.h | 114 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 5 files changed, 7 insertions(+), 135 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5f723c118..cb275ebc9 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -86,21 +86,18 @@ public: lp_settings & m_settings; vector m_y; // the buffer for yB = cb - // a device that is able to solve Bx=c, xB=d, and change the basis const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; - const vector & m_upper_bounds; - vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column + const vector & m_upper_bounds; vector m_copy_of_xB; unsigned m_basis_sort_counter; - vector m_steepest_edge_coefficients; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; - u_set* m_pivoted_rows; + u_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 8b87728e0..474d39b57 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -62,10 +62,8 @@ lp_core_solver_base(static_matrix & A, m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_column_norms(m_n()), m_copy_of_xB(m_m()), m_basis_sort_counter(0), - m_steepest_edge_coefficients(A.column_count()), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 10de624f5..219cc2ad3 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -369,24 +369,12 @@ public: unsigned get_number_of_non_basic_column_to_try_for_enter(); - void print_column_norms(std::ostream & out); // returns the number of iterations unsigned solve(); - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" - void init_column_norms(); - - T calculate_column_norm_exactly(unsigned j); - - void update_or_init_column_norms(unsigned entering, unsigned leaving); - - // following Swietanowski - A new steepest ... - void update_column_norms(unsigned entering, unsigned leaving); - - T calculate_norm_of_entering_exactly(); - + void find_feasible_solution(); // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 7a027f8d3..dac1c15fa 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -58,14 +58,8 @@ void lp_primal_core_solver::sort_non_basis() { sort_non_basis_rational(); return; } - for (unsigned j : this->m_nbasis) { - T const & da = this->m_d[j]; - this->m_steepest_edge_coefficients[j] = da * da / this->m_column_norms[j]; - } - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - return this->m_steepest_edge_coefficients[a] > this->m_steepest_edge_coefficients[b]; - }); - + + m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { @@ -190,41 +184,7 @@ int lp_primal_core_solver::choose_entering_column_presize(unsigned number_ template int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - if (numeric_traits::precise()) - return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } else { - this->m_basis_sort_counter--; - } - T steepest_edge = zero_of_type(); - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter= m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - T dj = this->m_d[j]; - T t = dj * dj / this->m_column_norms[j]; - if (t > steepest_edge) { - steepest_edge = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter != m_non_basis_list.end()) { - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; - } - return -1; + return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); } @@ -607,13 +567,6 @@ template unsigned lp_primal_core_solver::get_num return std::max(static_cast(this->m_settings.random_next() % ret), 1u); } -template void lp_primal_core_solver::print_column_norms(std::ostream & out) { - out << " column norms " << std::endl; - for (unsigned j = 0; j < this->m_n(); j++) { - out << this->m_column_norms[j] << " "; - } - out << std::endl; - } // returns the number of iterations template unsigned lp_primal_core_solver::solve() { @@ -677,67 +630,6 @@ template unsigned lp_primal_core_solver::solve() return this->total_iterations(); } -// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" -template void lp_primal_core_solver::init_column_norms() { - lp_assert(numeric_traits::precise() == false); - for (unsigned j = 0; j < this->m_n(); j++) { - this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) - - + T(static_cast(this->m_settings.random_next() % 10000)) / T(100000); - } -} - -// debug only -template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(false); -} - -template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); - if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - m_column_norm_update_counter = 0; - init_column_norms(); - } else { - m_column_norm_update_counter++; - update_column_norms(entering, leaving); - } -} - -// following Swietanowski - A new steepest ... -template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - T pivot = this->m_pivot_row[entering]; - T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; - if (!numeric_traits::precise()) { - if (g_ent < T(0.000001)) - g_ent = T(0.000001); - } - this->m_column_norms[leaving] = g_ent; - - for (unsigned j : this->m_pivot_row.m_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta.m_data, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 - 1 + tp * tp); - } - } -} - -template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { - T r = numeric_traits::one(); - for (auto i : this->m_ed.m_index) { - T t = this->m_ed[i]; - r += t * t; - } - return r; -} - // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { this->m_look_for_feasible_solution_only = true; diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index b63c54bfd..a63a6be17 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -295,10 +295,7 @@ template void lp_primal_core_solver::init_run_tab if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } + if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); From 9ec82632a3e3e746b6fed4b6ea6b85f8e8f587a1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 08:20:32 -0800 Subject: [PATCH 110/220] rp precise --- src/math/lp/core_solver_pretty_printer_def.h | 4 +- src/math/lp/lar_solver.cpp | 45 ++---- src/math/lp/lar_solver.h | 3 +- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 9 +- src/math/lp/lp_core_solver_base_def.h | 18 +-- src/math/lp/lp_primal_core_solver.h | 93 ++--------- src/math/lp/lp_primal_core_solver_def.h | 152 +----------------- .../lp/lp_primal_core_solver_tableau_def.h | 11 +- src/math/lp/lp_settings.h | 9 +- src/math/lp/lp_settings_def.h | 26 +-- src/math/lp/lp_utils.h | 3 - src/math/lp/matrix_def.h | 13 +- src/math/lp/numeric_pair.h | 8 - src/math/lp/static_matrix.h | 1 - 15 files changed, 48 insertions(+), 350 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 697106183..2f1d99d22 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -88,7 +88,6 @@ template T core_solver_pretty_printer::current_co } template void core_solver_pretty_printer::init_m_A_and_signs() { - if (numeric_traits::precise() ) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ @@ -115,8 +114,7 @@ template void core_solver_pretty_printer::init_m_ name); m_rs[row] += t[row] * m_core_solver.m_x[column]; } - } - } + } } template void core_solver_pretty_printer::init_column_widths() { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9bc850848..9b2361b74 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -281,9 +281,9 @@ namespace lp { unsigned m = A_r().row_count(); clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); - lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - ( m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau())); - + lp_assert( + m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_constraints.pop(k); m_term_count.pop(k); @@ -627,10 +627,6 @@ namespace lp { m_rows_with_changed_bounds.insert(rid); } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - lp_assert(false); - } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { @@ -676,20 +672,16 @@ namespace lp { } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta) { - { - for (const auto& c : A_r().m_columns[j]) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; - if (tableau_with_costs()) { - m_basic_columns_with_changed_cost.insert(bj); - } - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); - TRACE("change_x_del", - tout << "changed basis column " << bj << ", it is " << - (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); - - } + for (const auto& c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); } - } void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { @@ -819,19 +811,6 @@ namespace lp { A.set(last_row, basis_j, mpq(1)); } - template - void lar_solver::create_matrix_A(static_matrix& matr) { - lp_assert(false); // not implemented - /* - unsigned m = number_or_nontrivial_left_sides(); - unsigned n = m_vec_of_canonic_left_sides.size(); - if (matr.row_count() == m && matr.column_count() == n) - return; - matr.init_empty_matrix(m, n); - copy_from_mpq_matrix(matr); - */ - } - template void lar_solver::copy_from_mpq_matrix(static_matrix& matr) { matr.m_rows.resize(A_r().row_count()); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index b925b5ef0..356c86c2f 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -205,10 +205,9 @@ class lar_solver : public column_namer { void set_lower_bound_witness(var_index j, constraint_index ci); void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, vector> &left_side) const; - void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); + void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau_costs() const; - void detect_rows_of_column_with_bound_change(unsigned j); bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index b76976d37..059d801a0 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -43,7 +43,6 @@ template bool lp::lp_core_solver_base::print_statistics_with_ite template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -51,7 +50,6 @@ template void lp::lp_core_solver_base::fill_reduced_costs_from template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -61,7 +59,6 @@ template lp::lp_core_solver_base >::lp_core_s const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); -template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index cb275ebc9..6fa2ddead 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -238,11 +238,11 @@ public: } bool below_bound(const X & x, const X & bound) const { - return precise()? x < bound : below_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x < bound ; } bool above_bound(const X & x, const X & bound) const { - return precise()? x > bound : above_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x > bound ; } bool x_below_low_bound(unsigned p) const { @@ -323,9 +323,6 @@ public: void find_error_in_BxB(vector& rs); - // recalculates the projection of x to B, such that Ax = b, whereab is the right side - void solve_Ax_eq_b(); - bool snap_non_basic_x_to_bound() { bool ret = false; for (unsigned j : non_basis()) @@ -628,8 +625,6 @@ public: bool pivot_column_tableau(unsigned j, unsigned row_index); bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col); - bool precise() const { return numeric_traits::precise(); } - simplex_strategy_enum simplex_strategy() const { return m_settings.simplex_strategy(); } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 474d39b57..213d67e6e 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -233,18 +233,12 @@ column_is_dual_feasible(unsigned j) const { } template bool lp_core_solver_base:: d_is_not_negative(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] >= numeric_traits::zero(); - } - return m_d[j] > -T(0.00001); + return m_d[j] >= numeric_traits::zero(); } template bool lp_core_solver_base:: d_is_not_positive(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] <= numeric_traits::zero(); - } - return m_d[j] < T(0.00001); + return m_d[j] <= numeric_traits::zero(); } @@ -319,7 +313,6 @@ template bool lp_core_solver_base::inf_set_is_cor template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { - lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); @@ -517,13 +510,6 @@ find_error_in_BxB(vector& rs){ } } -// recalculates the projection of x to B, such that Ax = b -template void lp_core_solver_base:: -solve_Ax_eq_b() { - lp_assert(false); -} - - template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 219cc2ad3..aa771d7ee 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -511,7 +511,6 @@ public: bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); - if (numeric_traits::precise()) { if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); @@ -519,59 +518,30 @@ public: theta = zero_of_type(); unlimited = false; } - } else { - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - } return true; } bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets larger lp_assert(m > 0); - if (numeric_traits::precise()) { - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + if (this->above_bound(x, bound)) return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); } else { - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + theta = zero_of_type(); + unlimited = false; } + return true; } void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - if (numeric_traits::precise()) { // x gets larger - lp_assert(m > 0); - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } - } - else { - // x gets larger - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } - } + lp_assert(m > 0); + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { @@ -877,46 +847,13 @@ public: m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - if (!(numeric_traits::precise())) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } else { - m_converted_harris_eps = zero_of_type(); - } + + m_converted_harris_eps = zero_of_type(); + this->set_status(lp_status::UNKNOWN); } - // constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - m_lower_bounds_dummy, - upper_bound_values), - m_beta(A.row_count()), - m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { - lp_assert(initial_x_is_correct()); - m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); - m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); -#ifdef Z3DEBUG - lp_assert(false); -#endif - } + bool initial_x_is_correct() { std::set basis_set; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index dac1c15fa..fb62bbf54 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -33,9 +33,7 @@ namespace lp { template void lp_primal_core_solver::sort_non_basis_rational() { - lp_assert(numeric_traits::precise()); - - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { + std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; @@ -54,57 +52,15 @@ void lp_primal_core_solver::sort_non_basis_rational() { template void lp_primal_core_solver::sort_non_basis() { - if (numeric_traits::precise()) { - sort_non_basis_rational(); - return; - } - - - m_non_basis_list.clear(); - // reinit m_basis_heading - for (unsigned j = 0; j < this->m_nbasis.size(); j++) { - unsigned col = this->m_nbasis[j]; - this->m_basis_heading[col] = - static_cast(j) - 1; - m_non_basis_list.push_back(col); - } + sort_non_basis_rational(); } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { - if (numeric_traits::precise()) - return column_is_benefitial_for_entering_basis_precise(j); - const T& dj = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: break; - case column_type::free_column: - if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) - return true; - break; - case column_type::lower_bound: - if (dj > m_epsilon_of_reduced_cost) return true;; - break; - case column_type::upper_bound: - if (dj < -m_epsilon_of_reduced_cost) return true; - break; - case column_type::boxed: - if (dj > m_epsilon_of_reduced_cost) { - if (this->m_x[j] < this->m_upper_bounds[j] - this->bound_span(j)/2) - return true; - break; - } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) - return true; - } - break; - default: - lp_unreachable(); - break; - } - return false; + return column_is_benefitial_for_entering_basis_precise(j); } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { - lp_assert (numeric_traits::precise()); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -144,7 +100,6 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis template int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -231,8 +186,6 @@ find_leaving_on_harris_theta(X const & harris_theta, X & t) { } if (++k == steps) k = 0; } while (k != initial_k); - if (!this->precise()) - restore_harris_eps(); return leaving; } @@ -479,47 +432,14 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e // return 0 if the reduced cost at entering is close enough to the refreshed // 1 if it is way off, and 2 if it is unprofitable template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - if (numeric_traits::precise()) return 0; - T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); - T refreshed_cost = this->m_costs[entering]; - unsigned i = this->m_m(); - while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; - this->m_d[entering] = refreshed_cost; - T delta = abs(reduced_at_entering_was - refreshed_cost); - if (delta * 2 > abs(reduced_at_entering_was)) { - // this->m_status = UNSTABLE; - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - return 1; // go on with this entering - } else { - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - } return 0; + } template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) return; // no need to backup cost, since we are going to use only feasibility costs - if (numeric_traits::precise() ) { - m_costs_backup = this->m_costs; - } else { - T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - lp_assert(m_costs_backup.size() == 0); - for (unsigned j = 0; j < this->m_costs.size(); j++) - m_costs_backup.push_back(this->m_costs[j] /= cost_max); - } + m_costs_backup = this->m_costs; } template void lp_primal_core_solver::init_run() { @@ -527,10 +447,6 @@ template void lp_primal_core_solver::init_run( } -template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - lp_assert(false); -} - template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { @@ -571,63 +487,7 @@ template unsigned lp_primal_core_solver::get_num // returns the number of iterations template unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - if (numeric_traits::precise()) - return solve_with_tableau(); - - init_run(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FEASIBLE); - return 0; - } - - - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf" : "feas"), * this->m_settings.get_message_ostream())) { - return this->total_iterations(); - } - one_iteration(); - - TRACE("lar_solver", tout << "one iteration: " << this->get_status() << "\n";); - lp_assert(!this->using_infeas_costs() || this->costs_on_nbasis_are_zeros()); - switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - { // precise case - - } - break; - case lp_status::TENTATIVE_UNBOUNDED: - lp_assert(false); - break; - case lp_status::UNBOUNDED: - lp_assert(false); - break; - - case lp_status::UNSTABLE: - lp_assert(false); - break; - - default: - break; // do nothing - } - } while ( - this->get_status() != lp_status::UNBOUNDED - && - this->get_status() != lp_status::OPTIMAL - && - this->get_status() != lp_status::INFEASIBLE - && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - && - !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - - lp_assert( - this->current_x_is_feasible() == false - || - this->calc_current_x_is_feasible_include_non_basis()); - return this->total_iterations(); + return solve_with_tableau(); } // calling it stage1 is too cryptic diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index a63a6be17..96df997b1 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -43,19 +43,10 @@ template void lp_primal_core_solver::advance_on_e } advance_on_entering_and_leaving_tableau(entering, leaving, t); } -/* -template int lp_primal_core_solver::choose_entering_column_tableau_rows() { - int i = find_inf_row(); - if (i == -1) - return -1; - return find_shortest_beneficial_column_in_row(i); - } -*/ template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -294,7 +285,7 @@ template void lp_primal_core_solver::init_run_tab return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); + m_epsilon_of_reduced_cost = zero_of_type(); if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 38270230e..86a97e615 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -78,9 +78,8 @@ enum class lp_status { // when the ratio of the vector length to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only template unsigned ratio_of_index_size_to_all_size() { - if (numeric_traits::precise()) return 10; - return 120; + } const char* lp_status_to_string(lp_status status); @@ -274,7 +273,7 @@ public: statistics const& stats() const { return m_stats; } template static bool is_eps_small_general(const T & t, const double & eps) { - return (!numeric_traits::precise())? is_epsilon_small(t, eps) : numeric_traits::is_zero(t); + return numeric_traits::is_zero(t); } template @@ -373,9 +372,7 @@ inline std::string T_to_string(const mpq & t) { template bool val_is_smaller_than_eps(T const & t, double const & eps) { - if (!numeric_traits::precise()) { - return numeric_traits::get_double(t) < eps; - } + return t <= numeric_traits::zero(); } diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index bb87109f6..a439466d1 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -70,19 +70,12 @@ lp_status lp_status_from_string(std::string status) { template bool vectors_are_equal(T * a, vector &b, unsigned n) { - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { - return false; - } - } - } + return true; } @@ -91,27 +84,12 @@ template bool vectors_are_equal(const vector & a, const vector &b) { unsigned n = static_cast(a.size()); if (n != b.size()) return false; - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - double da = numeric_traits::get_double(a[i]); - double db = numeric_traits::get_double(b[i]); - double amax = std::max(fabs(da), fabs(db)); - if (amax > 1) { - da /= amax; - db /= amax; - } - - if (fabs(da - db) > 0.000001) { - return false; - } - } - } + return true; } #ifdef Z3DEBUG diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 40c5f0632..ad5ba380d 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -153,9 +153,6 @@ template inline X ceil_ratio(const X & a, const X & b) { return num template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } -template inline bool precise() { return numeric_traits::precise(); } - - // returns true if a factor of b template bool is_proper_factor(const T & a, const T & b) { diff --git a/src/math/lp/matrix_def.h b/src/math/lp/matrix_def.h index 95810bd5a..e3ac08f7e 100644 --- a/src/math/lp/matrix_def.h +++ b/src/math/lp/matrix_def.h @@ -32,16 +32,9 @@ bool matrix::is_equal(const matrix& other) { for (unsigned j = 0; j < column_count(); j++) { auto a = get_elem(i, j); auto b = other.get_elem(i, j); - if (numeric_traits::precise()) { - if (a != b) return false; - } else if (fabs(numeric_traits::get_double(a - b)) > 0.000001) { - // cout << "returning false from operator== of matrix comparison" << endl; - // cout << "this matrix is " << endl; - // print_matrix(*this); - // cout << "other matrix is " << endl; - // print_matrix(other); - return false; - } + + if (a != b) return false; + } } return true; diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 9f7e27c18..f59aa84ba 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -45,7 +45,6 @@ template class numeric_traits {}; template <> class numeric_traits { public: - static bool precise() { return true; } static unsigned zero() { return 0; } static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } @@ -56,7 +55,6 @@ public: template <> class numeric_traits { public: - static bool precise() { return true; } static int zero() { return 0; } static int one() { return 1; } static bool is_zero(int v) { return v == 0; } @@ -71,7 +69,6 @@ public: template <> class numeric_traits { public: - static bool precise() { return false; } static double g_zero; static double const &zero() { return g_zero; } static double g_one; @@ -88,7 +85,6 @@ public: template<> class numeric_traits { public: - static bool precise() { return true; } static rational const & zero() { return rational::zero(); } static rational const & one() { return rational::one(); } static bool is_zero(const rational & v) { return v.is_zero(); } @@ -251,8 +247,6 @@ struct numeric_pair { return numeric_pair(-x, -y); } - static bool precize() { return lp::numeric_traits::precize();} - bool is_zero() const { return x.is_zero() && y.is_zero(); } bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());} @@ -294,12 +288,10 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } -// template bool precise() { return numeric_traits::precise();} template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} template class numeric_traits> { public: - static bool precise() { return numeric_traits::precise();} static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index a42926853..7f81cae79 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -344,7 +344,6 @@ public: void fill_last_row_with_pivoting(const term& row, unsigned bj, // the index of the basis column const vector & basis_heading) { - lp_assert(numeric_traits::precise()); lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; From f351eb3ab276e9ec084311ecd9c807a794412278 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 08:52:08 -0800 Subject: [PATCH 111/220] remove many methods dealing with double Signed-off-by: Lev Nachmanson --- src/math/lp/binary_heap_priority_queue.cpp | 3 - src/math/lp/conversion_helper.h | 23 ----- src/math/lp/core_solver_pretty_printer.cpp | 3 - src/math/lp/dense_matrix.cpp | 6 -- src/math/lp/dense_matrix.h | 6 +- src/math/lp/dense_matrix_def.h | 11 --- src/math/lp/indexed_vector.cpp | 18 ---- src/math/lp/indexed_vector.h | 55 +----------- src/math/lp/indexed_vector_def.h | 29 +------ src/math/lp/lar_core_solver.h | 44 +--------- src/math/lp/lar_solver.cpp | 75 +--------------- src/math/lp/lar_solver.h | 4 - src/math/lp/lp_core_solver_base.cpp | 27 ------ src/math/lp/lp_core_solver_base.h | 6 +- src/math/lp/lp_core_solver_base_def.h | 17 ---- src/math/lp/lp_primal_core_solver.cpp | 4 - src/math/lp/lp_primal_core_solver.h | 4 +- src/math/lp/lp_primal_core_solver_def.h | 12 --- .../lp/lp_primal_core_solver_tableau_def.h | 2 +- src/math/lp/lp_settings.cpp | 1 - src/math/lp/lp_settings.h | 86 ++----------------- src/math/lp/lp_utils.cpp | 3 +- src/math/lp/matrix.cpp | 2 - src/math/lp/permutation_matrix.cpp | 16 ---- src/math/lp/permutation_matrix_def.h | 6 +- src/math/lp/sparse_vector.h | 1 - src/math/lp/static_matrix.cpp | 20 ----- src/math/lp/static_matrix.h | 1 - 28 files changed, 20 insertions(+), 465 deletions(-) diff --git a/src/math/lp/binary_heap_priority_queue.cpp b/src/math/lp/binary_heap_priority_queue.cpp index bbe735e58..ec6630b1d 100644 --- a/src/math/lp/binary_heap_priority_queue.cpp +++ b/src/math/lp/binary_heap_priority_queue.cpp @@ -23,15 +23,12 @@ namespace lp { template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue::enqueue(unsigned int, int const&); -template void binary_heap_priority_queue::enqueue(unsigned int, double const&); template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); template void binary_heap_priority_queue::remove(unsigned int); template unsigned binary_heap_priority_queue >::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); template void binary_heap_priority_queue >::resize(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template void binary_heap_priority_queue::resize(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); diff --git a/src/math/lp/conversion_helper.h b/src/math/lp/conversion_helper.h index feb999743..ba8b6a983 100644 --- a/src/math/lp/conversion_helper.h +++ b/src/math/lp/conversion_helper.h @@ -31,28 +31,5 @@ struct conversion_helper { } }; -template<> -struct conversion_helper { - static double get_upper_bound(const column_info & ci) { - if (!ci.upper_bound_is_strict()) - return ci.get_upper_bound().get_double(); - double eps = 0.00001; - if (!ci.lower_bound_is_set()) - return ci.get_upper_bound().get_double() - eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_upper_bound().get_double() - eps; - } - - static double get_lower_bound(const column_info & ci) { - if (!ci.lower_bound_is_strict()) - return ci.get_lower_bound().get_double(); - double eps = 0.00001; - if (!ci.upper_bound_is_set()) - return ci.get_lower_bound().get_double() + eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_lower_bound().get_double() + eps; - } - -}; } diff --git a/src/math/lp/core_solver_pretty_printer.cpp b/src/math/lp/core_solver_pretty_printer.cpp index 74d2f6048..18bef8303 100644 --- a/src/math/lp/core_solver_pretty_printer.cpp +++ b/src/math/lp/core_solver_pretty_printer.cpp @@ -19,9 +19,6 @@ Revision History: --*/ #include "math/lp/numeric_pair.h" #include "math/lp/core_solver_pretty_printer_def.h" -template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); -template void lp::core_solver_pretty_printer::print(); -template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); diff --git a/src/math/lp/dense_matrix.cpp b/src/math/lp/dense_matrix.cpp index f12e688d3..25fc65a5d 100644 --- a/src/math/lp/dense_matrix.cpp +++ b/src/math/lp/dense_matrix.cpp @@ -21,11 +21,6 @@ Revision History: #include "math/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" -template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); -template void lp::dense_matrix::apply_from_left(vector &); -template lp::dense_matrix::dense_matrix(lp::matrix const*); -template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); -template lp::dense_matrix& lp::dense_matrix::operator=(lp::dense_matrix const&); template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix >::dense_matrix(lp::matrix > const*); template void lp::dense_matrix >::apply_from_left(vector&); @@ -35,6 +30,5 @@ template lp::dense_matrix >::dense_matrix(uns template lp::dense_matrix >& lp::dense_matrix >::operator=(lp::dense_matrix > const&); template lp::dense_matrix > lp::operator* >(lp::matrix >&, lp::matrix >&); template void lp::dense_matrix >::apply_from_right( vector< lp::mpq> &); -template void lp::dense_matrix::apply_from_right(vector &); template void lp::dense_matrix::apply_from_left(vector&); #endif diff --git a/src/math/lp/dense_matrix.h b/src/math/lp/dense_matrix.h index 342fc7aeb..fcc85cdd1 100644 --- a/src/math/lp/dense_matrix.h +++ b/src/math/lp/dense_matrix.h @@ -90,11 +90,7 @@ public: void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; } - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon); - + // This method pivots void swap_columns(unsigned a, unsigned b); void swap_rows(unsigned a, unsigned b); diff --git a/src/math/lp/dense_matrix_def.h b/src/math/lp/dense_matrix_def.h index 8eb9ad5dd..e850a9acd 100644 --- a/src/math/lp/dense_matrix_def.h +++ b/src/math/lp/dense_matrix_def.h @@ -150,17 +150,6 @@ template void dense_matrix::apply_from_left_to_X( } } -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -template void dense_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon) { - for (unsigned j = 0; j < m_n; j++) { - m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha; - if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) { - m_values[i0 + m_n + j] = numeric_traits::zero();; - } - } -} template void dense_matrix::swap_columns(unsigned a, unsigned b) { for (unsigned i = 0; i < m_m; i++) { diff --git a/src/math/lp/indexed_vector.cpp b/src/math/lp/indexed_vector.cpp index d7cde2e1d..fe0892541 100644 --- a/src/math/lp/indexed_vector.cpp +++ b/src/math/lp/indexed_vector.cpp @@ -20,10 +20,6 @@ Revision History: #include "util/vector.h" #include "math/lp/indexed_vector_def.h" namespace lp { -template void indexed_vector::clear(); -template void indexed_vector::clear_all(); -template void indexed_vector::erase_from_index(unsigned int); -template void indexed_vector::set_value(const double&, unsigned int); template void indexed_vector::clear(); template void indexed_vector::clear(); template void indexed_vector::clear_all(); @@ -32,22 +28,8 @@ template void indexed_vector::resize(unsigned int); template void indexed_vector::resize(unsigned int); template void indexed_vector::set_value(const mpq&, unsigned int); template void indexed_vector::set_value(const unsigned&, unsigned int); -#ifdef Z3DEBUG -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector >::is_OK() const; -#endif template void lp::indexed_vector< lp::mpq>::print(std::basic_ostream > &); -template void lp::indexed_vector::print(std::basic_ostream > &); template void lp::indexed_vector >::print(std::ostream&); } -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector >(vector> const&, std::ostream&); -template void lp::indexed_vector::resize(unsigned int); -// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); -// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); diff --git a/src/math/lp/indexed_vector.h b/src/math/lp/indexed_vector.h index 017a25f6b..9f3119e9a 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -99,47 +99,9 @@ public: return m_data[i]; } - void clean_up() { -#if 0==1 - for (unsigned k = 0; k < m_index.size(); k++) { - unsigned i = m_index[k]; - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - m_index.erase(m_index.begin() + k--); - } - } -#endif - vector index_copy; - for (unsigned i : m_index) { - T & v = m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) { - index_copy.push_back(i); - } else if (!numeric_traits::is_zero(v)) { - v = zero_of_type(); - } - } - m_index = index_copy; - } - void erase_from_index(unsigned j); - - void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) { - T & v = m_data[j]; - bool was_zero = is_zero(v); - v += val_to_add; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - if (!was_zero) { - erase_from_index(j); - } - } else { - if (was_zero) - m_index.push_back(j); - } - } - + void add_value_at_index(unsigned j, const T& val_to_add) { T & v = m_data[j]; bool was_zero = is_zero(v); @@ -153,18 +115,6 @@ public: } } - void restore_index_and_clean_from_data() { - m_index.resize(0); - for (unsigned i = 0; i < m_data.size(); i++) { - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - } else { - m_index.push_back(i); - } - } - } - struct ival { unsigned m_var; const T & m_coeff; @@ -215,9 +165,6 @@ public: } -#ifdef Z3DEBUG - bool is_OK() const; -#endif void print(std::ostream & out); }; } diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 0e25ee271..1b904e14a 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -43,7 +43,7 @@ template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); - lp_assert(is_OK()); + } template @@ -72,33 +72,6 @@ void indexed_vector::erase_from_index(unsigned j) { m_index.erase(it); } -#ifdef Z3DEBUG -template -bool indexed_vector::is_OK() const { - return true; - const double drop_eps = 1e-14; - for (unsigned i = 0; i < m_data.size(); i++) { - if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) { - return false; - } - if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) { - return false; - } - } - - std::unordered_set s; - for (unsigned i : m_index) { - //no duplicates!!! - if (s.find(i) != s.end()) - return false; - s.insert(i); - if (i >= m_data.size()) - return false; - } - - return true; -} -#endif template void indexed_vector::print(std::ostream & out) { out << "m_index " << std::endl; diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index d40572b9e..e024b199c 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -27,8 +27,7 @@ class lar_core_solver { int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; - vector m_d_right_sides_dummy; - vector m_d_costs_dummy; + public: stacked_value m_stacked_simplex_strategy; stacked_vector m_column_types; @@ -45,10 +44,6 @@ public: stacked_vector m_r_rows_nz; // d - solver fields, for doubles - vector m_d_x; // the solution in doubles - vector m_d_lower_bounds; - vector m_d_upper_bounds; - static_matrix m_d_A; stacked_vector m_d_pushed_basis; vector m_d_basis; vector m_d_nbasis; @@ -146,7 +141,7 @@ public: m_r_lower_bounds.push(); m_r_upper_bounds.push(); - m_d_A.push(); + } @@ -185,10 +180,6 @@ public: m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - m_d_A.pop(k); - // doubles - - m_d_x.resize(m_d_A.column_count()); pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); @@ -279,15 +270,7 @@ public: } - void create_double_matrix(static_matrix & A) { - for (unsigned i = 0; i < m_r_A.row_count(); i++) { - auto & row = m_r_A.m_rows[i]; - for (row_cell & c : row) { - A.add_new_element(i, c.var(), c.coeff().get_double()); - } - } - } - + void fill_basis_d( vector& basis_d, vector& heading_d, @@ -308,27 +291,6 @@ public: } } - void get_bounds_for_double_solver() { - unsigned n = m_n(); - m_d_lower_bounds.resize(n); - m_d_upper_bounds.resize(n); - double delta = find_delta_for_strict_boxed_bounds().get_double(); - if (delta > 0.000001) - delta = 0.000001; - for (unsigned j = 0; j < n; j++) { - if (lower_bound_is_set(j)) { - const auto & lb = m_r_solver.m_lower_bounds[j]; - m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); - } - if (upper_bound_is_set(j)) { - const auto & ub = m_r_solver.m_upper_bounds[j]; - m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); - lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); - } - } - } - - bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9b2361b74..67cf90bd0 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -8,10 +8,6 @@ namespace lp { - static_matrix& lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A; } - - static_matrix const& lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A; } - lp_settings& lar_solver::settings() { return m_settings; } lp_settings const& lar_solver::settings() const { return m_settings; } @@ -574,7 +570,6 @@ namespace lp { void lar_solver::pop_core_solver_params(unsigned k) { A_r().pop(k); - A_d().pop(k); } @@ -1544,27 +1539,7 @@ namespace lp { add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } - - void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - } - else { - m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - } - } - + void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { unsigned j = A_r().column_count(); TRACE("add_var", tout << "j = " << j << std::endl;); @@ -1927,34 +1902,7 @@ namespace lp { } } - void lar_solver::adjust_initial_state_for_lu() { - copy_from_mpq_matrix(A_d()); - unsigned n = A_d().column_count(); - m_mpq_lar_core_solver.m_d_x.resize(n); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(n); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); - m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; - m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; - - /* - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - }else { - m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - }*/ - } - + void lar_solver::adjust_initial_state_for_tableau_rows() { for (unsigned i = 0; i < m_terms.size(); i++) { if (m_var_register.external_is_used(tv::mask_term(i))) @@ -1963,24 +1911,7 @@ namespace lp { } } - // this fills the last row of A_d and sets the basis column: -1 in the last column of the row - void lar_solver::fill_last_row_of_A_d(static_matrix& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].empty()); - - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff().get_double()); - } - - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, -1); - lp_assert(A.is_correct()); - } - + void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, unsigned constraint_index) { SASSERT(column_has_upper_bound(j)); if (column_has_lower_bound(j)) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 356c86c2f..237b9de81 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -112,8 +112,6 @@ class lar_solver : public column_namer { // end of fields ////////////////// methods //////////////////////////////// - static_matrix & A_d(); - static_matrix const & A_d() const; static bool valid_index(unsigned j) { return static_cast(j) >= 0;} const lar_term & get_term(unsigned j) const; @@ -162,9 +160,7 @@ class lar_solver : public column_namer { unsigned row_of_basic_column(unsigned) const; void decide_on_strategy_and_adjust_initial_state(); void adjust_initial_state(); - void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); - void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 059d801a0..14c454d6f 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,27 +23,10 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template lp::lp_core_solver_base::lp_core_solver_base( - lp::static_matrix&, // vector&, - vector&, - vector &, vector &, - vector&, - vector&, - lp::lp_settings&, const column_namer&, const vector&, - const vector&, - const vector&); -template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); @@ -74,35 +57,25 @@ template lp::lp_core_solver_base::lp_core_solver_base( const vector&, const vector&); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); -template std::string lp::lp_core_solver_base::column_name(unsigned int) const; -template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); -template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); -template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; -template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 6fa2ddead..14cb60ebf 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -309,10 +309,7 @@ public: column_type get_column_type(unsigned j) const {return m_column_types[j]; } - bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) { - return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]); - } - + X bound_span(unsigned j) const { return m_upper_bounds[j] - m_lower_bounds[j]; } @@ -410,7 +407,6 @@ public: non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 213d67e6e..16f56bade 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -533,23 +533,6 @@ get_non_basic_column_value_position(unsigned j) const { return at_lower_bound; } -template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; -} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index f4597da76..22042668d 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -27,15 +27,11 @@ Revision History: #include "math/lp/lp_primal_core_solver_tableau_def.h" namespace lp { -template void lp_primal_core_solver::find_feasible_solution(); template void lp::lp_primal_core_solver >::find_feasible_solution(); -template unsigned lp_primal_core_solver::solve(); -template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); -template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); template void lp::lp_primal_core_solver >::update_inf_cost_for_column_tableau(unsigned); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index aa771d7ee..aacabf944 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -56,7 +56,7 @@ public: unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; - // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); + std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); @@ -279,12 +279,10 @@ public: bool get_harris_theta(X & theta); - void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index fb62bbf54..cfd8fc947 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -144,7 +144,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef template bool lp_primal_core_solver::get_harris_theta(X & theta) { - lp_assert(this->m_ed.is_OK()); bool unlimited = true; for (unsigned i : this->m_ed.m_index) { if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; @@ -311,17 +310,6 @@ template int lp_primal_core_solver::find_leaving_ } -template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - X theta = zero_of_type(); - bool unlimited = get_harris_theta(theta); - lp_assert(unlimited || theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; - if (unlimited) - return -1; - return find_leaving_on_harris_theta(theta, t); -} - - // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 96df997b1..256853be7 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -107,7 +107,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::OPTIMAL: // check again that we are at optimum case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; diff --git a/src/math/lp/lp_settings.cpp b/src/math/lp/lp_settings.cpp index 592a98983..b72b837fd 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -21,7 +21,6 @@ Revision History: #include "util/vector.h" #include "smt/params/smt_params_helper.hpp" #include "math/lp/lp_settings_def.h" -template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); void lp::lp_settings::updt_params(params_ref const& _p) { diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 86a97e615..197b7c04f 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -92,7 +92,6 @@ lp_status lp_status_from_string(std::string status); enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; -template bool is_epsilon_small(const X & v, const double& eps); // forward definition class lp_resource_limit { public: @@ -182,36 +181,18 @@ public: unsigned reps_in_scaler { 20 }; // when the absolute value of an element is less than pivot_epsilon // in pivoting, we treat it as a zero - double pivot_epsilon { 0.00000001 }; - // see Chatal, page 115 - double positive_price_epsilon { 1e-7 }; // a quotation "if some choice of the entering variable leads to an eta matrix // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... - double entering_diag_epsilon { 1e-8 }; int c_partial_pivoting { 10 }; // this is the constant c from page 410 unsigned depth_of_rook_search { 4 }; bool using_partial_pivoting { true }; // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor - double refactor_tolerance { 1e-4 }; - double pivot_tolerance { 1e-6 }; - double zero_tolerance { 1e-12 }; - double drop_tolerance { 1e-14 }; - double tolerance_for_artificials { 1e-4 }; - double can_be_taken_to_basis_tolerance { 0.00001 }; - + unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns bool use_scaling { true }; - double scaling_maximum { 1.0 }; - double scaling_minimum { 0.5 }; - double harris_feasibility_tolerance { 1e-7 }; // page 179 of Istvan Maros - double ignore_epsilon_of_harris { 10e-5 }; unsigned max_number_of_iterations_with_no_improvements { 2000000 }; - double time_limit; // the maximum time limit of the total run time in seconds - // dual section - double dual_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double primal_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double relative_primal_feasibility_tolerance { 1e-9 }; // page 71 of the PhD thesis of Achim Koberstein + double time_limit; // the maximum time limit of the total run time in seconds // end of dual section bool m_bound_propagation { true }; bool presolve_with_double_solver_for_lar { true }; @@ -221,7 +202,6 @@ public: bool print_statistics { false }; unsigned column_norms_update_frequency { 12000 }; bool scale_with_ratio { true }; - double density_threshold { 0.7 }; unsigned max_row_length_for_bound_propagation { 300 }; bool backup_costs { true }; unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; @@ -272,61 +252,10 @@ public: statistics& stats() { return m_stats; } statistics const& stats() const { return m_stats; } - template static bool is_eps_small_general(const T & t, const double & eps) { - return numeric_traits::is_zero(t); - } + - template - bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, dual_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, primal_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) { - return is_eps_small_general(t, can_be_taken_to_basis_tolerance); - } - - template - bool abs_val_is_smaller_than_drop_tolerance(T const & t) const { - return is_eps_small_general(t, drop_tolerance); - } - - - template - bool abs_val_is_smaller_than_zero_tolerance(T const & t) { - return is_eps_small_general(t, zero_tolerance); - } - - template - bool abs_val_is_smaller_than_refactor_tolerance(T const & t) { - return is_eps_small_general(t, refactor_tolerance); - } - - - template - bool abs_val_is_smaller_than_pivot_tolerance(T const & t) { - return is_eps_small_general(t, pivot_tolerance); - } - - template - bool abs_val_is_smaller_than_harris_tolerance(T const & t) { - return is_eps_small_general(t, harris_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) { - return is_eps_small_general(t, ignore_epsilon_of_harris); - } - - template - bool abs_val_is_smaller_than_artificial_tolerance(T const & t) { - return is_eps_small_general(t, tolerance_for_artificials); - } + + // the method of lar solver to use simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; @@ -370,11 +299,6 @@ inline std::string T_to_string(const mpq & t) { return strs.str(); } -template -bool val_is_smaller_than_eps(T const & t, double const & eps) { - - return t <= numeric_traits::zero(); -} template bool vectors_are_equal(T * a, vector &b, unsigned n); diff --git a/src/math/lp/lp_utils.cpp b/src/math/lp/lp_utils.cpp index 9ce3b9894..b909a0389 100644 --- a/src/math/lp/lp_utils.cpp +++ b/src/math/lp/lp_utils.cpp @@ -20,8 +20,7 @@ Revision History: #include "math/lp/lp_utils.h" #ifdef lp_for_z3 namespace lp { -double numeric_traits::g_zero = 0.0; -double numeric_traits::g_one = 1.0; + } #endif diff --git a/src/math/lp/matrix.cpp b/src/math/lp/matrix.cpp index 5367c74d0..1ea2da263 100644 --- a/src/math/lp/matrix.cpp +++ b/src/math/lp/matrix.cpp @@ -22,10 +22,8 @@ Revision History: #include "math/lp/static_matrix.h" #include #ifdef Z3DEBUG -template bool lp::matrix::is_equal(lp::matrix const&); template bool lp::matrix >::is_equal(lp::matrix > const&); template bool lp::matrix::is_equal(lp::matrix const&); #endif -template void lp::print_matrix(lp::matrix const*, std::ostream & out); template void lp::print_matrix >(lp::matrix > const *, std::basic_ostream > &); template void lp::print_matrix(lp::matrix const*, std::ostream&); diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index 28319c2ee..762b85d63 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -21,16 +21,8 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix_def.h" #include "math/lp/numeric_pair.h" -template void lp::permutation_matrix::apply_from_right(vector&); -template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int, vector const&); -template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::apply_from_right(vector&); template bool lp::permutation_matrix::is_identity() const; @@ -50,21 +42,13 @@ template void lp::permutation_matrix >::multi template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index 703830ffc..b6f9924ff 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -133,7 +133,7 @@ template void permutation_matrix::apply_from_righ unsigned pj = m_permutation[j]; w.set_value(buffer[i], pj); } - lp_assert(w.is_OK()); + #ifdef Z3DEBUG lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif @@ -235,7 +235,6 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & // vector wcopy(w.m_data); // apply_reverse_from_right_to_T(wcopy); #endif - lp_assert(w.is_OK()); vector tmp; vector tmp_index(w.m_index); for (auto i : w.m_index) { @@ -248,8 +247,7 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w.set_value(tmp[k], m_rev[j]); } - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(w.m_data, wcopy)); + } diff --git a/src/math/lp/sparse_vector.h b/src/math/lp/sparse_vector.h index 1c27a8d96..3de701e10 100644 --- a/src/math/lp/sparse_vector.h +++ b/src/math/lp/sparse_vector.h @@ -42,7 +42,6 @@ public: } #endif void divide(T const & a) { - lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); for (auto & t : m_data) { t.second /= a; } } diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 12d2f9f0d..28a23b0c3 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -26,26 +26,8 @@ Revision History: #include "math/lp/lp_primal_core_solver.h" #include "math/lp/lar_solver.h" namespace lp { -template void static_matrix::add_columns_at_the_end(unsigned int); -template void static_matrix::clear(); -#ifdef Z3DEBUG -template bool static_matrix::is_correct() const; -#endif -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; - -template double static_matrix::get_balance() const; -template std::set> static_matrix::get_domain(); template std::set> lp::static_matrix::get_domain(); template std::set> lp::static_matrix >::get_domain(); -template double static_matrix::get_elem(unsigned int, unsigned int) const; -template double static_matrix::get_max_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_row(unsigned int) const; -template void static_matrix::init_empty_matrix(unsigned int, unsigned int); -template void static_matrix::init_row_columns(unsigned int, unsigned int); -template static_matrix::ref & static_matrix::ref::operator=(double const&); -template void static_matrix::set(unsigned int, unsigned int, double const&); -template static_matrix::static_matrix(unsigned int, unsigned int); template void static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; @@ -55,7 +37,6 @@ template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; template mpq static_matrix::get_max_abs_in_column(unsigned int) const; template mpq static_matrix::get_max_abs_in_row(unsigned int) const; -template double static_matrix::get_max_abs_in_row(unsigned int) const; template mpq static_matrix::get_min_abs_in_column(unsigned int) const; template mpq static_matrix::get_min_abs_in_row(unsigned int) const; template void static_matrix::init_row_columns(unsigned int, unsigned int); @@ -72,7 +53,6 @@ template void static_matrix >::init_empty_matrix(unsigned template void static_matrix >::set(unsigned int, unsigned int, mpq const&); -template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell &, unsigned int); template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell& , unsigned int); template bool lp::static_matrix >::pivot_row_to_row_given_cell(unsigned int, column_cell&, unsigned int); template void lp::static_matrix >::remove_element(vector, true, unsigned int>&, lp::row_cell&); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index 7f81cae79..b9870ceb5 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -359,7 +359,6 @@ public: for (auto p : row) { fill_last_row_with_pivoting_loop_block(p.column().index(), basis_heading); } - lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { From f6445891f32c7f7c98204ad767779d9e31c0a8b9 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 11:34:43 -0800 Subject: [PATCH 112/220] rm lu related fields from lp_core_solver_base.h --- src/math/lp/core_solver_pretty_printer.h | 18 +--- src/math/lp/core_solver_pretty_printer_def.h | 19 +--- src/math/lp/indexed_vector_def.h | 8 -- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 18 ---- src/math/lp/lp_core_solver_base_def.h | 93 +------------------- src/math/lp/permutation_matrix.h | 1 - src/math/lp/sparse_vector.h | 52 ----------- src/math/lp/static_matrix.h | 1 - 9 files changed, 8 insertions(+), 205 deletions(-) delete mode 100644 src/math/lp/sparse_vector.h diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 353212dcd..5bf29d511 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -59,7 +59,7 @@ class core_solver_pretty_printer { unsigned m_artificial_start; indexed_vector m_w_buff; indexed_vector m_ed_buff; - vector m_exact_column_norms; + public: core_solver_pretty_printer(const lp_core_solver_base & core_solver, std::ostream & out); @@ -85,14 +85,7 @@ public: } unsigned get_column_width(unsigned column); - - unsigned regular_cell_width(unsigned row, unsigned column, const std::string & name) { - return regular_cell_string(row, column, name).size(); - } - - std::string regular_cell_string(unsigned row, unsigned column, std::string name); - - + void set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name); void print_x(); @@ -105,12 +98,7 @@ public: void print_lows(); void print_upps(); - - string get_exact_column_norm_string(unsigned col) { - return T_to_string(m_exact_column_norms[col]); - } - - + void print_approx_norms(); void print(); diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 2f1d99d22..931333ee7 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -37,9 +37,8 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), m_costs(ncols(), ""), m_cost_signs(ncols(), " "), - m_rs(ncols(), zero_of_type()), - m_w_buff(core_solver.m_w), - m_ed_buff(core_solver.m_ed) { + m_rs(ncols(), zero_of_type()) + { m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; @@ -80,13 +79,6 @@ template void core_solver_pretty_printer::init_rs } } -template T core_solver_pretty_printer::current_column_norm() { - T ret = zero_of_type(); - for (auto i : m_core_solver.m_ed.m_index) - ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i]; - return ret; -} - template void core_solver_pretty_printer::init_m_A_and_signs() { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); @@ -167,13 +159,6 @@ template unsigned core_solver_pretty_printer:: ge return w; } -template std::string core_solver_pretty_printer::regular_cell_string(unsigned row, unsigned /* column */, std::string name) { - T t = fabs(m_core_solver.m_ed[row]); - if ( t == 1) return name; - return T_to_string(t) + name; -} - - template void core_solver_pretty_printer::set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name) { if (numeric_traits::is_zero(t)) { return; diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 1b904e14a..034325088 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -24,14 +24,6 @@ Revision History: #include "math/lp/lp_settings.h" namespace lp { -template -void print_sparse_vector(const vector & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) { - if (is_zero(t[i]))continue; - out << "[" << i << "] = " << t[i] << ", "; - } - out << std::endl; -} void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 14c454d6f..ccd6bf419 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -29,9 +29,7 @@ template lp::non_basic_column_value_position lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); @@ -68,7 +66,6 @@ template bool lp::lp_core_solver_base::column_is_feasible(unsi // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); -template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 14cb60ebf..964e447a1 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -74,7 +74,6 @@ public: void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row - indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side @@ -85,15 +84,11 @@ public: vector & m_costs; lp_settings & m_settings; - vector m_y; // the buffer for yB = cb const column_namer & m_column_names; - indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs - indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; const vector & m_upper_bounds; - vector m_copy_of_xB; unsigned m_basis_sort_counter; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; @@ -162,10 +157,6 @@ public: return dot_product(m_costs, m_x); } - void copy_m_w(T * buffer); - - void restore_m_w(T * buffer); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { @@ -298,11 +289,6 @@ public: bool basis_heading_is_correct() const; - void restore_x(unsigned entering, X const & t); - - void fill_reduced_costs_from_m_y_by_rows(); - - void copy_rs_to_xB(vector & rs); virtual bool lower_bounds_are_set() const { return false; } X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } @@ -316,10 +302,6 @@ public: std::string column_name(unsigned column) const; - void add_delta_to_xB(vector & del); - - void find_error_in_BxB(vector& rs); - bool snap_non_basic_x_to_bound() { bool ret = false; for (unsigned j : non_basis()) diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 16f56bade..2d7296567 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -44,25 +44,19 @@ lp_core_solver_base(static_matrix & A, m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), m_using_infeas_costs(false), - m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), - // m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), m_x(x), m_costs(costs), m_settings(settings), - m_y(m_m()), m_column_names(column_names), - m_w(m_m()), m_d(m_n()), - m_ed(m_m()), m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_copy_of_xB(m_m()), m_basis_sort_counter(0), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), @@ -122,25 +116,6 @@ pretty_print(std::ostream & out) { } -template void lp_core_solver_base:: -copy_m_w(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_w[i]; - } -} - -template void lp_core_solver_base:: -restore_m_w(T * buffer) { - m_w.m_index.clear(); - unsigned i = m_m(); - while (i--) { - if (!is_zero(m_w[i] = buffer[i])) - m_w.m_index.push_back(i); - } -} - - template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { m_x[entering] += delta; @@ -443,73 +418,11 @@ template bool lp_core_solver_base:: return true; } -template void lp_core_solver_base:: -restore_x(unsigned entering, X const & t) { - if (is_zero(t)) return; - m_x[entering] -= t; - for (unsigned i : m_ed.m_index) { - m_x[m_basis[i]] = m_copy_of_xB[i]; - } -} - -template void lp_core_solver_base:: -fill_reduced_costs_from_m_y_by_rows() { - unsigned j = m_n(); - while (j--) { - if (m_basis_heading[j] < 0) - m_d[j] = m_costs[j]; - else - m_d[j] = numeric_traits::zero(); - } - - unsigned i = m_m(); - while (i--) { - const T & y = m_y[i]; - if (is_zero(y)) continue; - for (row_cell & c : m_A.m_rows[i]) { - j = c.var(); - if (m_basis_heading[j] < 0) { - m_d[j] -= y * c.coeff(); - } - } - } -} - -template void lp_core_solver_base:: -copy_rs_to_xB(vector & rs) { - unsigned j = m_m(); - while (j--) { - m_x[m_basis[j]] = rs[j]; - } -} - template std::string lp_core_solver_base:: column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template void lp_core_solver_base:: -add_delta_to_xB(vector & del) { - unsigned i = m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= del[i]; - } -} - -template void lp_core_solver_base:: -find_error_in_BxB(vector& rs){ - unsigned row = m_m(); - while (row--) { - auto &rsv = rs[row]; - for (auto & it : m_A.m_rows[row]) { - unsigned j = it.var(); - if (m_basis_heading[j] >= 0) { - rsv -= m_x[j] * it.coeff(); - } - } - } -} - template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { @@ -543,9 +456,9 @@ template bool lp_core_solver_base::pivot_column_g lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; // the tableau case - if (pivot_column_tableau(j, row_index)) - change_basis(j, j_basic); - else return false; + if (pivot_column_tableau(j, row_index)) + change_basis(j, j_basic); + else return false; return true; } diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index 8ec78c14a..35a58906a 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -22,7 +22,6 @@ Revision History: #include #include "util/debug.h" #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/lp_settings.h" #include "math/lp/matrix.h" diff --git a/src/math/lp/sparse_vector.h b/src/math/lp/sparse_vector.h deleted file mode 100644 index 3de701e10..000000000 --- a/src/math/lp/sparse_vector.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_settings.h" -namespace lp { - -template -class sparse_vector { -public: - vector> m_data; - void push_back(unsigned index, T val) { - m_data.push_back(std::make_pair(index, val)); - } -#ifdef Z3DEBUG - T operator[] (unsigned i) const { - for (auto &t : m_data) { - if (t.first == i) return t.second; - } - return numeric_traits::zero(); - } -#endif - void divide(T const & a) { - for (auto & t : m_data) { t.second /= a; } - } - - unsigned size() const { - return m_data.size(); - } -}; -} diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index b9870ceb5..d7e4370a3 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -12,7 +12,6 @@ Author: #include #include #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/permutation_matrix.h" #include From e430f2881397464bc6620b1c955ccba4ad6c0a13 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 13:51:56 -0800 Subject: [PATCH 113/220] remove dead code Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 19 -- src/math/lp/lar_solver.cpp | 36 +-- src/math/lp/lar_solver.h | 16 - src/math/lp/lp_core_solver_base.h | 2 +- src/math/lp/lp_primal_core_solver.h | 135 +-------- src/math/lp/lp_primal_core_solver_def.h | 120 -------- .../lp/lp_primal_core_solver_tableau_def.h | 4 +- src/test/lp/lp.cpp | 279 ------------------ 8 files changed, 13 insertions(+), 598 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index e024b199c..52c5b6f1a 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -43,12 +43,6 @@ public: stacked_vector m_r_columns_nz; stacked_vector m_r_rows_nz; - // d - solver fields, for doubles - stacked_vector m_d_pushed_basis; - vector m_d_basis; - vector m_d_nbasis; - vector m_d_heading; - lp_primal_core_solver> m_r_solver; // solver in rational numbers @@ -123,14 +117,6 @@ public: void fill_not_improvable_zero_sum(); - void pop_basis(unsigned k) { - - m_d_basis = m_r_basis; - m_d_nbasis = m_r_nbasis; - m_d_heading = m_r_heading; - - } - void push() { lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); @@ -180,7 +166,6 @@ public: m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); @@ -356,10 +341,6 @@ public: return delta; } - void init_column_row_nz_for_r_solver() { - m_r_solver.init_column_row_non_zeroes(); - } - bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 67cf90bd0..500ee23e0 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -245,11 +245,7 @@ namespace lp { set.erase(j); } - void lar_solver::shrink_inf_set_after_pop(unsigned n, u_set& set) { - clean_popped_elements(n, set); - set.resize(n); - } - + void lar_solver::pop(unsigned k) { TRACE("lar_solver", tout << "k = " << k << std::endl;); @@ -714,11 +710,6 @@ namespace lp { detect_rows_with_changed_bounds_for_column(j); } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { - for (auto j : m_columns_with_changed_bounds) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); @@ -792,31 +783,6 @@ namespace lp { } - void lar_solver::fill_last_row_of_A_r(static_matrix>& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].size() == 0); - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff()); - } - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, mpq(1)); - } - - template - void lar_solver::copy_from_mpq_matrix(static_matrix& matr) { - matr.m_rows.resize(A_r().row_count()); - matr.m_columns.resize(A_r().column_count()); - for (unsigned i = 0; i < matr.row_count(); i++) { - for (auto& it : A_r().m_rows[i]) { - matr.set(i, it.var(), convert_struct::convert(it.coeff())); - } - } - } - bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { for (auto it : left_side) { if (!var_is_registered(it.second)) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 237b9de81..83d2a25fe 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -122,7 +122,6 @@ class lar_solver : public column_namer { bool term_is_int(const lar_term * t) const; bool term_is_int(const vector> & coeffs) const; void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); - void add_new_var_to_core_fields_for_doubles(bool register_in_basis); void add_new_var_to_core_fields_for_mpq(bool register_in_basis); mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&); @@ -131,7 +130,6 @@ class lar_solver : public column_namer { var_index add_term_undecided(const vector> & coeffs); bool term_coeffs_are_ok(const vector> & coeffs); void push_term(lar_term* t); - void add_row_for_term(const lar_term * term, unsigned term_ext_index); void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); void add_basic_var_to_core_fields(); bool compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs); @@ -187,7 +185,6 @@ class lar_solver : public column_namer { analyze_new_bounds_on_row_tableau(i, bp); } static void clean_popped_elements(unsigned n, u_set& set); - static void shrink_inf_set_after_pop(unsigned n, u_set & set); bool maximize_term_on_tableau(const lar_term & term, impq &term_max); bool costs_are_zeros_for_r_solver() const; @@ -213,17 +210,10 @@ class lar_solver : public column_namer { void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); - void update_x_and_inf_costs_for_columns_with_changed_bounds(); void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); bool x_is_correct() const; - void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); - template - void create_matrix_A(static_matrix & matr); - template - void copy_from_mpq_matrix(static_matrix & matr); - bool try_to_set_fixed(column_info & ci); bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; @@ -231,7 +221,6 @@ class lar_solver : public column_namer { static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; - bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); bool explanation_is_correct(explanation&) const; bool inf_explanation_is_correct() const; mpq sum_of_right_sides_of_explanation(explanation &) const; @@ -251,21 +240,16 @@ class lar_solver : public column_namer { void remove_last_column_from_tableau(); void pop_tableau(); void clean_inf_set_of_r_solver_after_pop(); - void shrink_explanation_to_minimum(vector> & explanation) const; inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } - void catch_up_in_updating_int_solver(); var_index to_column(unsigned ext_j) const; void fix_terms_with_rounded_columns(); - void update_delta_for_terms(const impq & delta, unsigned j, const vector&); - void fill_vars_to_terms(vector> & vars_to_terms); bool remove_from_basis(unsigned); lar_term get_term_to_maximize(unsigned ext_j) const; bool sum_first_coords(const lar_term& t, mpq & val) const; - void collect_rounded_rows_to_fix(); void register_normalized_term(const lar_term&, lpvar); void deregister_normalized_term(const lar_term&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 964e447a1..96f8729a0 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -80,7 +80,7 @@ public: vector & m_basis; vector& m_nbasis; vector& m_basis_heading; - vector & m_x; // a feasible solution, the fist time set in the constructor + vector & m_x; // a feasible solution, the first time set in the constructor vector & m_costs; lp_settings & m_settings; diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index aacabf944..fecf66e68 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -49,7 +49,6 @@ public: indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; - T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; u_set m_left_basis_tableau; @@ -277,13 +276,8 @@ public: return convert_struct::convert(std::numeric_limits::max()); } - bool get_harris_theta(X & theta); - - void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); void limit_theta(const X & lim, X & theta, bool & unlimited) { @@ -317,9 +311,6 @@ public: limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; - X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only @@ -489,20 +480,9 @@ public: this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); } - // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { - // lp_assert(m < 0); - // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); - // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); - // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { - // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); - // if (theta < zero_of_type()) theta = zero_of_type(); - // } - // } - void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); - limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) theta = zero_of_type(); } @@ -545,25 +525,21 @@ public: void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); - const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); + limit_theta((bound - x) / m, theta, unlimited); } } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & lbound = this->m_lower_bounds[j]; if (this->below_bound(x, lbound)) { - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta((lbound - x + eps) / m, theta, unlimited); + limit_theta((lbound - x) / m, theta, unlimited); } else { const X & ubound = this->m_upper_bounds[j]; if (this->below_bound(x, ubound)){ - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x + eps) / m, theta, unlimited); + limit_theta((ubound - x) / m, theta, unlimited); } else if (!this->above_bound(x, ubound)) { theta = zero_of_type(); unlimited = false; @@ -576,13 +552,11 @@ public: const X & x = this->m_x[j]; const X & ubound = this->m_upper_bounds[j]; if (this->above_bound(x, ubound)) { - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x - eps) / m, theta, unlimited); + limit_theta((ubound - x) / m, theta, unlimited); } else { const X & lbound = this->m_lower_bounds[j]; if (this->above_bound(x, lbound)){ - const X& eps = harris_eps_for_bound(lbound); - limit_theta((lbound - x - eps) / m, theta, unlimited); + limit_theta((lbound - x) / m, theta, unlimited); } else if (!this->below_bound(x, lbound)) { theta = zero_of_type(); unlimited = false; @@ -591,9 +565,8 @@ public: } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m > 0); - const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); unlimited = false; @@ -603,8 +576,7 @@ public: void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { lp_assert(m > 0); - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); + limit_theta( (this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); } @@ -612,9 +584,9 @@ public: // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) + // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) // or - // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) + // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { switch (this->m_column_types[j]) { case column_type::free_column: break; @@ -679,7 +651,6 @@ public: bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; bool can_enter_basis(unsigned j); - bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); @@ -694,90 +665,8 @@ public: return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } - - bool lower_bounds_are_set() const override { return true; } - void print_bound_info_and_x(unsigned j, std::ostream & out); - - void init_infeasibility_after_update_x_if_inf(unsigned leaving) { - if (this->using_infeas_costs()) { - init_infeasibility_costs_for_changed_basis_only(); - this->m_costs[leaving] = zero_of_type(); - this->remove_column_from_inf_set(leaving); - } - } - void init_inf_set() { - this->clear_inf_set(); - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->m_basis_heading[j] < 0) - continue; - if (!this->column_is_feasible(j)) - this->insert_column_into_inf_set(j); - } - } - - int get_column_out_of_bounds_delta_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(j)) - return -1; - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) - return -1; - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::free_column: - return 0; - default: - lp_assert(false); - } - return 0; - } - - void init_column_row_non_zeroes() { - this->m_columns_nz.resize(this->m_A.column_count()); - this->m_rows_nz.resize(this->m_A.row_count()); - for (unsigned i = 0; i < this->m_A.column_count(); i++) { - if (this->m_columns_nz[i] == 0) - this->m_columns_nz[i] = this->m_A.m_columns[i].size(); - } - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - if (this->m_rows_nz[i] == 0) - this->m_rows_nz[i] = this->m_A.m_rows[i].size(); - } - } - - - int x_at_bound_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - return 0; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) - return 1; - return -1; - break; - case column_type::lower_bound: - return 1; - break; - case column_type::upper_bound: - return -1; - break; - default: - lp_assert(false); - } - return 0; - - } - unsigned solve_with_tableau(); bool basis_column_is_set_correctly(unsigned j) const { @@ -844,10 +733,6 @@ public: m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { - - - m_converted_harris_eps = zero_of_type(); - this->set_status(lp_status::UNKNOWN); } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index cfd8fc947..04c32d41c 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -142,53 +142,6 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); } - -template bool lp_primal_core_solver::get_harris_theta(X & theta) { - bool unlimited = true; - for (unsigned i : this->m_ed.m_index) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); - if (!unlimited && is_zero(theta)) break; - } - return unlimited; -} - - -template int lp_primal_core_solver:: -find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max = zero_of_type(); - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { - if (++k == steps) - k = 0; - continue; - } - X ratio; - unsigned j = this->m_basis[i]; - bool unlimited = true; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); - if ((!unlimited) && ratio <= harris_theta) { - if (leaving == -1 || abs(ed) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(ed); - } - } - if (++k == steps) k = 0; - } while (k != initial_k); - return leaving; -} - - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, @@ -246,68 +199,6 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { return true; } -template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { - bool unlimited = true; - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - unsigned row_min_nz = this->m_n() + 1; - m_leaving_candidates.clear(); - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); - if (!unlimited) { - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } - if (++k == steps) k = 0; - } while (unlimited && k != initial_k); - if (unlimited) { - if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) - return entering; - return -1; - } - - X ratio; - while (k != initial_k) { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - unlimited = true; - limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); - if (unlimited) { - if (++k == steps) k = 0; - continue; - } - unsigned i_nz = this->m_rows_nz[i]; - if (ratio < t) { - t = ratio; - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz < row_min_nz) { - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz == row_min_nz) { - m_leaving_candidates.push_back(j); - } - if (++k == steps) k = 0; - } - - ratio = t; - unlimited = false; - if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { - t = ratio; - return entering; - } - k = this->m_settings.random_next() % m_leaving_candidates.size(); - return m_leaving_candidates[k]; -} @@ -499,17 +390,6 @@ template void lp_primal_core_solver::one_iteratio -template bool lp_primal_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL) return true; - if (this->get_status() == lp_status::INFEASIBLE) { - return true; - } - if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { - this->set_status(lp_status::CANCELLED); - return true; - } - return false; -} template void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 256853be7..d4355fa80 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -43,6 +43,7 @@ template void lp_primal_core_solver::advance_on_e } advance_on_entering_and_leaving_tableau(entering, leaving, t); } + template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); @@ -85,9 +86,6 @@ template void lp_primal_core_solver::advance_on_e } - - - template unsigned lp_primal_core_solver::solve_with_tableau() { init_run_tableau(); diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c993dbd7d..24aa8767a 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -56,7 +56,6 @@ #include "math/lp/int_cube.h" #include "math/lp/emonics.h" #include "math/lp/static_matrix.h" -#include "math/lp/dense_matrix.h" bool my_white_space(const char & a) { return a == ' ' || a == '\t'; @@ -431,205 +430,8 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, nbasis[place_in_non_basis] = leaving; } - - -#ifdef Z3DEBUG -void test_small_lu(lp_settings & settings) { - -} - -#endif - - -void fill_long_row(static_matrix &m, int i) { - int n = m.column_count(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} - - - -void fill_long_row_exp(static_matrix &m, int i) { - int n = m.column_count(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} - - - - - - int perm_id = 0; - - - - - - - -void init_b(vector & b, static_matrix & m, vector & x) { - for (unsigned i = 0; i < m.row_count(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} - - -void test_lp_0() { - std::cout << " test_lp_0 " << std::endl; - static_matrix m_(3, 7); - m_(0, 0) = 3; m_(0, 1) = 2; m_(0, 2) = 1; m_(0, 3) = 2; m_(0, 4) = 1; - m_(1, 0) = 1; m_(1, 1) = 1; m_(1, 2) = 1; m_(1, 3) = 1; m_(1, 5) = 1; - m_(2, 0) = 4; m_(2, 1) = 3; m_(2, 2) = 3; m_(2, 3) = 4; m_(2, 6) = 1; - vector x_star(7); - x_star[0] = 225; x_star[1] = 117; x_star[2] = 420; - x_star[3] = x_star[4] = x_star[5] = x_star[6] = 0; - vector b; - init_b(b, m_, x_star); - vector basis(3); - basis[0] = 0; basis[1] = 1; basis[2] = 2; - vector costs(7); - costs[0] = 19; - costs[1] = 13; - costs[2] = 12; - costs[3] = 17; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - lp_settings settings; - simple_column_namer cn; - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - -void test_lp_1() { - std::cout << " test_lp_1 " << std::endl; - static_matrix m(4, 7); - m(0, 0) = 1; m(0, 1) = 3; m(0, 2) = 1; m(0, 3) = 1; - m(1, 0) = -1; m(1, 2) = 3; m(1, 4) = 1; - m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; - m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; -#ifdef Z3DEBUG - //print_matrix(m, std::cout); -#endif - vector x_star(7); - x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; - x_star[3] = 3; x_star[4] = 2; x_star[5] = 4; x_star[6] = 2; - - vector basis(4); - basis[0] = 3; basis[1] = 4; basis[2] = 5; basis[3] = 6; - - vector b; - b.push_back(3); - b.push_back(2); - b.push_back(4); - b.push_back(2); - - vector costs(7); - costs[0] = 5; - costs[1] = 5; - costs[2] = 3; - costs[3] = 0; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - - std::cout << "calling lp\n"; - lp_settings settings; - simple_column_namer cn; - - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m, b, - x_star, - basis, - nbasis, heading, - costs, - column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - - -void test_lp_primal_core_solver() { - test_lp_0(); - test_lp_1(); -} - - - - -#ifdef Z3DEBUG - -void fill_uniformly(dense_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m.set_elem(i, j, v++); - } - } -} - - - - -void test_dense_matrix() { - dense_matrix d(3, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 2); - d.set_elem(2, 0, 3); - // print_matrix(d); - - dense_matrix unit(2, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 1); - - dense_matrix c = d * unit; - - // print_matrix(d); - - dense_matrix perm(3, 3); - perm.set_elem(0, 1, 1); - perm.set_elem(1, 0, 1); - perm.set_elem(2, 2, 1); - auto c1 = perm * d; - // print_matrix(c1); - - - dense_matrix p2(2, 2); - p2.set_elem(0, 1, 1); - p2.set_elem(1, 0, 1); - auto c2 = d * p2; -} - -#endif - - - - - -void lp_solver_test() { - // lp_revised_solver lp_revised; - // lp_revised.get_minimal_solution(); -} - bool get_int_from_args_parser(const char * option, argument_parser & args_parser, unsigned & n) { std::string s = args_parser.get_option_value(option); if (!s.empty()) { @@ -650,9 +452,6 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par -bool values_are_one_percent_close(double a, double b); - - void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition @@ -939,10 +738,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-gomory", "gomory"); parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); - parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); - parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); - parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); - parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); parser.add_option_with_help_string("--totalinf", "minimizes the total infeasibility instead of diminishing infeasibility of the rows"); parser.add_option_with_after_string_with_help("--rep_frq", "the report frequency, in how many iterations print the cost and other info "); @@ -1202,71 +997,6 @@ void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { n = atoi(r[1].c_str()); } -void read_row_cols(unsigned i, static_matrix& A, std::ifstream & f) { - do { - std::string line; - getline(f, line); - if (line== "row_end") - break; - auto r = split_and_trim(line); - lp_assert(r.size() == 4); - unsigned j = atoi(r[1].c_str()); - double v = atof(r[3].c_str()); - A.set(i, j, v); - } while (true); -} - -bool read_row(static_matrix & A, std::ifstream & f) { - std::string line; - getline(f, line); - if (static_cast(line.find("row")) == -1) - return false; - auto r = split_and_trim(line); - if (r[0] != "row") - std::cout << "wrong row line" << line << std::endl; - unsigned i = atoi(r[1].c_str()); - read_row_cols(i, A, f); - return true; -} - -void read_rows(static_matrix& A, std::ifstream & f) { - while (read_row(A, f)) {} -} - -void read_basis(vector & basis, std::ifstream & f) { - std::cout << "reading basis" << std::endl; - std::string line; - getline(f, line); - lp_assert(line == "basis_start"); - do { - getline(f, line); - if (line == "basis_end") - break; - unsigned j = atoi(line.c_str()); - basis.push_back(j); - } while (true); -} - -void read_indexed_vector(indexed_vector & v, std::ifstream & f) { - std::string line; - getline(f, line); - lp_assert(line == "vector_start"); - do { - getline(f, line); - if (line == "vector_end") break; - auto r = split_and_trim(line); - unsigned i = atoi(r[0].c_str()); - double val = atof(r[1].c_str()); - v.set_value(val, i); - std::cout << "setting value " << i << " = " << val << std::endl; - } while (true); -} - -void check_lu_from_file(std::string lufile_name) { - lp_assert(false); -} - - void print_st(lp_status status) { std::cout << lp_status_to_string(status) << std::endl; @@ -2298,15 +2028,6 @@ void test_lp_local(int argn, char**argv) { test_bound_propagation(); return finalize(0); } - - - std::string lufile = args_parser.get_option_value("--checklu"); - if (!lufile.empty()) { - check_lu_from_file(lufile); - return finalize(0); - } - - if (args_parser.option_is_used("-tbq")) { From 748c75275fa3f810e06b569ba04e7368fe8a691c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 13:53:23 -0800 Subject: [PATCH 114/220] more dead code removal --- src/math/lp/lar_solver.cpp | 4 ---- src/math/lp/lar_solver.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 500ee23e0..952f2ad43 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2330,10 +2330,6 @@ namespace lp { return true; } - void lar_solver::pivot_column_tableau(unsigned j, unsigned row_index) { - m_mpq_lar_core_solver.m_r_solver.pivot_column_tableau(j, row_index); - m_mpq_lar_core_solver.m_r_solver.change_basis(j, r_basis()[row_index]); - } } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 83d2a25fe..a321632fb 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -519,8 +519,6 @@ public: return m_mpq_lar_core_solver.lower_bound(j); } - void pivot_column_tableau(unsigned j, unsigned row_index); - inline const impq & column_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.upper_bound(j); } From c8c0a001907af348c54e885ebbd45d3a8fce4e79 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 15:37:22 -0800 Subject: [PATCH 115/220] remove more dead code --- src/math/lp/lar_core_solver.h | 128 +----------------- src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lp_core_solver_base.h | 2 - src/math/lp/lp_primal_core_solver.h | 55 +------- src/math/lp/lp_primal_core_solver_def.h | 64 +-------- .../lp/lp_primal_core_solver_tableau_def.h | 1 - src/math/lp/lp_settings.h | 6 - src/math/lp/numeric_pair.h | 15 +- 8 files changed, 5 insertions(+), 267 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 52c5b6f1a..966e2f0d7 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -20,9 +20,6 @@ Author: namespace lp { class lar_core_solver { - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - int m_sign_of_entering_delta; vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; @@ -40,8 +37,6 @@ public: vector m_r_basis; vector m_r_nbasis; vector m_r_heading; - stacked_vector m_r_columns_nz; - stacked_vector m_r_rows_nz; lp_primal_core_solver> m_r_solver; // solver in rational numbers @@ -82,16 +77,9 @@ public: m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } - bool row_is_infeasible(unsigned row); - - bool row_is_evidence(unsigned row); - - bool find_evidence_row(); - + void prefix_r(); - void prefix_d(); - unsigned m_m() const { return m_r_A.row_count(); } unsigned m_n() const { return m_r_A.column_count(); } @@ -103,8 +91,6 @@ public: template int get_sign(const L & v) { return v > zero_of_type() ? 1 : (v < zero_of_type() ? -1 : 0); } - void fill_evidence(unsigned row); - unsigned get_number_of_non_ints() const; void solve(); @@ -131,31 +117,6 @@ public: } - template - void push_vector(stacked_vector & pushed_vector, const vector & vector) { - lp_assert(pushed_vector.size() <= vector.size()); - for (unsigned i = 0; i < vector.size();i++) { - if (i == pushed_vector.size()) { - pushed_vector.push_back(vector[i]); - } else { - pushed_vector[i] = vector[i]; - } - } - pushed_vector.push(); - } - - void pop_markowitz_counts(unsigned k) { - m_r_columns_nz.pop(k); - m_r_rows_nz.pop(k); - m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); - m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); - for (unsigned i = 0; i < m_r_columns_nz.size(); i++) - m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; - for (unsigned i = 0; i < m_r_rows_nz.size(); i++) - m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; - } - - void pop(unsigned k) { // rationals m_r_lower_bounds.pop(k); @@ -172,72 +133,7 @@ public: } - template - bool is_zero_vector(const vector & b) { - for (const L & m: b) - if (!is_zero(m)) return false; - return true; - } - - - bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { - auto & x = m_r_x[j]; - switch (pos_type) { - case at_lower_bound: - if (x == m_r_solver.m_lower_bounds[j]) - return false; - delta = m_r_solver.m_lower_bounds[j] - x; - m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - if (x == m_r_solver.m_upper_bounds[j]) - return false; - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case free_of_bounds: { - return false; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - return false; - case column_type::upper_bound: - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case column_type::lower_bound: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - case column_type::boxed: - if (x > m_r_solver.m_upper_bounds[j]) { - delta = m_r_solver.m_upper_bounds[j] - x; - x += m_r_solver.m_upper_bounds[j]; - } else { - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - } - break; - case column_type::fixed: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - m_r_solver.remove_column_from_inf_set(j); - return true; - } - - bool r_basis_is_OK() const { #ifdef Z3DEBUG @@ -255,28 +151,6 @@ public: } - - void fill_basis_d( - vector& basis_d, - vector& heading_d, - vector& nbasis_d){ - basis_d = m_r_basis; - heading_d = m_r_heading; - nbasis_d = m_r_nbasis; - } - - template - void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { - signature.clear(); - lp_assert(signature.size() == 0); - for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { - if (solver.m_basis_heading[j] < 0) { - signature[j] = solver.get_non_basic_column_value_position(j); - } - } - } - - bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 419345f0c..2e205291e 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -46,7 +46,6 @@ void lar_core_solver::prefix_r() { } - void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 96f8729a0..9fb4d4333 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -72,8 +72,6 @@ public: unsigned inf_set_size() const { return m_inf_set.size(); } bool using_infeas_costs() const { return m_using_infeas_costs; } void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } - vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column - vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index fecf66e68..17b1ea494 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -41,54 +41,21 @@ namespace lp { template class lp_primal_core_solver:public lp_core_solver_base { public: - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - unsigned m_column_norm_update_counter; - T m_enter_price_eps; int m_sign_of_entering_delta; - indexed_vector m_beta; // see Swietanowski working vector beta for column norms - T m_epsilon_of_reduced_cost; vector m_costs_backup; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; - u_set m_left_basis_tableau; + u_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; std::list m_non_basis_list; void sort_non_basis(); - void sort_non_basis_rational(); int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); int choose_entering_column_tableau(); int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { - // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos - // we have xbj = -aj * xj - lp_assert(this->m_basis_heading[j] < 0); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: return true; - case column_type::fixed: return false; - case column_type::lower_bound: - if (sign < 0) - return true; - return !this->x_is_at_lower_bound(j); - case column_type::upper_bound: - if (sign > 0) - return true; - return !this->x_is_at_upper_bound(j); - case column_type::boxed: - if (sign < 0) - return !this->x_is_at_lower_bound(j); - return !this->x_is_at_upper_bound(j); - } - - lp_assert(false); // cannot be here - return false; - } - bool needs_to_grow(unsigned bj) const { lp_assert(!this->column_is_feasible(bj)); @@ -272,10 +239,7 @@ public: a_ent = rc.coeff(); return rc.var(); } - static X positive_infinity() { - return convert_struct::convert(std::numeric_limits::max()); - } - + bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); @@ -313,8 +277,6 @@ public: void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); - vector m_lower_bounds_dummy; // needed for the base class only - X get_max_bound(vector & b); #ifdef Z3DEBUG @@ -334,10 +296,6 @@ public: void backup_and_normalize_costs(); - void init_run(); - - void calc_working_vector_beta_for_column_norms(); - void advance_on_entering_and_leaving(int entering, int leaving, X & t); void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); void advance_on_entering_equal_leaving(int entering, X & t); @@ -368,7 +326,6 @@ public: // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - void one_iteration(); void one_iteration_tableau(); // this version assumes that the leaving already has the right value, and does not update it @@ -658,12 +615,6 @@ public: void init_infeasibility_costs_for_changed_basis_only(); void print_column(unsigned j, std::ostream & out); - // j is the basic column, x is the value at x[j] - // d is the coefficient before m_entering in the row with j as the basis column - template - bool same_sign_with_entering_delta(const L & a) { - return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); - } void print_bound_info_and_x(unsigned j, std::ostream & out); @@ -730,8 +681,6 @@ public: column_type_array, lower_bound_values, upper_bound_values), - m_beta(A.row_count()), - m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { this->set_status(lp_status::UNKNOWN); } diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 04c32d41c..1c48f1163 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -32,7 +32,7 @@ namespace lp { // The right side b is given implicitly by x and the basis template -void lp_primal_core_solver::sort_non_basis_rational() { +void lp_primal_core_solver::sort_non_basis() { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); @@ -50,10 +50,6 @@ void lp_primal_core_solver::sort_non_basis_rational() { } -template -void lp_primal_core_solver::sort_non_basis() { - sort_non_basis_rational(); -} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { @@ -249,15 +245,6 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( } } -template X lp_primal_core_solver::get_max_bound(vector & b) { - X ret = zero_of_type(); - for (auto & v : b) { - X a = abs(v); - if (a > ret) ret = a; - } - return ret; -} - #ifdef Z3DEBUG template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); @@ -282,38 +269,6 @@ template void lp_primal_core_solver::check_cor } #endif -// from page 183 of Istvan Maros's book -// the basis structures have not changed yet -template -void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving) { - // the basis heading has changed already -#ifdef Z3DEBUG - auto & basis_heading = this->m_basis_heading; - lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); - lp_assert(basis_heading[leaving] < 0); -#endif - T pivot = this->m_pivot_row[entering]; - T dq = this->m_d[entering]/pivot; - for (auto j : this->m_pivot_row.m_index) { - // for (auto j : this->m_nbasis) - if (this->m_basis_heading[j] >= 0) continue; - if (j != leaving) - this->m_d[j] -= dq * this->m_pivot_row[j]; - } - this->m_d[leaving] = -dq; - if (this->current_x_is_infeasible()) { - this->m_d[leaving] -= this->m_costs[leaving]; - this->m_costs[leaving] = zero_of_type(); - } - this->m_d[entering] = numeric_traits::zero(); -} - -// return 0 if the reduced cost at entering is close enough to the refreshed -// 1 if it is way off, and 2 if it is unprofitable -template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - return 0; - -} template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) @@ -321,9 +276,6 @@ template void lp_primal_core_solver::backup_an m_costs_backup = this->m_costs; } -template void lp_primal_core_solver::init_run() { - -} template @@ -377,20 +329,6 @@ template void lp_primal_core_solver::find_feas solve(); } -template void lp_primal_core_solver::one_iteration() { - unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); - if (entering == -1) { - decide_on_status_when_cannot_find_entering(); - } - else { - advance_on_entering(entering); - } -} - - - - template void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { for (unsigned i : this->m_ed.m_index) diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index d4355fa80..de2f1c208 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -283,7 +283,6 @@ template void lp_primal_core_solver::init_run_tab return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = zero_of_type(); if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 197b7c04f..314930fc1 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -179,15 +179,9 @@ public: bool int_run_gcd_test() const { return m_int_run_gcd_test; } bool& int_run_gcd_test() { return m_int_run_gcd_test; } unsigned reps_in_scaler { 20 }; - // when the absolute value of an element is less than pivot_epsilon - // in pivoting, we treat it as a zero - // a quotation "if some choice of the entering variable leads to an eta matrix - // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... int c_partial_pivoting { 10 }; // this is the constant c from page 410 unsigned depth_of_rook_search { 4 }; bool using_partial_pivoting { true }; - // dissertation of Achim Koberstein - // if Bx - b is different at any component more that refactor_epsilon then we refactor unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns bool use_scaling { true }; diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index f59aa84ba..4a60c82ca 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -107,7 +107,6 @@ public: template struct convert_struct { static X convert(const Y & y){ return X(y);} - static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } }; @@ -316,16 +315,12 @@ struct convert_struct> { typedef numeric_pair impq; -template bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct::is_epsilon_small(v, eps);} template struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); - } static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { // lp_unreachable(); return false; @@ -340,10 +335,7 @@ struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(q, 0.0); } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return std::abs(p.x) < eps && std::abs(p.y) < eps; - } - + static int compare_on_coord(const double & x, const double & bound, const double eps) { if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case double relative = (bound > 0)? - eps: eps; @@ -369,9 +361,6 @@ struct convert_struct, double> { template <> struct convert_struct { - static bool is_epsilon_small(const double& x, const double & eps) { - return x < eps && x > -eps; - } static double convert(const double & y){ return y;} static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { if (bound == 0) return x < - eps; @@ -384,8 +373,6 @@ struct convert_struct { return x > bound * (1.0 + relative) + eps; } }; - -template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { From c6be67bf3b8f19f7edc37ce34b03805cc0fc040c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 16:58:49 -0800 Subject: [PATCH 116/220] more dead code --- src/math/lp/numeric_pair.h | 75 -------------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 4a60c82ca..a64825cd8 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -112,18 +112,6 @@ struct convert_struct { }; -template <> -struct convert_struct { - static double convert(const mpq & q) {return q.get_double();} -}; - - -template <> -struct convert_struct { - static mpq convert(unsigned q) {return mpq(q);} -}; - - template struct numeric_pair { @@ -308,71 +296,8 @@ public: } }; -template <> -struct convert_struct> { - static double convert(const numeric_pair & q) {return q.x;} -}; - typedef numeric_pair impq; - -template -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); - } - static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } - static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } -}; -template <> -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(q, 0.0); - } - - static int compare_on_coord(const double & x, const double & bound, const double eps) { - if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case - double relative = (bound > 0)? - eps: eps; - return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0); - } - - static bool below_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == 1) return false; - if (r == -1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == -1; - } - - static bool above_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == -1) return false; - if (r == 1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == 1; - } -}; - -template <> -struct convert_struct { - static double convert(const double & y){ return y;} - static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x < - eps; - double relative = (bound > 0)? - eps: eps; - return x < bound * (1.0 + relative) - eps; - } - static bool above_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x > eps; - double relative = (bound > 0)? eps: - eps; - return x > bound * (1.0 + relative) + eps; - } -}; template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { From 13549aff6669bb9536bd3c1fe9f6aefbadf23abf Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:11:26 -0800 Subject: [PATCH 117/220] rm dead code --- src/math/lp/CMakeLists.txt | 1 - src/math/lp/lp_primal_core_solver.h | 2 -- src/math/lp/lp_primal_core_solver_def.h | 5 ---- .../lp/lp_primal_core_solver_tableau_def.h | 3 ++- src/math/lp/lp_utils.cpp | 26 ------------------- 5 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 src/math/lp/lp_utils.cpp diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 98241bf60..4ca8cb1d2 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -20,7 +20,6 @@ z3_add_component(lp lp_core_solver_base.cpp lp_primal_core_solver.cpp lp_settings.cpp - lp_utils.cpp matrix.cpp mon_eq.cpp monomial_bounds.cpp diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 17b1ea494..1719f9c55 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -618,8 +618,6 @@ public: void print_bound_info_and_x(unsigned j, std::ostream & out); - unsigned solve_with_tableau(); - bool basis_column_is_set_correctly(unsigned j) const { return this->m_A.m_columns[j].size() == 1; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 1c48f1163..cc8ad88b3 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -315,11 +315,6 @@ template unsigned lp_primal_core_solver::get_num } -// returns the number of iterations -template unsigned lp_primal_core_solver::solve() { - TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - return solve_with_tableau(); -} // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index de2f1c208..c7b604b95 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -87,7 +87,8 @@ template void lp_primal_core_solver::advance_on_e } template -unsigned lp_primal_core_solver::solve_with_tableau() { +unsigned lp_primal_core_solver::solve() { + TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FEASIBLE); diff --git a/src/math/lp/lp_utils.cpp b/src/math/lp/lp_utils.cpp deleted file mode 100644 index b909a0389..000000000 --- a/src/math/lp/lp_utils.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_utils.h" -#ifdef lp_for_z3 -namespace lp { - -} -#endif - From 11eab94321b1efccf040155fea424d34d8440810 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:52:59 -0800 Subject: [PATCH 118/220] more dead code --- src/math/lp/CMakeLists.txt | 2 - src/math/lp/binary_heap_priority_queue.cpp | 38 --- src/math/lp/binary_heap_priority_queue.h | 83 ------ src/math/lp/binary_heap_priority_queue_def.h | 214 --------------- src/math/lp/binary_heap_upair_queue.cpp | 32 --- src/math/lp/binary_heap_upair_queue.h | 65 ----- src/math/lp/binary_heap_upair_queue_def.h | 126 --------- src/math/lp/conversion_helper.h | 35 --- src/math/lp/indexer_of_constraints.h | 45 ---- src/math/lp/lar_core_solver.h | 1 - src/math/lp/lar_solver.h | 1 - src/math/lp/lp_primal_core_solver.h | 1 - src/math/lp/permutation_matrix.cpp | 23 -- src/math/lp/permutation_matrix.h | 61 +---- src/math/lp/permutation_matrix_def.h | 261 +------------------ src/math/lp/tail_matrix.h | 50 ---- src/test/lp/lp.cpp | 67 ----- 17 files changed, 10 insertions(+), 1095 deletions(-) delete mode 100644 src/math/lp/binary_heap_priority_queue.cpp delete mode 100644 src/math/lp/binary_heap_priority_queue.h delete mode 100644 src/math/lp/binary_heap_priority_queue_def.h delete mode 100644 src/math/lp/binary_heap_upair_queue.cpp delete mode 100644 src/math/lp/binary_heap_upair_queue.h delete mode 100644 src/math/lp/binary_heap_upair_queue_def.h delete mode 100644 src/math/lp/conversion_helper.h delete mode 100644 src/math/lp/indexer_of_constraints.h delete mode 100644 src/math/lp/tail_matrix.h diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 4ca8cb1d2..dd72f36ee 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -1,7 +1,5 @@ z3_add_component(lp SOURCES - binary_heap_priority_queue.cpp - binary_heap_upair_queue.cpp core_solver_pretty_printer.cpp dense_matrix.cpp emonics.cpp diff --git a/src/math/lp/binary_heap_priority_queue.cpp b/src/math/lp/binary_heap_priority_queue.cpp deleted file mode 100644 index ec6630b1d..000000000 --- a/src/math/lp/binary_heap_priority_queue.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_priority_queue_def.h" -namespace lp { -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, int const&); -template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); -template void binary_heap_priority_queue::remove(unsigned int); -template unsigned binary_heap_priority_queue >::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); -template void binary_heap_priority_queue >::resize(unsigned int); -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template void binary_heap_priority_queue::resize(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, unsigned int const&); -template void binary_heap_priority_queue::remove(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); -} diff --git a/src/math/lp/binary_heap_priority_queue.h b/src/math/lp/binary_heap_priority_queue.h deleted file mode 100644 index 1ea8363ef..000000000 --- a/src/math/lp/binary_heap_priority_queue.h +++ /dev/null @@ -1,83 +0,0 @@ - -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_utils.h" -namespace lp { -// the elements with the smallest priority are dequeued first -template -class binary_heap_priority_queue { - vector m_priorities; - - // indexing for A starts from 1 - vector m_heap; // keeps the elements of the queue - vector m_heap_inverse; // o = m_heap[m_heap_inverse[o]] - unsigned m_heap_size; - // is is the child place in heap - void swap_with_parent(unsigned i); - void put_at(unsigned i, unsigned h); - void decrease_priority(unsigned o, T newPriority); -public: -#ifdef Z3DEBUG - bool is_consistent() const; -#endif -public: - void remove(unsigned o); - unsigned size() const { return m_heap_size; } - binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror - // n is the initial queue capacity. - // The capacity will be enlarged each time twice if needed - binary_heap_priority_queue(unsigned n); - - void clear() { - for (unsigned i = 0; i < m_heap_size; i++) { - unsigned o = m_heap[i+1]; - m_heap_inverse[o] = -1; - } - m_heap_size = 0; - } - - void resize(unsigned n); - void put_to_heap(unsigned i, unsigned o); - - void enqueue_new(unsigned o, const T& priority); - - // This method can work with an element that is already in the queue. - // In this case the priority will be changed and the queue adjusted. - void enqueue(unsigned o, const T & priority); - void change_priority_for_existing(unsigned o, const T & priority); - T get_priority(unsigned o) const { return m_priorities[o]; } - bool is_empty() const { return m_heap_size == 0; } - - /// return the first element of the queue and removes it from the queue - unsigned dequeue_and_get_priority(T & priority); - void fix_heap_under(unsigned i); - void put_the_last_at_the_top_and_fix_the_heap(); - /// return the first element of the queue and removes it from the queue - unsigned dequeue(); - unsigned peek() const { - lp_assert(m_heap_size > 0); - return m_heap[1]; - } - void print(std::ostream & out); -}; -} diff --git a/src/math/lp/binary_heap_priority_queue_def.h b/src/math/lp/binary_heap_priority_queue_def.h deleted file mode 100644 index 0e640d949..000000000 --- a/src/math/lp/binary_heap_priority_queue_def.h +++ /dev/null @@ -1,214 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { -// "i" is the child's place in the heap -template void binary_heap_priority_queue::swap_with_parent(unsigned i) { - unsigned parent = m_heap[i >> 1]; - put_at(i >> 1, m_heap[i]); - put_at(i, parent); -} - -template void binary_heap_priority_queue::put_at(unsigned i, unsigned h) { - m_heap[i] = h; - m_heap_inverse[h] = i; -} - -template void binary_heap_priority_queue::decrease_priority(unsigned o, T newPriority) { - m_priorities[o] = newPriority; - int i = m_heap_inverse[o]; - while (i > 1) { - if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]]) - swap_with_parent(i); - else - break; - i >>= 1; - } -} - -#ifdef Z3DEBUG -template bool binary_heap_priority_queue::is_consistent() const { - for (int i = 0; i < m_heap_inverse.size(); i++) { - int i_index = m_heap_inverse[i]; - lp_assert(i_index <= static_cast(m_heap_size)); - lp_assert(i_index == -1 || m_heap[i_index] == i); - } - for (unsigned i = 1; i < m_heap_size; i++) { - unsigned ch = i << 1; - for (int k = 0; k < 2; k++) { - if (ch > m_heap_size) break; - if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){ - return false; - } - ch++; - } - } - return true; -} -#endif - -template void binary_heap_priority_queue::remove(unsigned o) { - T priority_of_o = m_priorities[o]; - int o_in_heap = m_heap_inverse[o]; - if (o_in_heap == -1) { - return; // nothing to do - } - lp_assert(static_cast(o_in_heap) <= m_heap_size); - if (static_cast(o_in_heap) < m_heap_size) { - put_at(o_in_heap, m_heap[m_heap_size--]); - if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { - fix_heap_under(o_in_heap); - } else { // we need to propagate the m_heap[o_in_heap] up - unsigned i = o_in_heap; - while (i > 1) { - unsigned ip = i >> 1; - if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]]) - swap_with_parent(i); - else - break; - i = ip; - } - } - } else { - lp_assert(static_cast(o_in_heap) == m_heap_size); - m_heap_size--; - } - m_heap_inverse[o] = -1; - // lp_assert(is_consistent()); -} -// n is the initial queue capacity. -// The capacity will be enlarged two times automatically if needed -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned n) : - m_priorities(n), - m_heap(n + 1), // because the indexing for A starts from 1 - m_heap_inverse(n, -1), - m_heap_size(0) -{ } - - -template void binary_heap_priority_queue::resize(unsigned n) { - m_priorities.resize(n); - m_heap.resize(n + 1); - m_heap_inverse.resize(n, -1); -} - -template void binary_heap_priority_queue::put_to_heap(unsigned i, unsigned o) { - m_heap[i] = o; - m_heap_inverse[o] = i; -} - -template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { - m_heap_size++; - int i = m_heap_size; - lp_assert(o < m_priorities.size()); - m_priorities[o] = priority; - put_at(i, o); - while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { - swap_with_parent(i); - i >>= 1; - } -} -// This method can work with an element that is already in the queue. -// In this case the priority will be changed and the queue adjusted. -template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { - if (o >= m_priorities.size()) { - if (o == 0) - resize(2); - else - resize(o << 1); // make the size twice larger - } - - if (m_heap_inverse[o] == -1) - enqueue_new(o, priority); - else - change_priority_for_existing(o, priority); -} - -template void binary_heap_priority_queue::change_priority_for_existing(unsigned o, const T & priority) { - if (m_priorities[o] > priority) { - decrease_priority(o, priority); - } else { - m_priorities[o] = priority; - fix_heap_under(m_heap_inverse[o]); - } -} - - -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { - lp_assert(m_heap_size != 0); - int ret = m_heap[1]; - priority = m_priorities[ret]; - put_the_last_at_the_top_and_fix_the_heap(); - return ret; -} - -template void binary_heap_priority_queue::fix_heap_under(unsigned i) { - while (true) { - unsigned smallest = i; - unsigned l = i << 1; - if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]]) - smallest = l; - unsigned r = l + 1; - if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]]) - smallest = r; - if (smallest != i) - swap_with_parent(smallest); - else - break; - i = smallest; - } -} - -template void binary_heap_priority_queue::put_the_last_at_the_top_and_fix_the_heap() { - if (m_heap_size > 1) { - put_at(1, m_heap[m_heap_size--]); - fix_heap_under(1); - } else { - m_heap_size--; - } -} -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue() { - lp_assert(m_heap_size > 0); - int ret = m_heap[1]; - put_the_last_at_the_top_and_fix_the_heap(); - m_heap_inverse[ret] = -1; - return ret; -} -template void binary_heap_priority_queue::print(std::ostream & out) { - vector index; - vector prs; - while (size()) { - T prior; - int j = dequeue_and_get_priority(prior); - index.push_back(j); - prs.push_back(prior); - out << "(" << j << ", " << prior << ")"; - } - out << std::endl; - // restore the queue - for (int i = 0; i < index.size(); i++) - enqueue(index[i], prs[i]); -} -} diff --git a/src/math/lp/binary_heap_upair_queue.cpp b/src/math/lp/binary_heap_upair_queue.cpp deleted file mode 100644 index f1ff1d639..000000000 --- a/src/math/lp/binary_heap_upair_queue.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/binary_heap_upair_queue_def.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, int const&); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, unsigned int const&); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -} diff --git a/src/math/lp/binary_heap_upair_queue.h b/src/math/lp/binary_heap_upair_queue.h deleted file mode 100644 index ce4542803..000000000 --- a/src/math/lp/binary_heap_upair_queue.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include -#include -#include "math/lp/binary_heap_priority_queue.h" - - -typedef std::pair upair; - -namespace lp { -template -class binary_heap_upair_queue { - binary_heap_priority_queue m_q; - std::unordered_map m_pairs_to_index; - svector m_pairs; // inverse to index - svector m_available_spots; -public: - binary_heap_upair_queue(unsigned size); - - unsigned dequeue_available_spot(); - bool is_empty() const { return m_q.is_empty(); } - - unsigned size() const {return m_q.size(); } - - bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end(); - } - - void remove(unsigned i, unsigned j); - bool ij_index_is_new(unsigned ij_index) const; - void enqueue(unsigned i, unsigned j, const T & priority); - void dequeue(unsigned & i, unsigned &j); - T get_priority(unsigned i, unsigned j) const; -#ifdef Z3DEBUG - bool pair_to_index_is_a_bijection() const; - bool available_spots_are_correct() const; - bool is_correct() const { - return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct(); - } -#endif - void resize(unsigned size) { m_q.resize(size); } -}; -} diff --git a/src/math/lp/binary_heap_upair_queue_def.h b/src/math/lp/binary_heap_upair_queue_def.h deleted file mode 100644 index 65485a6eb..000000000 --- a/src/math/lp/binary_heap_upair_queue_def.h +++ /dev/null @@ -1,126 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/lp_utils.h" -#include "math/lp/binary_heap_upair_queue.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) { - for (unsigned i = 0; i < size; i++) - m_available_spots.push_back(i); -} - -template unsigned -binary_heap_upair_queue::dequeue_available_spot() { - lp_assert(m_available_spots.empty() == false); - unsigned ret = m_available_spots.back(); - m_available_spots.pop_back(); - return ret; -} - -template void binary_heap_upair_queue::remove(unsigned i, unsigned j) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - if (it == m_pairs_to_index.end()) - return; // nothing to do - m_q.remove(it->second); - m_available_spots.push_back(it->second); - m_pairs_to_index.erase(it); -} - - -template bool binary_heap_upair_queue::ij_index_is_new(unsigned ij_index) const { - for (auto it : m_pairs_to_index) { - if (it.second == ij_index) - return false; - } - return true; -} - -template void binary_heap_upair_queue::enqueue(unsigned i, unsigned j, const T & priority) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - unsigned ij_index; - if (it == m_pairs_to_index.end()) { - // it is a new pair, let us find a spot for it - if (m_available_spots.empty()) { - // we ran out of empty spots - unsigned size_was = static_cast(m_pairs.size()); - unsigned new_size = size_was << 1; - for (unsigned i = size_was; i < new_size; i++) - m_available_spots.push_back(i); - m_pairs.resize(new_size); - } - ij_index = dequeue_available_spot(); - // lp_assert(ij_indexsecond; - } - m_q.enqueue(ij_index, priority); -} - -template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { - lp_assert(!m_q.is_empty()); - unsigned ij_index = m_q.dequeue(); - upair & p = m_pairs[ij_index]; - i = p.first; - j = p.second; - m_available_spots.push_back(ij_index); - m_pairs_to_index.erase(p); -} - - -template T binary_heap_upair_queue::get_priority(unsigned i, unsigned j) const { - auto it = m_pairs_to_index.find(std::make_pair(i, j)); - if (it == m_pairs_to_index.end()) - return T(0xFFFFFF); // big number - return m_q.get_priority(it->second); -} - -#ifdef Z3DEBUG -template bool binary_heap_upair_queue::pair_to_index_is_a_bijection() const { - std::set tmp; - for (auto p : m_pairs_to_index) { - unsigned j = p.second; - unsigned size = tmp.size(); - tmp.insert(j); - if (tmp.size() == size) - return false; - } - return true; -} - -template bool binary_heap_upair_queue::available_spots_are_correct() const { - std::set tmp; - for (auto p : m_available_spots){ - tmp.insert(p); - } - if (tmp.size() != m_available_spots.size()) - return false; - for (auto it : m_pairs_to_index) - if (tmp.find(it.second) != tmp.end()) - return false; - return true; -} -#endif -} diff --git a/src/math/lp/conversion_helper.h b/src/math/lp/conversion_helper.h deleted file mode 100644 index ba8b6a983..000000000 --- a/src/math/lp/conversion_helper.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -namespace lp { -template -struct conversion_helper { - static V get_lower_bound(const column_info & ci) { - return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); - } - - static V get_upper_bound(const column_info & ci) { - return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0); - } -}; - - -} diff --git a/src/math/lp/indexer_of_constraints.h b/src/math/lp/indexer_of_constraints.h deleted file mode 100644 index 9bda22bc1..000000000 --- a/src/math/lp/indexer_of_constraints.h +++ /dev/null @@ -1,45 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - Nikolaj Bjorner (nbjorner) - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { - -class indexer_of_constraints { - binary_heap_priority_queue m_queue_of_released_indices; - unsigned m_max; -public: - indexer_of_constraints() :m_max(0) {} - unsigned get_new_index() { - unsigned ret; - if (m_queue_of_released_indices.is_empty()) { - ret = m_max++; - } - else { - ret = m_queue_of_released_indices.dequeue(); - } - return ret; - }; - void release_index(unsigned i) { - m_queue_of_released_indices.enqueue(i, i); - }; - unsigned max() const { return m_max; } -}; -} diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 966e2f0d7..59929e34f 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -12,7 +12,6 @@ Author: #include "math/lp/lp_core_solver_base.h" #include #include "math/lp/indexed_vector.h" -#include "math/lp/binary_heap_priority_queue.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" #include "math/lp/lar_solution_signature.h" diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index a321632fb..5c77c679a 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -37,7 +37,6 @@ #include "math/lp/stacked_vector.h" #include "math/lp/implied_bound.h" #include "math/lp/bound_analyzer_on_row.h" -#include "math/lp/conversion_helper.h" #include "math/lp/int_solver.h" #include "math/lp/nra_solver.h" #include "math/lp/lp_types.h" diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 1719f9c55..628777f33 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -32,7 +32,6 @@ Revision History: #include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/binary_heap_priority_queue.h" #include "math/lp/u_set.h" namespace lp { diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index 762b85d63..be4b7335c 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -24,31 +24,8 @@ Revision History: template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); -template void lp::permutation_matrix::apply_from_right(vector&); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int); template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix >::apply_from_right(vector&); -template bool lp::permutation_matrix >::is_identity() const; -template void lp::permutation_matrix >::multiply_by_permutation_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_from_right(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_reverse_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_reverse_from_right(lp::permutation_matrix >&); template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); -template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); -template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index 35a58906a..a3fff4f7f 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -25,20 +25,16 @@ Revision History: #include "math/lp/indexed_vector.h" #include "math/lp/lp_settings.h" #include "math/lp/matrix.h" -#include "math/lp/tail_matrix.h" namespace lp { -#ifdef Z3DEBUG - inline bool is_even(int k) { return (k/2)*2 == k; } -#endif - template -class permutation_matrix : public tail_matrix { +template +class permutation_matrix +#ifdef Z3DEBUG + : public matrix +#endif +{ vector m_permutation; vector m_rev; - vector m_work_array; - vector m_T_buffer; - vector m_X_buffer; - class ref { permutation_matrix & m_p; @@ -63,42 +59,15 @@ class permutation_matrix : public tail_matrix { // create a unit permutation of the given length void init(unsigned length); unsigned get_rev(unsigned i) { return m_rev[i]; } - bool is_dense() const override { return false; } -#ifdef Z3DEBUG - permutation_matrix get_inverse() const { - return permutation_matrix(size(), m_rev); - } + +#ifdef Z3DEBUG void print(std::ostream & out) const; #endif ref operator[](unsigned i) { return ref(*this, i); } unsigned operator[](unsigned i) const { return m_permutation[i]; } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void apply_from_right(vector & w) override; - - void apply_from_right(indexed_vector & w) override; - template - void copy_aside(vector & t, vector & tmp_index, indexed_vector & w); - - template - void clear_data(indexed_vector & w); - - template - void apply_reverse_from_left(indexed_vector & w); - - void apply_reverse_from_left_to_T(vector & w); - void apply_reverse_from_left_to_X(vector & w); - - void apply_reverse_from_right_to_T(vector & w); - void apply_reverse_from_right_to_T(indexed_vector & w); - void apply_reverse_from_right_to_X(vector & w); - void set_val(unsigned i, unsigned pi) { lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } @@ -116,18 +85,6 @@ class permutation_matrix : public tail_matrix { void set_number_of_rows(unsigned /*m*/) override { } void set_number_of_columns(unsigned /*n*/) override { } #endif - void multiply_by_permutation_from_left(permutation_matrix & p); - - // this is multiplication in the matrix sense - void multiply_by_permutation_from_right(permutation_matrix & p); - - void multiply_by_reverse_from_right(permutation_matrix & q); - - void multiply_by_permutation_reverse_from_left(permutation_matrix & r); - - void shrink_by_one_identity(); - - bool is_identity() const; unsigned size() const { return static_cast(m_rev.size()); } @@ -135,8 +92,6 @@ class permutation_matrix : public tail_matrix { unsigned old_size = m_permutation.size(); m_permutation.resize(size); m_rev.resize(size); - m_T_buffer.resize(size); - m_X_buffer.resize(size); for (unsigned i = old_size; i < size; i++) { m_permutation[i] = m_rev[i] = i; } diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index b6f9924ff..c86fef4f4 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -22,13 +22,13 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix.h" namespace lp { -template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3 m_permutation[i] = m_rev[i] = i; } } -template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { set_val(i, values[i]); } @@ -37,8 +37,6 @@ template permutation_matrix::permutation_matrix(u template void permutation_matrix::init(unsigned length) { m_permutation.resize(length); m_rev.resize(length); - m_T_buffer.resize(length); - m_X_buffer.resize(length); for (unsigned i = 0; i < length; i++) { m_permutation[i] = m_rev[i] = i; } @@ -59,211 +57,6 @@ template void permutation_matrix::print(std::ostr } #endif -template -void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // L * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); -#endif - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_from_left_to_T(indexed_vector & w, lp_settings & ) { - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - copy_aside(t, tmp_index, w); // todo: is it too much copying - clear_data(w); - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_rev[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -} - -template void permutation_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_right(deb_w); -#endif - lp_assert(m_T_buffer.size() == w.size()); - for (unsigned i = 0; i < size(); i++) { - m_T_buffer[i] = w[m_rev[i]]; - } - - for (unsigned i = 0; i < size(); i++) { - w[i] = m_T_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template void permutation_matrix::apply_from_right(indexed_vector & w) { -#ifdef Z3DEBUG - vector wcopy(w.m_data); - apply_from_right(wcopy); -#endif - vector buffer(w.m_index.size()); - vector index_copy(w.m_index); - for (unsigned i = 0; i < w.m_index.size(); i++) { - buffer[i] = w.m_data[w.m_index[i]]; - } - w.clear(); - - for (unsigned i = 0; i < index_copy.size(); i++) { - unsigned j = index_copy[i]; - unsigned pj = m_permutation[j]; - w.set_value(buffer[i], pj); - } - -#ifdef Z3DEBUG - lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} - - -template template -void permutation_matrix::copy_aside(vector & t, vector & tmp_index, indexed_vector & w) { - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - t[i] = w[j]; // copy aside all non-zeroes - tmp_index[i] = j; // and the indices too - } -} - -template template -void permutation_matrix::clear_data(indexed_vector & w) { - // clear old non-zeroes - for (unsigned i = static_cast(w.m_index.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - w[j] = zero_of_type(); - } -} - -template template -void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { - // the result will be w = p(-1) * w -#ifdef Z3DEBUG - // dense_matrix deb(get_reverse()); - // L * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_left(deb_w); -#endif - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - - copy_aside(t, tmp_index, w); - clear_data(w); - - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_permutation[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} -template -void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w) { - // the result will be w = w * p(-1) -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_reverse_from_right_to_T(wcopy); -#endif - vector tmp; - vector tmp_index(w.m_index); - for (auto i : w.m_index) { - tmp.push_back(w[i]); - } - w.clear(); - - for (unsigned k = 0; k < tmp_index.size(); k++) { - unsigned j = tmp_index[k]; - w.set_value(tmp[k], m_rev[j]); - } - - -} - - -template -void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this @@ -283,55 +76,5 @@ template void permutation_matrix::transpose_from_ set_val(j, pi); } -template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -// this is multiplication in the matrix sense -template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) - set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - -} - -template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? - lp_assert(q.size() == size()); - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -template void permutation_matrix::multiply_by_permutation_reverse_from_left(permutation_matrix & r){ // todo : condensed permutations? - // the result is this = r(-1)*this - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[r.m_rev[i]]); - } -} - - -template bool permutation_matrix::is_identity() const { - unsigned i = size(); - while (i-- > 0) { - if (m_permutation[i] != i) { - return false; - } - } - return true; -} - } diff --git a/src/math/lp/tail_matrix.h b/src/math/lp/tail_matrix.h deleted file mode 100644 index 9fa1a4a47..000000000 --- a/src/math/lp/tail_matrix.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/matrix.h" -#include "math/lp/lp_settings.h" -// These matrices appear at the end of the list - -namespace lp { -template -class tail_matrix -#ifdef Z3DEBUG - : public matrix -#endif -{ -public: - virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; - virtual void apply_from_left(vector & w, lp_settings & settings) = 0; - virtual void apply_from_right(vector & w) = 0; - virtual void apply_from_right(indexed_vector & w) = 0; - virtual ~tail_matrix() = default; - virtual bool is_dense() const = 0; - struct ref_row { - const tail_matrix & m_A; - unsigned m_row; - ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} - T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} - }; - ref_row operator[](unsigned i) const { return ref_row(*this, i);} -}; -} diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 24aa8767a..446ead415 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -34,13 +34,11 @@ #include #include "math/lp/lp_utils.h" #include "test/lp/smt_reader.h" -#include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" #include "test/lp/test_file_reader.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_upair_queue.h" #include "util/stacked_value.h" #include "math/lp/u_set.h" #include "util/stopwatch.h" @@ -458,72 +456,7 @@ void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, uns -void test_upair_queue() { - int n = 10; - binary_heap_upair_queue q(2); - std::unordered_map m; - for (int k = 0; k < 100; k++) { - int i = my_random()%n; - int j = my_random()%n; - q.enqueue(i, j, my_random()%n); - } - q.remove(5, 5); - - while (!q.is_empty()) { - unsigned i, j; - q.dequeue(i, j); - } -} - -void test_binary_priority_queue() { - std::cout << "testing binary_heap_priority_queue..."; - auto q = binary_heap_priority_queue(10); - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 25); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 30); - q.enqueue(6, 6); - q.enqueue(0, 0); - q.enqueue(5, 5); - q.enqueue(7, 7); - - for (unsigned i = 0; i < 10; i++) { - unsigned de = q.dequeue(); - lp_assert(i == de); - std::cout << de << std::endl; - } - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 5); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 2); - q.enqueue(0, 1); - q.enqueue(6, 6); - q.enqueue(7, 7); - q.enqueue(33, 1000); - q.enqueue(20, 0); - q.dequeue(); - q.remove(33); - q.enqueue(0, 0); -#ifdef Z3DEBUG - unsigned t = 0; - while (q.size() > 0) { - unsigned d =q.dequeue(); - lp_assert(t++ == d); - std::cout << d << std::endl; - } -#endif - test_upair_queue(); - std::cout << " done" << std::endl; -} int get_random_rows() { From 1fb24ebc3583735c764ccaa38093536d8fa2a8df Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 7 Mar 2023 17:54:34 -0800 Subject: [PATCH 119/220] fix lp_tst --- src/test/lp/lp.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 446ead415..8ad73f0c0 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -693,7 +693,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); parser.add_option_with_help_string("--lar", "test lar_solver"); parser.add_option_with_after_string_with_help("--maxng", "max iterations without progress"); - parser.add_option_with_help_string("-tbq", "test binary queue"); parser.add_option_with_help_string("--randomize_lar", "test randomize functionality"); parser.add_option_with_help_string("--smap", "test stacked_map"); parser.add_option_with_help_string("--term", "simple term test"); @@ -1962,13 +1961,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - - if (args_parser.option_is_used("-tbq")) { - test_binary_priority_queue(); - ret = 0; - return finalize(ret); - } - return finalize(0); // has_violations() ? 1 : 0); } } From 3efe91c3e323cc3e0ce2d48fd04ed57972b84c29 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 8 Mar 2023 08:22:25 -0800 Subject: [PATCH 120/220] more dead code --- src/math/lp/lar_core_solver.h | 1 - src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lar_solution_signature.h | 28 ----------------- src/math/lp/lar_solver.cpp | 45 --------------------------- src/math/lp/lar_solver.h | 12 ++----- src/math/lp/lp_core_solver_base.cpp | 4 --- src/math/lp/lp_core_solver_base.h | 5 --- src/math/lp/lp_core_solver_base_def.h | 44 -------------------------- src/math/lp/lp_settings.h | 2 -- 9 files changed, 2 insertions(+), 140 deletions(-) delete mode 100644 src/math/lp/lar_solution_signature.h diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 59929e34f..4aa62f0df 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -14,7 +14,6 @@ Author: #include "math/lp/indexed_vector.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" -#include "math/lp/lar_solution_signature.h" #include "util/stacked_value.h" namespace lp { diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 2e205291e..e22d77c1b 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -14,7 +14,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/lar_core_solver.h" -#include "math/lp/lar_solution_signature.h" namespace lp { lar_core_solver::lar_core_solver( lp_settings & settings, diff --git a/src/math/lp/lar_solution_signature.h b/src/math/lp/lar_solution_signature.h deleted file mode 100644 index 5b8bfae48..000000000 --- a/src/math/lp/lar_solution_signature.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_settings.h" -#include -namespace lp { -typedef std::unordered_map lar_solution_signature; -} diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 952f2ad43..1c9f5461b 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -221,9 +221,6 @@ namespace lp { evidence.add_pair(ul.lower_bound_witness(), -numeric_traits::one()); } - - unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } - void lar_solver::push() { m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); @@ -761,20 +758,6 @@ namespace lp { return r; } - bool lar_solver::x_is_correct() const { - if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { - return false; - } - for (unsigned i = 0; i < A_r().row_count(); i++) { - numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); - if (!delta.is_zero()) { - return false; - } - } - return true;; - - } - bool lar_solver::var_is_registered(var_index vj) const { if (tv::is_term(vj)) { return tv::unmask_term(vj) < m_terms.size(); @@ -824,28 +807,6 @@ namespace lp { return false; // it is unreachable } - bool lar_solver::the_relations_are_of_same_type(const vector>& evidence, lconstraint_kind& the_kind_of_sum) const { - unsigned n_of_G = 0, n_of_L = 0; - bool strict = false; - for (auto& it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - lconstraint_kind kind = coeff.is_pos() ? - m_constraints[con_ind].kind() : - flip_kind(m_constraints[con_ind].kind()); - if (kind == GT || kind == LT) - strict = true; - if (kind == GE || kind == GT) - n_of_G++; - else if (kind == LE || kind == LT) - n_of_L++; - } - the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); - if (strict) - the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); - - return n_of_G == 0 || n_of_L == 0; - } void lar_solver::register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a) { for (auto& it : cn.coeffs()) { @@ -1227,12 +1188,6 @@ namespace lp { insert_row_with_changed_bounds(r.var()); } - - - void lar_solver::pivot_fixed_vars_from_basis() { - m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); - } - void lar_solver::pop() { pop(1); } diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 5c77c679a..955710b3c 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -212,11 +212,9 @@ class lar_solver : public column_namer { void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - bool x_is_correct() const; bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; - bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; @@ -229,7 +227,6 @@ class lar_solver : public column_namer { int inf_sign) const; mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); - void pivot_fixed_vars_from_basis(); bool column_represents_row_in_tableau(unsigned j); void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); void remove_last_row_and_column_from_tableau(unsigned j); @@ -264,10 +261,7 @@ public: return m_fixed_var_table_int; } - map, default_eq>& fixed_var_table_int() { - return m_fixed_var_table_int; - } - + const map, default_eq>& fixed_var_table_real() const { return m_fixed_var_table_real; } @@ -293,9 +287,7 @@ public: inline void set_column_value_test(unsigned j, const impq& v) { set_column_value(j, v); } - - unsigned get_total_iterations() const; - + var_index add_named_var(unsigned ext_j, bool is_integer, const std::string&); lp_status maximize_term(unsigned j_or_term, impq &term_max); diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index ccd6bf419..61eae344f 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,9 +23,6 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; - template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; @@ -61,7 +58,6 @@ template std::string lp::lp_core_solver_base template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; -template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 9fb4d4333..2e751330b 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -383,11 +383,6 @@ public: } - - - non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - - void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool remove_from_basis(unsigned j, const impq&); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 2d7296567..a1255ade3 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -423,29 +423,6 @@ column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template non_basic_column_value_position lp_core_solver_base:: -get_non_basic_column_value_position(unsigned j) const { - switch (m_column_types[j]) { - case column_type::fixed: - return x_is_at_lower_bound(j)? at_fixed : not_at_bound; - case column_type::free_column: - return free_of_bounds; - case column_type::boxed: - return x_is_at_lower_bound(j)? at_lower_bound :( - x_is_at_upper_bound(j)? at_upper_bound: - not_at_bound - ); - case column_type::lower_bound: - return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; - case column_type::upper_bound: - return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; - default: - lp_unreachable(); - } - lp_unreachable(); - return at_lower_bound; -} - template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); @@ -463,27 +440,6 @@ template bool lp_core_solver_base::pivot_column_g return true; } -template void lp_core_solver_base::pivot_fixed_vars_from_basis() { - // run over basis and non-basis at the same time - indexed_vector w(m_basis.size()); // the buffer - unsigned i = 0; // points to basis - for (; i < m_basis.size(); i++) { - unsigned basic_j = m_basis[i]; - - if (get_column_type(basic_j) != column_type::fixed) continue; - T a; - unsigned j; - for (auto &c : m_A.m_rows[i]) { - j = c.var(); - if (j == basic_j) - continue; - if (get_column_type(j) != column_type::fixed) { - if (pivot_column_general(j, basic_j, w)) - break; - } - } - } -} template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { indexed_vector w(m_basis.size()); // the buffer diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 314930fc1..9a38fd582 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -90,8 +90,6 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) { lp_status lp_status_from_string(std::string status); -enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; - class lp_resource_limit { public: From 8b0aa22631bb49dac2fd550e783e32aa50ddb310 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 8 Mar 2023 09:27:09 -0800 Subject: [PATCH 121/220] replace lp_assert(false) with UNREACHABLE --- src/math/lp/core_solver_pretty_printer_def.h | 2 +- src/math/lp/general_matrix.h | 22 +- src/math/lp/lar_constraints.h | 2 +- src/math/lp/lar_core_solver.h | 4 +- src/math/lp/lar_solver.cpp | 16 +- src/math/lp/lar_term.h | 2 +- src/math/lp/lia_move.h | 2 +- src/math/lp/lp_core_solver_base.cpp | 3 - src/math/lp/lp_core_solver_base.h | 81 +- src/math/lp/lp_core_solver_base_def.h | 40 +- src/math/lp/lp_primal_core_solver.h | 1226 ++++++++--------- src/math/lp/lp_primal_core_solver_def.h | 76 +- .../lp/lp_primal_core_solver_tableau_def.h | 4 +- src/math/lp/lp_settings_def.h | 6 +- src/math/lp/lp_utils.h | 1 - src/math/lp/nra_solver.cpp | 2 +- src/math/lp/numeric_pair.h | 12 +- src/math/lp/static_matrix.cpp | 2 - src/math/lp/static_matrix.h | 4 +- src/math/lp/static_matrix_def.h | 8 - src/test/lp/gomory_test.h | 2 +- src/test/lp/lp.cpp | 26 +- src/test/lp/smt_reader.h | 2 +- 23 files changed, 654 insertions(+), 891 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 931333ee7..27aa6c75d 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -139,7 +139,7 @@ template void core_solver_pretty_printer::adjust_ case column_type::free_column: break; default: - lp_assert(false); + UNREACHABLE(); break; } } diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index 749fa30dc..a4f6693a2 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -114,9 +114,6 @@ public: } } - void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { - lp_assert(false); // not implemented - } general_matrix operator*(const general_matrix & m) const { lp_assert(m.row_count() == column_count()); general_matrix ret(row_count(), m.column_count()); @@ -172,24 +169,7 @@ public: return r; } - // bool create_upper_triangle(general_matrix& m, vector& x) { - // for (unsigned i = 1; i < m.row_count(); i++) { - // lp_assert(false); // to be continued - // } - // } - - // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { - // auto m_copy = m; - // // for square matrices - // lp_assert(row_count() == b.size()); - // lp_assert(x.size() == column_count()); - // lp_assert(row_count() == column_count()); - // x = b; - // create_upper_triangle(copy_of_m, x); - // solve_on_triangle(copy_of_m, x); - // } - // - + void transpose_rows(unsigned i, unsigned l) { lp_assert(i != l); m_row_permutation.transpose_from_right(i, l); diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 8e6311683..f8cffbe57 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -44,7 +44,7 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) { case EQ: return std::string("="); case NE: return std::string("!="); } - lp_unreachable(); + UNREACHABLE(); return std::string(); // it is unreachable } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 4aa62f0df..06ef4d50b 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -159,7 +159,7 @@ public: case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } @@ -174,7 +174,7 @@ public: case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 1c9f5461b..9fab5e437 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -466,7 +466,7 @@ namespace lp { default: - lp_unreachable(); // wrong mode + UNREACHABLE(); // wrong mode } return false; } @@ -802,7 +802,7 @@ namespace lp { case GT: return left_side_val > constr.rhs(); case EQ: return left_side_val == constr.rhs(); default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } @@ -857,7 +857,7 @@ namespace lp { case EQ: lp_assert(rs != zero_of_type()); break; default: - lp_assert(false); + UNREACHABLE(); return false; } #endif @@ -1816,7 +1816,7 @@ namespace lp { adjust_initial_state_for_tableau_rows(); break; case simplex_strategy_enum::tableau_costs: - lp_assert(false); // not implemented + UNREACHABLE(); // not implemented case simplex_strategy_enum::undecided: adjust_initial_state_for_tableau_rows(); break; @@ -1905,7 +1905,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } if (m_mpq_lar_core_solver.m_r_upper_bounds[j] == m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; @@ -1959,7 +1959,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } @@ -2009,7 +2009,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { @@ -2050,7 +2050,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 3ef424e24..fc73f949f 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -179,7 +179,7 @@ public: return p.coeff().is_one(); } } - lp_unreachable(); + UNREACHABLE(); return false; } diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index 65da5826e..ca61d7b7a 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -45,7 +45,7 @@ inline std::string lia_move_to_string(lia_move m) { case lia_move::unsat: return "unsat"; default: - lp_assert(false); + UNREACHABLE(); }; return "strange"; } diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 61eae344f..9a4c375f6 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -27,7 +27,6 @@ template bool lp::lp_core_solver_base >::prin template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -61,7 +60,6 @@ template bool lp::lp_core_solver_base >::calc template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; -template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); @@ -70,6 +68,5 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); -template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 2e751330b..fc1673242 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -173,8 +173,6 @@ public: void set_total_iterations(unsigned s) { m_total_iterations = s; } - void set_non_basic_x_to_correct_bounds(); - bool at_bound(const X &x, const X & bound) const { return !below_bound(x, bound) && !above_bound(x, bound); } @@ -300,45 +298,6 @@ public: std::string column_name(unsigned column) const; - bool snap_non_basic_x_to_bound() { - bool ret = false; - for (unsigned j : non_basis()) - ret = snap_column_to_bound(j) || ret; - return ret; - } - - bool snap_column_to_bound(unsigned j) { - switch (m_column_types[j]) { - case column_type::fixed: - if (x_is_at_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::boxed: - if (x_is_at_bound(j)) - break; // we should preserve x if possible - // snap randomly - if (m_settings.random_next() % 2 == 1) - m_x[j] = m_lower_bounds[j]; - else - m_x[j] = m_upper_bounds[j]; - return true; - case column_type::lower_bound: - if (x_is_at_lower_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::upper_bound: - if (x_is_at_upper_bound(j)) - break; - m_x[j] = m_upper_bounds[j]; - return true; - default: - break; - } - return false; - } - bool make_column_feasible(unsigned j, numeric_pair & delta) { bool ret = false; lp_assert(m_basis_heading[j] < 0); @@ -384,7 +343,6 @@ public: } bool remove_from_basis(unsigned j); - bool remove_from_basis(unsigned j, const impq&); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); void init_basic_part_of_basis_heading() { unsigned m = m_basis.size(); @@ -456,31 +414,6 @@ public: change_basis_unconditionally(leaving, entering); } - bool non_basic_column_is_set_correctly(unsigned j) const { - if (j >= this->m_n()) - return false; - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (!this->x_is_at_bound(j)) - return false; - break; - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) - return false; - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) - return false; - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } - return true; - } bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { @@ -540,13 +473,11 @@ public: out << "[-oo, oo]"; break; default: - lp_assert(false); + UNREACHABLE(); } return out << "\n"; } - bool column_is_free(unsigned j) const { return this->m_column_types[j] == column_type::free_column; } - bool column_is_fixed(unsigned j) const { return this->m_column_types[j] == column_type::fixed; } @@ -579,16 +510,6 @@ public: } } - // only check for basic columns - bool calc_current_x_is_feasible() const { - unsigned i = this->m_m(); - while (i--) { - if (!column_is_feasible(m_basis[i])) - return false; - } - return true; - } - void transpose_rows_tableau(unsigned i, unsigned ii); void pivot_to_reduced_costs_tableau(unsigned i, unsigned j); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index a1255ade3..cb0084a16 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -166,26 +166,6 @@ print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream return time_is_over(); } -template void lp_core_solver_base:: -set_non_basic_x_to_correct_bounds() { - for (unsigned j : non_basis()) { - switch (m_column_types[j]) { - case column_type::boxed: - m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; - break; - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - default: - break; - } - } -} template bool lp_core_solver_base:: column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { @@ -201,9 +181,9 @@ column_is_dual_feasible(unsigned j) const { case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: - lp_unreachable(); + UNREACHABLE(); } - lp_unreachable(); + UNREACHABLE(); return false; } template bool lp_core_solver_base:: @@ -257,7 +237,7 @@ template bool lp_core_solver_base::column_is_feas return true; break; default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } @@ -453,18 +433,6 @@ template bool lp_core_solver_base::remove_from_ba return false; } -template bool lp_core_solver_base::remove_from_basis(unsigned basic_j, const impq& val) { - indexed_vector w(m_basis.size()); // the buffer - unsigned i = m_basis_heading[basic_j]; - for (auto &c : m_A.m_rows[i]) { - if (c.var() == basic_j) - continue; - if (pivot_column_general(c.var(), basic_j, w)) - return true; - } - return false; -} - template bool lp_core_solver_base::infeasibility_costs_are_correct() const { @@ -513,7 +481,7 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) case column_type::free_column: return is_zero(this->m_costs[j]); default: - lp_assert(false); + UNREACHABLE(); return true; } } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 628777f33..641f6be21 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -19,704 +19,680 @@ Revision History: --*/ #pragma once -#include -#include -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" +#include "math/lp/static_matrix.h" #include "math/lp/u_set.h" +#include "util/vector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace lp { -// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) -// The right side b is given implicitly by x and the basis +// This core solver solves (Ax=b, lower_bound_values \leq x \leq +// upper_bound_values, maximize costs*x ) The right side b is given implicitly +// by x and the basis template -class lp_primal_core_solver:public lp_core_solver_base { +class lp_primal_core_solver : public lp_core_solver_base { public: - int m_sign_of_entering_delta; - vector m_costs_backup; - unsigned m_inf_row_index_for_tableau; - bool m_bland_mode_tableau; - u_set m_left_basis_tableau; - unsigned m_bland_mode_threshold; - unsigned m_left_basis_repeated; - vector m_leaving_candidates; - - std::list m_non_basis_list; - void sort_non_basis(); - int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); - int choose_entering_column_tableau(); - int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - + int m_sign_of_entering_delta; + vector m_costs_backup; + unsigned m_inf_row_index_for_tableau; + bool m_bland_mode_tableau; + u_set m_left_basis_tableau; + unsigned m_bland_mode_threshold; + unsigned m_left_basis_repeated; + vector m_leaving_candidates; - bool needs_to_grow(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return false; - case column_type::fixed: - case column_type::lower_bound: - case column_type::boxed: - return this-> x_below_low_bound(bj); - default: - return false; - } - lp_assert(false); // unreachable - return false; + std::list m_non_basis_list; + void sort_non_basis(); + int choose_entering_column_tableau(); + + bool needs_to_grow(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return false; + case column_type::fixed: + case column_type::lower_bound: + case column_type::boxed: + return this->x_below_low_bound(bj); + default: + return false; } + UNREACHABLE(); // unreachable + return false; + } - int inf_sign_of_column(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return 0; - case column_type::lower_bound: - return 1; - case column_type::fixed: - case column_type::boxed: - return this->x_above_upper_bound(bj)? -1: 1; - default: - return -1; - } - lp_assert(false); // unreachable - return 0; - + int inf_sign_of_column(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return 0; + case column_type::lower_bound: + return 1; + case column_type::fixed: + case column_type::boxed: + return this->x_above_upper_bound(bj) ? -1 : 1; + default: + return -1; } - + UNREACHABLE(); // unreachable + return 0; + } - bool monoid_can_decrease(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } + bool monoid_can_decrease(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return true; - case column_type::upper_bound: - if (is_pos(rc.coeff())) { - return true; - } + return true; + case column_type::upper_bound: + if (is_pos(rc.coeff())) { + return true; + } - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; + return this->x_below_upper_bound(j); + default: + return false; } + UNREACHABLE(); // unreachable + return false; + } - bool monoid_can_increase(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } + bool monoid_can_increase(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return true; - case column_type::upper_bound: - if (is_neg(rc.coeff())) { - return true; - } + return true; + case column_type::upper_bound: + if (is_neg(rc.coeff())) { + return true; + } - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; + return this->x_below_upper_bound(j); + default: + return false; } + UNREACHABLE(); // unreachable + return false; + } - unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo - unsigned r = 0; - for (const auto & cc : this->m_A.m_columns[j]) { - unsigned k = this->m_basis[cc.var()]; - if (this->m_column_types[k] != column_type::free_column) - r++; - } - return r; + unsigned get_number_of_basic_vars_that_might_become_inf( + unsigned j) const { // consider looking at the signs here: todo + unsigned r = 0; + for (const auto &cc : this->m_A.m_columns[j]) { + unsigned k = this->m_basis[cc.var()]; + if (this->m_column_types[k] != column_type::free_column) + r++; } + return r; + } - - int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) { - int j = -1; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (const row_cell& rc : this->m_A.m_rows[i]) { - if (rc.var() == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - if (rc.var() < static_cast(j) ) { - j = rc.var(); - a_ent = rc.coeff(); - } - } - if (j == -1) { - m_inf_row_index_for_tableau = i; - } - - return j; - } - - int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) { - if (m_bland_mode_tableau) - return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); - // a short row produces short infeasibility explanation and benefits at least one pivot operation - int choice = -1; - int nchoices = 0; - unsigned num_of_non_free_basics = 1000000; - unsigned len = 100000000; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { - const row_cell& rc = this->m_A.m_rows[i][k]; - unsigned j = rc.var(); - if (j == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); - if (damage < num_of_non_free_basics) { - num_of_non_free_basics = damage; - len = this->m_A.m_columns[j].size(); - choice = k; - nchoices = 1; - } else if (damage == num_of_non_free_basics && - this->m_A.m_columns[j].size() <= len && (this->m_settings.random_next() % (++nchoices))) { - choice = k; - len = this->m_A.m_columns[j].size(); - } - } - - - if (choice == -1) { - m_inf_row_index_for_tableau = i; - return -1; - } - const row_cell& rc = this->m_A.m_rows[i][choice]; + int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T &a_ent) { + int j = -1; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (const row_cell &rc : this->m_A.m_rows[i]) { + if (rc.var() == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + if (rc.var() < static_cast(j)) { + j = rc.var(); a_ent = rc.coeff(); - return rc.var(); + } } - - bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); - bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t_tableau(unsigned entering, X & t); - - void limit_theta(const X & lim, X & theta, bool & unlimited) { - if (unlimited) { - theta = lim; - unlimited = false; - } else { - theta = std::min(lim, theta); - } + if (j == -1) { + m_inf_row_index_for_tableau = i; } - void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); + return j; + } + + int find_beneficial_column_in_row_tableau_rows(int i, T &a_ent) { + if (m_bland_mode_tableau) + return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); + // a short row produces short infeasibility explanation and benefits at + // least one pivot operation + int choice = -1; + int nchoices = 0; + unsigned num_of_non_free_basics = 1000000; + unsigned len = 100000000; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { + const row_cell &rc = this->m_A.m_rows[i][k]; + unsigned j = rc.var(); + if (j == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); + if (damage < num_of_non_free_basics) { + num_of_non_free_basics = damage; + len = this->m_A.m_columns[j].size(); + choice = k; + nchoices = 1; + } else if (damage == num_of_non_free_basics && + this->m_A.m_columns[j].size() <= len && + (this->m_settings.random_next() % (++nchoices))) { + choice = k; + len = this->m_A.m_columns[j].size(); + } } - - void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); + if (choice == -1) { + m_inf_row_index_for_tableau = i; + return -1; } + const row_cell &rc = this->m_A.m_rows[i][choice]; + a_ent = rc.coeff(); + return rc.var(); + } + bool try_jump_to_another_bound_on_entering(unsigned entering, const X &theta, + X &t, bool &unlimited); + bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X &t); + int find_leaving_and_t_tableau(unsigned entering, X &t); - void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); + void limit_theta(const X &lim, X &theta, bool &unlimited) { + if (unlimited) { + theta = lim; + unlimited = false; + } else { + theta = std::min(lim, theta); } + } - void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); - }; + void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], + theta, unlimited); + } - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); + void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, + unlimited); + } - X get_max_bound(vector & b); + void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], + theta, unlimited); + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, + unlimited); + }; + + void get_bound_on_variable_and_update_leaving_precisely( + unsigned j, vector &leavings, T m, X &t, + T &abs_of_d_of_leaving); + + X get_max_bound(vector &b); #ifdef Z3DEBUG - void check_Ax_equal_b(); - void check_the_bounds(); - void check_bound(unsigned i); - void check_correctness(); + void check_Ax_equal_b(); + void check_the_bounds(); + void check_bound(unsigned i); + void check_correctness(); #endif - // from page 183 of Istvan Maros's book - // the basis structures have not changed yet - void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); + // from page 183 of Istvan Maros's book + // the basis structures have not changed yet + void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); - // return 0 if the reduced cost at entering is close enough to the refreshed - // 1 if it is way off, and 2 if it is unprofitable - int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); + // return 0 if the reduced cost at entering is close enough to the refreshed + // 1 if it is way off, and 2 if it is unprofitable + int refresh_reduced_cost_at_entering_and_check_that_it_is_off( + unsigned entering); - void backup_and_normalize_costs(); + void backup_and_normalize_costs(); - void advance_on_entering_and_leaving(int entering, int leaving, X & t); - void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); - void advance_on_entering_equal_leaving(int entering, X & t); - void advance_on_entering_equal_leaving_tableau(int entering, X & t); + void advance_on_entering_and_leaving_tableau(int entering, int leaving, X &t); + void advance_on_entering_equal_leaving_tableau(int entering, X &t); - bool need_to_switch_costs() const { - if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) - return false; - // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); - return this->current_x_is_feasible() == this->using_infeas_costs(); - } + bool need_to_switch_costs() const { + if (this->m_settings.simplex_strategy() == + simplex_strategy_enum::tableau_rows) + return false; + // lp_assert(calc_current_x_is_feasible() == + // current_x_is_feasible()); + return this->current_x_is_feasible() == this->using_infeas_costs(); + } + void advance_on_entering_tableau(int entering); - void advance_on_entering(int entering); - void advance_on_entering_tableau(int entering); - void advance_on_entering_precise(int entering); - void push_forward_offset_in_non_basis(unsigned & offset_in_nb); + void push_forward_offset_in_non_basis(unsigned &offset_in_nb); - unsigned get_number_of_non_basic_column_to_try_for_enter(); + unsigned get_number_of_non_basic_column_to_try_for_enter(); + // returns the number of iterations + unsigned solve(); - // returns the number of iterations - unsigned solve(); + void find_feasible_solution(); + // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - - void find_feasible_solution(); + void one_iteration_tableau(); - // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - - void one_iteration_tableau(); - - // this version assumes that the leaving already has the right value, and does not update it - void update_x_tableau_rows(unsigned entering, unsigned leaving, const X& delta) { - this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - if (leaving != this->m_basis[c.var()]) { - this->add_delta_to_x_and_track_feasibility(this->m_basis[c.var()], - delta * this->m_A.get_val(c)); - } - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned j = this->m_basis[c.var()]; - if (j != leaving) - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } + // this version assumes that the leaving already has the right value, and does + // not update it + void update_x_tableau_rows(unsigned entering, unsigned leaving, + const X &delta) { + this->add_delta_to_x(entering, delta); + if (!this->using_infeas_costs()) { + for (const auto &c : this->m_A.m_columns[entering]) { + if (leaving != this->m_basis[c.var()]) { + this->add_delta_to_x_and_track_feasibility( + this->m_basis[c.var()], -delta * this->m_A.get_val(c)); } - } - - void update_basis_and_x_tableau_rows(int entering, int leaving, X const & tt) { - lp_assert(entering != leaving); - update_x_tableau_rows(entering, leaving, tt); - this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); - this->change_basis(entering, leaving); - } - - - void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { - update_basis_and_x_tableau_rows(entering, leaving, theta); - this->track_column_feasibility(entering); - } - - int find_smallest_inf_column() { - int j = -1; - for (unsigned k : this->inf_set()) { - if (k < static_cast(j)) { - j = k; - } - } - return j; - } - - const X& get_val_for_leaving(unsigned j) const { - lp_assert(!this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::upper_bound: - return this->m_upper_bounds[j]; - case column_type::lower_bound: - return this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_above_upper_bound(j)) - return this->m_upper_bounds[j]; - else - return this->m_lower_bounds[j]; - break; - default: - UNREACHABLE(); - return this->m_lower_bounds[j]; - } - } - - - void one_iteration_tableau_rows() { - int leaving = find_smallest_inf_column(); - if (leaving == -1) { - this->set_status(lp_status::OPTIMAL); - return; - } - - SASSERT(this->column_is_base(leaving)); - - if (!m_bland_mode_tableau) { - if (m_left_basis_tableau.contains(leaving)) { - if (++m_left_basis_repeated > m_bland_mode_threshold) { - m_bland_mode_tableau = true; - } - } else { - m_left_basis_tableau.insert(leaving); - } - } - T a_ent; - int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); - if (entering == -1) { - this->set_status(lp_status::INFEASIBLE); - return; - } - const X& new_val_for_leaving = get_val_for_leaving(leaving); - X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; - this->m_x[leaving] = new_val_for_leaving; - this->remove_column_from_inf_set(leaving); - advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); - if (this->current_x_is_feasible()) - this->set_status(lp_status::OPTIMAL); - } - - void decide_on_status_when_cannot_find_entering() { - lp_assert(!need_to_switch_costs()); - this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); - } - - void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0); - limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) theta = zero_of_type(); - } - - bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - return true; - } - - bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets larger - lp_assert(m > 0); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } - - return true; - } - - void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets larger - lp_assert(m > 0); - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); } - + } else { // using_infeas_costs() == true + lp_assert(this->column_is_feasible(entering)); + lp_assert(this->m_costs[entering] == zero_of_type()); + // m_d[entering] can change because of the cost change for basic columns. + for (const auto &c : this->m_A.m_columns[entering]) { + unsigned j = this->m_basis[c.var()]; + if (j != leaving) + this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); + update_inf_cost_for_column_tableau(j); + if (is_zero(this->m_costs[j])) + this->remove_column_from_inf_set(j); + else + this->insert_column_into_inf_set(j); + } + } + } + + void update_basis_and_x_tableau_rows(int entering, int leaving, X const &tt) { + lp_assert(entering != leaving); + update_x_tableau_rows(entering, leaving, tt); + this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); + this->change_basis(entering, leaving); + } + + void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, + const X &theta) { + update_basis_and_x_tableau_rows(entering, leaving, theta); + this->track_column_feasibility(entering); + } + + int find_smallest_inf_column() { + int j = -1; + for (unsigned k : this->inf_set()) { + if (k < static_cast(j)) { + j = k; + } + } + return j; + } + + const X &get_val_for_leaving(unsigned j) const { + lp_assert(!this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::fixed: + case column_type::upper_bound: + return this->m_upper_bounds[j]; + case column_type::lower_bound: + return this->m_lower_bounds[j]; + break; + case column_type::boxed: + if (this->x_above_upper_bound(j)) + return this->m_upper_bounds[j]; + else + return this->m_lower_bounds[j]; + break; + default: + UNREACHABLE(); + return this->m_lower_bounds[j]; + } + } + + void one_iteration_tableau_rows() { + int leaving = find_smallest_inf_column(); + if (leaving == -1) { + this->set_status(lp_status::OPTIMAL); + return; } - void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - if (this->above_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); + SASSERT(this->column_is_base(leaving)); + + if (!m_bland_mode_tableau) { + if (m_left_basis_tableau.contains(leaving)) { + if (++m_left_basis_repeated > m_bland_mode_threshold) { + m_bland_mode_tableau = true; } + } else { + m_left_basis_tableau.insert(leaving); + } + } + T a_ent; + int entering = find_beneficial_column_in_row_tableau_rows( + this->m_basis_heading[leaving], a_ent); + if (entering == -1) { + this->set_status(lp_status::INFEASIBLE); + return; + } + const X &new_val_for_leaving = get_val_for_leaving(leaving); + X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; + this->m_x[leaving] = new_val_for_leaving; + this->remove_column_from_inf_set(leaving); + advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); + if (this->current_x_is_feasible()) + this->set_status(lp_status::OPTIMAL); + } + + void decide_on_status_when_cannot_find_entering() { + lp_assert(!need_to_switch_costs()); + this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL + : lp_status::INFEASIBLE); + } + + void limit_theta_on_basis_column_for_feas_case_m_neg_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0); + limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) + theta = zero_of_type(); + } + + bool limit_inf_on_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->below_bound(x, bound)) + return false; + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + return true; + } + + bool limit_inf_on_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->above_bound(x, bound)) + return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; } - void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - const X & x = this->m_x[j]; - const X & lbound = this->m_lower_bounds[j]; + return true; + } - if (this->below_bound(x, lbound)) { - limit_theta((lbound - x) / m, theta, unlimited); + void limit_inf_on_lower_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } + + void limit_inf_on_upper_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + const X &x = this->m_x[j]; + const X &lbound = this->m_lower_bounds[j]; + + if (this->below_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else { + const X &ubound = this->m_upper_bounds[j]; + if (this->below_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else if (!this->above_bound(x, ubound)) { + theta = zero_of_type(); + unlimited = false; + } + } + } + + void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); + const X &x = this->m_x[j]; + const X &ubound = this->m_upper_bounds[j]; + if (this->above_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else { + const X &lbound = this->m_lower_bounds[j]; + if (this->above_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else if (!this->below_bound(x, lbound)) { + theta = zero_of_type(); + unlimited = false; + } + } + } + + void limit_theta_on_basis_column_for_feas_case_m_pos_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0); + limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); + } + } + + // j is a basic column or the entering, in any case x[j] has to stay feasible. + // m is the multiplier. updating t in a way that holds the following + // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) + // or + // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) + void limit_theta_on_basis_column(unsigned j, T m, X &theta, bool &unlimited) { + switch (this->m_column_types[j]) { + case column_type::free_column: + break; + case column_type::upper_bound: + if (this->current_x_is_feasible()) { + if (m > 0) + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); + } else { // inside of feasibility_loop + if (m > 0) + limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + j, m, theta, unlimited); + } + break; + case column_type::lower_bound: + if (this->current_x_is_feasible()) { + if (m < 0) + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); + } else { + if (m < 0) + limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + j, m, theta, unlimited); + } + break; + // case fixed: + // if (get_this->current_x_is_feasible()) { + // theta = zero_of_type(); + // break; + // } + // if (m < 0) + // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, + // theta); + // else + // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, + // theta); + // break; + case column_type::fixed: + case column_type::boxed: + if (this->current_x_is_feasible()) { + if (m > 0) { + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); } else { - const X & ubound = this->m_upper_bounds[j]; - if (this->below_bound(x, ubound)){ - limit_theta((ubound - x) / m, theta, unlimited); - } else if (!this->above_bound(x, ubound)) { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); } - } - - void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & ubound = this->m_upper_bounds[j]; - if (this->above_bound(x, ubound)) { - limit_theta((ubound - x) / m, theta, unlimited); + } else { + if (m > 0) { + limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, + unlimited); } else { - const X & lbound = this->m_lower_bounds[j]; - if (this->above_bound(x, lbound)){ - limit_theta((lbound - x) / m, theta, unlimited); - } else if (!this->below_bound(x, lbound)) { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, + unlimited); } - } - void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0); - if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - unlimited = false; - } - } - } + } - void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(m > 0); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - } + break; + default: + UNREACHABLE(); } - - // j is a basic column or the entering, in any case x[j] has to stay feasible. - // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) - // or - // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) - void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { - switch (this->m_column_types[j]) { - case column_type::free_column: break; - case column_type::upper_bound: - if (this->current_x_is_feasible()) { - if (m > 0) - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { // inside of feasibility_loop - if (m > 0) - limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); - } - break; - case column_type::lower_bound: - if (this->current_x_is_feasible()) { - if (m < 0) - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } else { - if (m < 0) - limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); - } - break; - // case fixed: - // if (get_this->current_x_is_feasible()) { - // theta = zero_of_type(); - // break; - // } - // if (m < 0) - // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta); - // else - // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta); - // break; - case column_type::fixed: - case column_type::boxed: - if (this->current_x_is_feasible()) { - if (m > 0) { - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } - } else { - if (m > 0) { - limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited); - } - } - - break; - default: - lp_unreachable(); - } - if (!unlimited && theta < zero_of_type()) { - theta = zero_of_type(); - } + if (!unlimited && theta < zero_of_type()) { + theta = zero_of_type(); } + } - - bool column_is_benefitial_for_entering_basis(unsigned j) const; - bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - bool can_enter_basis(unsigned j); - void init_infeasibility_costs(); - - void init_infeasibility_cost_for_column(unsigned j); - T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); + bool column_is_benefitial_for_entering_basis(unsigned j) const; + void init_infeasibility_costs(); - void print_column(unsigned j, std::ostream & out); - - void print_bound_info_and_x(unsigned j, std::ostream & out); - - bool basis_column_is_set_correctly(unsigned j) const { - return this->m_A.m_columns[j].size() == 1; - - } - - bool basis_columns_are_set_correctly() const { - for (unsigned j : this->m_basis) - if(!basis_column_is_set_correctly(j)) - return false; - - return this->m_basis_heading.size() == this->m_A.column_count() && this->m_basis.size() == this->m_A.row_count(); - } + void init_infeasibility_cost_for_column(unsigned j); + T get_infeasibility_cost_for_column(unsigned j) const; + void init_infeasibility_costs_for_changed_basis_only(); - void init_run_tableau(); - void update_x_tableau(unsigned entering, const X & delta); - void update_inf_cost_for_column_tableau(unsigned j); + void print_column(unsigned j, std::ostream &out); -// the delta is between the old and the new cost (old - new) - void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { - lp_assert(this->m_basis_heading[j] >= 0); - unsigned i = static_cast(this->m_basis_heading[j]); - for (const row_cell & rc : this->m_A.m_rows[i]) { - unsigned k = rc.var(); - if (k == j) - continue; - this->m_d[k] += delta * rc.coeff(); - } + void print_bound_info_and_x(unsigned j, std::ostream &out); + + bool basis_column_is_set_correctly(unsigned j) const { + return this->m_A.m_columns[j].size() == 1; + } + + bool basis_columns_are_set_correctly() const { + for (unsigned j : this->m_basis) + if (!basis_column_is_set_correctly(j)) + return false; + + return this->m_basis_heading.size() == this->m_A.column_count() && + this->m_basis.size() == this->m_A.row_count(); + } + + void init_run_tableau(); + void update_x_tableau(unsigned entering, const X &delta); + void update_inf_cost_for_column_tableau(unsigned j); + + // the delta is between the old and the new cost (old - new) + void update_reduced_cost_for_basic_column_cost_change(const T &delta, + unsigned j) { + lp_assert(this->m_basis_heading[j] >= 0); + unsigned i = static_cast(this->m_basis_heading[j]); + for (const row_cell &rc : this->m_A.m_rows[i]) { + unsigned k = rc.var(); + if (k == j) + continue; + this->m_d[k] += delta * rc.coeff(); } - - bool update_basis_and_x_tableau(int entering, int leaving, X const & tt); - void init_reduced_costs_tableau(); - void init_tableau_rows() { - m_bland_mode_tableau = false; - m_left_basis_tableau.clear(); - m_left_basis_tableau.resize(this->m_A.column_count()); - m_left_basis_repeated = 0; - } -// stage1 constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & lower_bound_values, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, // b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), + } + + bool update_basis_and_x_tableau(int entering, int leaving, X const &tt); + void init_reduced_costs_tableau(); + void init_tableau_rows() { + m_bland_mode_tableau = false; + m_left_basis_tableau.clear(); + m_left_basis_tableau.resize(this->m_A.column_count()); + m_left_basis_repeated = 0; + } + // stage1 constructor + lp_primal_core_solver( + static_matrix &A, + vector &b, // the right side vector + vector &x, // the number of elements in x needs to be at least as large + // as the number of columns in A + vector &basis, vector &nbasis, vector &heading, + vector &costs, const vector &column_type_array, + const vector &lower_bound_values, const vector &upper_bound_values, + lp_settings &settings, const column_namer &column_names) + : lp_core_solver_base(A, // b, + basis, nbasis, heading, x, costs, settings, + column_names, column_type_array, + lower_bound_values, upper_bound_values), m_bland_mode_threshold(1000) { - this->set_status(lp_status::UNKNOWN); - } + this->set_status(lp_status::UNKNOWN); + } - - - bool initial_x_is_correct() { - std::set basis_set; - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - basis_set.insert(this->m_basis[i]); - } - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { - LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); - return false; - } - - if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) { - LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: " << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl); - return false; - } - - if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_types[j] == column_type::lower_bound) { - if (numeric_traits::zero() != this->m_x[j]) { - LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); - return false; - } - } - if (this->m_column_types[j] == column_type::boxed) { - if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { - return false; - } - } - } - return true; - } - - - friend core_solver_pretty_printer; + friend core_solver_pretty_printer; }; -} +} // namespace lp diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index cc8ad88b3..4ee8305fa 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -53,10 +53,6 @@ void lp_primal_core_solver::sort_non_basis() { template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { - return column_is_benefitial_for_entering_basis_precise(j); -} -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -88,56 +84,12 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis } break; default: - lp_unreachable(); + UNREACHABLE(); break; } return false; } -template -int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } - else { - this->m_basis_sort_counter--; - } - unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - unsigned t = this->m_columns_nz[j]; - if (t < j_nz) { - j_nz = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { - entering_iter = non_basis_iter; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter == m_non_basis_list.end()) - return -1; - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; -} - - -template -int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); -} - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, @@ -278,24 +230,6 @@ template void lp_primal_core_solver::backup_an -template -void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - -} - -template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - -} - - -template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - lp_assert(false); -} - -template void lp_primal_core_solver::advance_on_entering(int entering) { - lp_assert(false); -} - template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { if (++offset_in_nb == this->m_nbasis.size()) offset_in_nb = 0; @@ -377,7 +311,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); break; default: - lp_assert(false); + UNREACHABLE(); ret = numeric_traits::zero(); // does not matter break; } @@ -427,7 +361,7 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - lp_assert(false); + UNREACHABLE(); break; } @@ -458,7 +392,7 @@ template void lp_primal_core_solver::print_column out << "( _" << this->m_x[j] << "_)" << std::endl; break; default: - lp_unreachable(); + UNREACHABLE(); } } @@ -480,7 +414,7 @@ template void lp_primal_core_solver::print_bound_ out << "inf, inf" << std::endl; break; default: - lp_assert(false); + UNREACHABLE(); break; } } diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index c7b604b95..241164c02 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -122,7 +122,7 @@ unsigned lp_primal_core_solver::solve() { } break; case lp_status::TENTATIVE_UNBOUNDED: - lp_assert(false); + UNREACHABLE(); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { @@ -132,7 +132,7 @@ unsigned lp_primal_core_solver::solve() { break; case lp_status::UNSTABLE: - lp_assert(false); + UNREACHABLE(); break; default: diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index a439466d1..a19558949 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -31,7 +31,7 @@ std::string column_type_to_string(column_type t) { case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; - default: lp_unreachable(); + default: UNREACHABLE(); } return "unknown"; // it is unreachable } @@ -50,7 +50,7 @@ const char* lp_status_to_string(lp_status status) { case lp_status::UNSTABLE: return "UNSTABLE"; case lp_status::CANCELLED: return "CANCELLED"; default: - lp_unreachable(); + UNREACHABLE(); } return "UNKNOWN"; // it is unreachable } @@ -63,7 +63,7 @@ lp_status lp_status_from_string(std::string status) { if (status == "FEASIBLE") return lp_status::FEASIBLE; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; - lp_unreachable(); + UNREACHABLE(); return lp_status::UNKNOWN; // it is unreachable } diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index ad5ba380d..3c1383cb3 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -141,7 +141,6 @@ inline void throw_exception(std::string && str) { typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } -inline void lp_unreachable() { lp_assert(false); } template inline X zero_of_type() { return numeric_traits::zero(); } template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 56a84d1f0..1f4e0b76a 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -171,7 +171,7 @@ struct solver::imp { lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break; default: - lp_assert(false); // unreachable + UNREACHABLE(); // unreachable } m_nlsat->mk_clause(1, &lit, a); } diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index a64825cd8..251274006 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -107,8 +107,8 @@ public: template struct convert_struct { static X convert(const Y & y){ return X(y);} - static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} - static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } + static bool below_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false;} + static bool above_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false; } }; @@ -190,7 +190,7 @@ struct numeric_pair { } numeric_pair operator/(const numeric_pair &) const { - // lp_unreachable(); + // UNREACHABLE(); } @@ -199,7 +199,7 @@ struct numeric_pair { } numeric_pair operator*(const numeric_pair & /*a*/) const { - // lp_unreachable(); + // UNREACHABLE(); } numeric_pair& operator+=(const numeric_pair & a) { @@ -275,14 +275,14 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } -template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} +template double get_double(const lp::numeric_pair & ) { /* UNREACHABLE(); */ return 0;} template class numeric_traits> { public: static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate - static double one() { /*lp_unreachable();*/ return 0;} + static double one() { /*UNREACHABLE();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 28a23b0c3..efb6e07cf 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -31,7 +31,6 @@ template std::set> lp::static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; @@ -47,7 +46,6 @@ template static_matrix::static_matrix(unsigned int, unsigned int); #ifdef Z3DEBUG template bool static_matrix >::is_correct() const; #endif -template void static_matrix >::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix >::get_elem(unsigned int, unsigned int) const; template void static_matrix >::init_empty_matrix(unsigned int, unsigned int); template void static_matrix >::set(unsigned int, unsigned int, mpq const&); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index d7e4370a3..f79ff36ac 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -168,8 +168,6 @@ public: std::set> get_domain(); - void copy_column_to_indexed_vector(unsigned j, indexed_vector & v) const; - T get_max_abs_in_row(unsigned row) const; void add_column_to_vector (const T & a, unsigned j, T * v) const { for (const auto & it : m_columns[j]) { @@ -222,7 +220,7 @@ public: virtual void set_number_of_columns(unsigned /*n*/) { } #endif - T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } + T get_max_val_in_row(unsigned /* i */) const { UNREACHABLE(); } T get_balance() const; diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index af2eac360..76c1dec54 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -174,14 +174,6 @@ std::set> static_matrix::get_domain() { return ret; } -template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { - lp_assert(j < m_columns.size()); - for (auto & it : m_columns[j]) { - const T& val = get_val(it); - if (!is_zero(val)) - v.set_value(val, it.var()); - } -} template T static_matrix::get_max_abs_in_row(unsigned row) const { T ret = numeric_traits::zero(); diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index 890ff90e3..c64c01036 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -130,7 +130,7 @@ struct gomory_test { void report_conflict_from_gomory_cut(mpq &k) { - lp_assert(false); + UNREACHABLE(); } void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 8ad73f0c0..9120d64cf 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1365,7 +1365,7 @@ void test_gomory_cut_0() { if (j == 2) return zero_of_type(); if (j == 3) return mpq(3); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -1375,7 +1375,7 @@ void test_gomory_cut_0() { return true; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -1385,31 +1385,31 @@ void test_gomory_cut_0() { return true; if (j == 3) return false; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(3); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper if (j == 1) { - lp_assert(false); //unlimited from above + UNREACHABLE(); //unlimited from above return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(10); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, @@ -1437,7 +1437,7 @@ void test_gomory_cut_1() { return mpq(4363334, 2730001); if (j == 3) return mpq(1); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -1447,7 +1447,7 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -1457,19 +1457,19 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(1); if (j == 3) return impq(1); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper @@ -1480,7 +1480,7 @@ void test_gomory_cut_1() { return impq(3333); if (j == 3) return impq(10000); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 75edb23b9..7843d5714 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -272,7 +272,7 @@ namespace lp { } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { - lp_assert(false); // unexpected input + UNREACHABLE(); // unexpected input } } From 4b3408696dfe37d41f30d350fda6e0a667086a21 Mon Sep 17 00:00:00 2001 From: igcontreras Date: Wed, 8 Mar 2023 16:13:38 -0500 Subject: [PATCH 122/220] use uintptr_t instead of size_t (tptr) for portability (#6627) --- src/util/tptr.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/tptr.h b/src/util/tptr.h index 806b7637c..6213b2efa 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -26,19 +26,19 @@ Revision History: #define TAG_MASK (ALIGNMENT_VALUE - 1) #define PTR_MASK (~TAG_MASK) -#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ - static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) +#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ + static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) -#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) +#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) -#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) +#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) -#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) +#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) -#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) +#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) -#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) +#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) -#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) +#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) From 1612b57e1a99c4874a761510161a9b4d376d2825 Mon Sep 17 00:00:00 2001 From: Bram V <33070319+BramVerb@users.noreply.github.com> Date: Wed, 8 Mar 2023 22:43:51 +0100 Subject: [PATCH 123/220] Make all methods show in java API (#6626) * Make all methods show in java API * Add final modifier to all generic methods --- src/api/java/Context.java | 170 +++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 0f15d9411..06b312303 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -227,7 +227,7 @@ public class Context implements AutoCloseable { /** * Create a new array sort. **/ - public ArraySort mkArraySort(D domain, R range) + public final ArraySort mkArraySort(D domain, R range) { checkContextMatch(domain); checkContextMatch(range); @@ -238,7 +238,7 @@ public class Context implements AutoCloseable { /** * Create a new array sort. **/ - public ArraySort mkArraySort(Sort[] domains, R range) + public final ArraySort mkArraySort(Sort[] domains, R range) { checkContextMatch(domains); checkContextMatch(range); @@ -256,7 +256,7 @@ public class Context implements AutoCloseable { /** * Create a new sequence sort **/ - public SeqSort mkSeqSort(R s) + public final SeqSort mkSeqSort(R s) { return new SeqSort<>(this, Native.mkSeqSort(nCtx(), s.getNativeObject())); } @@ -264,7 +264,7 @@ public class Context implements AutoCloseable { /** * Create a new regular expression sort **/ - public ReSort mkReSort(R s) + public final ReSort mkReSort(R s) { return new ReSort<>(this, Native.mkReSort(nCtx(), s.getNativeObject())); } @@ -286,7 +286,7 @@ public class Context implements AutoCloseable { /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) + public final EnumSort mkEnumSort(Symbol name, Symbol... enumNames) { checkContextMatch(name); @@ -297,7 +297,7 @@ public class Context implements AutoCloseable { /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(String name, String... enumNames) + public final EnumSort mkEnumSort(String name, String... enumNames) { return new EnumSort<>(this, mkSymbol(name), mkSymbols(enumNames)); @@ -306,7 +306,7 @@ public class Context implements AutoCloseable { /** * Create a new list sort. **/ - public ListSort mkListSort(Symbol name, R elemSort) + public final ListSort mkListSort(Symbol name, R elemSort) { checkContextMatch(name); checkContextMatch(elemSort); @@ -316,7 +316,7 @@ public class Context implements AutoCloseable { /** * Create a new list sort. **/ - public ListSort mkListSort(String name, R elemSort) + public final ListSort mkListSort(String name, R elemSort) { checkContextMatch(elemSort); return new ListSort<>(this, mkSymbol(name), elemSort); @@ -325,7 +325,7 @@ public class Context implements AutoCloseable { /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) + public final FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) { checkContextMatch(name); @@ -335,7 +335,7 @@ public class Context implements AutoCloseable { /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(String name, long size) + public final FiniteDomainSort mkFiniteDomainSort(String name, long size) { return new FiniteDomainSort<>(this, mkSymbol(name), size); @@ -352,7 +352,7 @@ public class Context implements AutoCloseable { * an index referring to one of the recursive datatypes that is * declared. **/ - public Constructor mkConstructor(Symbol name, Symbol recognizer, + public final Constructor mkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { @@ -362,7 +362,7 @@ public class Context implements AutoCloseable { /** * Create a datatype constructor. **/ - public Constructor mkConstructor(String name, String recognizer, + public final Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) { return of(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); @@ -371,7 +371,7 @@ public class Context implements AutoCloseable { /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) { checkContextMatch(name); checkContextMatch(constructors); @@ -381,7 +381,7 @@ public class Context implements AutoCloseable { /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) { checkContextMatch(constructors); @@ -431,7 +431,7 @@ public class Context implements AutoCloseable { * that is passed in as argument is updated with value v, * the remaining fields of t are unchanged. **/ - public Expr mkUpdateField(FuncDecl field, Expr t, Expr v) + public final Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return (Expr) Expr.create(this, @@ -444,7 +444,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -455,7 +455,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) { checkContextMatch(name); @@ -468,7 +468,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort[] domain, R range) { checkContextMatch(domain); @@ -479,7 +479,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort domain, R range) { checkContextMatch(domain); @@ -491,7 +491,7 @@ public class Context implements AutoCloseable { /** * Creates a new recursive function declaration. **/ - public FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -506,7 +506,7 @@ public class Context implements AutoCloseable { * MkRecFuncDecl. The body may contain recursive uses of the function or * other mutually recursive functions. */ - public void AddRecDef(FuncDecl f, Expr[] args, Expr body) + public final void AddRecDef(FuncDecl f, Expr[] args, Expr body) { checkContextMatch(f); checkContextMatch(args); @@ -521,7 +521,7 @@ public class Context implements AutoCloseable { * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) + public final FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) { checkContextMatch(domain); @@ -532,7 +532,7 @@ public class Context implements AutoCloseable { /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(Symbol name, R range) + public final FuncDecl mkConstDecl(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -542,7 +542,7 @@ public class Context implements AutoCloseable { /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(String name, R range) + public final FuncDecl mkConstDecl(String name, R range) { checkContextMatch(range); return new FuncDecl<>(this, mkSymbol(name), null, range); @@ -554,7 +554,7 @@ public class Context implements AutoCloseable { * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshConstDecl(String prefix, R range) + public final FuncDecl mkFreshConstDecl(String prefix, R range) { checkContextMatch(range); @@ -566,7 +566,7 @@ public class Context implements AutoCloseable { * @param index The de-Bruijn index of the variable * @param ty The sort of the variable **/ - public Expr mkBound(int index, R ty) + public final Expr mkBound(int index, R ty) { return (Expr) Expr.create(this, Native.mkBound(nCtx(), index, ty.getNativeObject())); @@ -590,7 +590,7 @@ public class Context implements AutoCloseable { * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(Symbol name, R range) + public final Expr mkConst(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -605,7 +605,7 @@ public class Context implements AutoCloseable { * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(String name, R range) + public final Expr mkConst(String name, R range) { return mkConst(mkSymbol(name), range); } @@ -614,7 +614,7 @@ public class Context implements AutoCloseable { * Creates a fresh Constant of sort {@code range} and a name * prefixed with {@code prefix}. **/ - public Expr mkFreshConst(String prefix, R range) + public final Expr mkFreshConst(String prefix, R range) { checkContextMatch(range); return (Expr) Expr.create(this, @@ -625,7 +625,7 @@ public class Context implements AutoCloseable { * Creates a fresh constant from the FuncDecl {@code f}. * @param f A decl of a 0-arity function **/ - public Expr mkConst(FuncDecl f) + public final Expr mkConst(FuncDecl f) { return mkApp(f, (Expr[]) null); } @@ -754,7 +754,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code not(a)}. **/ - public BoolExpr mkNot(Expr a) + public final BoolExpr mkNot(Expr a) { checkContextMatch(a); return new BoolExpr(this, Native.mkNot(nCtx(), a.getNativeObject())); @@ -767,7 +767,7 @@ public class Context implements AutoCloseable { * @param t2 An expression * @param t3 An expression with the same sort as {@code t2} **/ - public Expr mkITE(Expr t1, Expr t2, Expr t3) + public final Expr mkITE(Expr t1, Expr t2, Expr t3) { checkContextMatch(t1); checkContextMatch(t2); @@ -867,7 +867,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code -t}. **/ - public ArithExpr mkUnaryMinus(Expr t) + public final ArithExpr mkUnaryMinus(Expr t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, @@ -877,7 +877,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code t1 / t2}. **/ - public ArithExpr mkDiv(Expr t1, Expr t2) + public final ArithExpr mkDiv(Expr t1, Expr t2) { checkContextMatch(t1); checkContextMatch(t2); @@ -914,7 +914,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code t1 ^ t2}. **/ - public ArithExpr mkPower(Expr t1, + public final ArithExpr mkPower(Expr t1, Expr t2) { checkContextMatch(t1); @@ -1693,7 +1693,7 @@ public class Context implements AutoCloseable { /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(Symbol name, D domain, R range) + public final ArrayExpr mkArrayConst(Symbol name, D domain, R range) { return (ArrayExpr) mkConst(name, mkArraySort(domain, range)); @@ -1702,7 +1702,7 @@ public class Context implements AutoCloseable { /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(String name, D domain, R range) + public final ArrayExpr mkArrayConst(String name, D domain, R range) { return (ArrayExpr) mkConst(mkSymbol(name), mkArraySort(domain, range)); @@ -1720,7 +1720,7 @@ public class Context implements AutoCloseable { * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr i) + public final Expr mkSelect(Expr> a, Expr i) { checkContextMatch(a); checkContextMatch(i); @@ -1742,7 +1742,7 @@ public class Context implements AutoCloseable { * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr[] args) + public final Expr mkSelect(Expr> a, Expr[] args) { checkContextMatch(a); checkContextMatch(args); @@ -1767,7 +1767,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr i, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr i, Expr v) { checkContextMatch(a); checkContextMatch(i); @@ -1792,7 +1792,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) { checkContextMatch(a); checkContextMatch(args); @@ -1810,7 +1810,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) * **/ - public ArrayExpr mkConstArray(D domain, Expr v) + public final ArrayExpr mkConstArray(D domain, Expr v) { checkContextMatch(domain); checkContextMatch(v); @@ -1847,7 +1847,7 @@ public class Context implements AutoCloseable { * value, for arrays that can be represented as finite maps with a default * range value. **/ - public Expr mkTermArray(Expr> array) + public final Expr mkTermArray(Expr> array) { checkContextMatch(array); return (Expr) Expr.create(this, @@ -1857,7 +1857,7 @@ public class Context implements AutoCloseable { /** * Create Extentionality index. Two arrays are equal if and only if they are equal on the index returned by MkArrayExt. **/ - public Expr mkArrayExt(Expr> arg1, Expr> arg2) + public final Expr mkArrayExt(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1868,7 +1868,7 @@ public class Context implements AutoCloseable { /** * Create a set type. **/ - public SetSort mkSetSort(D ty) + public final SetSort mkSetSort(D ty) { checkContextMatch(ty); return new SetSort<>(this, ty); @@ -1877,7 +1877,7 @@ public class Context implements AutoCloseable { /** * Create an empty set. **/ - public ArrayExpr mkEmptySet(D domain) + public final ArrayExpr mkEmptySet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1887,7 +1887,7 @@ public class Context implements AutoCloseable { /** * Create the full set. **/ - public ArrayExpr mkFullSet(D domain) + public final ArrayExpr mkFullSet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1897,7 +1897,7 @@ public class Context implements AutoCloseable { /** * Add an element to the set. **/ - public ArrayExpr mkSetAdd(Expr> set, Expr element) + public final ArrayExpr mkSetAdd(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1909,7 +1909,7 @@ public class Context implements AutoCloseable { /** * Remove an element from a set. **/ - public ArrayExpr mkSetDel(Expr> set, Expr element) + public final ArrayExpr mkSetDel(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1945,7 +1945,7 @@ public class Context implements AutoCloseable { /** * Take the difference between two sets. **/ - public ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) + public final ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1957,7 +1957,7 @@ public class Context implements AutoCloseable { /** * Take the complement of a set. **/ - public ArrayExpr mkSetComplement(Expr> arg) + public final ArrayExpr mkSetComplement(Expr> arg) { checkContextMatch(arg); return (ArrayExpr)Expr.create(this, @@ -1967,7 +1967,7 @@ public class Context implements AutoCloseable { /** * Check for set membership. **/ - public BoolExpr mkSetMembership(Expr elem, Expr> set) + public final BoolExpr mkSetMembership(Expr elem, Expr> set) { checkContextMatch(elem); checkContextMatch(set); @@ -1979,7 +1979,7 @@ public class Context implements AutoCloseable { /** * Check for subsetness of sets. **/ - public BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) + public final BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1996,7 +1996,7 @@ public class Context implements AutoCloseable { /** * Create the empty sequence. */ - public SeqExpr mkEmptySeq(R s) + public final SeqExpr mkEmptySeq(R s) { checkContextMatch(s); return (SeqExpr) Expr.create(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); @@ -2005,7 +2005,7 @@ public class Context implements AutoCloseable { /** * Create the singleton sequence. */ - public SeqExpr mkUnit(Expr elem) + public final SeqExpr mkUnit(Expr elem) { checkContextMatch(elem); return (SeqExpr) Expr.create(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); @@ -2073,7 +2073,7 @@ public class Context implements AutoCloseable { /** * Retrieve the length of a given sequence. */ - public IntExpr mkLength(Expr> s) + public final IntExpr mkLength(Expr> s) { checkContextMatch(s); return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); @@ -2082,7 +2082,7 @@ public class Context implements AutoCloseable { /** * Check for sequence prefix. */ - public BoolExpr mkPrefixOf(Expr> s1, Expr> s2) + public final BoolExpr mkPrefixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2091,7 +2091,7 @@ public class Context implements AutoCloseable { /** * Check for sequence suffix. */ - public BoolExpr mkSuffixOf(Expr> s1, Expr> s2) + public final BoolExpr mkSuffixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr)Expr.create(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2100,7 +2100,7 @@ public class Context implements AutoCloseable { /** * Check for sequence containment of s2 in s1. */ - public BoolExpr mkContains(Expr> s1, Expr> s2) + public final BoolExpr mkContains(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2129,7 +2129,7 @@ public class Context implements AutoCloseable { /** * Retrieve sequence of length one at index. */ - public SeqExpr mkAt(Expr> s, Expr index) + public final SeqExpr mkAt(Expr> s, Expr index) { checkContextMatch(s, index); return (SeqExpr) Expr.create(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2138,7 +2138,7 @@ public class Context implements AutoCloseable { /** * Retrieve element at index. */ - public Expr mkNth(Expr> s, Expr index) + public final Expr mkNth(Expr> s, Expr index) { checkContextMatch(s, index); return (Expr) Expr.create(this, Native.mkSeqNth(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2148,7 +2148,7 @@ public class Context implements AutoCloseable { /** * Extract subsequence. */ - public SeqExpr mkExtract(Expr> s, Expr offset, Expr length) + public final SeqExpr mkExtract(Expr> s, Expr offset, Expr length) { checkContextMatch(s, offset, length); return (SeqExpr) Expr.create(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); @@ -2157,7 +2157,7 @@ public class Context implements AutoCloseable { /** * Extract index of sub-string starting at offset. */ - public IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) + public final IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) { checkContextMatch(s, substr, offset); return (IntExpr)Expr.create(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); @@ -2166,7 +2166,7 @@ public class Context implements AutoCloseable { /** * Replace the first occurrence of src by dst in s. */ - public SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) + public final SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) { checkContextMatch(s, src, dst); return (SeqExpr) Expr.create(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); @@ -2175,7 +2175,7 @@ public class Context implements AutoCloseable { /** * Convert a regular expression that accepts sequence s. */ - public ReExpr mkToRe(Expr> s) + public final ReExpr mkToRe(Expr> s) { checkContextMatch(s); return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); @@ -2185,7 +2185,7 @@ public class Context implements AutoCloseable { /** * Check for regular expression membership. */ - public BoolExpr mkInRe(Expr> s, Expr> re) + public final BoolExpr mkInRe(Expr> s, Expr> re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); @@ -2194,7 +2194,7 @@ public class Context implements AutoCloseable { /** * Take the Kleene star of a regular expression. */ - public ReExpr mkStar(Expr> re) + public final ReExpr mkStar(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); @@ -2203,7 +2203,7 @@ public class Context implements AutoCloseable { /** * Create power regular expression. */ - public ReExpr mkPower(Expr> re, int n) + public final ReExpr mkPower(Expr> re, int n) { return (ReExpr) Expr.create(this, Native.mkRePower(nCtx(), re.getNativeObject(), n)); } @@ -2211,7 +2211,7 @@ public class Context implements AutoCloseable { /** * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo, int hi) + public final ReExpr mkLoop(Expr> re, int lo, int hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } @@ -2219,7 +2219,7 @@ public class Context implements AutoCloseable { /** * Take the lower-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo) + public final ReExpr mkLoop(Expr> re, int lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } @@ -2228,7 +2228,7 @@ public class Context implements AutoCloseable { /** * Take the Kleene plus of a regular expression. */ - public ReExpr mkPlus(Expr> re) + public final ReExpr mkPlus(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkRePlus(nCtx(), re.getNativeObject())); @@ -2237,7 +2237,7 @@ public class Context implements AutoCloseable { /** * Create the optional regular expression. */ - public ReExpr mkOption(Expr> re) + public final ReExpr mkOption(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); @@ -2246,7 +2246,7 @@ public class Context implements AutoCloseable { /** * Create the complement regular expression. */ - public ReExpr mkComplement(Expr> re) + public final ReExpr mkComplement(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReComplement(nCtx(), re.getNativeObject())); @@ -2285,7 +2285,7 @@ public class Context implements AutoCloseable { /** * Create a difference regular expression. */ - public ReExpr mkDiff(Expr> a, Expr> b) + public final ReExpr mkDiff(Expr> a, Expr> b) { checkContextMatch(a, b); return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); @@ -2296,7 +2296,7 @@ public class Context implements AutoCloseable { * Create the empty regular expression. * Coresponds to re.none */ - public ReExpr mkEmptyRe(R s) + public final ReExpr mkEmptyRe(R s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } @@ -2305,7 +2305,7 @@ public class Context implements AutoCloseable { * Create the full regular expression. * Corresponds to re.all */ - public ReExpr mkFullRe(R s) + public final ReExpr mkFullRe(R s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } @@ -2314,7 +2314,7 @@ public class Context implements AutoCloseable { * Create regular expression that accepts all characters * Corresponds to re.allchar */ - public ReExpr mkAllcharRe(R s) + public final ReExpr mkAllcharRe(R s) { return (ReExpr) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject())); } @@ -2322,7 +2322,7 @@ public class Context implements AutoCloseable { /** * Create a range expression. */ - public ReExpr mkRange(Expr> lo, Expr> hi) + public final ReExpr mkRange(Expr> lo, Expr> hi) { checkContextMatch(lo, hi); return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); @@ -2429,7 +2429,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and sort {@code ty} **/ - public Expr mkNumeral(String v, R ty) + public final Expr mkNumeral(String v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2446,7 +2446,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(int v, R ty) + public final Expr mkNumeral(int v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, Native.mkInt(nCtx(), v, ty.getNativeObject())); @@ -2462,7 +2462,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(long v, R ty) + public final Expr mkNumeral(long v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2717,7 +2717,7 @@ public class Context implements AutoCloseable { * @param names names of the bound variables. * @param body the body of the quantifier. **/ - public Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) + public final Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) { return Lambda.of(this, sorts, names, body); } @@ -2728,7 +2728,7 @@ public class Context implements AutoCloseable { * Creates a lambda expression using a list of constants that will * form the set of bound variables. **/ - public Lambda mkLambda(Expr[] boundConstants, Expr body) + public final Lambda mkLambda(Expr[] boundConstants, Expr body) { return Lambda.of(this, boundConstants, body); } @@ -4179,7 +4179,7 @@ public class Context implements AutoCloseable { * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkLinearOrder(R sort, int index) { + public final FuncDecl mkLinearOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkLinearOrder( @@ -4195,7 +4195,7 @@ public class Context implements AutoCloseable { * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkPartialOrder(R sort, int index) { + public final FuncDecl mkPartialOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkPartialOrder( From cf4df08fd079d6a93a1ed679e9217c5ff07f9d77 Mon Sep 17 00:00:00 2001 From: Declan Hwang Date: Fri, 10 Mar 2023 02:29:30 +0900 Subject: [PATCH 124/220] fix typo (#6628) --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 92bb2aa24..7e7c58052 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11343,7 +11343,7 @@ def Range(lo, hi, ctx=None): return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx) def Diff(a, b, ctx=None): - """Create the difference regular epression + """Create the difference regular expression """ return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx) From a0f3727e90c4446ba2c1fa7e4392637587ad9632 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 12 Mar 2023 19:26:47 +0000 Subject: [PATCH 125/220] BV: add missing neg internalizer usually bvneg is eliminated during rewriting, but it can be left behind during e.g. a badly-timed timeout --- src/smt/theory_bv.cpp | 2 ++ src/smt/theory_bv.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index b3a6e77ff..00564ed3e 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -811,6 +811,7 @@ namespace smt { init_bits(e, bits); } + MK_UNARY(internalize_neg, mk_neg); MK_UNARY(internalize_not, mk_not); MK_UNARY(internalize_redand, mk_redand); MK_UNARY(internalize_redor, mk_redor); @@ -895,6 +896,7 @@ namespace smt { } switch (term->get_decl_kind()) { case OP_BV_NUM: internalize_num(term); return true; + case OP_BNEG: internalize_neg(term); return true; case OP_BADD: internalize_add(term); return true; case OP_BSUB: internalize_sub(term); return true; case OP_BMUL: internalize_mul(term); return true; diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index 588f19d89..73d659c68 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -196,6 +196,7 @@ namespace smt { void internalize_ext_rotate_right(app * n); void internalize_and(app * n); void internalize_or(app * n); + void internalize_neg(app * n); void internalize_not(app * n); void internalize_nand(app * n); void internalize_nor(app * n); From d1c7ff1a369711f64bf16165efa5c6e8c8ad423a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2023 09:23:13 +0100 Subject: [PATCH 126/220] add unconstrained elimination for sequences --- src/api/api_tactic.cpp | 4 +- src/ast/converters/expr_inverter.cpp | 49 +++++++++++++++++++ src/ast/euf/euf_egraph.cpp | 5 ++ src/ast/rewriter/seq_rewriter.cpp | 36 ++++++++++++++ src/ast/rewriter/seq_rewriter.h | 1 + src/ast/simplifiers/elim_unconstrained.cpp | 6 +-- .../{seq_simplifier.h => then_simplifier.h} | 6 +-- src/cmd_context/simplifier_cmds.cpp | 4 +- src/solver/simplifier_solver.cpp | 4 +- src/solver/solver_preprocess.cpp | 2 +- src/solver/solver_preprocess.h | 4 +- src/tactic/core/elim_uncnstr_tactic.cpp | 43 +++++++++++++++- 12 files changed, 148 insertions(+), 16 deletions(-) rename src/ast/simplifiers/{seq_simplifier.h => then_simplifier.h} (94%) diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 4e5156ba9..351b3d1b9 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -24,7 +24,7 @@ Revision History: #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } @@ -589,7 +589,7 @@ extern "C" { auto fac1 = *to_simplifier_ref(t1); auto fac2 = *to_simplifier_ref(t2); auto new_s = [fac1, fac2](auto& m, auto& p, auto& st) { - auto* r = alloc(seq_simplifier, m, p, st); + auto* r = alloc(then_simplifier, m, p, st); r->add_simplifier(fac1(m, p, st)); r->add_simplifier(fac2(m, p, st)); return r; diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index abf3125c6..4d435f7e8 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -19,6 +19,7 @@ Author: #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "ast/converters/expr_inverter.h" class basic_expr_inverter : public iexpr_inverter { @@ -742,6 +743,53 @@ public: }; +class seq_expr_inverter : public iexpr_inverter { + seq_util seq; +public: + seq_expr_inverter(ast_manager& m) : iexpr_inverter(m), seq(m) {} + + family_id get_fid() const override { return seq.get_family_id(); } + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + expr* x, *y; + + if (uncnstr(args[0]) && num == 2 && + seq.str.is_concat(args[1], x, y) && + uncnstr(x)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], seq.str.mk_empty(args[0]->get_sort())); + add_def(x, r); + } + r = seq.str.mk_concat(r, y); + return true; + } + + if (!uncnstr(num, args)) + return false; + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], r); + for (unsigned i = 1; i < num; ++i) + add_def(args[i], seq.str.mk_empty(args[0]->get_sort())); + } + return true; + } + default: + return false; + + } + } + + bool mk_diff(expr* t, expr_ref& r) override { + return false; + } + +}; + expr_inverter::~expr_inverter() { for (auto* v : m_inverters) @@ -796,6 +844,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { add(alloc(array_expr_inverter, m, *this)); add(alloc(dt_expr_inverter, m)); add(alloc(basic_expr_inverter, m, *this)); + add(alloc(seq_expr_inverter, m)); } diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 7939c23f2..ac60a98ba 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -67,6 +67,8 @@ namespace euf { } enode_bool_pair egraph::insert_table(enode* p) { + TRACE("euf", tout << bpp(p) << "\n"); + //SASSERT(!m_table.contains_ptr(p)); auto rc = m_table.insert(p); p->m_cg = rc.first; return rc; @@ -280,6 +282,7 @@ namespace euf { if (!m.is_bool(n->get_sort())) return; if (enable_merge_tf != n->merge_tf()) { + TRACE("euf", tout << "set tf " << enable_merge_tf << " " << bpp(n) << "\n"); n->set_merge_tf(enable_merge_tf); m_updates.push_back(update_record(n, update_record::toggle_merge_tf())); } @@ -487,6 +490,7 @@ namespace euf { } void egraph::remove_parents(enode* r) { + TRACE("euf", tout << bpp(r) << "\n"); for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; @@ -496,6 +500,7 @@ namespace euf { SASSERT(m_table.contains_ptr(p)); p->mark1(); erase_from_table(p); + CTRACE("euf", m_table.contains_ptr(p), tout << bpp(p) << "\n"; display(tout)); SASSERT(!m_table.contains_ptr(p)); } else if (p->is_equality()) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index eed36af81..ea3e16f6e 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5518,6 +5518,7 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ reduce_front(ls, rs, eqs) && reduce_itos(ls, rs, eqs) && reduce_itos(rs, ls, eqs) && + reduce_value_clash(ls, rs, eqs) && reduce_by_length(ls, rs, eqs) && reduce_subsequence(ls, rs, eqs) && reduce_non_overlap(ls, rs, eqs) && @@ -5943,6 +5944,41 @@ bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, return true; } + +/** + * partial check for value clash. + * checks that elements that do not occur in + * other sequence are non-values. + * The check could be extended to take non-value + * characters (units) into account. + */ +bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { + ptr_buffer es; + if (ls.empty() || rs.empty()) + return true; + es.append(ls.size(), ls.data()); + auto remove = [&](expr* r) { + for (unsigned i = 0; i < es.size(); ++i) { + if (r == es[i]) { + es[i] = es.back(); + es.pop_back(); + return true; + } + } + return false; + }; + auto is_unit_value = [&](expr* r) { + return m().is_value(r) && str().is_unit(r); + }; + for (expr* r : rs) { + if (remove(r)) + continue; + if (!is_unit_value(r)) + return true; + } + return any_of(es, [&](expr* e) { return is_unit_value(e); }); +} + bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { if (ls.size() > rs.size()) diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 26dc00675..92a6a17fa 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -340,6 +340,7 @@ class seq_rewriter { bool is_sequence(expr* e, expr_ref_vector& seq); bool is_sequence(eautomaton& aut, expr_ref_vector& seq); bool get_lengths(expr* e, expr_ref_vector& lens, rational& pos); + bool reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_back(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_front(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); void remove_empty_and_concats(expr_ref_vector& es); diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 3d2566193..231858897 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -73,7 +73,7 @@ void elim_unconstrained::eliminate() { node& n = get_node(v); if (n.m_refcount == 0) continue; - if (n.m_refcount > 1) + if (n.m_refcount > 1) return; if (n.m_parents.empty()) { @@ -90,10 +90,10 @@ void elim_unconstrained::eliminate() { unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); - bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r); + bool inverted = m_inverter(t->get_decl(), t->get_num_args(), m_args.data() + sz, r); proof_ref pr(m); if (inverted && m_enable_proofs) { - expr * s = m.mk_app(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + expr * s = m.mk_app(t->get_decl(), t->get_num_args(), m_args.data() + sz); expr * eq = m.mk_eq(s, r); proof * pr1 = m.mk_def_intro(eq); proof * pr = m.mk_apply_def(s, r, pr1); diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/then_simplifier.h similarity index 94% rename from src/ast/simplifiers/seq_simplifier.h rename to src/ast/simplifiers/then_simplifier.h index a5ef91d7c..6ee8b9412 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/then_simplifier.h @@ -3,7 +3,7 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - seq_simplifier.h + then_simplifier.h Abstract: @@ -21,7 +21,7 @@ Author: #include "ast/simplifiers/dependent_expr_state.h" -class seq_simplifier : public dependent_expr_simplifier { +class then_simplifier : public dependent_expr_simplifier { scoped_ptr_vector m_simplifiers; struct collect_stats { @@ -53,7 +53,7 @@ class seq_simplifier : public dependent_expr_simplifier { public: - seq_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + then_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls) { } diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index 0050dc548..ac8d3e839 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -21,7 +21,7 @@ Author: #include "cmd_context/parametric_cmd.h" #include "model/model_smt2_pp.h" #include "ast/ast_smt2_pp.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/simplifier_solver.h" typedef dependent_expr_simplifier simplifier; @@ -37,7 +37,7 @@ static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2simplifier(ctx, n->get_child(i))); simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { - scoped_ptr s = alloc(seq_simplifier, m, p, st); + scoped_ptr s = alloc(then_simplifier, m, p, st); for (auto & simp : args) s->add_simplifier(simp(m, p, st)); return s.detach(); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index a717c4932..12222194d 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -23,7 +23,7 @@ Author: #include "ast/ast_util.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/solver.h" #include "solver/simplifier_solver.h" #include "solver/solver_preprocess.h" @@ -91,7 +91,7 @@ class simplifier_solver : public solver { solver_ref s; vector m_fmls; dep_expr_state m_preprocess_state; - seq_simplifier m_preprocess; + then_simplifier m_preprocess; expr_ref_vector m_assumptions; model_converter_ref m_mc; bool m_inconsistent = false; diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 7fd9d1dba..9cac4b835 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -45,7 +45,7 @@ Notes: #include "solver/solver_preprocess.h" #include "qe/lite/qe_lite_tactic.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st) { smt_params smtp(p); s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); diff --git a/src/solver/solver_preprocess.h b/src/solver/solver_preprocess.h index c0dfc42f3..6f610d59d 100644 --- a/src/solver/solver_preprocess.h +++ b/src/solver/solver_preprocess.h @@ -19,7 +19,7 @@ Author: #pragma once -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st); +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index b8b4334f4..869716f59 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -24,6 +24,7 @@ Notes: #include "ast/recfun_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "tactic/core/collect_occs.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" @@ -44,6 +45,7 @@ class elim_uncnstr_tactic : public tactic { bv_util m_bv_util; array_util m_ar_util; datatype_util m_dt_util; + seq_util m_seq_util; app_ref_vector m_fresh_vars; obj_map m_cache; app_ref_vector m_cache_domain; @@ -60,6 +62,7 @@ class elim_uncnstr_tactic : public tactic { m_bv_util(m), m_ar_util(m), m_dt_util(m), + m_seq_util(m), m_fresh_vars(m), m_cache_domain(m), m_max_memory(max_memory), @@ -792,6 +795,43 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } + + // x ++ y -> z, x -> z, y -> eps + app * process_seq_app(func_decl * f, unsigned num, expr * const * args) { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + app * r = nullptr; + expr* x, *y; + if (uncnstr(args[0]) && num == 2 && + m_seq_util.str.is_concat(args[1], x, y) && + uncnstr(x)) { + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + if (m_mc) { + add_def(args[0], r); + add_def(x, m_seq_util.str.mk_empty(args[0]->get_sort())); + } + r = m_seq_util.str.mk_concat(r, y); + return r; + + } + if (!uncnstr(num, args)) + return nullptr; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + expr_ref id(m_seq_util.str.mk_empty(args[0]->get_sort()), m()); + add_defs(num, args, r, id); + + return r; + } + default: + return nullptr; + } + } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) @@ -817,7 +857,8 @@ class elim_uncnstr_tactic : public tactic { u = process_array_app(f, num, args); else if (fid == m_dt_util.get_family_id()) u = process_datatype_app(f, num, args); - + else if (fid == m_seq_util.get_family_id()) + u = process_seq_app(f, num, args); if (u == nullptr) return BR_FAILED; From a9e6e567b054bf0aaa03463b56c15d2a743696c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Mar 2023 21:43:26 +0100 Subject: [PATCH 127/220] make generation of "some" Boolean value fair --- src/model/value_factory.cpp | 8 ++++---- src/model/value_factory.h | 3 ++- src/smt/proto_model/proto_model.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/model/value_factory.cpp b/src/model/value_factory.cpp index 2b412f850..30fa82caf 100644 --- a/src/model/value_factory.cpp +++ b/src/model/value_factory.cpp @@ -28,13 +28,13 @@ value_factory::value_factory(ast_manager & m, family_id fid): value_factory::~value_factory() { } -basic_factory::basic_factory(ast_manager & m): - value_factory(m, m.get_basic_family_id()) { +basic_factory::basic_factory(ast_manager & m, unsigned seed): + value_factory(m, m.get_basic_family_id()), m_rand(seed) { } expr * basic_factory::get_some_value(sort * s) { - if (m_manager.is_bool(s)) - return m_manager.mk_false(); + if (m_manager.is_bool(s)) + return (m_rand() % 2 == 0) ? m_manager.mk_false() : m_manager.mk_true(); return nullptr; } diff --git a/src/model/value_factory.h b/src/model/value_factory.h index cf56439d9..edb12b095 100644 --- a/src/model/value_factory.h +++ b/src/model/value_factory.h @@ -60,8 +60,9 @@ public: }; class basic_factory : public value_factory { + random_gen m_rand; public: - basic_factory(ast_manager & m); + basic_factory(ast_manager & m, unsigned seed); expr * get_some_value(sort * s) override; diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index d1614f521..d61bcf028 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -32,7 +32,7 @@ proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), m_eval(*this), m_rewrite(m) { - register_factory(alloc(basic_factory, m)); + register_factory(alloc(basic_factory, m, m.get_num_asts())); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); From c6e3fb446abbf9c6d99619e0443ca53ee978d1e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:06:14 +0100 Subject: [PATCH 128/220] print lemmas2console faster - add option pp.no_lets (default = false) to print formulas without let (used by the low-level SMT2 printer). - print lemmas2console faster by using the low level printer --- src/ast/ast_smt_pp.cpp | 5 ++++- src/ast/pp_params.pyg | 1 + src/ast/simplifiers/elim_unconstrained.h | 2 ++ src/smt/smt_internalizer.cpp | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index f40b8a554..bea669438 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -34,6 +34,7 @@ Revision History: #include "ast/for_each_ast.h" #include "ast/decl_collector.h" #include "math/polynomial/algebraic_numbers.h" +#include "ast/pp_params.hpp" // --------------------------------------- @@ -911,7 +912,9 @@ ast_smt_pp::ast_smt_pp(ast_manager& m): void ast_smt_pp::display_expr_smt2(std::ostream& strm, expr* n, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; - smt_printer p(strm, m_manager, ql, rn, m_logic, false, m_simplify_implies, indent, num_var_names, var_names); + pp_params params; + bool no_lets = params.no_lets(); + smt_printer p(strm, m_manager, ql, rn, m_logic, no_lets, m_simplify_implies, indent, num_var_names, var_names); p(n); } diff --git a/src/ast/pp_params.pyg b/src/ast/pp_params.pyg index 6b43cbea3..c833b624a 100644 --- a/src/ast/pp_params.pyg +++ b/src/ast/pp_params.pyg @@ -6,6 +6,7 @@ def_module_params('pp', ('max_width', UINT, 80, 'max. width in pretty printer'), ('max_ribbon', UINT, 80, 'max. ribbon (width - indentation) in pretty printer'), ('max_depth', UINT, 5, 'max. term depth (when pretty printing SMT2 terms/formulas)'), + ('no_lets', BOOL, False, 'dont print lets in low level SMT printer'), ('min_alias_size', UINT, 10, 'min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas)'), ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 0fdde5af2..5dced90d0 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -22,6 +22,8 @@ Author: class elim_unconstrained : public dependent_expr_simplifier { + friend class seq_simplifier; + struct node { unsigned m_refcount = 0; expr* m_term = nullptr; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 3a7b95e2c..68879b8ac 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1536,7 +1536,7 @@ namespace smt { fml = mk_or(fmls); m_lemma_visitor.collect(fml); m_lemma_visitor.display_skolem_decls(std::cout); - m_lemma_visitor.display_assert(std::cout, fml.get(), true); + m_lemma_visitor.display_assert(std::cout, fml.get(), false); } } From 48de7c2da8dfdcd577c56d601f720d1aecbe390e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:06:59 +0100 Subject: [PATCH 129/220] missing updates --- src/ast/simplifiers/seq_simplifier.cpp | 156 +++++++++++++++++++++++++ src/ast/simplifiers/seq_simplifier.h | 36 ++++++ 2 files changed, 192 insertions(+) create mode 100644 src/ast/simplifiers/seq_simplifier.cpp create mode 100644 src/ast/simplifiers/seq_simplifier.h diff --git a/src/ast/simplifiers/seq_simplifier.cpp b/src/ast/simplifiers/seq_simplifier.cpp new file mode 100644 index 000000000..907bfaa93 --- /dev/null +++ b/src/ast/simplifiers/seq_simplifier.cpp @@ -0,0 +1,156 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + seq_simplifier.cpp + +Abstract: + + Global simplifier for sequences + +Author: + + Nikolaj Bjorner (nbjorner) 2023-03-12. + +--*/ + + +#include "ast/simplifiers/seq_simplifier.h" + +// +// if x, y always are in pattern .. ++ x ++ y ++ .. -> z +// x ++ a ++ y = s, where s containing a, x, y are unique -> true +// Parikh pre-processing based on regex membership constraints and equations +// Sequencing abstractions: +// example x in a*, y in b*, z = (ba)+, xy = z +// last(xy) = a => first(xy) = a +// last(z) = a +// first(z) = b +// +// Lookahead +// to help with sequence abstractions? +// +// solve for queues: +// X = ABC +// XX = AABBCC +// XXX = AAABBBCCC +// B = bB' +// C = cC' +// XX = ABCABC +// |A| = |C| => ABC = AAB = BCC +// + +void seq_simplifier::reduce() { + if (!m_seq.has_seq()) + return; + elim_unconstrained elim(m, m_fmls); + elim.init_nodes(); + eliminate(elim); +} + +bool seq_simplifier::invert(elim_unconstrained& elim, app* t, expr_ref& r) { + if (!m_seq.str.is_concat(t)) + return false; + + + auto is_valid_parent = [&](expr* p) { + return elim.get_node(p).m_refcount > 0 && elim.get_node(p).m_term == elim.get_node(p).m_orig; + }; + + expr* first = nullptr, *second = nullptr; + expr* a, *b, *c, *d, *e; + for (expr* p : elim.get_node(t).m_parents) { + if (!is_valid_parent(p)) + continue; + if (!m_seq.str.is_concat(p, a, b)) + return false; + if (!m_seq.str.is_concat(b, c, d)) + c = b; + if (first && (first != a || second != c)) + return false; + first = a; + second = c; + // parents of b are all of the form (seq.++ a b) + for (expr* q : elim.get_node(b).m_parents) { + if (!is_valid_parent(q)) + continue; + if (!m_seq.str.is_concat(q, d, e)) + return false; + if (e != b || d != a) + return false; + } + } + + if (!first) + return false; + + expr* x = nullptr; + // replace p := a ++ b ++ c by x ++ c + for (expr* p : elim.get_node(t).m_parents) { + if (!is_valid_parent(p)) + continue; + VERIFY(m_seq.str.is_concat(p, a, b)); + if (m_seq.str.is_concat(b, c, d)) + r = m_seq.str.mk_concat(x, d); + else + r = x; + // p := r + } + + return false; +} + +void seq_simplifier::eliminate(elim_unconstrained& elim) { +#if 0 + while (!elim.m_heap.empty()) { + expr_ref r(m); + int v = elim.m_heap.erase_min(); + node& n = elim.get_node(v); + if (n.m_refcount == 0) + continue; + if (n.m_parents.empty()) { + n.m_refcount = 0; + continue; + } + expr* e = elim.get_parent(v); + IF_VERBOSE(11, for (expr* p : n.m_parents) verbose_stream() << "parent " << mk_bounded_pp(p, m) << " @ " << get_node(p).m_refcount << "\n";); + if (!e || !is_app(e) || !is_ground(e)) { + n.m_refcount = 0; + continue; + } + app* t = to_app(e); + bool inverted = invert(elim, t, r); + n.m_refcount = 0; + if (!inverted) { + IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); + continue; + } + + TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); + SASSERT(r->get_sort() == t->get_sort()); + elim.m_stats.m_num_eliminated++; + elim.m_trail.push_back(r); + SASSERT(r); + elim.gc(e); + elim.invalidate_parents(e); + elim.freeze_rec(r); + + elim.m_root.setx(r->get_id(), e->get_id(), UINT_MAX); + elim.get_node(e).m_term = r; + elim.get_node(e).m_proof = pr; + elim.get_node(e).m_refcount++; + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); + SASSERT(!elim.m_heap.contains(root(e))); + if (is_uninterp_const(r)) + elim.m_heap.insert(root(e)); + else + elim.m_created_compound = true; + + IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << elim.get_node(e).m_refcount << "\n";); + + } + +#endif +} + diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h new file mode 100644 index 000000000..11d904761 --- /dev/null +++ b/src/ast/simplifiers/seq_simplifier.h @@ -0,0 +1,36 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + seq_simplifier.h + +Abstract: + + Global simplifier for sequences + +Author: + + Nikolaj Bjorner (nbjorner) 2023-03-12. + +--*/ + + +#pragma once + +#include "ast/seq_decl_plugin.h" +#include "ast/simplifiers/dependent_expr_state.h" +#include "ast/simplifiers/elim_unconstrained.h" + + +class seq_simplifier : public dependent_expr_simplifier { + seq_util m_seq; + + void eliminate(elim_unconstrained& elim); + bool invert(elim_unconstrained& elim, app* t, expr_ref& r); +public: + + seq_simplifier(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_seq(m) {} + char const* name() const override { return "seq-simplifier"; } + void reduce() override; +}; From f075dc28826644052977be6dfe228d3f194b6029 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 17:07:48 +0100 Subject: [PATCH 130/220] remove experimental files --- src/ast/simplifiers/seq_simplifier.cpp | 156 ------------------------- src/ast/simplifiers/seq_simplifier.h | 36 ------ 2 files changed, 192 deletions(-) delete mode 100644 src/ast/simplifiers/seq_simplifier.cpp delete mode 100644 src/ast/simplifiers/seq_simplifier.h diff --git a/src/ast/simplifiers/seq_simplifier.cpp b/src/ast/simplifiers/seq_simplifier.cpp deleted file mode 100644 index 907bfaa93..000000000 --- a/src/ast/simplifiers/seq_simplifier.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*++ -Copyright (c) 2022 Microsoft Corporation - -Module Name: - - seq_simplifier.cpp - -Abstract: - - Global simplifier for sequences - -Author: - - Nikolaj Bjorner (nbjorner) 2023-03-12. - ---*/ - - -#include "ast/simplifiers/seq_simplifier.h" - -// -// if x, y always are in pattern .. ++ x ++ y ++ .. -> z -// x ++ a ++ y = s, where s containing a, x, y are unique -> true -// Parikh pre-processing based on regex membership constraints and equations -// Sequencing abstractions: -// example x in a*, y in b*, z = (ba)+, xy = z -// last(xy) = a => first(xy) = a -// last(z) = a -// first(z) = b -// -// Lookahead -// to help with sequence abstractions? -// -// solve for queues: -// X = ABC -// XX = AABBCC -// XXX = AAABBBCCC -// B = bB' -// C = cC' -// XX = ABCABC -// |A| = |C| => ABC = AAB = BCC -// - -void seq_simplifier::reduce() { - if (!m_seq.has_seq()) - return; - elim_unconstrained elim(m, m_fmls); - elim.init_nodes(); - eliminate(elim); -} - -bool seq_simplifier::invert(elim_unconstrained& elim, app* t, expr_ref& r) { - if (!m_seq.str.is_concat(t)) - return false; - - - auto is_valid_parent = [&](expr* p) { - return elim.get_node(p).m_refcount > 0 && elim.get_node(p).m_term == elim.get_node(p).m_orig; - }; - - expr* first = nullptr, *second = nullptr; - expr* a, *b, *c, *d, *e; - for (expr* p : elim.get_node(t).m_parents) { - if (!is_valid_parent(p)) - continue; - if (!m_seq.str.is_concat(p, a, b)) - return false; - if (!m_seq.str.is_concat(b, c, d)) - c = b; - if (first && (first != a || second != c)) - return false; - first = a; - second = c; - // parents of b are all of the form (seq.++ a b) - for (expr* q : elim.get_node(b).m_parents) { - if (!is_valid_parent(q)) - continue; - if (!m_seq.str.is_concat(q, d, e)) - return false; - if (e != b || d != a) - return false; - } - } - - if (!first) - return false; - - expr* x = nullptr; - // replace p := a ++ b ++ c by x ++ c - for (expr* p : elim.get_node(t).m_parents) { - if (!is_valid_parent(p)) - continue; - VERIFY(m_seq.str.is_concat(p, a, b)); - if (m_seq.str.is_concat(b, c, d)) - r = m_seq.str.mk_concat(x, d); - else - r = x; - // p := r - } - - return false; -} - -void seq_simplifier::eliminate(elim_unconstrained& elim) { -#if 0 - while (!elim.m_heap.empty()) { - expr_ref r(m); - int v = elim.m_heap.erase_min(); - node& n = elim.get_node(v); - if (n.m_refcount == 0) - continue; - if (n.m_parents.empty()) { - n.m_refcount = 0; - continue; - } - expr* e = elim.get_parent(v); - IF_VERBOSE(11, for (expr* p : n.m_parents) verbose_stream() << "parent " << mk_bounded_pp(p, m) << " @ " << get_node(p).m_refcount << "\n";); - if (!e || !is_app(e) || !is_ground(e)) { - n.m_refcount = 0; - continue; - } - app* t = to_app(e); - bool inverted = invert(elim, t, r); - n.m_refcount = 0; - if (!inverted) { - IF_VERBOSE(11, verbose_stream() << "not inverted " << mk_bounded_pp(e, m) << "\n"); - continue; - } - - TRACE("elim_unconstrained", tout << mk_pp(t, m) << " -> " << r << "\n"); - SASSERT(r->get_sort() == t->get_sort()); - elim.m_stats.m_num_eliminated++; - elim.m_trail.push_back(r); - SASSERT(r); - elim.gc(e); - elim.invalidate_parents(e); - elim.freeze_rec(r); - - elim.m_root.setx(r->get_id(), e->get_id(), UINT_MAX); - elim.get_node(e).m_term = r; - elim.get_node(e).m_proof = pr; - elim.get_node(e).m_refcount++; - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(e, m) << "\n"); - SASSERT(!elim.m_heap.contains(root(e))); - if (is_uninterp_const(r)) - elim.m_heap.insert(root(e)); - else - elim.m_created_compound = true; - - IF_VERBOSE(11, verbose_stream() << mk_bounded_pp(get_node(v).m_orig, m) << " " << mk_bounded_pp(t, m) << " -> " << r << " " << elim.get_node(e).m_refcount << "\n";); - - } - -#endif -} - diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/seq_simplifier.h deleted file mode 100644 index 11d904761..000000000 --- a/src/ast/simplifiers/seq_simplifier.h +++ /dev/null @@ -1,36 +0,0 @@ -/*++ -Copyright (c) 2022 Microsoft Corporation - -Module Name: - - seq_simplifier.h - -Abstract: - - Global simplifier for sequences - -Author: - - Nikolaj Bjorner (nbjorner) 2023-03-12. - ---*/ - - -#pragma once - -#include "ast/seq_decl_plugin.h" -#include "ast/simplifiers/dependent_expr_state.h" -#include "ast/simplifiers/elim_unconstrained.h" - - -class seq_simplifier : public dependent_expr_simplifier { - seq_util m_seq; - - void eliminate(elim_unconstrained& elim); - bool invert(elim_unconstrained& elim, app* t, expr_ref& r); -public: - - seq_simplifier(ast_manager& m, dependent_expr_state& fmls) : dependent_expr_simplifier(m, fmls), m_seq(m) {} - char const* name() const override { return "seq-simplifier"; } - void reduce() override; -}; From 53ca65a62ec2bc441784f7e2ebff1308a0d3c0ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 20 Mar 2023 18:55:40 +0100 Subject: [PATCH 131/220] fix unsound rewrite --- src/ast/rewriter/seq_rewriter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index ea3e16f6e..6fd518cb0 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5954,6 +5954,7 @@ bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, */ bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { ptr_buffer es; + if (ls.empty() || rs.empty()) return true; es.append(ls.size(), ls.data()); @@ -5976,7 +5977,12 @@ bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, if (!is_unit_value(r)) return true; } - return any_of(es, [&](expr* e) { return is_unit_value(e); }); + if (es.empty()) + return true; + for (expr* e : es) + if (!is_unit_value(e)) + return true; + return false; } bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { From 2683a2d6ed9039ddd1a59aeccbdd91f67f7b708a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 08:49:33 +0100 Subject: [PATCH 132/220] fix #6637 --- src/math/lp/nla_divisions.cpp | 5 +++-- src/sat/smt/euf_proof_checker.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 91b674d58..cbb30d9d9 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -58,10 +58,11 @@ namespace nla { auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { - if (y1val >= y2val && y2val > 0 && x1val <= x2val && q1val > q2val) { - new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); + if (y1val >= y2val && y2val > 0 && 0 <= x1val && x1val <= x2val && q1val > q2val) { + new_lemma lemma(c, "y1 >= y2 > 0 & 0 <= x1 <= x2 => x1/y1 <= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y2, llc::LE, 0); + lemma |= ineq(x1, llc::LT, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); lemma |= ineq(term(q1, rational(-1), q2), llc::LE, 0); return true; diff --git a/src/sat/smt/euf_proof_checker.h b/src/sat/smt/euf_proof_checker.h index 9a84015e4..d84e4d19f 100644 --- a/src/sat/smt/euf_proof_checker.h +++ b/src/sat/smt/euf_proof_checker.h @@ -35,7 +35,7 @@ namespace euf { virtual bool check(app* jst) = 0; virtual expr_ref_vector clause(app* jst) = 0; virtual void register_plugins(theory_checker& pc) = 0; - virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.reset(); v.append(this->clause(jst)); return false; } + virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.append(this->clause(jst)); return false; } }; class theory_checker { From 03a44803b6da631cf7d3773319685e0c0f9fb0da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 13:38:02 +0100 Subject: [PATCH 133/220] fix #6635 --- src/smt/theory_lra.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 3eda1ddff..568143d36 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -436,6 +436,9 @@ class theory_lra::imp { if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); if (m_nla && !a.is_numeral(n2)) { // shortcut to create non-linear division axioms. + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -443,6 +446,9 @@ class theory_lra::imp { } if (a.is_numeral(n2) && a.is_bounded(n1)) { ensure_nla(); + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -1512,11 +1518,9 @@ public: } } TRACE("arith", - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { + for (theory_var v = 0; v < sz; ++v) + if (th.is_relevant_and_shared(get_enode(v))) tout << "v" << v << " "; - } - } tout << "\n"; ); if (!vars.empty()) { lp().random_update(vars.size(), vars.data()); From 50bd6efea4baba343afffa26f742b49a47b30970 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2023 14:00:09 +0100 Subject: [PATCH 134/220] fix #6624 --- src/smt/theory_array_full.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index a4876ab4d..079c2f62e 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -834,8 +834,8 @@ namespace smt { } for (enode* n : m_lambdas) for (enode* p : n->get_parents()) - if (!ctx.is_beta_redex(p, n)) { - TRACE("array", tout << "not a beta redex " << enode_pp(p, ctx) << "\n"); + if (!is_default(p) && !ctx.is_beta_redex(p, n)) { + TRACE("array", tout << "lambda is not a beta redex " << enode_pp(p, ctx) << "\n"); return true; } return false; From 9ca0faa0918c25656bda75c86e9df32b53e99d5b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 18:13:44 +0100 Subject: [PATCH 135/220] enable interactive example --- src/tactic/core/eliminate_predicates_tactic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index c2eb90742..51bb4a6c3 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -30,7 +30,7 @@ resolution. the predicate `p` occurs once positively. All negative occurrences of `p` are resolved against this positive occurrence. The result of resolution is a set of equalities between arguments to `p`. The function `f` is replaced by a partial solution. -``` +```z3 (declare-fun f (Int Int Int) Int) (declare-fun p (Int) Bool) (declare-const a Int) From cd2ea6b703216eaa68b3c86ee182ae443431f882 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 18:14:08 +0100 Subject: [PATCH 136/220] add parameter access to C++ API --- src/api/c++/z3++.h | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 52d5e6573..88b520147 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -151,6 +151,7 @@ namespace z3 { } + /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ @@ -750,6 +751,7 @@ namespace z3 { func_decl_vector recognizers(); }; + /** \brief Function declaration (aka function definition). It is the signature of interpreted and uninterpreted functions in Z3. The basic building block in Z3 is the function application. @@ -770,6 +772,8 @@ namespace z3 { sort range() const { Z3_sort r = Z3_get_range(ctx(), *this); check_error(); return sort(ctx(), r); } symbol name() const { Z3_symbol s = Z3_get_decl_name(ctx(), *this); check_error(); return symbol(ctx(), s); } Z3_decl_kind decl_kind() const { return Z3_get_decl_kind(ctx(), *this); } + unsigned num_parameters() const { return Z3_get_decl_num_parameters(ctx(), *this); } + func_decl transitive_closure(func_decl const&) { Z3_func_decl tc = Z3_mk_transitive_closure(ctx(), *this); check_error(); return func_decl(ctx(), tc); @@ -2687,6 +2691,43 @@ namespace z3 { return out; } + /** + \brief class for auxiliary parameters associated with func_decl + The class is initialized with a func_decl or application expression and an index + The accessor get_expr, get_sort, ... is available depending on the value of kind(). + The caller is responsible to check that the kind of the parameter aligns with the call (get_expr etc). + + Parameters are available on some declarations to contain additional information that is not passed as + arguments when a function is applied to arguments. For example, bit-vector extraction has two + integer parameters. Array map has a function parameter. + */ + class parameter { + Z3_parameter_kind m_kind; + func_decl m_decl; + unsigned m_index; + context& ctx() const { return m_decl.ctx(); } + void check_error() const { ctx().check_error(); } + public: + parameter(func_decl const& d, unsigned idx) : m_decl(d), m_index(idx) { + if (ctx().enable_exceptions() && idx >= d.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), d, idx); + } + parameter(expr const& e, unsigned idx) : m_decl(e.decl()), m_index(idx) { + if (ctx().enable_exceptions() && idx >= m_decl.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), m_decl, idx); + } + Z3_parameter_kind kind() const { return m_kind; } + expr get_expr() const { Z3_ast a = Z3_get_decl_ast_parameter(ctx(), m_decl, m_index); check_error(); return expr(ctx(), a); } + sort get_sort() const { Z3_sort s = Z3_get_decl_sort_parameter(ctx(), m_decl, m_index); check_error(); return sort(ctx(), s); } + func_decl get_decl() const { Z3_func_decl f = Z3_get_decl_func_decl_parameter(ctx(), m_decl, m_index); check_error(); return func_decl(ctx(), f); } + symbol get_symbol() const { Z3_symbol s = Z3_get_decl_symbol_parameter(ctx(), m_decl, m_index); check_error(); return symbol(ctx(), s); } + std::string get_rational() const { Z3_string s = Z3_get_decl_rational_parameter(ctx(), m_decl, m_index); check_error(); return s; } + double get_double() const { double d = Z3_get_decl_double_parameter(ctx(), m_decl, m_index); check_error(); return d; } + int get_int() const { int i = Z3_get_decl_int_parameter(ctx(), m_decl, m_index); check_error(); return i; } + }; + class solver : public object { Z3_solver m_solver; From ce501e0b6e6bab4c2d88f6eff434561f172920bf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2023 17:37:59 -0700 Subject: [PATCH 137/220] #6646 - always enable special-relations theory to deal with default setting and push - fix bugs related to equality and transitivity. --- src/smt/smt_setup.cpp | 4 +-- src/smt/theory_special_relations.cpp | 39 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 854ddb9e0..7699fc3a5 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -802,7 +802,7 @@ namespace smt { setup_dl(); setup_seq_str(st); setup_fpa(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); } void setup::setup_unknown(static_features & st) { @@ -818,7 +818,7 @@ namespace smt { setup_seq_str(st); setup_fpa(); setup_recfuns(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); return; } diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 9113f189e..245a2ee58 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -71,7 +71,7 @@ namespace smt { ensure_var(v1); ensure_var(v2); literal_vector ls; - ls.push_back(l); + ls.push_back(l); return m_graph.add_non_strict_edge(v1, v2, ls) && m_graph.add_non_strict_edge(v2, v1, ls); } @@ -130,6 +130,7 @@ namespace smt { } bool theory_special_relations::internalize_term(app * term) { + verbose_stream() << mk_pp(term, m) << "\n"; return false; } @@ -156,9 +157,8 @@ namespace smt { } theory_var theory_special_relations::mk_var(expr* e) { - if (!ctx.e_internalized(e)) { + if (!ctx.e_internalized(e)) ctx.internalize(e, false); - } enode * n = ctx.get_enode(e); theory_var v = n->get_th_var(get_id()); if (null_theory_var == v) { @@ -582,18 +582,18 @@ namespace smt { lbool theory_special_relations::final_check_po(relation& r) { for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; - if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { - // v1 !-> v2 - // find v1 -> v3 -> v4 -> v2 path - r.m_explanation.reset(); - unsigned timestamp = r.m_graph.get_timestamp(); - bool found_path = r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); - if (found_path) { - TRACE("special_relations", tout << "check po conflict\n";); - r.m_explanation.push_back(a.explanation()); - set_conflict(r); - return l_false; - } + if (a.phase()) + continue; + // v1 !-> v2 + // find v1 -> v3 -> v4 -> v2 path + r.m_explanation.reset(); + unsigned timestamp = r.m_graph.get_timestamp(); + bool found_path = a.v1() == a.v2() || r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); + if (found_path) { + TRACE("special_relations", tout << "check po conflict\n";); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; } } return l_true; @@ -601,9 +601,8 @@ namespace smt { void theory_special_relations::propagate() { if (m_can_propagate) { - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) propagate(*kv.m_value); - } m_can_propagate = false; } } @@ -1124,12 +1123,12 @@ namespace smt { } void theory_special_relations::display(std::ostream & out) const { - if (m_relations.empty()) return; + if (m_relations.empty()) + return; out << "Theory Special Relations\n"; display_var2enode(out); - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) kv.m_value->display(*this, out); - } } void theory_special_relations::collect_asserted_po_atoms(vector>& atoms) const { From 8a3a3dc91bf228209615d5c563c7713e47713873 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 26 Mar 2023 15:31:37 -0700 Subject: [PATCH 138/220] fix #6648 --- src/sat/smt/pb_internalize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 1a83dbc87..17f2bd4de 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -119,7 +119,7 @@ namespace pb { else { bool_var v = s().add_var(true); literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } @@ -146,7 +146,7 @@ namespace pb { else { sat::bool_var v = s().add_var(true); sat::literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } From b4ad747e0b1ab7926f9863910b39a9c642b9aad8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:00:38 -0700 Subject: [PATCH 139/220] fix #6644 --- examples/java/JavaExample.java | 21 +++++++++++++++++++++ src/api/java/Context.java | 15 ++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 150efd545..9297dee47 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2259,6 +2259,24 @@ class JavaExample System.out.println(e1.equals(e3)); } + public void stringExample() { + System.out.println("String example"); + Context ctx = new Context(); + var a = ctx.mkToRe(ctx.mkString("abcd")); + var b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); + System.out.println(a); + System.out.println(b); + System.out.println(a.getSort()); + System.out.println(b.getSort()); + var c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), + ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkEmptyRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkAllcharRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkToRe(ctx.mkString("d"))); + System.out.println(c); + + } + public static void main(String[] args) { JavaExample p = new JavaExample(); @@ -2274,12 +2292,15 @@ class JavaExample System.out.print("Z3 Full Version String: "); System.out.println(Version.getFullVersion()); + p.stringExample(); + p.simpleExample(); { // These examples need model generation turned on. HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); + p.optimizeExample(ctx); p.basicTests(ctx); diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 06b312303..da2426305 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2175,10 +2175,10 @@ public class Context implements AutoCloseable { /** * Convert a regular expression that accepts sequence s. */ - public final ReExpr mkToRe(Expr> s) + public final ReExpr> mkToRe(Expr> s) { checkContextMatch(s); - return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } @@ -2296,7 +2296,7 @@ public class Context implements AutoCloseable { * Create the empty regular expression. * Coresponds to re.none */ - public final ReExpr mkEmptyRe(R s) + public final ReExpr mkEmptyRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } @@ -2305,16 +2305,17 @@ public class Context implements AutoCloseable { * Create the full regular expression. * Corresponds to re.all */ - public final ReExpr mkFullRe(R s) + public final ReExpr mkFullRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } /** * Create regular expression that accepts all characters + * R has to be a sequence sort. * Corresponds to re.allchar */ - public final ReExpr mkAllcharRe(R s) + public final ReExpr mkAllcharRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject())); } @@ -2322,10 +2323,10 @@ public class Context implements AutoCloseable { /** * Create a range expression. */ - public final ReExpr mkRange(Expr> lo, Expr> hi) + public final ReExpr> mkRange(Expr> lo, Expr> hi) { checkContextMatch(lo, hi); - return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); } /** From ce09c2ea6dc4fbc92acd435c49c7dcb40650f2e2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:56:09 -0700 Subject: [PATCH 140/220] fix build --- examples/java/JavaExample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 9297dee47..f6f171c46 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2262,13 +2262,13 @@ class JavaExample public void stringExample() { System.out.println("String example"); Context ctx = new Context(); - var a = ctx.mkToRe(ctx.mkString("abcd")); - var b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); + Expr a = ctx.mkToRe(ctx.mkString("abcd")); + Expr b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); System.out.println(a); System.out.println(b); System.out.println(a.getSort()); System.out.println(b.getSort()); - var c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), + Expr c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())), ctx.mkEmptyRe(ctx.mkReSort(ctx.mkStringSort())), ctx.mkAllcharRe(ctx.mkReSort(ctx.mkStringSort())), From 0a59617bac66ceda59f50d67ea036ec75bb72bb7 Mon Sep 17 00:00:00 2001 From: Patrick LaFontaine <32135464+Pat-Lafon@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:04:32 -0400 Subject: [PATCH 141/220] Fix Ocaml bindings FuncEntry to_string (#6639) Hello, I was looking at the different api string conversions for FuncEntry and I believe that the ml version is incorrect? Clearly we want the argument(`c`) to be comma separated from the accumulated string `p`. The current implementation just so happens to have most of the arguments separated, but the order is flipped and one of the commas is misplaced. --- src/api/ml/z3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index ac21902fe..4c8b0322a 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1542,7 +1542,7 @@ struct let to_string (x:func_entry) = let a = get_args x in - let f c p = (p ^ (Expr.to_string c) ^ ", ") in + let f c p = ((Expr.to_string c) ^ ", " ^ p) in "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end From f366772f0c1ce34ee1f2fdaae1f8ad5e31609f51 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 09:56:38 -0700 Subject: [PATCH 142/220] use field 'm' for streamlined representation --- src/tactic/core/symmetry_reduce_tactic.cpp | 108 +++++++++------------ 1 file changed, 45 insertions(+), 63 deletions(-) diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index e94e83679..9c308b60f 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -47,13 +47,13 @@ public: }; class ac_rewriter { - ast_manager& m_manager; + ast_manager& m; public: - ac_rewriter(ast_manager& m): m_manager(m) {} + ac_rewriter(ast_manager& m): m(m) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if ((f->is_associative() && f->is_commutative()) || - m_manager.is_distinct(f)) { + m.is_distinct(f)) { ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); @@ -62,13 +62,13 @@ public: change = (args[i] != buffer[i]); } if (change) { - result = m().mk_app(f, num_args, buffer.begin()); + result = m.mk_app(f, num_args, buffer.begin()); return BR_DONE; } } else if (f->is_commutative() && num_args == 2 && args[0]->get_id() > args[1]->get_id()) { expr* args2[2] = { args[1], args[0] }; - result = m().mk_app(f, num_args, args2); + result = m.mk_app(f, num_args, args2); return BR_DONE; } return BR_FAILED; @@ -76,10 +76,8 @@ public: void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) - result = m().mk_app(f, num_args, args); + result = m.mk_app(f, num_args, args); } -private: - ast_manager& m() const { return m_manager; } }; @@ -110,13 +108,12 @@ class symmetry_reduce_tactic::imp { typedef ptr_vector term_set; typedef obj_map app_map; typedef u_map > inv_app_map; - ast_manager& m_manager; + ast_manager& m; ac_rewriter_star m_rewriter; scoped_ptr m_replace; - ast_manager& m() const { return m_manager; } public: - imp(ast_manager& m) : m_manager(m), m_rewriter(m) { + imp(ast_manager& m) : m(m), m_rewriter(m) { m_replace = mk_default_expr_replacer(m, false); } @@ -124,10 +121,10 @@ public: void operator()(goal & g) { if (g.inconsistent()) - return; + return; tactic_report report("symmetry-reduce", g); vector > P; - expr_ref fml(m()); + expr_ref fml(m); to_formula(g, fml); app_map occs; compute_occurrences(fml, occs); @@ -149,11 +146,12 @@ public: app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); - expr* mem = mk_member(t, cts); + expr_ref mem(mk_member(t, cts), m); g.assert_expr(mem); num_sym_break_preds++; - TRACE("symmetry_reduce", tout << "member predicate: " << mk_pp(mem, m()) << "\n";); - fml = m().mk_and(fml.get(), mem); + TRACE("symmetry_reduce", tout << "member predicate: " << mem << "\n";); + + fml = m.mk_and(fml.get(), mem); normalize(fml); } } @@ -167,7 +165,7 @@ private: for (unsigned i = 0; i < g.size(); ++i) { conjs.push_back(g.form(i)); } - fml = m().mk_and(conjs.size(), conjs.data()); + fml = m.mk_and(conjs.size(), conjs.data()); normalize(fml); } @@ -184,28 +182,17 @@ private: // compute_siblings(fml, coloring); compute_inv_app(coloring, inv_color); - inv_app_map::iterator it = inv_color.begin(), end = inv_color.end(); - for (; it != end; ++it) { - if (it->m_value.size() < 2) { + for (auto const& [k, v] : inv_color) { + if (v.size() < 2) continue; - } - VERIFY(occs.find(it->m_value[0], num_occs)); - if (num_occs < 2) { + VERIFY(occs.find(v[0], num_occs)); + if (num_occs < 2) continue; - } - bool is_const = true; - for (unsigned j = 0; is_const && j < it->m_value.size(); ++j) { - is_const = it->m_value[j]->get_num_args() == 0; - } - if (!is_const) { + bool is_const = all_of(v, [&](app* a) { return a->get_num_args() == 0; }); + if (!is_const) continue; - } - P.push_back(it->m_value); - TRACE("symmetry_reduce", - for (unsigned i = 0; i < it->m_value.size(); ++i) { - tout << mk_pp(it->m_value[i], m()) << " "; - } - tout << "\n";); + P.push_back(v); + TRACE("symmetry_reduce", for (app * a : v) tout << mk_pp(a, m) << " "; tout << "\n";); } } @@ -426,14 +413,14 @@ private: tout << "Not symmetric: "; } for (unsigned i = 0; i < p.size(); ++i) { - tout << mk_pp(p[i], m()) << " "; + tout << mk_pp(p[i], m) << " "; } tout << "\n";); return result; } bool check_swap(expr* fml, app* t1, app* t2) { - expr_substitution sub(m()); + expr_substitution sub(m); sub.insert(t1, t2); sub.insert(t2, t1); m_replace->set_substitution(&sub); @@ -441,7 +428,7 @@ private: } bool check_cycle(expr* fml, permutation& p) { - expr_substitution sub(m()); + expr_substitution sub(m); for (unsigned i = 0; i + 1 < p.size(); ++i) { sub.insert(p[i], p[i+1]); } @@ -451,15 +438,15 @@ private: } bool check_substitution(expr* t) { - expr_ref r(m()); + expr_ref r(m); (*m_replace)(t, r); normalize(r); return t == r.get(); } void normalize(expr_ref& r) { - proof_ref pr(m()); - expr_ref result(m()); + proof_ref pr(m); + expr_ref result(m); m_rewriter(r.get(), result, pr); r = result; } @@ -473,7 +460,7 @@ private: while (!todo.empty()) { fml = todo.back(); todo.pop_back(); - if (m().is_and(fml)) { + if (m.is_and(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } else if (is_range_restriction(fml, p, t)) { @@ -482,13 +469,13 @@ private: } } bool is_range_restriction(expr* form, term_set const& C, app*& t) { - if (!m().is_or(form)) return false; + if (!m.is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); t = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; - if (!m().is_eq(e, e1, e2)) return false; + if (!m.is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); if (C.contains(a1) && (t == nullptr || t == a2)) { @@ -515,13 +502,9 @@ private: num_occurrences(app_map& occs): m_occs(occs) {} void operator()(app* n) { m_occs.insert_if_not_there(n, 0); - unsigned sz = n->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = n->get_arg(i); - if (is_app(arg)) { - m_occs.insert_if_not_there(to_app(arg), 0)++; - } - } + for (expr* arg : *n) + if (is_app(arg)) + m_occs.insert_if_not_there(to_app(arg), 0)++; } void operator()(quantifier * n) {} void operator()(var * n) {} @@ -540,7 +523,7 @@ private: unsigned weight = 0, weight1 = 0; VERIFY(occs.find(t, weight)); unsigned cts_delta = compute_cts_delta(t, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t, m()) << " " << weight << " " << cts_delta << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t, m) << " " << weight << " " << cts_delta << "\n";); for (unsigned i = 1; i < T.size(); ++i) { app* t1 = T[i]; VERIFY(occs.find(t1, weight1)); @@ -548,7 +531,7 @@ private: continue; } unsigned cts_delta1 = compute_cts_delta(t1, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t1, m()) << " " << weight1 << " " << cts_delta1 << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t1, m) << " " << weight1 << " " << cts_delta1 << "\n";); if ((t->get_num_args() == t1->get_num_args() && (weight1 > weight || cts_delta1 < cts_delta)) || t->get_num_args() > t1->get_num_args()) { cts_delta = cts_delta1; @@ -577,15 +560,15 @@ private: member_of mem(consts, cts); for_each_expr(mem, t); TRACE("symmetry_reduce", - tout << "Term: " << mk_pp(t, m()) << "\n"; + tout << "Term: " << mk_pp(t, m) << "\n"; tout << "Support set: "; for (unsigned i = 0; i < consts.size(); ++i) { - tout << mk_pp(consts[i], m()) << " "; + tout << mk_pp(consts[i], m) << " "; } tout << "\n"; tout << "Constants: "; for (unsigned i = 0; i < cts.size(); ++i) { - tout << mk_pp(cts[i], m()) << " "; + tout << mk_pp(cts[i], m) << " "; } tout << "\n"; ); @@ -606,15 +589,14 @@ private: app* select_const(term_set const& A, term_set const& B) { unsigned j; for (j = 0; j < A.size() && B.contains(A[j]); ++j); - return (j == A.size())?0:A[j]; + return (j == A.size())? nullptr:A[j]; } expr* mk_member(app* t, term_set const& C) { - expr_ref_vector eqs(m()); - for (unsigned i = 0; i < C.size(); ++i) { - eqs.push_back(m().mk_eq(t, C[i])); - } - return mk_or(m(), eqs.size(), eqs.data()); + expr_ref_vector eqs(m); + for (expr* e : C) + eqs.push_back(m.mk_eq(t, e)); + return mk_or(eqs); } }; From adec9372960c271d1e0b7f67706e8c926a333ead Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 14:02:15 -0700 Subject: [PATCH 143/220] fix #6650 --- src/tactic/core/symmetry_reduce_tactic.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 9c308b60f..9ad12d504 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -146,9 +146,10 @@ public: app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); - expr_ref mem(mk_member(t, cts), m); + expr_ref mem = mk_member(t, cts); g.assert_expr(mem); num_sym_break_preds++; + TRACE("symmetry_reduce", tout << "member predicate: " << mem << "\n";); fml = m.mk_and(fml.get(), mem); @@ -592,7 +593,7 @@ private: return (j == A.size())? nullptr:A[j]; } - expr* mk_member(app* t, term_set const& C) { + expr_ref mk_member(app* t, term_set const& C) { expr_ref_vector eqs(m); for (expr* e : C) eqs.push_back(m.mk_eq(t, e)); From fe348b84c96a50c64979b58feac7bff5e8bb57ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 27 Mar 2023 16:20:33 -0700 Subject: [PATCH 144/220] fix #6652 --- src/tactic/aig/aig_tactic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index 9c5390c16..8027e6484 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -103,6 +103,7 @@ public: new_f = conj; g->assert_expr(new_f); } + g->elim_true(); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { From 130400d76e3cc37e4b6f3a9ed6e8a4781b7f3599 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 28 Mar 2023 08:55:52 -0700 Subject: [PATCH 145/220] Remove non feasible costs (#6653) * before rm lu Signed-off-by: Lev Nachmanson * rm get_column_in_lu_mode * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm_lp Signed-off-by: Lev Nachmanson * rm_lu * rm lu * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm lu * rm lu Signed-off-by: Lev Nachmanson * rm lu Signed-off-by: Lev Nachmanson * rm lu * cleanup * rm breakpoints * rm dealing with doubles * Revert "rm dealing with doubles" This reverts commit 547254abe786c80231ca78bcd245e6ddb5a15c47. * rm lu Signed-off-by: Lev Nachmanson * rm lu * rm lu * rm scaler * rm square_sparse_matrix * more cleanup * rm dead code * rp precise * remove many methods dealing with double Signed-off-by: Lev Nachmanson * rm lu related fields from lp_core_solver_base.h * remove dead code Signed-off-by: Lev Nachmanson * more dead code removal * remove more dead code * more dead code * rm dead code * more dead code * fix lp_tst * more dead code * replace lp_assert(false) with UNREACHABLE * rm non feas costs Signed-off-by: Lev Nachmanson * fix the build Signed-off-by: Lev Nachmanson --------- Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lar_solver.cpp | 23 +--- src/math/lp/lp_core_solver_base.cpp | 2 - src/math/lp/lp_core_solver_base.h | 13 +- src/math/lp/lp_core_solver_base_def.h | 56 --------- src/math/lp/lp_primal_core_solver.cpp | 1 - src/math/lp/lp_primal_core_solver.h | 36 +----- src/math/lp/lp_primal_core_solver_def.h | 112 ------------------ .../lp/lp_primal_core_solver_tableau_def.h | 76 ++---------- 9 files changed, 21 insertions(+), 299 deletions(-) diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index e22d77c1b..550b6fe36 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -40,7 +40,6 @@ void lar_core_solver::prefix_r() { if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); - m_r_solver.set_using_infeas_costs(true); } } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9fab5e437..21eec31d8 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -191,9 +191,11 @@ namespace lp { stats().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); - + auto strategy_was = settings().simplex_strategy(); + settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); + settings().set_simplex_strategy(strategy_was); return ret; } @@ -355,7 +357,6 @@ namespace lp { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); move_non_basic_columns_to_bounds(false); auto& rslv = m_mpq_lar_core_solver.m_r_solver; - rslv.set_using_infeas_costs(false); lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); @@ -490,11 +491,11 @@ namespace lp { lp_status lar_solver::maximize_term(unsigned j_or_term, impq& term_max) { TRACE("lar_solver", print_values(tout);); + lar_term term = get_term_to_maximize(j_or_term); if (term.is_empty()) { return lp_status::UNBOUNDED; } - impq prev_value; auto backup = m_mpq_lar_core_solver.m_r_x; if (m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()) { @@ -710,14 +711,6 @@ namespace lp { void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); - - if (tableau_with_costs()) { - if (m_mpq_lar_core_solver.m_r_solver.using_infeas_costs()) { - for (unsigned j : m_basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } } @@ -1344,14 +1337,6 @@ namespace lp { for (unsigned j : became_feas) m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); - - if (use_tableau_costs()) { - for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - for (unsigned j : basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } } bool lar_solver::model_is_int_feasible() const { diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 9a4c375f6..f1ae95ea0 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -65,8 +65,6 @@ template bool lp::lp_core_solver_base::pivot_column_tableau(un template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; -template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index fc1673242..fb0c28507 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -63,15 +63,12 @@ public: bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } private: u_set m_inf_set; - bool m_using_infeas_costs; public: const u_set& inf_set() const { return m_inf_set; } u_set& inf_set() { return m_inf_set; } void inf_set_increase_size_by_one() { m_inf_set.increase_size_by_one(); } bool inf_set_contains(unsigned j) const { return m_inf_set.contains(j); } - unsigned inf_set_size() const { return m_inf_set.size(); } - bool using_infeas_costs() const { return m_using_infeas_costs; } - void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } + unsigned inf_set_size() const { return m_inf_set.size(); } indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A // vector const & m_b; // the right side @@ -198,11 +195,7 @@ public: if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; CASSERT("check_static_matrix", m_A.is_correct()); - if (m_using_infeas_costs) { - if (infeasibility_costs_are_correct() == false) { - return false; - } - } + unsigned n = m_A.column_count(); for (unsigned j = 0; j < n; j++) { @@ -236,8 +229,6 @@ public: return below_bound(m_x[p], m_lower_bounds[p]); } - bool infeasibility_costs_are_correct() const; - bool infeasibility_cost_is_correct_for_column(unsigned j) const; bool x_above_lower_bound(unsigned p) const { return above_bound(m_x[p], m_lower_bounds[p]); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index cb0084a16..8619c926e 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -43,7 +43,6 @@ lp_core_solver_base(static_matrix & A, m_iters_with_no_cost_growing(0), m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), - m_using_infeas_costs(false), m_pivot_row(A.column_count()), m_A(A), m_basis(basis), @@ -94,8 +93,6 @@ pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { - - // template void lp_core_solver_base:: // update_index_of_ed() { // m_index_of_ed.clear(); @@ -434,57 +431,4 @@ template bool lp_core_solver_base::remove_from_ba } -template bool -lp_core_solver_base::infeasibility_costs_are_correct() const { - if (! this->m_using_infeas_costs) - return true; - lp_assert(costs_on_nbasis_are_zeros()); - for (unsigned j :this->m_basis) { - if (!infeasibility_cost_is_correct_for_column(j)) { - TRACE("lar_solver", tout << "incorrect cost for column " << j << std::endl;); - return false; - } - if (!is_zero(m_d[j])) { - TRACE("lar_solver", tout << "non zero inf cost for basis j = " << j << std::endl;); - return false; - } - } - return true; -} - -template bool -lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { - T r = -one_of_type(); - - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - return (this->m_costs[j] == r); - } - if (this->x_below_low_bound(j)) { - return (this->m_costs[j] == -r); - } - return is_zero(this->m_costs[j]); - - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - return this->m_costs[j] == -r; - } - return is_zero(this->m_costs[j]); - - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - return this->m_costs[j] == r; - } - return is_zero(this->m_costs[j]); - case column_type::free_column: - return is_zero(this->m_costs[j]); - default: - UNREACHABLE(); - return true; - } -} - - } diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index 22042668d..efbfd27e1 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -33,6 +33,5 @@ template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); -template void lp::lp_primal_core_solver >::update_inf_cost_for_column_tableau(unsigned); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 641f6be21..207428985 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -334,28 +334,12 @@ public: void update_x_tableau_rows(unsigned entering, unsigned leaving, const X &delta) { this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto &c : this->m_A.m_columns[entering]) { - if (leaving != this->m_basis[c.var()]) { - this->add_delta_to_x_and_track_feasibility( - this->m_basis[c.var()], -delta * this->m_A.get_val(c)); - } - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto &c : this->m_A.m_columns[entering]) { - unsigned j = this->m_basis[c.var()]; - if (j != leaving) - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } - } + for (const auto &c : this->m_A.m_columns[entering]) { + if (leaving != this->m_basis[c.var()]) { + this->add_delta_to_x_and_track_feasibility( + this->m_basis[c.var()], -delta * this->m_A.get_val(c)); + } + } } void update_basis_and_x_tableau_rows(int entering, int leaving, X const &tt) { @@ -437,7 +421,6 @@ public: } void decide_on_status_when_cannot_find_entering() { - lp_assert(!need_to_switch_costs()); this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL : lp_status::INFEASIBLE); } @@ -628,11 +611,6 @@ public: bool column_is_benefitial_for_entering_basis(unsigned j) const; void init_infeasibility_costs(); - - void init_infeasibility_cost_for_column(unsigned j); - T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - void print_column(unsigned j, std::ostream &out); void print_bound_info_and_x(unsigned j, std::ostream &out); @@ -652,8 +630,6 @@ public: void init_run_tableau(); void update_x_tableau(unsigned entering, const X &delta); - void update_inf_cost_for_column_tableau(unsigned j); - // the delta is between the old and the new cost (old - new) void update_reduced_cost_for_basic_column_cost_change(const T &delta, unsigned j) { diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 4ee8305fa..c3c545fdd 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -258,121 +258,9 @@ template void lp_primal_core_solver::find_feas solve(); } -template -void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { - for (unsigned i : this->m_ed.m_index) - init_infeasibility_cost_for_column(this->m_basis[i]); - this->set_using_infeas_costs(true); -} -template -void lp_primal_core_solver::init_infeasibility_costs() { - lp_assert(this->m_x.size() >= this->m_n()); - lp_assert(this->m_column_types.size() >= this->m_n()); - for (unsigned j = this->m_n(); j--;) - init_infeasibility_cost_for_column(j); - this->set_using_infeas_costs(true); -} -template T -lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const { - if (this->m_basis_heading[j] < 0) { - return zero_of_type(); - } - T ret; - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::free_column: - ret = numeric_traits::zero(); - break; - default: - UNREACHABLE(); - ret = numeric_traits::zero(); // does not matter - break; - } - - ret = - ret; - - return ret; -} - - -// changed m_inf_set too! -template void -lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { - - if (this->m_basis_heading[j] < 0) { - this->m_costs[j] = numeric_traits::zero(); - this->remove_column_from_inf_set(j); - return; - } - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::free_column: - this->m_costs[j] = numeric_traits::zero(); - break; - default: - UNREACHABLE(); - break; - } - - if (numeric_traits::is_zero(this->m_costs[j])) { - this->remove_column_from_inf_set(j); - } else { - this->insert_column_into_inf_set(j); - } - this->m_costs[j] = - this->m_costs[j]; - -} template void lp_primal_core_solver::print_column(unsigned j, std::ostream & out) { diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 241164c02..898abb152 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -96,7 +96,7 @@ unsigned lp_primal_core_solver::solve() { } do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { + if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over( "feas t", * this->m_settings.get_message_ostream())) { return this->total_iterations(); } if (this->m_settings.use_tableau_rows()) { @@ -107,28 +107,12 @@ unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { case lp_status::OPTIMAL: // check again that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - { // precise case - if ((!this->infeasibility_costs_are_correct())) { - init_reduced_costs_tableau(); // forcing recalc - if (choose_entering_column_tableau() == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } - } break; case lp_status::TENTATIVE_UNBOUNDED: UNREACHABLE(); break; case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs_tableau(); - this->set_status(lp_status::UNKNOWN); - } + lp_assert (this->current_x_is_feasible()); break; case lp_status::UNSTABLE: @@ -169,7 +153,7 @@ template void lp_primal_core_solver::advance_on_en lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || !is_neg(t)); + lp_assert(!is_neg(t)); lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); @@ -191,11 +175,6 @@ template void lp_primal_core_solver::advance_on_en return; if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if (need_to_switch_costs()) { - this->init_reduced_costs_tableau(); - } - - lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -208,9 +187,7 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; - if (need_to_switch_costs()) { - init_reduced_costs_tableau(); - } + this->iters_with_no_cost_growing() = 0; } template int lp_primal_core_solver::find_leaving_and_t_tableau(unsigned entering, X & t) { @@ -299,54 +276,19 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) { this->change_basis(entering, leaving); return true; } + template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - unsigned j = this->m_basis[i]; - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } + for (const auto & c : this->m_A.m_columns[entering]) { + unsigned i = c.var(); + this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } } -template void lp_primal_core_solver:: -update_inf_cost_for_column_tableau(unsigned j) { - lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); - - lp_assert(this->using_infeas_costs()); - - T new_cost = get_infeasibility_cost_for_column(j); - T delta = this->m_costs[j] - new_cost; - if (is_zero(delta)) - return; - this->m_costs[j] = new_cost; - update_reduced_cost_for_basic_column_cost_change(delta, j); -} template void lp_primal_core_solver::init_reduced_costs_tableau() { - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } + unsigned size = this->m_basis_heading.size(); for (unsigned j = 0; j < size; j++) { if (this->m_basis_heading[j] >= 0) From 667080710366eab733cbf9ce404a7e7b9cb455b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=8A=B9=EC=99=84?= <36997987+Hhro@users.noreply.github.com> Date: Wed, 29 Mar 2023 21:49:33 +0900 Subject: [PATCH 146/220] update ocaml binding to support more string apis (#6656) --- .gitignore | 3 +++ src/api/ml/z3.ml | 12 ++++++++++++ src/api/ml/z3.mli | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/.gitignore b/.gitignore index b0c19a432..710ce4b7e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,12 @@ callgrind.out.* .z3-trace # OCaml generated files *.a +*.o *.cma *.cmo *.cmi +*.cmx +*.byte *.cmxa ocamlz3 # Java generated files diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 4c8b0322a..98807bedd 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1253,7 +1253,9 @@ struct let mk_re_sort = Z3native.mk_re_sort let is_re_sort = Z3native.is_re_sort let mk_string_sort = Z3native.mk_string_sort + let mk_char_sort = Z3native.mk_char_sort let is_string_sort = Z3native.is_string_sort + let is_char_sort = Z3native.is_char_sort let mk_string = Z3native.mk_string let is_string = Z3native.is_string let get_string = Z3native.get_string @@ -1274,6 +1276,10 @@ struct let mk_str_le = Z3native.mk_str_le let mk_str_lt = Z3native.mk_str_lt let mk_int_to_str = Z3native.mk_int_to_str + let mk_string_to_code = Z3native.mk_string_to_code + let mk_string_from_code = Z3native.mk_string_from_code + let mk_ubv_to_str = Z3native.mk_ubv_to_str + let mk_sbv_to_str = Z3native.mk_sbv_to_str let mk_seq_to_re = Z3native.mk_seq_to_re let mk_seq_in_re = Z3native.mk_seq_in_re let mk_re_plus = Z3native.mk_re_plus @@ -1287,6 +1293,12 @@ struct let mk_re_complement = Z3native.mk_re_complement let mk_re_empty = Z3native.mk_re_empty let mk_re_full = Z3native.mk_re_full + let mk_char = Z3native.mk_char + let mk_char_le = Z3native.mk_char_le + let mk_char_to_int = Z3native.mk_char_to_int + let mk_char_to_bv = Z3native.mk_char_to_bv + let mk_char_from_bv = Z3native.mk_char_from_bv + let mk_char_is_digit = Z3native.mk_char_is_digit end module FloatingPoint = diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 27b0992ca..53e92b491 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1881,9 +1881,15 @@ sig (** create string sort *) val mk_string_sort : context -> Sort.sort + (** create char sort *) + val mk_char_sort : context -> Sort.sort + (** test if sort is a string sort (a sequence of 8-bit bit-vectors) *) val is_string_sort : context -> Sort.sort -> bool + (** test if sort is a char sort *) + val is_char_sort : context -> Sort.sort -> bool + (** create a string literal *) val mk_string : context -> string -> Expr.expr @@ -1936,6 +1942,7 @@ sig (** retrieve integer expression encoded in string *) val mk_str_to_int : context -> Expr.expr -> Expr.expr + (** compare strings less-than-or-equal *) val mk_str_le : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1945,6 +1952,18 @@ sig (** convert an integer expression to a string *) val mk_int_to_str : context -> Expr.expr -> Expr.expr + (** [mk_string_to_code ctx s] convert a unit length string [s] to integer code *) + val mk_string_to_code : context -> Expr.expr -> Expr.expr + + (** [mk_string_from_code ctx c] convert code [c] to a string *) + val mk_string_from_code : context -> Expr.expr -> Expr.expr + + (** [mk_ubv_to_str ctx ubv] convert a unsigned bitvector [ubv] to a string *) + val mk_ubv_to_str : context -> Expr.expr -> Expr.expr + + (** [mk_sbv_to_str ctx sbv] convert a signed bitvector [sbv] to a string *) + val mk_sbv_to_str : context -> Expr.expr -> Expr.expr + (** create regular expression that accepts the argument sequence *) val mk_seq_to_re : context -> Expr.expr -> Expr.expr @@ -1984,6 +2003,24 @@ sig (** the regular expression that accepts all sequences *) val mk_re_full : context -> Sort.sort -> Expr.expr + (** [mk_char ctx i] converts an integer to a character *) + val mk_char : context -> int -> Expr.expr + + (** [mk_char_le ctx lc rc] compares two characters *) + val mk_char_le : context -> Expr.expr -> Expr.expr -> Expr.expr + + (** [mk_char_to_int ctx c] converts the character [c] to an integer *) + val mk_char_to_int : context -> Expr.expr -> Expr.expr + + (** [mk_char_to_bv ctx c] converts the character [c] to a bitvector *) + val mk_char_to_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_from_bv ctx bv] converts the bitvector [bv] to a character *) + val mk_char_from_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_is_digit ctx c] checks if the character [c] is a digit *) + val mk_char_is_digit: context -> Expr.expr -> Expr.expr + end (** Floating-Point Arithmetic *) From b386b84f34b2995147a264c9efc97465592ec974 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 02:55:55 -0700 Subject: [PATCH 147/220] #6658 --- src/api/java/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index da2426305..7aaef4801 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2185,7 +2185,7 @@ public class Context implements AutoCloseable { /** * Check for regular expression membership. */ - public final BoolExpr mkInRe(Expr> s, Expr> re) + public final BoolExpr mkInRe(Expr> s, ReExpr> re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); From 996e5b1755a30196cf540730954cf7462a7c88e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 03:25:20 -0700 Subject: [PATCH 148/220] fix #6655 --- src/util/zstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 570510458..eaa5bb5ee 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -78,7 +78,7 @@ zstring::zstring(char const* s) { m_buffer.push_back(ch); } else { - m_buffer.push_back(*s); + m_buffer.push_back((unsigned char)*s); ++s; } } From 6aaaa3b0156c05ddd1a51b28f14eba91db038270 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 09:56:23 -0700 Subject: [PATCH 149/220] fix #6660 Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/update_api.py b/scripts/update_api.py index a3d92a7e9..4295b8961 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1825,6 +1825,7 @@ if _lib is None: else: print(" import builtins") print(" builtins.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) + print(_failures) raise Z3Exception("libz3.%s not found." % _ext) From a849a29b4f0af61730e986b4aed89ba0d05c089f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 10:31:18 -0700 Subject: [PATCH 150/220] fix #6659 --- src/ast/simplifiers/dominator_simplifier.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/dominator_simplifier.h b/src/ast/simplifiers/dominator_simplifier.h index b412f6db0..048f6991b 100644 --- a/src/ast/simplifiers/dominator_simplifier.h +++ b/src/ast/simplifiers/dominator_simplifier.h @@ -41,7 +41,7 @@ class dominator_simplifier : public dependent_expr_simplifier { bool is_subexpr(expr * a, expr * b); expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } - void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } + void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); m_trail.push_back(t); } void reset_cache() { m_result.reset(); } ptr_vector const & tree(expr * e); From a62e4b2893e57192dad477f9f48bb74e1e32a5bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 12:45:51 -0700 Subject: [PATCH 151/220] extract multi-patterns when pattern can be decomposed deals with fluke regression for F* reported by Guido Martinez Background: The automatic pattern inference facility looks for terms that contains all bound variables of a quantifier. It may end up with a term that contains all bound variables but the extracted term can be simplified. Example. The pattern (ApplyTT (ApplyTT @x3!1 (ApplyTT @x4!0 (:var 1))) (ApplyTT @x4!0 (:var 0))) can be decomposed into a multi-pattern (ApplyTT @x4!0 (:var 1))) (ApplyTT @x4!0 (:var 0)) The multi-pattern may enable a quantifier instantiation while the original pattern does not. The multi-pattern should be preferred. The regression showed up based on a change that should not be considered harmful but turned out to be noticeable. The change was a simplification of and-or expressions based on sorting. This played with the case split queue used by F* (smt.case_split = 3) that uses a top-level case split of clauses to avoid redundant branches. The net effect was that without sorting, the benchmarks would always choose the opportune branch that enabled matching against the larger term. With sorting it would mostly choose inopportune branches. --- src/ast/pattern/pattern_inference.cpp | 40 ++++++++++++++++++++++++++- src/ast/pattern/pattern_inference.h | 3 ++ src/smt/smt_case_split_queue.cpp | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index d751a1388..e795188fb 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -405,6 +405,44 @@ bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); } + +app* pattern_inference_cfg::mk_pattern(app* candidate) { + auto has_var_arg = [&](expr* e) { + if (!is_app(e)) + return false; + for (expr* arg : *to_app(e)) + if (is_var(arg)) + return true; + return false; + }; + if (has_var_arg(candidate)) + return m.mk_pattern(candidate); + m_args.reset(); + for (expr* arg : *candidate) { + if (!is_app(arg)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(arg)); + } + for (unsigned i = 0; i < m_args.size(); ++i) { + app* arg = m_args[i]; + if (has_var_arg(arg)) + continue; + m_args[i] = m_args.back(); + --i; + m_args.pop_back(); + + if (is_ground(arg)) + continue; + + for (expr* e : *to_app(arg)) { + if (!is_app(e)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(e)); + } + } + return m.mk_pattern(m_args.size(), (app* const*)m_args.data()); +} + /** \brief Create unary patterns (single expressions that contain all bound variables). If a candidate does not contain all bound @@ -418,7 +456,7 @@ void pattern_inference_cfg::candidates2unary_patterns(ptr_vector const & ca expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { - app * new_pattern = m.mk_pattern(candidate); + app * new_pattern = mk_pattern(candidate); result.push_back(new_pattern); } else { diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index bb4cf4238..da905dca4 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -188,6 +188,9 @@ class pattern_inference_cfg : public default_rewriter_cfg { ptr_vector m_pre_patterns; expr_pattern_match m_database; + ptr_buffer m_args; + app* mk_pattern(app* candidate); + void candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result); diff --git a/src/smt/smt_case_split_queue.cpp b/src/smt/smt_case_split_queue.cpp index 9f5bdb0d4..711e3de3a 100644 --- a/src/smt/smt_case_split_queue.cpp +++ b/src/smt/smt_case_split_queue.cpp @@ -964,7 +964,7 @@ namespace { } void display(std::ostream & out) override { - if (m_queue.empty() && m_queue2.empty()) + if (m_queue.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); From 7664429fda65291ebe4d43c169f3923fc082a1a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 12:51:23 -0700 Subject: [PATCH 152/220] remove cast expression Signed-off-by: Nikolaj Bjorner --- src/ast/pattern/pattern_inference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index e795188fb..2fd2b4c82 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -440,7 +440,7 @@ app* pattern_inference_cfg::mk_pattern(app* candidate) { m_args.push_back(to_app(e)); } } - return m.mk_pattern(m_args.size(), (app* const*)m_args.data()); + return m.mk_pattern(m_args.size(), m_args.data()); } /** From e0a066efa3d509a7404c503cd95cb5febe32a6fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2023 15:38:29 -0700 Subject: [PATCH 153/220] #6654 fix reflexivity for tree-order --- src/smt/theory_special_relations.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 245a2ee58..ddddfbc00 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -405,6 +405,12 @@ namespace smt { TRACE("special_relations", tout << "already: " << a.v2() << " <= " << a.v1() << "\n";); continue; } + if (a.v1() == a.v2()) { + r.m_explanation.reset(); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; + } // the nodes visited from v1 become target for v2 if (r.m_graph.reachable(a.v2(), visited, target, w)) { // From 6324db207bd9300928ef610622a399a10040eb97 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Sun, 2 Apr 2023 13:39:13 -0400 Subject: [PATCH 154/220] Only print func-decl names for indexed parameters (#6663) --- src/ast/ast_smt2_pp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 74bb871ec..bfa262410 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -121,8 +121,10 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { std::string str = f->get_parameter(i).get_rational().to_string(); fs.push_back(mk_string(get_manager(), str)); } - else - fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); + else { + unsigned len; + fs.push_back(pp_fdecl_name(to_func_decl(f->get_parameter(i).get_ast()), len)); + } } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } From 5b385bd2fe03f781e0e59e25139006ae8d9354ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 10:58:01 -0700 Subject: [PATCH 155/220] fix #6665 Signed-off-by: Nikolaj Bjorner --- examples/java/JavaExample.java | 2 +- src/smt/theory_bv.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index f6f171c46..a27a60721 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2275,7 +2275,7 @@ class JavaExample ctx.mkToRe(ctx.mkString("d"))); System.out.println(c); - } + } public static void main(String[] args) { diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 00564ed3e..7adab35f4 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -946,6 +946,9 @@ namespace smt { internalize_bv2int(term); } return params().m_bv_enable_int2bv2int; + case OP_BSREM: return false; + case OP_BUREM: return false; + case OP_BSMOD: return false; default: TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, m) << "\n";); UNREACHABLE(); From def83ed26edbe786baabfdc760211d8537e10d9d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 11:13:37 -0700 Subject: [PATCH 156/220] fix #6661 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.h | 16 ++++++++-------- src/ast/special_relations_decl_plugin.cpp | 6 +++++- src/ast/special_relations_decl_plugin.h | 4 +++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 8fae83b26..4187fece6 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -161,7 +161,7 @@ public: bool is_zstring() const { return get_kind() == PARAM_ZSTRING; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } - bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } + bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), a && true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } @@ -180,13 +180,13 @@ public: */ void del_eh(ast_manager & m, family_id fid); - int get_int() const { return std::get(m_val); } - ast * get_ast() const { return std::get(m_val); } - symbol get_symbol() const { return std::get(m_val); } - rational const & get_rational() const { return *std::get(m_val); } - zstring const& get_zstring() const { return *std::get(m_val); } - double get_double() const { return std::get(m_val); } - unsigned get_ext_id() const { return std::get(m_val); } + int get_int() const { SASSERT(is_int()); return std::get(m_val); } + ast * get_ast() const { SASSERT(is_ast()); return std::get(m_val); } + symbol get_symbol() const { SASSERT(is_symbol()); return std::get(m_val); } + rational const & get_rational() const { SASSERT(is_rational()); return *std::get(m_val); } + zstring const& get_zstring() const { SASSERT(is_zstring()); return *std::get(m_val); } + double get_double() const { SASSERT(is_double()); return std::get(m_val); } + unsigned get_ext_id() const { SASSERT(is_external()); return std::get(m_val); } bool operator==(parameter const & p) const; bool operator!=(parameter const & p) const { return !operator==(p); } diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index 7ed5e8346..b45778d2f 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -54,7 +54,11 @@ func_decl * special_relations_decl_plugin::mk_func_decl( case OP_SPECIAL_RELATION_LO: name = m_lo; break; case OP_SPECIAL_RELATION_PLO: name = m_plo; break; case OP_SPECIAL_RELATION_TO: name = m_to; break; - case OP_SPECIAL_RELATION_TC: name = m_tc; break; + case OP_SPECIAL_RELATION_TC: + name = m_tc; + if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) + m_manager->raise_exception("parameter to transitive closure should be a function declaration"); + break; } return m_manager->mk_func_decl(name, arity, domain, range, info); } diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index daff802e7..75b5cb134 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -71,11 +71,13 @@ class special_relations_util { ast_manager& m; mutable family_id m_fid; func_decl* mk_rel_decl(func_decl* f, decl_kind k) { + SASSERT(f); parameter p(f); SASSERT(f->get_arity() == 2); return m.mk_func_decl(fid(), k, 1, &p, 2, f->get_domain(), f->get_range()); } family_id fid() const { - if (null_family_id == m_fid) m_fid = m.get_family_id("specrels"); + if (null_family_id == m_fid) + m_fid = m.get_family_id("specrels"); return m_fid; } public: From 479f8442009987726e3c03fe5618b250acca383a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Apr 2023 11:14:20 -0700 Subject: [PATCH 157/220] fix #6661 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 4187fece6..3e2f0288d 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -161,7 +161,7 @@ public: bool is_zstring() const { return get_kind() == PARAM_ZSTRING; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } - bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), a && true); } + bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } From f8242c58dd41bdb6cda7860ecdafe1a056b07b56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Apr 2023 22:29:22 -0700 Subject: [PATCH 158/220] fix regression from Grobner port - scan_for_linear returns true if it finds a new linear equation. It then should break GB. - if scan_for_linear returns false, it should still allow try_modify_eqs. This behavior was masked by requiring scan_for_linear to always be true before allowing try_to_modify_eqs. based on repro from Guido Martinez @mtzguido --- src/smt/theory_arith_nl.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 3aa34cbc7..d6d30cb33 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -2264,8 +2264,10 @@ typename theory_arith::gb_result theory_arith::compute_grobner(svector return GB_FAIL; if (get_gb_eqs_and_look_for_conflict(eqs, gb)) return GB_PROGRESS; + if (scan_for_linear(eqs, gb)) + return GB_NEW_EQ; } - while(scan_for_linear(eqs, gb) && m_params.m_nl_arith_gb_perturbate && + while(m_params.m_nl_arith_gb_perturbate && (!m_nl_gb_exhausted) && try_to_modify_eqs(eqs, gb, next_weight)); return GB_FAIL; } From 84b92046163239331fc6f20ac2951ff67a640ee1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 5 Apr 2023 16:39:10 -0700 Subject: [PATCH 159/220] inherit and reset rlimit counter on children limits addresses rlimit leak reported by @mtzguido --- src/ast/ast.cpp | 8 ++++++++ src/ast/ast.h | 2 ++ src/ast/rewriter/seq_eq_solver.cpp | 4 ++-- src/ast/special_relations_decl_plugin.h | 6 ++++++ src/util/rlimit.cpp | 2 ++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 0a34d3e12..7f9542fe4 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2322,6 +2322,14 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const return d; } +bool ast_manager::is_parametric_function(func_decl* f, func_decl *& g) const { + // is-as-array + // is-map + // is-transitive-closure + return false; +} + + sort * ast_manager::mk_fresh_sort(char const * prefix) { string_buffer<32> buffer; buffer << prefix << "!" << m_fresh_id; diff --git a/src/ast/ast.h b/src/ast/ast.h index 3e2f0288d..e0ae7b92f 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1924,6 +1924,8 @@ public: return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem); } + bool is_parametric_function(func_decl* f, func_decl *& g) const; + app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) { return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem)); } diff --git a/src/ast/rewriter/seq_eq_solver.cpp b/src/ast/rewriter/seq_eq_solver.cpp index 6d8734bc9..c6778c45e 100644 --- a/src/ast/rewriter/seq_eq_solver.cpp +++ b/src/ast/rewriter/seq_eq_solver.cpp @@ -190,8 +190,8 @@ namespace seq { expr_ref digit = m_ax.sk().mk_digit2int(u); add_consequence(m_ax.mk_ge(digit, 1)); } - expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); - ctx.add_solution(seq.str.mk_itos(n), y); + expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); + ctx.add_solution(seq.str.mk_itos(n), y); return true; } diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index 75b5cb134..0c4377864 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -101,6 +101,12 @@ public: bool is_to(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TO); } bool is_tc(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TC); } + bool is_lo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_LO); } + bool is_po(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PO); } + bool is_plo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PLO); } + bool is_to(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TO); } + bool is_tc(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TC); } + app * mk_lo (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_LO, arg1, arg2); } app * mk_po (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PO, arg1, arg2); } app * mk_plo(expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PLO, arg1, arg2); } diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index f71d2764a..ecc527681 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -87,6 +87,8 @@ void reslimit::push_child(reslimit* r) { void reslimit::pop_child() { lock_guard lock(*g_rlimit_mux); + m_count += m_children.back()->m_count; + m_children.back()->m_count = 0; m_children.pop_back(); } From 7b513b4a4012035464f650bfcf4183ed5e4638d6 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Sat, 8 Apr 2023 21:50:46 +0200 Subject: [PATCH 160/220] Some UP bugfixes in the new core (#6673) --- src/sat/sat_solver/inc_sat_solver.cpp | 4 ++++ src/sat/sat_solver/sat_smt_solver.cpp | 4 ++++ src/sat/smt/euf_solver.cpp | 14 ++++++++++++++ src/sat/smt/euf_solver.h | 6 ++++++ src/sat/smt/user_solver.cpp | 15 +++++++++------ src/solver/simplifier_solver.cpp | 1 + 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 75351f053..8bf665ebf 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -701,6 +701,10 @@ public: ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 82aeded6f..a5dd9b415 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -568,6 +568,10 @@ public: ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: void add_assumption(expr* a) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index f4e9c2c64..0ae56beb3 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -349,6 +349,20 @@ namespace euf { si.uncache(literal(v, true)); } + bool solver::decide(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->decide(var, phase)) + return true; + return false; + } + + bool solver::get_case_split(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->get_case_split(var, phase)) + return true; + return false; + } + void solver::asserted(literal l) { m_relevancy.asserted(l); if (!m_relevancy.is_relevant(l)) diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 1a2cdf123..72776b7ff 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -369,6 +369,8 @@ namespace euf { void add_explain(size_t* p) { m_explain.push_back(p); } void reset_explain() { m_explain.reset(); } void set_eliminated(bool_var v) override; + bool decide(bool_var& var, lbool& phase) override; + bool get_case_split(bool_var& var, lbool& phase) override; void asserted(literal l) override; sat::check_result check() override; void push() override; @@ -540,6 +542,10 @@ namespace euf { check_for_user_propagator(); m_user_propagator->register_created(ceh); } + void user_propagate_register_decide(user_propagator::decide_eh_t& ceh) { + check_for_user_propagator(); + m_user_propagator->register_decide(ceh); + } void user_propagate_register_expr(expr* e) { check_for_user_propagator(); m_user_propagator->add_expr(e); diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 99e4eeeb1..34f2b10b4 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -21,7 +21,7 @@ Author: namespace user_solver { solver::solver(euf::solver& ctx) : - th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user")) + th_euf_solver(ctx, symbol(user_propagator::plugin::name()), ctx.get_manager().mk_family_id(user_propagator::plugin::name())) {} solver::~solver() { @@ -92,24 +92,24 @@ namespace user_solver { euf::enode* original_enode = bool_var2enode(var); - if (!is_attached_to_var(original_enode)) + if (!original_enode || !is_attached_to_var(original_enode)) return false; unsigned new_bit = 0; // ignored; currently no bv-support - expr* e = bool_var2expr(var); + expr* e = original_enode->get_expr(); m_decide_eh(m_user_context, this, &e, &new_bit, &phase); euf::enode* new_enode = ctx.get_enode(e); - if (original_enode == new_enode) + if (original_enode == new_enode || new_enode->bool_var() == sat::null_bool_var) return false; var = new_enode->bool_var(); return true; } - bool solver::get_case_split(sat::bool_var& var, lbool &phase){ + bool solver::get_case_split(sat::bool_var& var, lbool& phase){ if (!m_next_split_expr) return false; @@ -123,9 +123,12 @@ namespace user_solver { void solver::asserted(sat::literal lit) { if (!m_fixed_eh) return; - force_push(); auto* n = bool_var2enode(lit.var()); euf::theory_var v = n->get_th_var(get_id()); + if (!m_id2justification.get(v, sat::literal_vector()).empty()) + // the core merged variables. We already issued the respective fixed callback for an equated variable + return; + force_push(); sat::literal_vector lits; lits.push_back(lit); m_id2justification.setx(v, lits, sat::literal_vector()); diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 12222194d..d70d232e4 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -365,6 +365,7 @@ public: void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { s->user_propagate_register_diseq(diseq_eh); } 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); } }; From ccb250c32b6c1075d97b2e43599100f45ac5b749 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 16:39:40 -0700 Subject: [PATCH 161/220] fix #6671 --- src/smt/theory_lra.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 568143d36..97ca025e3 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1138,6 +1138,17 @@ public: expr_ref zero(a.mk_real(0), m); mk_axiom(~mk_literal(a.mk_le(p, zero))); } + bool can_be_underspecified = false; + if (a.is_numeral(x, r) && r == 0 && (!a.is_numeral(y, r) || r == 0)) + can_be_underspecified = true; + if (!a.is_extended_numeral(x, r) && + !a.is_extended_numeral(y, r)) + can_be_underspecified = true; + if (can_be_underspecified) { + literal lit = th.mk_eq(p, a.mk_power0(x, y), false); + ctx().mark_as_relevant(lit); + ctx().assign(lit, nullptr); + } } // n < 0 || rem(a, n) = mod(a, n) @@ -1622,6 +1633,8 @@ public: final_check_status eval_unsupported(expr* e) { if (a.is_power(e)) return eval_power(e); + if (a.is_power0(e)) + return FC_DONE; return FC_GIVEUP; } @@ -1681,6 +1694,7 @@ public: st = FC_CONTINUE; break; case FC_GIVEUP: + TRACE("arith", tout << "give up " << mk_pp(e, m) << "\n"); if (st != FC_CONTINUE) st = FC_GIVEUP; break; From af9c760a68376fb1bbdc2139f5b57869dfe719a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 16:55:23 -0700 Subject: [PATCH 162/220] fix #6670 --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 97ca025e3..d73930a8b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1314,7 +1314,6 @@ public: expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m); expr_ref mone(a.mk_int(-1), m); expr_ref modmq(a.mk_sub(mod, abs_q), m); - ctx().get_rewriter()(modmq); literal eqz = mk_literal(m.mk_eq(q, zero)); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); literal mod_lt_q = mk_literal(a.mk_le(modmq, mone)); From e6ea81546eb78b6ad1e5920954796f6d1c2074e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 8 Apr 2023 17:14:39 -0700 Subject: [PATCH 163/220] fix #6662 --- src/ast/special_relations_decl_plugin.cpp | 1 + src/ast/special_relations_decl_plugin.h | 5 +++++ src/smt/smt_model_checker.cpp | 23 ++++++++++++++++++++++- src/smt/smt_model_checker.h | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index b45778d2f..24a756bf7 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -47,6 +47,7 @@ func_decl * special_relations_decl_plugin::mk_func_decl( if (!m_manager->is_bool(range)) { m_manager->raise_exception("range type is expected to be Boolean for special relations"); } + m_has_special_relation = true; func_decl_info info(m_family_id, k, num_parameters, parameters); symbol name; switch(k) { diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index 0c4377864..c422cbcdc 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -37,6 +37,7 @@ class special_relations_decl_plugin : public decl_plugin { symbol m_plo; symbol m_to; symbol m_tc; + bool m_has_special_relation = false; public: special_relations_decl_plugin(); @@ -50,6 +51,8 @@ public: void get_op_names(svector & op_names, symbol const & logic) override; sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { return nullptr; } + + bool has_special_relation() const { return m_has_special_relation; } }; enum sr_property { @@ -82,6 +85,8 @@ class special_relations_util { } public: special_relations_util(ast_manager& m) : m(m), m_fid(null_family_id) { } + + bool has_special_relation() const { return static_cast(m.get_plugin(m.mk_family_id("specrels")))->has_special_relation(); } bool is_special_relation(func_decl* f) const { return f->get_family_id() == fid(); } bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index e55508853..2d2350494 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -28,6 +28,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" +#include "ast/special_relations_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_checker.h" #include "smt/smt_context.h" @@ -358,7 +359,7 @@ namespace smt { TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); if (r != l_true) { - return r == l_false; // quantifier is satisfied by m_curr_model + return is_safe_for_mbqi(q) && r == l_false; // quantifier is satisfied by m_curr_model } model_ref complete_cex; @@ -398,6 +399,26 @@ namespace smt { return false; } + bool model_checker::is_safe_for_mbqi(quantifier * q) const { + special_relations_util sp(m); + if (!sp.has_special_relation()) + return true; + ast_fast_mark1 visited; + struct proc { + special_relations_util& sp; + bool found = false; + proc(special_relations_util& sp):sp(sp) {} + void operator()(app* f) { + found |= sp.is_special_relation(f); + } + void operator()(expr* e) {} + }; + proc p(sp); + quick_for_each_expr(p, visited, q); + return !p.found; + } + + void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index 6332a890e..46d51f9a0 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -87,6 +87,7 @@ namespace smt { expr_mark m_visited; bool contains_model_value(expr * e); void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def); + bool is_safe_for_mbqi(quantifier * q) const; public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); From 4a142b0f81c16b3e60972a112233d21ee909c540 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 9 Apr 2023 21:10:24 -0700 Subject: [PATCH 164/220] fix #6623 --- src/sat/smt/pb_internalize.cpp | 5 +++++ src/sat/smt/sat_internalizer.h | 1 + src/sat/tactic/goal2sat.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 17f2bd4de..abbd79c44 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -41,6 +41,11 @@ namespace pb { SASSERT(m_pb.is_pb(e)); app* t = to_app(e); rational k = m_pb.get_k(t); + if (!root && is_app(e)) { + sat::literal lit = si.get_cached(to_app(e)); + if (lit != sat::null_literal) + return sign ? ~lit : lit; + } switch (t->get_decl_kind()) { case OP_AT_MOST_K: return convert_at_most_k(t, k, root, sign); diff --git a/src/sat/smt/sat_internalizer.h b/src/sat/smt/sat_internalizer.h index 5be20c4e0..7b1d932ec 100644 --- a/src/sat/smt/sat_internalizer.h +++ b/src/sat/smt/sat_internalizer.h @@ -26,6 +26,7 @@ namespace sat { virtual literal internalize(expr* e) = 0; virtual bool_var to_bool_var(expr* e) = 0; virtual bool_var add_bool_var(expr* e) = 0; + virtual literal get_cached(app* t) const = 0; virtual bool is_cached(app* t, literal l) const = 0; virtual void cache(app* t, literal l) = 0; virtual void uncache(literal l) = 0; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index c107a74c5..267888804 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -276,11 +276,16 @@ struct goal2sat::imp : public sat::sat_internalizer { m_cache_trail.push_back(t); } + sat::literal get_cached(app* t) const override { + sat::literal lit = sat::null_literal; + m_app2lit.find(t, lit); + return lit; + } + bool is_cached(app* t, sat::literal l) const override { - if (!m_app2lit.contains(t)) - return false; - SASSERT(m_app2lit[t] == l); - return true; + sat::literal lit = get_cached(t); + SASSERT(lit == sat::null_literal || l == lit); + return l == lit; } void convert_atom(expr * t, bool root, bool sign) { From 98d3fabc24068182e1b321624a84e14a12db2fc0 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:57:59 +0200 Subject: [PATCH 165/220] Bugfix relevancy propagation + UP (old core) (#6678) * Some UP bugfixes in the new core * Bugfix relevancy propagation + UP (old core) * Revert smt_context.cpp --- src/smt/theory_user_propagator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index f19f933f2..8eeaf4382 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -92,6 +92,9 @@ void theory_user_propagator::propagate_cb( expr_ref _conseq(conseq, m); ctx.get_rewriter()(conseq, _conseq); + if (!ctx.get_manager().is_true(_conseq) && !ctx.get_manager().is_false(_conseq)) + ctx.mark_as_relevant((expr*)_conseq); + if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) return; m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, _conseq)); From bb44b91e45fbc7eb222baae8d35af15d343cd341 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Apr 2023 15:10:54 -0700 Subject: [PATCH 166/220] fix #6677 --- src/cmd_context/cmd_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 1d3476b3d..c2ec7fe30 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1237,6 +1237,9 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c if (num_args != 1) return false; + if (!dt.is_datatype(args[0]->get_sort())) + return false; + for (auto* a : dt.plugin().get_accessors(s)) { fn = a->instantiate(args[0]->get_sort()); r = m().mk_app(fn, num_args, args); From 368d60f5536f1881ea10c62c605edf3cc5b30d27 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Apr 2023 22:14:03 -0700 Subject: [PATCH 167/220] add branch / cut selection heuristic from solver=2 disabled for testing. --- src/ast/rewriter/arith_rewriter.cpp | 22 ++++++--- src/math/lp/gomory.cpp | 15 ++++++- src/math/lp/int_branch.cpp | 11 ++++- src/math/lp/int_solver.cpp | 69 +++++++++++++++++++++++++++++ src/math/lp/int_solver.h | 3 ++ src/smt/theory_arith_int.h | 6 ++- 6 files changed, 114 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 7cade3bfa..ed36562ca 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -16,6 +16,7 @@ Author: Notes: --*/ + #include "params/arith_rewriter_params.hpp" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" @@ -1046,19 +1047,21 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu set_curr_sort(arg1->get_sort()); numeral v1, v2; bool is_int; - if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { + bool is_num1 = m_util.is_numeral(arg1, v1, is_int); + bool is_num2 = m_util.is_numeral(arg2, v2, is_int); + if (is_num1 && is_num2 && !v2.is_zero()) { result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { + if (is_num2 && v2.is_one()) { result = arg1; return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_minus_one()) { + if (is_num2 && v2.is_minus_one()) { result = m_util.mk_mul(m_util.mk_int(-1), arg1); return BR_REWRITE1; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + if (is_num2 && v2.is_zero()) { return BR_FAILED; } if (arg1 == arg2) { @@ -1066,7 +1069,7 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); return BR_REWRITE3; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) { + if (is_num2 && v2.is_pos() && m_util.is_add(arg1)) { expr_ref_buffer args(m); bool change = false; rational add(0); @@ -1092,7 +1095,14 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu expr_ref zero(m_util.mk_int(0), m); result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result); return BR_REWRITE_FULL; - } + } +#if 0 + expr* x = nullptr, *y = nullptr, *z = nullptr; + if (is_num2 && m_util.is_idiv(arg1, x, y) && m_util.is_numeral(y, v1) && v1 > 0 && v2 > 0) { + result = m_util.mk_idiv(x, m_util.mk_numeral(v1*v2, is_int)); + return BR_DONE; + } +#endif return BR_FAILED; } diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2d187c9f4..8c86e0989 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -377,10 +377,20 @@ bool gomory::is_gomory_cut_target(const row_strip& row) { } int gomory::find_basic_var() { - int result = -1; unsigned n = 0; + int result = -1; unsigned min_row_size = UINT_MAX; - // Prefer smaller row size + +#if 0 + result = lia.select_int_infeasible_var(); + + if (result == -1) + return result; + + const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); + if (is_gomory_cut_target(row)) + return result; +#endif for (unsigned j : lra.r_basis()) { if (!lia.column_is_int_inf(j)) @@ -389,6 +399,7 @@ int gomory::find_basic_var() { if (!is_gomory_cut_target(row)) continue; IF_VERBOSE(20, lia.display_row_info(verbose_stream(), lia.row_of_basic_column(j))); + // Prefer smaller row size if (min_row_size == UINT_MAX || 2*row.size() < min_row_size || (4*row.size() < 5*min_row_size && lia.random() % (++n) == 0)) { diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index fc7b5c3ec..da34f77fd 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -52,16 +52,22 @@ lia_move int_branch::create_branch_on_column(int j) { int int_branch::find_inf_int_base_column() { + +#if 0 + return lia.select_int_infeasible_var(); +#endif + int result = -1; mpq range; mpq new_range; - mpq small_range_thresold(1024); + mpq small_value(1024); unsigned n = 0; lar_core_solver & lcs = lra.m_mpq_lar_core_solver; unsigned prev_usage = 0; // to quiet down the compile unsigned k = 0; unsigned usage; unsigned j; + // this loop looks for a column with the most usages, but breaks when // a column with a small span of bounds is found for (; k < lra.r_basis().size(); k++) { @@ -69,12 +75,13 @@ int int_branch::find_inf_int_base_column() { if (!lia.column_is_int_inf(j)) continue; usage = lra.usage_in_terms(j); - if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_range_thresold) { + if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { result = j; k++; n = 1; break; } + if (n == 0 || usage > prev_usage) { result = j; prev_usage = usage; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index e338e222a..6c34ce16f 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -632,4 +632,73 @@ bool int_solver::non_basic_columns_are_at_bounds() const { return true; } +int int_solver::select_int_infeasible_var() { + int result = -1; + mpq range; + mpq new_range; + mpq small_value(1024); + unsigned n = 0; + lar_core_solver & lcs = lra.m_mpq_lar_core_solver; + unsigned prev_usage = 0; // to quiet down the compile + unsigned k = 0; + unsigned usage; + unsigned j; + + enum state { small_box, is_small_value, any_value, not_found }; + state st = not_found; + + // 1. small box + // 2. small value + // 3. any value + for (; k < lra.r_basis().size(); k++) { + j = lra.r_basis()[k]; + if (!column_is_int_inf(j)) + continue; + usage = lra.usage_in_terms(j); + if (is_boxed(j) && (new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { + SASSERT(!is_fixed(j)); + if (st != small_box) { + n = 0; + st = small_box; + } + if (n == 0 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && (random() % (++n) == 0)) { + result = j; + } + continue; + } + if (st == small_box) + continue; + impq const& value = get_value(j); + if (abs(value.x) < small_value || + (has_upper(j) && small_value > upper_bound(j).x - value.x) || + (has_lower(j) && small_value > value.x - lower_bound(j).x)) { + if (st != is_small_value) { + n = 0; + st = is_small_value; + } + if (random() % (++n) == 0) + result = j; + } + if (st == is_small_value) + continue; + SASSERT(st == not_found || st == any_value); + st = any_value; + if (n == 0 /*|| usage > prev_usage*/) { + result = j; + prev_usage = usage; + n = 1; + } + else if (usage > 0 && /*usage == prev_usage && */ (random() % (++n) == 0)) + result = j; + } + + return result; +} + + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index c348f27f2..822e1cf1e 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -129,5 +129,8 @@ public: void find_feasible_solution(); lia_move hnf_cut(); void patch_nbasic_column(unsigned j) { m_patcher.patch_nbasic_column(j); } + + int select_int_infeasible_var(); + }; } diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 699ebd5d2..7b76960dd 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -514,9 +514,11 @@ namespace smt { // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. - if (constrain_free_vars(r) || !is_gomory_cut_target(r)) { + bool cfv = constrain_free_vars(r); + + if (cfv || !is_gomory_cut_target(r)) { TRACE("gomory_cut", tout << "failed to apply gomory cut:\n"; - tout << "constrain_free_vars(r): " << constrain_free_vars(r) << "\n";); + tout << "constrain_free_vars(r): " << cfv << "\n";); return false; } From ccc4f2d382540b645c3ffdd4c9b0440d09121330 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 05:10:03 -0700 Subject: [PATCH 168/220] fix #6682 --- src/api/python/z3/z3.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 7e7c58052..6b79dd1fe 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -3173,12 +3173,8 @@ def _to_int_str(val): return "1" else: return "0" - elif _is_int(val): + else: return str(val) - elif isinstance(val, str): - return val - if z3_debug(): - _z3_assert(False, "Python value cannot be used as a Z3 integer") def IntVal(val, ctx=None): From 58a2a9c79c4b0b48a16c537225489fe3fde0c271 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 14:42:47 -0700 Subject: [PATCH 169/220] fix #6680 --- src/parsers/smt2/smt2parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 7a1fd640a..081a8c838 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -372,8 +372,8 @@ namespace smt2 { return true; } catch (scanner_exception & ex) { - SASSERT(ex.has_pos()); - error(ex.line(), ex.pos(), ex.msg()); + if (ex.has_pos()) + error(ex.line(), ex.pos(), ex.msg()); ++num_errors; } } From 0b5c38dea555a11a70f0ffe6a67ac8f8b5fee9a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2023 16:46:30 -0700 Subject: [PATCH 170/220] fix #6676 get rid of rem0 declare it to be mod0 semantics to simplify code paths --- src/ast/arith_decl_plugin.cpp | 7 ++----- src/ast/arith_decl_plugin.h | 10 ++++------ src/math/subpaving/tactic/expr2subpaving.cpp | 1 - src/smt/theory_arith_core.h | 1 - src/smt/theory_lra.cpp | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index a67464a92..4778caf89 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -365,7 +365,6 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { case OP_MOD: return m_i_mod_decl; case OP_DIV0: return m_manager->mk_func_decl(symbol("/0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_DIV0)); case OP_IDIV0: return m_manager->mk_func_decl(symbol("div0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_IDIV0)); - case OP_REM0: return m_manager->mk_func_decl(symbol("rem0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_REM0)); case OP_MOD0: return m_manager->mk_func_decl(symbol("mod0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_MOD0)); case OP_POWER0: if (is_real) { @@ -612,7 +611,6 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con op_names.push_back(builtin_name("euler", OP_E)); op_names.push_back(builtin_name("/0",OP_DIV0)); op_names.push_back(builtin_name("div0",OP_IDIV0)); - op_names.push_back(builtin_name("rem0",OP_REM0)); op_names.push_back(builtin_name("mod0",OP_MOD0)); } } @@ -821,7 +819,7 @@ bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* con } if (is_decl_of(f, arith_family_id, OP_REM) && n == 2 && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_int(), mk_int() }; - f_out = m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); return true; } if (is_decl_of(f, arith_family_id, OP_POWER) && n == 2 && is_numeral(args[1], r) && r.is_zero() && is_numeral(args[0], r) && r.is_zero()) { @@ -857,7 +855,7 @@ func_decl* arith_util::mk_idiv0() { func_decl* arith_util::mk_rem0() { sort* rs[2] = { mk_int(), mk_int() }; - return m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + return m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); } func_decl* arith_util::mk_mod0() { @@ -942,7 +940,6 @@ bool arith_util::is_underspecified(expr* e) const { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 93d0edf2f..5dbf3e8cf 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -50,7 +50,6 @@ enum arith_op_kind { OP_IDIVIDES, OP_REM, OP_MOD, - OP_REM0, OP_MOD0, OP_TO_REAL, OP_TO_INT, @@ -216,7 +215,6 @@ public: case OP_U_ACOS: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: case OP_POWER0: return true; @@ -270,7 +268,7 @@ public: bool is_div0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_DIV0); } bool is_idiv0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_IDIV0); } - bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_REM0); } + bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_mod0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_power0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER0); } bool is_power(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER); } @@ -296,7 +294,7 @@ public: bool is_mod(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM); } bool is_mod0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } - bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM0); } + bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } bool is_to_real(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_REAL); } bool is_to_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_INT); } bool is_is_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_IS_INT); } @@ -355,7 +353,7 @@ public: MATCH_BINARY(is_div); MATCH_BINARY(is_idiv); MATCH_BINARY(is_mod0); - MATCH_BINARY(is_rem0); + // MATCH_BINARY(is_rem0); MATCH_BINARY(is_div0); MATCH_BINARY(is_idiv0); MATCH_BINARY(is_power); @@ -465,7 +463,7 @@ public: app * mk_mod(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD, arg1, arg2); } app * mk_div0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV0, arg1, arg2); } app * mk_idiv0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV0, arg1, arg2); } - app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_REM0, arg1, arg2); } + app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_mod0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_to_real(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_REAL, arg1); } app * mk_to_int(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_INT, arg1); } diff --git a/src/math/subpaving/tactic/expr2subpaving.cpp b/src/math/subpaving/tactic/expr2subpaving.cpp index 3afbc1ecb..e2c43d12b 100644 --- a/src/math/subpaving/tactic/expr2subpaving.cpp +++ b/src/math/subpaving/tactic/expr2subpaving.cpp @@ -311,7 +311,6 @@ struct expr2subpaving::imp { case OP_REM: case OP_IRRATIONAL_ALGEBRAIC_NUM: case OP_DIV0: - case OP_REM0: case OP_MOD0: case OP_IDIV0: throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module."); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3a5c86207..690775d08 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -154,7 +154,6 @@ namespace smt { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d73930a8b..3b3e3dd79 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -469,7 +469,7 @@ class theory_lra::imp { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } - else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2) || a.is_rem0(n, n1, n2)) { + else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2)) { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } From f61168cd538dcdaff586317636cf3d9f90251c7a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 14:22:14 -0700 Subject: [PATCH 171/220] module for maintaining probability distributions --- src/util/distribution.h | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/util/distribution.h diff --git a/src/util/distribution.h b/src/util/distribution.h new file mode 100644 index 000000000..de385a08e --- /dev/null +++ b/src/util/distribution.h @@ -0,0 +1,100 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + distribution.h + +Abstract: + + Probabiltiy distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-4-12 + +Notes: + + Distribution class works by pushing identifiers with associated scores. + After they have been pushed, you can access a random element using choose + or you can enumerate the elements in random order, sorted by the score probability. + +--*/ +#pragma once + +#include "vector.h" + +class distribution { + + random_gen m_random; + svector> m_elems; + unsigned m_sum = 0; + + unsigned choose(unsigned sum) { + unsigned s = m_random(sum); + for (auto const& [j, score] : m_elems) { + if (s < score) + return j; + s -= score; + } + UNREACHABLE(); + return 0; + } + +public: + + distribution(unsigned seed): m_random(seed) {} + + void reset() { + m_elems.reset(); + m_sum = 0; + } + + bool empty() const { + return m_elems.empty(); + } + + void push(unsigned id, unsigned score) { + SASSERT(score > 0); + if (score > 0) { + m_elems.push_back({id, score}); + m_sum += score; + } + } + + /** + \brief choose an element at random with probability proportional to the score + relative to the sum of scores of other. + */ + unsigned choose() { + return m_elems[choose(m_sum)].first; + } + + class iterator { + distribution& d; + unsigned m_sz = 0; + unsigned m_sum = 0; + unsigned m_index = 0; + void next_index() { + if (0 == m_sz) + return; + m_index = d.choose(m_sum); + } + public: + iterator(distribution& d, bool start): d(d), m_sz(start?d.m_elems.size():0), m_sum(d.m_sum) { + next_index(); + } + unsigned operator*() const { return d.m_elems[m_index].first; } + iterator operator++() { + m_sum -= d.m_elems[m_index].second; + --m_sz; + std::swap(d.m_elems[m_index], d.m_elems[d.m_elems.size() - 1]); + next_index(); + } + bool operator==(iterator const& other) const { return m_sz == other.m_sz; } + bool operator!=(iterator const& other) const { return m_sz != other.m_sz; } + }; + + iterator begin() { return iterator(*this, true); } + iterator end() { return iterator(*this, false); } +}; From 444238bc538e4017c38cf2955daad873503b04f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 14:28:08 -0700 Subject: [PATCH 172/220] formatting updates --- src/ast/rewriter/maximize_ac_sharing.cpp | 2 +- src/model/value_factory.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index a8bee62d5..b5ebb9f1c 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -92,7 +92,7 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr else { result = m.mk_app(f, numeral, _args[0]); } - TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m) << "\n";); + TRACE("ac_sharing_detail", tout << "result: " << result << "\n";); return BR_DONE; } } diff --git a/src/model/value_factory.h b/src/model/value_factory.h index edb12b095..20c383efe 100644 --- a/src/model/value_factory.h +++ b/src/model/value_factory.h @@ -194,9 +194,8 @@ public: while (!is_new) { result = mk_value(next, s, is_new); next++; - if (has_max && next > max_size + start) { - return nullptr; - } + if (has_max && next > max_size + start) + return nullptr; } SASSERT(result != 0); return result; From e8222433c3676020b4dc03af7160f18be57c5b60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 19:40:19 -0700 Subject: [PATCH 173/220] count internal nodes, use to block expanding use of hoist, #6683 --- src/ast/for_each_expr.cpp | 54 ++++++++++++++++++++++++++++++ src/ast/for_each_expr.h | 2 ++ src/ast/rewriter/bool_rewriter.cpp | 9 +++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 1e7b6da3b..374d7b496 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -44,6 +44,60 @@ unsigned get_num_exprs(expr * n) { return get_num_exprs(n, visited); } + +static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo, expr * n) { + counts.reserve(n->get_id() + 1); + unsigned& rc = counts[n->get_id()]; + if (rc > 0) { + --rc; + return; + } + rc = n->get_ref_count() - 1; + unsigned i = todo.size(); + todo.push_back(n); + unsigned count = 0; + for (; i < todo.size(); ++i) { + n = todo[i]; + if (!is_app(n)) + continue; + for (expr* arg : *to_app(n)) { + unsigned id = arg->get_id(); + counts.reserve(id + 1); + unsigned& rc = counts[id]; + if (rc > 0) { + --rc; + continue; + } + rc = arg->get_ref_count() - 1; + todo.push_back(arg); + } + } +} + +unsigned get_num_internal_exprs(expr * n) { + unsigned_vector counts; + sbuffer todo; + unsigned internal_nodes = 0; + get_num_internal_exprs(counts, todo, n); + for (expr* t : todo) + if (counts[t->get_id()] == 0) + ++internal_nodes; + return internal_nodes; +} + +unsigned get_num_internal_exprs(unsigned sz, expr * const * args) { + unsigned_vector counts; + sbuffer todo; + unsigned internal_nodes = 0; + for (unsigned i = 0; i < sz; ++i) + get_num_internal_exprs(counts, todo, args[i]); + for (expr* t : todo) + if (counts[t->get_id()] == 0) + ++internal_nodes; + return internal_nodes; +} + + namespace has_skolem_functions_ns { struct found {}; struct proc { diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 2d5ed05ae..c94348964 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -163,6 +163,8 @@ struct for_each_expr_proc : public EscapeProc { unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); +unsigned get_num_internal_exprs(expr * n); +unsigned get_num_internal_exprs(unsigned sz, expr * const * args); bool has_skolem_functions(expr * n); diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 378c794cd..9ebdbe7fd 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -20,6 +20,7 @@ Notes: #include "params/bool_rewriter_params.hpp" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_lt.h" +#include "ast/for_each_expr.h" #include void bool_rewriter::updt_params(params_ref const & _p) { @@ -268,14 +269,18 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args return BR_DONE; } -#if 1 br_status st; st = m_hoist.mk_or(buffer.size(), buffer.data(), result); + if (st != BR_FAILED) { + unsigned count1 = get_num_internal_exprs(result); + unsigned count2 = get_num_internal_exprs(buffer.size(), buffer.data()); + if (count1 > count2) + st = BR_FAILED; + } if (st == BR_DONE) return BR_REWRITE1; if (st != BR_FAILED) return st; -#endif if (s) { ast_lt lt; From eba0732629e257f67417a46481bd1e54fd61c79e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 19:50:01 -0700 Subject: [PATCH 174/220] fix #6675 disable remove_unused_defs from pb-solver until it is integrated with model reconstruction. --- src/ast/rewriter/pb2bv_rewriter.cpp | 15 ++++++++------- src/cmd_context/cmd_context.cpp | 2 +- src/sat/smt/pb_solver.cpp | 9 +++++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index d3dd8ae76..98c7a4c5e 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -825,16 +825,17 @@ struct pb2bv_rewriter::imp { if (a->get_family_id() == au.get_family_id()) { switch (a->get_decl_kind()) { case OP_ADD: - for (unsigned i = 0; i < sz; ++i) { - if (!is_pb(a->get_arg(i), mul)) return false; - } + for (unsigned i = 0; i < sz; ++i) + if (!is_pb(a->get_arg(i), mul)) + return false; return true; case OP_SUB: { - if (!is_pb(a->get_arg(0), mul)) return false; + if (!is_pb(a->get_arg(0), mul)) + return false; r = -mul; - for (unsigned i = 1; i < sz; ++i) { - if (!is_pb(a->get_arg(1), r)) return false; - } + for (unsigned i = 1; i < sz; ++i) + if (!is_pb(a->get_arg(i), r)) + return false; return true; } case OP_UMINUS: diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index c2ec7fe30..938393e88 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1240,7 +1240,7 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c if (!dt.is_datatype(args[0]->get_sort())) return false; - for (auto* a : dt.plugin().get_accessors(s)) { + for (auto* a : dt.plugin().get_accessors(s)) { fn = a->instantiate(args[0]->get_sort()); r = m().mk_app(fn, num_args, args); return true; diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index fed6aaf7c..d334df48f 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2039,7 +2039,7 @@ namespace pb { for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); init_use_lists(); - remove_unused_defs(); + // remove_unused_defs(); set_non_external(); elim_pure(); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); @@ -2528,8 +2528,13 @@ namespace pb { } void solver::remove_unused_defs() { - if (incremental_mode()) return; + if (incremental_mode()) + return; // remove constraints where indicator literal isn't used. + NOT_IMPLEMENTED_YET(); + // TODO: #6675 + // need to add this inequality to the model reconstruction + // stack in order to produce correct models. for (constraint* cp : m_constraints) { constraint& c = *cp; literal lit = c.lit(); From f0afbcbb877fb6ff222a19e0afa2adefa3debd50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 20:13:24 -0700 Subject: [PATCH 175/220] fix #6686 --- src/math/interval/interval.h | 9 +++++++++ src/math/interval/interval_def.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index fd9f5a246..0d036a16e 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -72,6 +72,7 @@ public: void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } + // Reference to numeral manager numeral_manager & m() const { return m_manager; } @@ -184,6 +185,14 @@ public: bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); } bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); } bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); } + bool is_empty(interval const& a) const { + if (lower_is_inf(a) || upper_is_inf(a)) + return false; + ext_numeral_kind lk = lower_kind(a), uk = upper_kind(a); + if (lower_is_open(a) || upper_is_open(a)) + return !(::lt(m(), lower(a), lk, upper(a), uk)); + return ::lt(m(), upper(a), uk, lower(a), lk); + } bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); } bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); } diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index bbce66420..9918f39a1 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -681,7 +681,7 @@ void interval_manager::set(interval & t, interval const & s) { } set_lower_is_open(t, lower_is_open(s)); set_upper_is_open(t, upper_is_open(s)); - SASSERT(check_invariant(t)); + SASSERT(is_empty(t) || check_invariant(t)); } template @@ -813,7 +813,7 @@ void interval_manager::add(interval const & a, interval const & b, interval & set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || lower_is_open(b)); set_upper_is_open(c, upper_is_open(a) || upper_is_open(b)); - SASSERT(check_invariant(c)); + SASSERT(is_empty(a) || is_empty(b) || check_invariant(c)); } template From 7cd8edce1fce4023fc6bcff54d6180987f5aa71e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Apr 2023 21:01:05 -0700 Subject: [PATCH 176/220] perf and memory smash fixes to internal node count routine --- src/ast/for_each_expr.cpp | 27 ++++++++------------------- src/ast/for_each_expr.h | 5 +++-- src/ast/rewriter/bool_rewriter.cpp | 17 +++++++++++++---- src/ast/rewriter/bool_rewriter.h | 2 ++ 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 374d7b496..832c1d0bc 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -45,7 +45,7 @@ unsigned get_num_exprs(expr * n) { } -static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo, expr * n) { +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n) { counts.reserve(n->get_id() + 1); unsigned& rc = counts[n->get_id()]; if (rc > 0) { @@ -55,7 +55,6 @@ static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo rc = n->get_ref_count() - 1; unsigned i = todo.size(); todo.push_back(n); - unsigned count = 0; for (; i < todo.size(); ++i) { n = todo[i]; if (!is_app(n)) @@ -74,27 +73,17 @@ static void get_num_internal_exprs(unsigned_vector& counts, sbuffer& todo } } -unsigned get_num_internal_exprs(expr * n) { - unsigned_vector counts; - sbuffer todo; +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo) { unsigned internal_nodes = 0; - get_num_internal_exprs(counts, todo, n); - for (expr* t : todo) - if (counts[t->get_id()] == 0) - ++internal_nodes; - return internal_nodes; -} - -unsigned get_num_internal_exprs(unsigned sz, expr * const * args) { - unsigned_vector counts; - sbuffer todo; - unsigned internal_nodes = 0; - for (unsigned i = 0; i < sz; ++i) - get_num_internal_exprs(counts, todo, args[i]); - for (expr* t : todo) + for (expr* t : todo) { if (counts[t->get_id()] == 0) ++internal_nodes; + else + counts[t->get_id()] = 0; + } + todo.reset(); return internal_nodes; + } diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index c94348964..0ba0dc992 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -163,12 +163,13 @@ struct for_each_expr_proc : public EscapeProc { unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); -unsigned get_num_internal_exprs(expr * n); -unsigned get_num_internal_exprs(unsigned sz, expr * const * args); +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n); +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo); bool has_skolem_functions(expr * n); // pre-order traversal of subterms + class subterms { bool m_include_bound = false; expr_ref_vector m_es; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9ebdbe7fd..95c0950d8 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -269,19 +269,28 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args return BR_DONE; } +#if 1 br_status st; - st = m_hoist.mk_or(buffer.size(), buffer.data(), result); + expr_ref r(m()); + st = m_hoist.mk_or(buffer.size(), buffer.data(), r); if (st != BR_FAILED) { - unsigned count1 = get_num_internal_exprs(result); - unsigned count2 = get_num_internal_exprs(buffer.size(), buffer.data()); + m_counts1.reserve(m().get_num_asts() + 1); + m_counts2.reserve(m().get_num_asts() + 1); + get_num_internal_exprs(m_counts1, m_todo1, r); + for (unsigned i = 0; i < num_args; ++i) + get_num_internal_exprs(m_counts2, m_todo2, args[i]); + unsigned count1 = count_internal_nodes(m_counts1, m_todo1); + unsigned count2 = count_internal_nodes(m_counts2, m_todo2); if (count1 > count2) st = BR_FAILED; } + if (st != BR_FAILED) + result = r; if (st == BR_DONE) return BR_REWRITE1; if (st != BR_FAILED) return st; - +#endif if (s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index e02bf86d3..0693e94ba 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -62,6 +62,8 @@ class bool_rewriter { unsigned m_local_ctx_limit; unsigned m_local_ctx_cost; bool m_elim_ite; + ptr_vector m_todo1, m_todo2; + unsigned_vector m_counts1, m_counts2; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); From b783879752444527a9710fd356c3314bc4d9b1cf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 08:45:11 -0700 Subject: [PATCH 177/220] #6687 --- src/tactic/core/elim_uncnstr_tactic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 869716f59..a372a1f8b 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -804,6 +804,7 @@ class elim_uncnstr_tactic : public tactic { app * r = nullptr; expr* x, *y; if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && m_seq_util.str.is_concat(args[1], x, y) && uncnstr(x)) { if (!mk_fresh_uncnstr_var_for(f, num, args, r)) From 1a70ac75df70fb02229d3eebd496721e9d7844e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 09:01:17 -0700 Subject: [PATCH 178/220] fix #6687 --- src/ast/converters/expr_inverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 4d435f7e8..0ee3e130d 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -757,6 +757,7 @@ public: expr* x, *y; if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && seq.str.is_concat(args[1], x, y) && uncnstr(x)) { mk_fresh_uncnstr_var_for(f, r); From 624907823dbf10e5df874eb72989d6afa807edf8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Apr 2023 11:19:06 -0700 Subject: [PATCH 179/220] add tests for distribution utility and fix loose ends --- src/test/CMakeLists.txt | 1 + src/test/distribution.cpp | 45 +++++++++++++++++++++++++++++++++++++++ src/test/main.cpp | 1 + src/util/distribution.h | 16 ++++++++------ 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/test/distribution.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index f959e9bd5..df3010295 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(test-z3 datalog_parser.cpp ddnf.cpp diff_logic.cpp + distribution.cpp dl_context.cpp dl_product_relation.cpp dl_query.cpp diff --git a/src/test/distribution.cpp b/src/test/distribution.cpp new file mode 100644 index 000000000..c67757737 --- /dev/null +++ b/src/test/distribution.cpp @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + distribution.cpp + +Abstract: + + Test distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-04-13 + + +--*/ +#include "util/distribution.h" +#include + +static void tst1() { + distribution dist(1); + dist.push(1, 3); + dist.push(2, 1); + dist.push(3, 1); + dist.push(4, 1); + + unsigned counts[4] = { 0, 0, 0, 0 }; + for (unsigned i = 0; i < 1000; ++i) + counts[dist.choose()-1]++; + for (unsigned i = 1; i <= 4; ++i) + std::cout << "count " << i << ": " << counts[i-1] << "\n"; + + for (unsigned i = 0; i < 5; ++i) { + std::cout << "enum "; + for (auto j : dist) + std::cout << j << " "; + std::cout << "\n"; + } + +} + +void tst_distribution() { + tst1(); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index f9e4e0815..7cd4b6cf9 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -264,4 +264,5 @@ int main(int argc, char ** argv) { //TST_ARGV(hs); TST(finder); TST(totalizer); + TST(distribution); } diff --git a/src/util/distribution.h b/src/util/distribution.h index de385a08e..0ed63d510 100644 --- a/src/util/distribution.h +++ b/src/util/distribution.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2017 Microsoft Corporation +Copyright (c) 2023 Microsoft Corporation Module Name: @@ -18,6 +18,8 @@ Notes: Distribution class works by pushing identifiers with associated scores. After they have been pushed, you can access a random element using choose or you can enumerate the elements in random order, sorted by the score probability. + Only one iterator can be active at a time because the iterator reshuffles the registered elements. + The requirement is not checked or enforced. --*/ #pragma once @@ -32,10 +34,12 @@ class distribution { unsigned choose(unsigned sum) { unsigned s = m_random(sum); + unsigned idx = 0; for (auto const& [j, score] : m_elems) { if (s < score) - return j; + return idx; s -= score; + ++idx; } UNREACHABLE(); return 0; @@ -76,9 +80,8 @@ public: unsigned m_sum = 0; unsigned m_index = 0; void next_index() { - if (0 == m_sz) - return; - m_index = d.choose(m_sum); + if (0 != m_sz) + m_index = d.choose(m_sum); } public: iterator(distribution& d, bool start): d(d), m_sz(start?d.m_elems.size():0), m_sum(d.m_sum) { @@ -88,8 +91,9 @@ public: iterator operator++() { m_sum -= d.m_elems[m_index].second; --m_sz; - std::swap(d.m_elems[m_index], d.m_elems[d.m_elems.size() - 1]); + std::swap(d.m_elems[m_index], d.m_elems[m_sz]); 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; } From b75d81f3c2b878726982111dc48b9b4765ad7cca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 14 Apr 2023 16:38:33 -0700 Subject: [PATCH 180/220] fix #6690 --- src/sat/smt/arith_internalize.cpp | 6 ++++-- src/smt/theory_lra.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index d35f79954..60ca9651a 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -107,10 +107,12 @@ namespace arith { e = a.mk_idiv0(x, y); } else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 3b3e3dd79..657c48d38 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -315,11 +315,13 @@ class theory_lra::imp { else if (a.is_idiv(n, x, y)) { e = a.mk_idiv0(x, y); } - else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + else if (a.is_rem(n, x, y)) { + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); From 97b66d13c043dd392e5146affd0c7281af8f1311 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2023 17:09:05 -0700 Subject: [PATCH 181/220] fix soundness bug in disabled code --- src/math/lp/gomory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 8c86e0989..2ecbc49ac 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -390,6 +390,7 @@ int gomory::find_basic_var() { const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); if (is_gomory_cut_target(row)) return result; + result = -1; #endif for (unsigned j : lra.r_basis()) { From 1319b64bb03b1db8e2c84b6d04e494aa69603f24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Apr 2023 09:11:16 -0700 Subject: [PATCH 182/220] fix #6692 --- src/sat/smt/pb_solver.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index d334df48f..496042f49 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2858,19 +2858,20 @@ namespace pb { void solver::subsumes(pbc& p1, literal lit) { for (constraint* c : m_cnstr_use_list[lit.index()]) { - if (c == &p1 || c->was_removed()) continue; - bool s = false; + if (c == &p1 || c->was_removed() || c->lit() != sat::null_literal) + continue; + bool sub = false; switch (c->tag()) { case pb::tag_t::card_t: - s = subsumes(p1, c->to_card()); + sub = subsumes(p1, c->to_card()); break; case pb::tag_t::pb_t: - s = subsumes(p1, c->to_pb()); + sub = subsumes(p1, c->to_pb()); break; default: break; } - if (s) { + if (sub) { ++m_stats.m_num_pb_subsumes; set_non_learned(p1); remove_constraint(*c, "subsumed"); From cb041c1b6d43208597744233bfca90ae7340927d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Apr 2023 12:05:08 -0700 Subject: [PATCH 183/220] fix #6689 --- src/ast/normal_forms/nnf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index a35956454..b04445d16 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -149,7 +149,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); + p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); else p = m.mk_skolemization(q, r); } From ec1480b12a2f10f6050124cfbcf4d3cdf51ecd2a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Apr 2023 08:40:29 -0700 Subject: [PATCH 184/220] fix #6693 --- src/smt/theory_arith_core.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 690775d08..412744360 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -47,11 +47,15 @@ namespace smt { else if (m_util.is_idiv(n)) { e = m_util.mk_idiv0(n->get_arg(0), n->get_arg(1)); } - else if (m_util.is_rem(n)) { - e = m_util.mk_rem0(n->get_arg(0), n->get_arg(1)); + else if (m_util.is_rem(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_rem0(n->get_arg(0), z); + n = m_util.mk_rem(n->get_arg(0), z); } - else if (m_util.is_mod(n)) { - e = m_util.mk_mod0(n->get_arg(0), n->get_arg(1)); + else if (m_util.is_mod(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_mod0(n->get_arg(0), z); + n = m_util.mk_mod(n->get_arg(0), z); } else if (m_util.is_power(n)) { e = m_util.mk_power0(n->get_arg(0), n->get_arg(1)); From a2bac119d3376ec9be68e88c51590e28772fade3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Apr 2023 08:40:51 -0700 Subject: [PATCH 185/220] differentiate fixed from offset-eq in statistics --- src/math/lp/lp_bound_propagator.h | 8 ++++++-- src/math/lp/lp_settings.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 14f646fc1..dba93398e 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -578,8 +578,12 @@ public: ); bool added = m_imp.add_eq(je, ke, exp, is_fixed); - if (added) - lp().stats().m_offset_eqs++; + if (added) { + if (is_fixed) + lp().stats().m_fixed_eqs++; + else + lp().stats().m_offset_eqs++; + } return added; } diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 9a38fd582..5234e3bf0 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -121,6 +121,7 @@ struct statistics { unsigned m_grobner_calls; unsigned m_grobner_conflicts; unsigned m_offset_eqs; + unsigned m_fixed_eqs; statistics() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } void collect_statistics(::statistics& st) const { @@ -142,6 +143,7 @@ struct statistics { st.update("arith-grobner-calls", m_grobner_calls); st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-offset-eqs", m_offset_eqs); + st.update("arith-fixed-eqs", m_fixed_eqs); } }; From 7a689c3298ea70d100eea427d3edc0d613c7cd73 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 17:59:41 -0700 Subject: [PATCH 186/220] disable destructive equality resolution simplification if there are patterns - regression from F\star - reported by @mtzguido (stlc_min.smt2) --- src/ast/rewriter/th_rewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index d75a31a66..9278ae5ae 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -827,7 +827,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref r(m()); bool der_change = false; - if (is_quantifier(result)) { + if (is_quantifier(result) && to_quantifier(result)->get_num_patterns() == 0) { m_der(to_quantifier(result), r, p2); der_change = result.get() != r.get(); if (m().proofs_enabled() && der_change) From fdd5c923edce21a8690ff4c246a6c88e77579e63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 20:20:26 -0700 Subject: [PATCH 187/220] use only maxres if there is a lexicographic objective, fix #6697 - maxlex.enable heuristic does not work if it is chained among multiple objectives. Only maxres is set up to commit the proper constraints. --- src/opt/maxsmt.cpp | 6 +++--- src/opt/maxsmt.h | 2 +- src/opt/opt_context.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 3d0834472..37f888120 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -178,14 +178,14 @@ namespace opt { maxsmt::maxsmt(maxsat_context& c, unsigned index): m(c.get_manager()), m_c(c), m_index(index), m_answer(m) {} - lbool maxsmt::operator()() { + lbool maxsmt::operator()(bool committed) { lbool is_sat = l_undef; m_msolver = nullptr; opt_params optp(m_params); symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); - if (optp.maxlex_enable() && is_maxlex(m_soft)) + if (!committed && optp.maxlex_enable() && is_maxlex(m_soft)) m_msolver = mk_maxlex(m_c, m_index, m_soft); else if (m_soft.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) m_msolver = mk_maxres(m_c, m_index, m_soft); @@ -401,7 +401,7 @@ namespace opt { for (auto const& p : soft) { maxsmt.add(p.first, p.second); } - lbool r = maxsmt(); + lbool r = maxsmt(true); if (r == l_true) { svector labels; maxsmt.get_model(m_model, labels); diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index ac39d7891..17306b222 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -137,7 +137,7 @@ namespace opt { params_ref m_params; public: maxsmt(maxsat_context& c, unsigned id); - lbool operator()(); + lbool operator()(bool committed); void updt_params(params_ref& p); void add(expr* f, rational const& w); unsigned size() const { return m_soft.size(); } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 5895643bd..11eddc2eb 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -453,8 +453,8 @@ namespace opt { lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { model_ref tmp; maxsmt& ms = *m_maxsmts.find(id); - if (scoped) get_solver().push(); - lbool result = ms(); + if (scoped) get_solver().push(); + lbool result = ms(committed); if (result != l_false && (ms.get_model(tmp, m_labels), tmp.get())) { ms.get_model(m_model, m_labels); } From d8156aeff31fb224531b026daacfafe2222f00e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2023 21:14:42 -0700 Subject: [PATCH 188/220] weird latent bug in wmax: init() succeeds and it returns undef --- src/opt/maxsmt.cpp | 3 ++- src/opt/wmax.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 37f888120..e684c64d6 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -213,7 +213,8 @@ namespace opt { try { is_sat = (*m_msolver)(); } - catch (z3_exception&) { + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n"); is_sat = l_undef; } if (is_sat != l_false) { diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 1fb6c05dd..9a7d1a3ca 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -53,7 +53,7 @@ namespace opt { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); reset(); - if (init()) + if (!init()) return l_undef; lbool is_sat = l_true; From d4fa990b6e5c433dc5205dfb1f012385fb7425da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:47:02 -0700 Subject: [PATCH 189/220] return diagnostics --- src/math/lp/bound_analyzer_on_row.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 6084145c2..0008a0ee9 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -54,32 +54,40 @@ public : {} - static void analyze_row(const C & row, + static unsigned analyze_row(const C & row, unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, B & bp) { bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); - a.analyze(); + return a.analyze(); } private: - void analyze() { + unsigned analyze() { + unsigned num_prop = 0; for (const auto & c : m_row) { if ((m_column_of_l == -2) && (m_column_of_u == -2)) - return; + return 0; analyze_bound_on_var_on_coeff(c.var(), c.coeff()); } + ++num_prop; if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) limit_all_monoids_from_below(); + else + --num_prop; + ++num_prop; if (m_column_of_l >= 0) limit_monoid_l_from_above(); else if (m_column_of_l == -1) limit_all_monoids_from_above(); + else + --num_prop; + return num_prop; } bool bound_is_available(unsigned j, bool lower_bound) { From e689dea99ce257d4cef7c73c831448ebcc17616f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:47:49 -0700 Subject: [PATCH 190/220] basic formatting updates --- src/math/lp/lar_solver.cpp | 66 ++++++++++++++------------------------ 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 21eec31d8..e127a53d1 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1,11 +1,12 @@ -#include "math/lp/lar_solver.h" -#include "smt/params/smt_params_helper.hpp" - /* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner, Lev Nachmanson */ +#include "math/lp/lar_solver.h" +#include "smt/params/smt_params_helper.hpp" + + namespace lp { lp_settings& lar_solver::settings() { return m_settings; } @@ -134,10 +135,9 @@ namespace lp { bool lar_solver::row_has_a_big_num(unsigned i) const { - for (const auto& c : A_r().m_rows[i]) { + for (const auto& c : A_r().m_rows[i]) if (c.coeff().is_big()) return true; - } return false; } @@ -601,15 +601,14 @@ namespace lp { else { const lar_term& term = *m_terms[tv::unmask_term(t.second)]; - for (auto p : term) { + for (auto p : term) register_monoid_in_map(coeffs, t.first * p.coeff(), p.column()); - } } } - for (auto& p : coeffs) - if (!is_zero(p.second)) - left_side.push_back(std::make_pair(p.second, p.first)); + for (auto& [v, c] : coeffs) + if (!is_zero(c)) + left_side.push_back(std::make_pair(c, v)); } void lar_solver::insert_row_with_changed_bounds(unsigned rid) { @@ -633,8 +632,7 @@ namespace lp { r += c.coeff() * m_mpq_lar_core_solver.m_r_x[c.var()]; } CTRACE("lar_solver", !is_zero(r), tout << "row = " << i << ", j = " << m_mpq_lar_core_solver.m_r_basis[i] << "\n"; - print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"; - ); + print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"); return is_zero(r); } @@ -692,15 +690,11 @@ namespace lp { } } - void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); - return; - } - - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - + else + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); } void lar_solver::detect_rows_with_changed_bounds() { @@ -1442,8 +1436,7 @@ namespace lp { register_new_ext_var_index(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); - add_new_var_to_core_fields_for_mpq(false); // false for not adding a row - + add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { @@ -1481,24 +1474,21 @@ namespace lp { #if Z3DEBUG_CHECK_UNIQUE_TERMS bool lar_solver::term_coeffs_are_ok(const vector>& coeffs) { - for (const auto& p : coeffs) { + for (const auto& p : coeffs) if (column_is_real(p.second)) return true; - } mpq g; bool g_is_set = false; for (const auto& p : coeffs) { - if (!p.first.is_int()) { + if (!p.first.is_int()) return false; - } if (!g_is_set) { g_is_set = true; g = p.first; } - else { + else g = gcd(g, p.first); - } } if (g == one_of_type()) return true; @@ -1524,20 +1514,17 @@ namespace lp { std::set seen_terms; for (auto p : *t) { auto j = p.column(); - if (this->column_corresponds_to_term(j)) { + if (this->column_corresponds_to_term(j)) seen_terms.insert(j); - } } while (!seen_terms.empty()) { unsigned j = *seen_terms.begin(); seen_terms.erase(j); auto tj = this->m_var_register.local_to_external(j); auto& ot = this->get_term(tj); - for (auto p : ot){ - if (this->column_corresponds_to_term(p.column())) { + for (auto p : ot) + if (this->column_corresponds_to_term(p.column())) seen_terms.insert(p.column()); - } - } t->subst_by_term(ot, j); } } @@ -1555,15 +1542,14 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if ( !coeffs.empty()) { + if (!coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) insert_row_with_changed_bounds(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); - if (m_need_register_terms) { + if (m_need_register_terms) register_normalized_term(*t, A_r().column_count() - 1); - } return ret; } @@ -1585,12 +1571,10 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); for (lar_term::ival c : *term) { unsigned j = c.column(); - while (m_usage_in_terms.size() <= j) { + while (m_usage_in_terms.size() <= j) m_usage_in_terms.push_back(0); - } m_usage_in_terms[j] = m_usage_in_terms[j] + 1; } - } void lar_solver::add_basic_var_to_core_fields() { @@ -1603,9 +1587,7 @@ namespace lp { } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { - if (!column_is_int(j)) - return true; - return right_side.is_int(); + return !column_is_int(j) || right_side.is_int(); } constraint_index lar_solver::add_var_bound_check_on_equal(var_index j, lconstraint_kind kind, const mpq& right_side, var_index& equal_var) { From 8fb45158724de904bb0cafd5b9df0ded1b201af0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:48:33 -0700 Subject: [PATCH 191/220] remove redundant function, add checker function to test missed propagations --- src/math/lp/lar_solver.h | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 955710b3c..10ca16d0a 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -161,28 +161,23 @@ class lar_solver : public column_namer { bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; - template - void analyze_new_bounds_on_row_tableau( - unsigned row_index, - lp_bound_propagator & bp ) { - - if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation - || row_has_a_big_num(row_index)) - return; - - bound_analyzer_on_row, lp_bound_propagator>::analyze_row(A_r().m_rows[row_index], - null_ci, - zero_of_type>(), - row_index, - bp - ); - } void substitute_basis_var_in_terms_for_row(unsigned i); + template - void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - analyze_new_bounds_on_row_tableau(i, bp); + unsigned calculate_implied_bounds_for_row(unsigned row_index, lp_bound_propagator & bp) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) + return 0; + + return bound_analyzer_on_row, lp_bound_propagator>::analyze_row( + A_r().m_rows[row_index], + null_ci, + zero_of_type>(), + row_index, + bp); } + static void clean_popped_elements(unsigned n, u_set& set); bool maximize_term_on_tableau(const lar_term & term, impq &term_max); @@ -339,8 +334,9 @@ public: void mark_rows_for_bound_prop(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { + unsigned num_prop = 0; for (unsigned i : m_rows_with_changed_bounds) { - calculate_implied_bounds_for_row(i, bp); + num_prop += calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) return; } @@ -360,6 +356,15 @@ public: } m_rows_with_changed_bounds.clear(); } + + template + void check_missed_propagations(lp_bound_propagator & bp) { + for (unsigned i = 0; i < A_r().row_count(); i++) + if (!m_rows_with_changed_bounds.contains(i)) + if (0 < calculate_implied_bounds_for_row(i, bp)) { + verbose_stream() << i << ": " << get_row(i) << "\n"; + } + } bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } From ace6e8eea107e93c5400caff92963c73e9ad4526 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 13:49:06 -0700 Subject: [PATCH 192/220] add gcd-conflicts stats, formatting updates --- src/smt/theory_arith.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 34a76d955..a2a7617e5 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -41,7 +41,7 @@ Revision History: namespace smt { struct theory_arith_stats { - unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; + unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_gcd_conflicts, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; @@ -452,18 +452,18 @@ namespace smt { svector m_var_pos; // temporary array used in add_rows atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds - unsigned m_asserted_qhead; + unsigned m_asserted_qhead = 0; ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning vector m_row_vars; // variables in a given row. Used during internalization to detect repeated variables. - unsigned m_row_vars_top; + unsigned m_row_vars_top = 0; var_heap m_to_patch; // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v. nat_set m_left_basis; // temporary: set of variables that already left the basis in make_feasible - bool m_blands_rule; + bool m_blands_rule = false; svector m_update_trail_stack; // temporary trail stack used to restore the last feasible assignment. nat_set m_in_update_trail_stack; // set of variables in m_update_trail_stack @@ -473,11 +473,11 @@ namespace smt { inf_numeral m_tmp; random_gen m_random; - unsigned m_num_conflicts; + unsigned m_num_conflicts = 0; - unsigned m_branch_cut_counter; + unsigned m_branch_cut_counter = 0; bool m_eager_gcd; // true if gcd should be applied at every add_row - unsigned m_final_check_idx; + unsigned m_final_check_idx = 0; // backtracking @@ -676,7 +676,7 @@ namespace smt { See also m_changed_assignment flag. */ - bool m_liberal_final_check; + bool m_liberal_final_check = true; final_check_status final_check_core(); final_check_status final_check_eh() override; @@ -734,7 +734,7 @@ namespace smt { // Assignment management // // ----------------------------------- - bool m_changed_assignment; //!< auxiliary variable set to true when the assignment is changed. + bool m_changed_assignment = false; //!< auxiliary variable set to true when the assignment is changed. void save_value(theory_var v); void discard_update_trail(); void restore_assignment(); @@ -790,11 +790,12 @@ namespace smt { void mark_row_for_bound_prop(unsigned r1); void mark_rows_for_bound_prop(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; - void imply_bound_for_monomial(row const & r, int idx, bool lower); - void imply_bound_for_all_monomials(row const & r, bool lower); + unsigned imply_bound_for_monomial(row const & r, int idx, bool lower); + unsigned imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); - void mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); + bool m_validating = false; + unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); @@ -821,7 +822,7 @@ namespace smt { var_set m_tmp_var_set; var_set m_tmp_var_set2; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head; + unsigned m_assume_eq_head = 0; bool random_update(theory_var v); void mutate_assignment(); bool assume_eqs_core(); @@ -953,10 +954,10 @@ namespace smt { // // ----------------------------------- typedef int_hashtable > row_set; - bool m_model_depends_on_computed_epsilon; - unsigned m_nl_rounds; - bool m_nl_gb_exhausted; - unsigned m_nl_strategy_idx; // for fairness + bool m_model_depends_on_computed_epsilon = false; + unsigned m_nl_rounds = 0; + bool m_nl_gb_exhausted = false; + unsigned m_nl_strategy_idx = 0; // for fairness expr_ref_vector m_nl_new_exprs; typedef obj_map var2num_occs; var2num_occs m_var2num_occs; From 59bc070268e955cc19a639fdc7f83df4abea49b4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:31:56 -0700 Subject: [PATCH 193/220] count gcd conflicts --- src/smt/theory_arith_core.h | 1 - src/smt/theory_arith_int.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 412744360..3da706e67 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1735,7 +1735,6 @@ namespace smt { m_util(m), m_arith_eq_solver(m), m_arith_eq_adapter(*this, m_util), - m_asserted_qhead(0), m_row_vars_top(0), m_to_patch(1024), m_blands_rule(false), diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 7b76960dd..c9bc9f31a 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -754,6 +754,7 @@ namespace smt { if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, r);); antecedents ante(*this); + m_stats.m_gcd_conflicts++; collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( @@ -833,6 +834,7 @@ namespace smt { numeral u1 = floor(u/gcds); if (u1 < l1) { + m_stats.m_gcd_conflicts++; TRACE("gcd_test", tout << "row failed the extended GCD test:\n"; display_row_info(tout, r);); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); From 50c855e2ebc715972b1fa8365a5e4c8fc66cda06 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:32:16 -0700 Subject: [PATCH 194/220] count gcd conflicts, log row id in rows --- src/smt/theory_arith_pp.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index d10bfca55..b0d43bc00 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -37,6 +37,7 @@ namespace smt { st.update("arith assume eqs", m_stats.m_assume_eqs); st.update("arith offset eqs", m_stats.m_offset_eqs); st.update("arith gcd tests", m_stats.m_gcd_tests); + st.update("arith gcd conflicts", m_stats.m_gcd_conflicts); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); st.update("arith branch int", m_stats.m_branch_infeasible_int); @@ -82,8 +83,9 @@ namespace smt { template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { - - out << "(v" << r.get_base_var() << ") : "; + + column const & c = m_columns[r.get_base_var()]; + out << "(v" << r.get_base_var() << " r" << c[0].m_row_id << ") : "; bool first = true; for (auto const& e : r) { if (!e.is_dead()) { From 3029fb24a16c7d22597ab529172cbf0584c68ecc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2023 16:34:58 -0700 Subject: [PATCH 195/220] remove references to validating --- src/smt/theory_arith.h | 1 - src/smt/theory_arith_core.h | 62 ++++++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index a2a7617e5..92aa4baec 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -794,7 +794,6 @@ namespace smt { unsigned imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); - bool m_validating = false; unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3da706e67..159758c80 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1735,22 +1735,11 @@ namespace smt { m_util(m), m_arith_eq_solver(m), m_arith_eq_adapter(*this, m_util), - m_row_vars_top(0), m_to_patch(1024), - m_blands_rule(false), m_random(ctx.get_fparams().m_arith_random_seed), - m_num_conflicts(0), - m_branch_cut_counter(0), m_eager_gcd(m_params.m_arith_eager_gcd), - m_final_check_idx(0), m_antecedents_index(0), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_liberal_final_check(true), - m_changed_assignment(false), - m_assume_eq_head(0), - m_model_depends_on_computed_epsilon(false), - m_nl_rounds(0), - m_nl_gb_exhausted(false), m_nl_new_exprs(m), m_bound_watch(null_bool_var) { } @@ -2702,8 +2691,9 @@ namespace smt { Then this bound is used to produce a bound for the monomial variable. */ template - void theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { + unsigned theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { row_entry const & entry = r[idx]; + unsigned count = 0; if (m_unassigned_atoms[entry.m_var] > 0) { inf_numeral implied_k; typename vector::const_iterator it = r.begin_entries(); @@ -2725,7 +2715,7 @@ namespace smt { tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); } } else { @@ -2736,10 +2726,11 @@ namespace smt { tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } } + return count; } /** @@ -2750,7 +2741,7 @@ namespace smt { for the monomial variables. */ template - void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { + unsigned theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false @@ -2763,6 +2754,7 @@ namespace smt { } } + unsigned count = 0; inf_numeral implied_k; typename vector::const_iterator it = r.begin(); typename vector::const_iterator end = r.end(); @@ -2786,7 +2778,7 @@ namespace smt { tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); } } else { @@ -2798,11 +2790,12 @@ namespace smt { tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); } } } } + return count; } /** @@ -2924,10 +2917,11 @@ namespace smt { } template - void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { + unsigned theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { atoms const & as = m_var_occs[v]; inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; + unsigned count = 0; for (atom* a : as) { bool_var bv = a->get_bool_var(); literal l(bv); @@ -2944,6 +2938,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } // v <= k k < k2 |- v < k2 |- not v >= k2 if (kind == B_UPPER && k < k2) { @@ -2960,6 +2955,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } } @@ -2975,6 +2971,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } // v <= k k <= k2 |- v <= k2 @@ -2986,10 +2983,12 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } } } } + return count; } @@ -3000,11 +2999,17 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); + TRACE("propagate_bounds", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); + + + TRACE("arith", tout << ctx.get_scope_level() << "\n"; + ctx.display_detailed_literal(tout, l) << "\n"); + if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; lits.reset(); @@ -3033,6 +3038,7 @@ namespace smt { template void theory_arith::propagate_bounds() { TRACE("propagate_bounds_detail", display(tout);); + unsigned num_prop = 0, count = 0; for (unsigned r_idx : m_to_check) { row & r = m_rows[r_idx]; if (r.get_base_var() != null_theory_var) { @@ -3041,15 +3047,21 @@ namespace smt { int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); + ++num_prop; if (lower_idx >= 0) - imply_bound_for_monomial(r, lower_idx, true); + count += imply_bound_for_monomial(r, lower_idx, true); else if (lower_idx == -1) - imply_bound_for_all_monomials(r, true); - + count += imply_bound_for_all_monomials(r, true); + else + --num_prop; + + ++num_prop; if (upper_idx >= 0) - imply_bound_for_monomial(r, upper_idx, false); + count += imply_bound_for_monomial(r, upper_idx, false); else if (upper_idx == -1) - imply_bound_for_all_monomials(r, false); + count += imply_bound_for_all_monomials(r, false); + else + --num_prop; // sneaking cheap eq detection in this loop propagate_cheap_eq(r_idx); @@ -3065,6 +3077,7 @@ namespace smt { #endif } } + TRACE("arith_eq", tout << "done\n";); m_to_check.reset(); m_in_to_check.reset(); @@ -3379,7 +3392,7 @@ namespace smt { } template - void theory_arith::pop_scope_eh(unsigned num_scopes) { + void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); @@ -3399,7 +3412,6 @@ namespace smt { restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim); m_asserted_bounds.shrink(s.m_asserted_bounds_lim); m_asserted_qhead = s.m_asserted_qhead_old; - TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";); restore_nl_propagated_flag(s.m_nl_propagated_lim); m_nl_monomials.shrink(s.m_nl_monomials_lim); del_atoms(s.m_atoms_lim); From d2e3e4895e5d672fe1cf4d748ab0da4e23584e95 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 10:04:45 -0700 Subject: [PATCH 196/220] add instrumentation to theory_lra for shuffling final check --- src/math/lp/hnf_cutter.cpp | 3 +- src/smt/theory_lra.cpp | 200 ++++++++++++++++++++++++++----------- 2 files changed, 145 insertions(+), 58 deletions(-) diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index c1f93c9d8..3c4ea10ab 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -248,9 +248,8 @@ branch y_i >= ceil(y0_i) is impossible. bool hnf_cutter::init_terms_for_hnf_cut() { clear(); - for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) { + for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) try_add_term_to_A_for_hnf(tv::term(i)); - } return hnf_has_var_with_non_integral_value(); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 657c48d38..bea080116 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1539,10 +1539,14 @@ public: } } - bool assume_eqs() { + bool assume_eqs() { + if (delayed_assume_eqs()) + return true; + TRACE("arith", display(tout);); random_update(); m_model_eqs.reset(); + theory_var sz = static_cast(th.get_num_vars()); unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; @@ -1639,6 +1643,8 @@ public: return FC_GIVEUP; } + unsigned m_final_check_idx = 0; + final_check_status final_check_eh() { if (propagate_core()) return FC_CONTINUE; @@ -1649,44 +1655,79 @@ public: if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } - final_check_status st = FC_DONE; - + final_check_status st = FC_DONE, result = FC_DONE; + m_final_check_idx = 0; // remove to experiment. + unsigned old_idx = m_final_check_idx; + switch (is_sat) { case l_true: TRACE("arith", display(tout)); - switch (check_lia()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-lia giveup\n";); - if (ctx().get_fparams().m_arith_ignore_int) - return FC_GIVEUP; - st = FC_CONTINUE; - break; - } +#if 0 + distribution dist(++m_final_check_idx); - switch (check_nla()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-nra giveup\n";); - st = FC_GIVEUP; - break; + dist.add(0, 2); + dist.add(1, 1); + dist.add(1, 1); + + for (auto idx : dist) { + if (!m.inc()) + return FC_GIVEUP; + + switch (idx) { + case 0: + case 1: + case 2: + default: + + } } +#endif - if (delayed_assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; - } - if (assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; + do { + if (!m.inc()) + return FC_GIVEUP; + + switch (m_final_check_idx) { + case 0: + if (assume_eqs()) { + ++m_stats.m_assume_eqs; + st = FC_CONTINUE; + } + break; + case 1: + st = check_lia(); + break; + case 2: + switch (check_nla()) { + case l_true: + st = FC_DONE; + break; + case l_false: + st = FC_CONTINUE; + break; + case l_undef: + TRACE("arith", tout << "check-nra giveup\n";); + st = FC_GIVEUP; + break; + } + break; + } + m_final_check_idx = (m_final_check_idx + 1) % 3; + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + result = st; + break; + } } + while (old_idx != m_final_check_idx); + + if (result == FC_GIVEUP) + return result; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; @@ -1914,21 +1955,21 @@ public: visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } - - lbool check_lia() { + + final_check_status check_lia() { TRACE("arith",); if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_CONTINUE; } - lbool lia_check = l_undef; + final_check_status lia_check = FC_GIVEUP; auto cr = m_lia->check(&m_explanation); if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; switch (cr) { case lp::lia_move::sat: - lia_check = l_true; + lia_check = FC_DONE; break; case lp::lia_move::branch: { @@ -1951,13 +1992,13 @@ public: // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to - lia_check = l_false; + lia_check = FC_CONTINUE; ++m_stats.m_branch; break; } case lp::lia_move::cut: { if (ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; TRACE("arith", tout << "cut\n";); ++m_stats.m_gomory_cuts; // m_explanation implies term <= k @@ -1979,26 +2020,26 @@ public: ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.data(), m_eqs.size(), m_eqs.data(), lit); display(tout);); assign(lit, m_core, m_eqs, m_params); - lia_check = l_false; + lia_check = FC_CONTINUE; break; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core set_conflict(); - return l_false; + return FC_CONTINUE; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); - lia_check = l_undef; + lia_check = FC_CONTINUE; break; case lp::lia_move::continue_with_check: - lia_check = l_undef; + lia_check = FC_CONTINUE; break; default: UNREACHABLE(); } if (lia_check != l_false && !check_idiv_bounds()) - return l_false; + return FC_CONTINUE; return lia_check; } @@ -2188,7 +2229,6 @@ public: set_evidence(j, m_core, m_eqs); m_explanation.add_pair(j, v); } - void propagate_bounds_with_lp_solver() { if (!should_propagate()) @@ -2202,13 +2242,16 @@ public: if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); + // verbose_stream() << "unsat\n"; } else { + unsigned count = 0, prop = 0; for (auto& ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - propagate_lp_solver_bound(ib); + ++prop; + count += propagate_lp_solver_bound(ib); } } } @@ -2229,12 +2272,14 @@ public: return false; } - void propagate_lp_solver_bound(const lp::implied_bound& be) { + +#if 0 + unsigned propagate_lp_solver_bound_dry_run(const lp::implied_bound& be) { lpvar vi = be.m_j; theory_var v = lp().local_to_external(vi); if (v == null_theory_var) - return; + return 0; TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); @@ -2242,20 +2287,58 @@ public: if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { TRACE("arith", tout << "return\n";); - return; + return 0; } lp_bounds const& bounds = m_bounds[v]; bool first = true; + unsigned count = 0; for (unsigned i = 0; i < bounds.size(); ++i) { api_bound* b = bounds[i]; - if (ctx().get_assignment(b->get_lit()) != l_undef) { + if (ctx().get_assignment(b->get_lit()) != l_undef) continue; - } literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == null_literal) { + if (lit == null_literal) continue; - } TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + ctx().display_literal_verbose(verbose_stream() << "miss ", lit) << "\n"; + display(verbose_stream()); + TRACE("arith", ctx().display_literal_verbose(tout << "miss ", lit) << "\n"); + exit(0); + + ++count; + } + return count; + } +#endif + + unsigned propagate_lp_solver_bound(const lp::implied_bound& be) { + lpvar vi = be.m_j; + theory_var v = lp().local_to_external(vi); + + if (v == null_theory_var) + return 0; + + TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); + + reserve_bounds(v); + + if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { + TRACE("arith", tout << "return\n";); + return 0; + } + lp_bounds const& bounds = m_bounds[v]; + bool first = true; + unsigned count = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + api_bound* b = bounds[i]; + if (ctx().get_assignment(b->get_lit()) != l_undef) + continue; + literal lit = is_bound_implied(be.kind(), be.m_bound, *b); + if (lit == null_literal) + continue; + TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + + ++count; lp().settings().stats().m_num_of_implied_bounds ++; if (first) { @@ -2274,6 +2357,8 @@ public: display_evidence(tout, m_explanation); lp().print_implied_bound(be, tout); ); + + DEBUG_CODE( for (auto& lit : m_core) { VERIFY(ctx().get_assignment(lit) == l_true); @@ -2283,7 +2368,9 @@ public: } if (should_refine_bounds() && first) - refine_bound(v, be); + refine_bound(v, be); + + return count; } void refine_bound(theory_var v, const lp::implied_bound& be) { @@ -2907,7 +2994,7 @@ public: propagate_eqs(b.tv(), ci, k, b, value.get_rational()); } #if 0 - if (propagation_mode() != BP_NONE) + if (should_propagate()) lp().mark_rows_for_bound_prop(b.tv().id()); #endif } @@ -3928,5 +4015,6 @@ void theory_lra::setup() { } template class lp::lp_bound_propagator; template void lp::lar_solver::propagate_bounds_for_touched_rows(lp::lp_bound_propagator&); +template void lp::lar_solver::check_missed_propagations(lp::lp_bound_propagator&); template void lp::lar_solver::explain_implied_bound(const lp::implied_bound&, lp::lp_bound_propagator&); -template void lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); +template unsigned lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); From ef943347eec0fd7af188659fc5f7316518fcbb6b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 11:16:15 -0700 Subject: [PATCH 197/220] ensure assume-eqs is invoked after check-lia statically Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 9 +++++ src/math/lp/lar_solver.h | 4 +- src/math/lp/lp_settings.h | 77 ++++++++++++++++---------------------- src/smt/theory_lra.cpp | 13 ++++--- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index e127a53d1..1016c972b 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1378,6 +1378,15 @@ namespace lp { return m_mpq_lar_core_solver.column_is_free(j); } + // column is at lower or upper bound, lower and upper bound are different. + // the lower/upper bound is not strict. + // the LP obtained by making the bound strict is infeasible + // -> the column has to be fixed + bool is_fixed_at_bound(column_index const& j) { + NOT_IMPLEMENTED_YET(); + return false; + } + // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 10ca16d0a..76d8528a0 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -365,8 +365,10 @@ public: verbose_stream() << i << ": " << get_row(i) << "\n"; } } + + bool is_fixed_at_bound(column_index const& j); - bool is_fixed(column_index const& j) const { return column_is_fixed(j); } + bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } bool external_is_used(unsigned) const; void pop(unsigned k); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 5234e3bf0..c213333e0 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -163,11 +163,11 @@ private: }; default_lp_resource_limit m_default_resource_limit; - lp_resource_limit* m_resource_limit; + lp_resource_limit* m_resource_limit = nullptr; // used for debug output - std::ostream* m_debug_out; + std::ostream* m_debug_out = nullptr; // used for messages, for example, the computation progress messages - std::ostream* m_message_out; + std::ostream* m_message_out = nullptr; statistics m_stats; random_gen m_rand; @@ -178,40 +178,40 @@ public: unsigned nlsat_delay() const { return m_nlsat_delay; } bool int_run_gcd_test() const { return m_int_run_gcd_test; } bool& int_run_gcd_test() { return m_int_run_gcd_test; } - unsigned reps_in_scaler { 20 }; - int c_partial_pivoting { 10 }; // this is the constant c from page 410 - unsigned depth_of_rook_search { 4 }; - bool using_partial_pivoting { true }; + unsigned reps_in_scaler = 20; + int c_partial_pivoting = 10; // this is the constant c from page 410 + unsigned depth_of_rook_search = 4; + bool using_partial_pivoting = true; - unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns - bool use_scaling { true }; - unsigned max_number_of_iterations_with_no_improvements { 2000000 }; + unsigned percent_of_entering_to_check = 5; // we try to find a profitable column in a percentage of the columns + bool use_scaling = true; + unsigned max_number_of_iterations_with_no_improvements = 2000000; double time_limit; // the maximum time limit of the total run time in seconds // end of dual section - bool m_bound_propagation { true }; - bool presolve_with_double_solver_for_lar { true }; + bool m_bound_propagation = true; + bool presolve_with_double_solver_for_lar = true; simplex_strategy_enum m_simplex_strategy; - int report_frequency { 1000 }; - bool print_statistics { false }; - unsigned column_norms_update_frequency { 12000 }; - bool scale_with_ratio { true }; - unsigned max_row_length_for_bound_propagation { 300 }; - bool backup_costs { true }; - unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; - unsigned m_int_gomory_cut_period { 4 }; - unsigned m_int_find_cube_period { 4 }; + int report_frequency = 1000; + bool print_statistics = false; + unsigned column_norms_update_frequency = 12000; + bool scale_with_ratio = true; + unsigned max_row_length_for_bound_propagation = 300; + bool backup_costs = true; + unsigned column_number_threshold_for_using_lu_in_lar_solver = 4000; + unsigned m_int_gomory_cut_period = 4; + unsigned m_int_find_cube_period = 4; private: - unsigned m_hnf_cut_period { 4 }; - bool m_int_run_gcd_test { true }; + unsigned m_hnf_cut_period = 4; + bool m_int_run_gcd_test = true; public: - unsigned limit_on_rows_for_hnf_cutter { 75 }; - unsigned limit_on_columns_for_hnf_cutter { 150 }; + unsigned limit_on_rows_for_hnf_cutter = 75; + unsigned limit_on_columns_for_hnf_cutter = 150; private: unsigned m_nlsat_delay; - bool m_enable_hnf { true }; - bool m_print_external_var_name { false }; - bool m_propagate_eqs { false }; + bool m_enable_hnf = true; + bool m_print_external_var_name = false; + bool m_propagate_eqs = false; public: bool print_external_var_name() const { return m_print_external_var_name; } bool propagate_eqs() const { return m_propagate_eqs;} @@ -244,25 +244,12 @@ public: std::ostream* get_debug_ostream() { return m_debug_out; } std::ostream* get_message_ostream() { return m_message_out; } statistics& stats() { return m_stats; } - statistics const& stats() const { return m_stats; } - - - - + statistics const& stats() const { return m_stats; } // the method of lar solver to use - simplex_strategy_enum simplex_strategy() const { - return m_simplex_strategy; - } - - void set_simplex_strategy(simplex_strategy_enum s) { - m_simplex_strategy = s; - } - - - bool use_tableau_rows() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows; - } + simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } + void set_simplex_strategy(simplex_strategy_enum s) { m_simplex_strategy = s; } + bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } #ifdef Z3DEBUG static unsigned ddd; // used for debugging diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index bea080116..f5a10f942 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1595,8 +1595,10 @@ public: CTRACE("arith", is_eq(v1, v2) && n1->get_root() != n2->get_root(), tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + ++m_stats.m_assume_eqs; return true; + } } return false; } @@ -1690,13 +1692,12 @@ public: switch (m_final_check_idx) { case 0: - if (assume_eqs()) { - ++m_stats.m_assume_eqs; - st = FC_CONTINUE; - } + st = check_lia(); break; case 1: - st = check_lia(); + if (assume_eqs()) + st = FC_CONTINUE; + break; case 2: switch (check_nla()) { From c48dc6905056c7dbfc976ff7c54e3f7b962c240f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2023 19:39:42 -0700 Subject: [PATCH 198/220] adding stubs to find fixed variables Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 55 +++++++++++++++++- src/math/lp/lar_solver.h | 1 + src/smt/theory_lra.cpp | 114 +++++++++++++++++++------------------ 3 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 1016c972b..0eb65e197 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1382,11 +1382,62 @@ namespace lp { // the lower/upper bound is not strict. // the LP obtained by making the bound strict is infeasible // -> the column has to be fixed - bool is_fixed_at_bound(column_index const& j) { - NOT_IMPLEMENTED_YET(); + bool lar_solver::is_fixed_at_bound(column_index const& j) { + if (column_is_fixed(j)) + return false; + mpq val; + if (!has_value(j, val)) + return false; + lp::lconstraint_kind k; + if (column_has_upper_bound(j) && + get_upper_bound(j).x == val) { + verbose_stream() << "check upper " << j << "\n"; + push(); + if (column_is_int(j)) + k = LE, val -= 1; + else + k = LT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + if (column_has_lower_bound(j) && + get_lower_bound(j).x == val) { + verbose_stream() << "check lower " << j << "\n"; + push(); + if (column_is_int(j)) + k = GE, val += 1; + else + k = GT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + return false; } + bool lar_solver::has_fixed_at_bound() { + verbose_stream() << "has-fixed-at-bound\n"; + unsigned num_fixed = 0; + for (unsigned j = 0; j < A_r().m_columns.size(); ++j) { + auto ci = column_index(j); + if (is_fixed_at_bound(ci)) { + ++num_fixed; + verbose_stream() << "fixed " << j << "\n"; + } + } + verbose_stream() << "num fixed " << num_fixed << "\n"; + if (num_fixed > 0) + find_feasible_solution(); + return num_fixed > 0; + } + + // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 76d8528a0..182ef0be3 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -367,6 +367,7 @@ public: } bool is_fixed_at_bound(column_index const& j); + bool has_fixed_at_bound(); bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f5a10f942..a5a31709b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -45,6 +45,7 @@ #include "ast/ast_ll_pp.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" +#include "util/distribution.h" typedef lp::var_index lpvar; @@ -1646,6 +1647,7 @@ public: } unsigned m_final_check_idx = 0; + distribution m_dist { 0 }; final_check_status final_check_eh() { if (propagate_core()) @@ -1657,34 +1659,53 @@ public: if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } - final_check_status st = FC_DONE, result = FC_DONE; - m_final_check_idx = 0; // remove to experiment. + bool giveup = false; + final_check_status st = FC_DONE; + // m_final_check_idx = 0; // remove to experiment. unsigned old_idx = m_final_check_idx; - switch (is_sat) { case l_true: TRACE("arith", display(tout)); + // if (lp().has_fixed_at_bound()) // explain and propagate. + #if 0 - distribution dist(++m_final_check_idx); + m_dist.reset(); + m_dist.push(0, 1); + m_dist.push(1, 1); + m_dist.push(2, 1); - dist.add(0, 2); - dist.add(1, 1); - dist.add(1, 1); - - for (auto idx : dist) { + for (auto idx : m_dist) { if (!m.inc()) return FC_GIVEUP; switch (idx) { case 0: + if (assume_eqs()) + st = FC_CONTINUE; + break; case 1: + st = check_nla(); + break; case 2: + st = check_lia(); + break; default: - + UNREACHABLE(); + break; + } + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + giveup = true; + break; } } -#endif + +#else do { if (!m.inc()) @@ -1696,22 +1717,10 @@ public: break; case 1: if (assume_eqs()) - st = FC_CONTINUE; - + st = FC_CONTINUE; break; case 2: - switch (check_nla()) { - case l_true: - st = FC_DONE; - break; - case l_false: - st = FC_CONTINUE; - break; - case l_undef: - TRACE("arith", tout << "check-nra giveup\n";); - st = FC_GIVEUP; - break; - } + st = check_nla(); break; } m_final_check_idx = (m_final_check_idx + 1) % 3; @@ -1721,14 +1730,15 @@ public: case FC_CONTINUE: return st; case FC_GIVEUP: - result = st; + giveup = true; break; } } while (old_idx != m_final_check_idx); +#endif - if (result == FC_GIVEUP) - return result; + if (giveup) + return FC_GIVEUP; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; @@ -1963,14 +1973,12 @@ public: TRACE("arith", tout << "canceled\n";); return FC_CONTINUE; } - final_check_status lia_check = FC_GIVEUP; auto cr = m_lia->check(&m_explanation); if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int) return FC_GIVEUP; switch (cr) { case lp::lia_move::sat: - lia_check = FC_DONE; break; case lp::lia_move::branch: { @@ -1993,9 +2001,8 @@ public: // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to - lia_check = FC_CONTINUE; ++m_stats.m_branch; - break; + return FC_CONTINUE; } case lp::lia_move::cut: { if (ctx().get_fparams().m_arith_ignore_int) @@ -2021,8 +2028,7 @@ public: ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.data(), m_eqs.size(), m_eqs.data(), lit); display(tout);); assign(lit, m_core, m_eqs, m_params); - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); @@ -2031,18 +2037,19 @@ public: return FC_CONTINUE; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; case lp::lia_move::continue_with_check: - lia_check = FC_CONTINUE; - break; + return FC_CONTINUE; default: UNREACHABLE(); } - if (lia_check != l_false && !check_idiv_bounds()) + if (!check_idiv_bounds()) return FC_CONTINUE; - return lia_check; + if (assume_eqs()) + return FC_CONTINUE; + + return FC_DONE; } nla::lemma m_lemma; @@ -2079,36 +2086,31 @@ public: set_conflict_or_lemma(core, false); } - lbool check_nla_continue() { + final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nla->check(m_nla_lemma_vector); switch (r) { - case l_false: { + case l_false: for (const nla::lemma & l : m_nla_lemma_vector) - false_case_of_check_nla(l); - break; - } + false_case_of_check_nla(l); + return FC_CONTINUE; case l_true: - if (assume_eqs()) { - return l_false; - } - break; - case l_undef: - break; + return assume_eqs()? FC_CONTINUE: FC_DONE; + default: + return FC_GIVEUP; } - return r; } - lbool check_nla() { + final_check_status check_nla() { if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_GIVEUP; } CTRACE("arith",!m_nla, tout << "no nla\n";); if (!m_nla) - return l_true; + return FC_DONE; if (!m_nla->need_check()) - return l_true; + return FC_DONE; return check_nla_continue(); } From d5231f8b3318ca4eee922a29414218d9f7a82922 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Apr 2023 08:43:59 -0700 Subject: [PATCH 199/220] fix regressions #6703 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a5a31709b..c6bd12f03 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1661,7 +1661,7 @@ public: } bool giveup = false; final_check_status st = FC_DONE; - // m_final_check_idx = 0; // remove to experiment. + m_final_check_idx = 0; // remove to experiment. unsigned old_idx = m_final_check_idx; switch (is_sat) { case l_true: @@ -1713,12 +1713,12 @@ public: switch (m_final_check_idx) { case 0: - st = check_lia(); - break; - case 1: if (assume_eqs()) st = FC_CONTINUE; break; + case 1: + st = check_lia(); + break; case 2: st = check_nla(); break; From 392266c278c3b1ca21a89610e8a731c40a41bca2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:16:58 -0700 Subject: [PATCH 200/220] fix processing of else expression for model table --- src/model/func_interp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index b180a8a1f..ebb22f079 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -146,7 +146,8 @@ void func_interp::set_else(expr * e) { ptr_vector args; while (e && is_fi_entry_expr(e, args)) { - insert_entry(args.data(), to_app(e)->get_arg(1)); + if (!get_entry(args.data())) + insert_entry(args.data(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); } From c64d61bd0a19907233ac1bffa5b9fdc9edb15713 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:17:32 -0700 Subject: [PATCH 201/220] formatting updates --- src/model/model_smt2_pp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index df1a8b734..b5ac3fbad 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -195,12 +195,10 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co ptr_buffer func_decls; sort_fun_decls(m, md, func_decls); for (func_decl * f : func_decls) { - if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) { + if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) continue; - } - if (!m.is_considered_uninterpreted(f)) { + if (!m.is_considered_uninterpreted(f)) continue; - } func_interp * f_i = md.get_func_interp(f); SASSERT(f->get_arity() == f_i->get_arity()); format_ref body(fm(m)); From f17691715bbe6c87e9e40921bd354336a43a8e52 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 12:18:31 -0700 Subject: [PATCH 202/220] make default argument to ensure_def and mk_def explicit - insert also macro definitions into models --- src/api/api_ast.cpp | 6 ++---- src/ast/recfun_decl_plugin.cpp | 4 ++-- src/ast/recfun_decl_plugin.h | 5 +++-- src/cmd_context/cmd_context.cpp | 36 +++++++++++++++++++-------------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index bc76d02bc..bc29826ff 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -120,10 +120,8 @@ extern "C" { RESET_ERROR_CODE(); // recfun::promise_def def = - mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s), - domain_size, - to_sorts(domain), - to_sort(range)); + mk_c(c)->recfun().get_plugin().mk_def( + to_symbol(s), domain_size, to_sorts(domain), to_sort(range), false); func_decl* d = def.get_def()->get_decl(); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 84d68d782..7a3e9521d 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -492,7 +492,7 @@ namespace recfun { def* plugin::mk_def(replace& subst, bool is_macro, symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs) { - promise_def d = mk_def(name, n, params, range); + promise_def d = mk_def(name, n, params, range, false); SASSERT(! m_defs.contains(d.get_def()->get_decl())); set_definition(subst, d, is_macro, n_vars, vars, rhs); return d.get_def(); @@ -581,7 +581,7 @@ namespace recfun { } symbol fresh_name("fold-rec-" + std::to_string(m().mk_fresh_id())); - auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort()); + auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort(), false); func_decl* f = pd.get_def()->get_decl(); expr_ref new_body(m().mk_app(f, n, args.data()), m()); set_definition(subst, pd, false, n, vars, max_expr); diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 66544bec7..442bdadbd 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -192,9 +192,9 @@ namespace recfun { void get_op_names(svector & op_names, symbol const & logic) override; - promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); - promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); void set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs); @@ -248,6 +248,7 @@ namespace recfun { bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); } bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); } bool is_generated(func_decl* f) const { return is_defined(f) && f->get_parameter(0).get_int() == 1; } + 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; } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 938393e88..a4adbb0ed 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -361,7 +361,7 @@ void cmd_context::insert_macro(symbol const& s, unsigned arity, sort*const* doma vars.push_back(m().mk_var(i, domain[i])); rvars.push_back(m().mk_var(i, domain[arity - i - 1])); } - recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort()); + recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort(), false); // recursive functions have opposite calling convention from macros! var_subst sub(m(), true); @@ -984,7 +984,7 @@ recfun::decl::plugin& cmd_context::get_recfun_plugin() { recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) { SASSERT(logic_has_recfun()); - return get_recfun_plugin().mk_def(name, arity, domain, range); + return get_recfun_plugin().mk_def(name, arity, domain, range, false); } void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* rhs) { @@ -1982,25 +1982,31 @@ void cmd_context::complete_model(model_ref& md) const { } } - for (auto kd : m_func_decls) { - symbol const & k = kd.m_key; - func_decls & v = kd.m_value; + for (auto& [k, v] : m_func_decls) { IF_VERBOSE(12, verbose_stream() << "(model.completion " << k << ")\n"; ); for (unsigned i = 0; i < v.get_num_entries(); i++) { func_decl * f = v.get_entry(i); - if (!md->has_interpretation(f)) { - sort * range = f->get_range(); - expr * some_val = m().get_some_value(range); - if (f->get_arity() > 0) { - func_interp * fi = alloc(func_interp, m(), f->get_arity()); - fi->set_else(some_val); - md->register_decl(f, fi); - } - else - md->register_decl(f, some_val); + + if (md->has_interpretation(f)) + continue; + macro_decls decls; + expr* body = nullptr; + + if (m_macros.find(k, decls)) + body = decls.find(f->get_arity(), f->get_domain()); + sort * range = f->get_range(); + if (!body) + body = m().get_some_value(range); + if (f->get_arity() > 0) { + func_interp * fi = alloc(func_interp, m(), f->get_arity()); + fi->set_else(body); + md->register_decl(f, fi); } + else + md->register_decl(f, body); } } + verbose_stream() << *md << "\n"; } /** From 6c24a70c44aeab3d3de78f8af71fce454cd02342 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 May 2023 13:05:08 -0700 Subject: [PATCH 203/220] remove debug output Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index a4adbb0ed..8d1e375d1 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -2006,7 +2006,6 @@ void cmd_context::complete_model(model_ref& md) const { md->register_decl(f, body); } } - verbose_stream() << *md << "\n"; } /** From 0c9a5f69fd97e48e45da3dd7ab4edc7b2eb73e4f Mon Sep 17 00:00:00 2001 From: Guillaume Bagan Date: Sat, 6 May 2023 20:53:43 +0200 Subject: [PATCH 204/220] JS/TS: add Optimize class (#6712) * implement Optimize class for the high level Typescript API * javascript and wasm: add _malloc to exported functions fix the bug https://github.com/Z3Prover/z3/issues/6709 * javascript: add tests for the Optimize class * javascript: no reason that minimize and optimize must be constants --- src/api/js/scripts/build-wasm.ts | 2 +- src/api/js/src/high-level/high-level.test.ts | 47 ++++++++++ src/api/js/src/high-level/high-level.ts | 99 ++++++++++++++++++++ src/api/js/src/high-level/types.ts | 37 ++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/src/api/js/scripts/build-wasm.ts b/src/api/js/scripts/build-wasm.ts index 3caf643df..0f51c84d3 100644 --- a/src/api/js/scripts/build-wasm.ts +++ b/src/api/js/scripts/build-wasm.ts @@ -40,7 +40,7 @@ function spawnSync(command: string, opts: SpawnOptions = {}) { } function exportedFuncs(): string[] { - const extras = ['_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; + const extras = ['_malloc', '_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; // TODO(ritave): This variable is unused in original script, find out if it's important const fns: any[] = (functions as any[]).filter(f => !asyncFuncs.includes(f.name)); diff --git a/src/api/js/src/high-level/high-level.test.ts b/src/api/js/src/high-level/high-level.test.ts index d42075169..99f95e5b4 100644 --- a/src/api/js/src/high-level/high-level.test.ts +++ b/src/api/js/src/high-level/high-level.test.ts @@ -698,4 +698,51 @@ describe('high-level', () => { expect(m.eval(f.call(0, 0)).eqIdentity(Int.val(0))).toBeTruthy(); }); }); + + describe('optimize', () => { + it("maximization problem over reals", async () => { + const { Real, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Real.const('x'); + const y = Real.const('y'); + const z = Real.const('z'); + + opt.add(x.ge(0), y.ge(0), z.ge(0)); + opt.add(x.le(1), y.le(1), z.le(1)); + opt.maximize(x.mul(7).add(y.mul(9)).sub(z.mul(3))) + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Real.val(0))).toBeTruthy(); + }); + + it("minimization problem over integers using addSoft", async () => { + const { Int, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + opt.add(x.ge(0), y.ge(0)); + opt.add(x.le(1), y.le(1)); + opt.addSoft(x.eq(1), 2); + opt.addSoft(y.eq(1), 1); + opt.add(z.eq(x.mul(5).add(y.mul(5)))); + opt.add(z.le(5)); + opt.minimize(z); + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Int.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Int.val(0))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Int.val(5))).toBeTruthy(); + }); + }); + }); diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index 80f89b104..0869dbd7b 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -35,6 +35,7 @@ import { Z3_app, Z3_params, Z3_func_entry, + Z3_optimize, } from '../low-level'; import { AnyAst, @@ -68,6 +69,7 @@ import { FuncInterp, IntNum, Model, + Optimize, Pattern, Probe, Quantifier, @@ -1327,6 +1329,102 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class OptimizeImpl implements Optimize { + declare readonly __typename: Optimize['__typename']; + + readonly ptr: Z3_optimize; + readonly ctx: Context; + + constructor(ptr: Z3_optimize = Z3.mk_optimize(contextPtr)) { + this.ctx = ctx; + let myPtr: Z3_optimize; + myPtr = ptr; + this.ptr = myPtr; + Z3.optimize_inc_ref(contextPtr, myPtr); + cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr)); + } + + set(key: string, value: any): void { + Z3.optimize_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + + push() { + Z3.optimize_push(contextPtr, this.ptr); + } + + pop() { + Z3.optimize_pop(contextPtr, this.ptr); + } + + add(...exprs: (Bool | AstVector>)[]) { + _flattenArgs(exprs).forEach(expr => { + _assertContext(expr); + check(Z3.optimize_assert(contextPtr, this.ptr, expr.ast)); + }); + } + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id: number | string = "") { + if (isCoercibleRational(weight)) { + weight = `${weight.numerator}/${weight.denominator}`; + } + check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id))) + } + + addAndTrack(expr: Bool, constant: Bool | string) { + if (typeof constant === 'string') { + constant = Bool.const(constant); + } + assert(isConst(constant), 'Provided expression that is not a constant to addAndTrack'); + check(Z3.optimize_assert_and_track(contextPtr, this.ptr, expr.ast, constant.ast)); + } + + assertions(): AstVector> { + return new AstVectorImpl(check(Z3.optimize_get_assertions(contextPtr, this.ptr))); + } + + maximize(expr: Arith) { + check(Z3.optimize_maximize(contextPtr, this.ptr, expr.ast)); + } + + minimize(expr: Arith) { + check(Z3.optimize_minimize(contextPtr, this.ptr, expr.ast)); + } + + async check(...exprs: (Bool | AstVector>)[]): Promise { + const assumptions = _flattenArgs(exprs).map(expr => { + _assertContext(expr); + return expr.ast; + }); + const result = await asyncMutex.runExclusive(() => + check(Z3.optimize_check(contextPtr, this.ptr, assumptions)), + ); + switch (result) { + case Z3_lbool.Z3_L_FALSE: + return 'unsat'; + case Z3_lbool.Z3_L_TRUE: + return 'sat'; + case Z3_lbool.Z3_L_UNDEF: + return 'unknown'; + default: + assertExhaustive(result); + } + } + + model() { + return new ModelImpl(check(Z3.optimize_get_model(contextPtr, this.ptr))); + } + + toString() { + return check(Z3.optimize_to_string(contextPtr, this.ptr)); + } + + fromString(s: string) { + Z3.optimize_from_string(contextPtr, this.ptr, s); + throwIfError(); + } + } + + class ModelImpl implements Model { declare readonly __typename: Model['__typename']; readonly ctx: Context; @@ -2671,6 +2769,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // Classes // ///////////// Solver: SolverImpl, + Optimize: OptimizeImpl, Model: ModelImpl, Tactic: TacticImpl, AstVector: AstVectorImpl as AstVectorCtor, diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index c4b560442..6f3630a6d 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -10,6 +10,7 @@ import { Z3_model, Z3_probe, Z3_solver, + Z3_optimize, Z3_sort, Z3_sort_kind, Z3_tactic, @@ -317,6 +318,9 @@ export interface Context { * @category Classes */ readonly Solver: new (logic?: string) => Solver; + + readonly Optimize: new () => Optimize; + /** * Creates an empty Model * @see {@link Solver.model} for common usage of Model @@ -661,6 +665,39 @@ export interface Solver { model(): Model; } +export interface Optimize { + /** @hidden */ + readonly __typename: 'Optimize'; + + readonly ctx: Context; + readonly ptr: Z3_optimize; + + set(key: string, value: any): void; + + push(): void; + + pop(num?: number): void; + + add(...exprs: (Bool | AstVector>)[]): void; + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id?: number | string): void; + + addAndTrack(expr: Bool, constant: Bool | string): void; + + assertions(): AstVector>; + + fromString(s: string): void; + + maximize(expr: Arith): void; + + minimize(expr: Arith): void; + + check(...exprs: (Bool | AstVector>)[]): Promise; + + model(): Model; +} + + /** @hidden */ export interface ModelCtor { new (): Model; From 2e441e38c9c3ba202a4c215224c2526100dd422b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 May 2023 12:24:20 -0700 Subject: [PATCH 205/220] fix #6713 fix #6714 Signed-off-by: Nikolaj Bjorner --- src/ast/array_decl_plugin.cpp | 4 ++-- src/ast/simplifiers/dependent_expr_state.cpp | 2 +- src/smt/theory_arith_nl.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 7c2b357c2..6778bec7c 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -315,13 +315,13 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * r = to_sort(s->get_parameter(i).get_ast()); diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index d784e6fe4..7d00708ac 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -81,7 +81,7 @@ void dependent_expr_state::freeze_recfun() { ast_mark visited; for (func_decl* f : rec.get_rec_funs()) { auto& d = rec.get_def(f); - if (!d.is_macro()) + if (!d.is_macro() && d.get_rhs()) freeze_terms(d.get_rhs(), false, visited); } m_trail.push(value_trail(m_num_recfun)); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index d6d30cb33..f44516cad 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -892,6 +892,7 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { } tout << "\n";); + return true; } From 12e45c9d17aa48151b2c20573fb3b527b32fdb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Hyv=C3=A4rinen?= Date: Tue, 9 May 2023 19:37:46 +0200 Subject: [PATCH 206/220] Implement proposed smtlib2 bitvector overflow predicates (#6715) * Logical names for function declarations in c++ Currently, for example, the function declaration symbol member for checking whether multiplication *does not* overflow is called `m_bv_smul_ovfl`. Since we are introducing the upcoming smtlib2 symbols that check that multpliciation *does* overflow, the not overflow check symbols are renamed to `m_bv_smul_no_ovfl` etc. * Implement smtlib overflow preds for multiplication Smtlib2 is being extended to include overflow predicates for bit vectors (see https://groups.google.com/u/1/g/smt-lib/c/J4D99wT0aKI). This commit introduces the predicates `bvumulo` and `bvsmulo` that return `true` if the unsigned multiplication overflows or the signed multiplication underflows or overflows, respectively. * Move mul overflow predicates to BV logic * Add a todo on illogical argument order * Implement mk_unary_pred for bv * Implement bvnego * Implement bvuaddo * Implement bvsaddo * Implement bvusubo * Implement bvssubo * Implement bvsdivo --- src/ast/bv_decl_plugin.cpp | 47 +++++++++++- src/ast/bv_decl_plugin.h | 43 ++++++++++- src/ast/rewriter/bv_rewriter.cpp | 123 +++++++++++++++++++++++++++++++ src/ast/rewriter/bv_rewriter.h | 16 ++++ 4 files changed, 222 insertions(+), 7 deletions(-) diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 351d037cb..4d38327a5 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -118,9 +118,22 @@ void bv_decl_plugin::finalize() { DEC_REF(m_bv_redand); DEC_REF(m_bv_comp); + DEC_REF(m_bv_mul_no_ovfl); + DEC_REF(m_bv_smul_no_ovfl); + DEC_REF(m_bv_smul_no_udfl); + DEC_REF(m_bv_mul_ovfl); DEC_REF(m_bv_smul_ovfl); - DEC_REF(m_bv_smul_udfl); + + DEC_REF(m_bv_neg_ovfl); + + DEC_REF(m_bv_uadd_ovfl); + DEC_REF(m_bv_sadd_ovfl); + + DEC_REF(m_bv_usub_ovfl); + DEC_REF(m_bv_ssub_ovfl); + + DEC_REF(m_bv_sdiv_ovfl); DEC_REF(m_bv_shl); DEC_REF(m_bv_lshr); @@ -245,6 +258,16 @@ func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, return m_bv2int[bv_size]; } +func_decl * bv_decl_plugin::mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { + force_ptr_array_size(decls, bv_size+1); + + if (decls[bv_size] == 0) { + decls[bv_size] = m_manager->mk_func_decl(symbol(name), get_bv_sort(bv_size), m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); + m_manager->inc_ref(decls[bv_size]); + } + return decls[bv_size]; +} + func_decl * bv_decl_plugin::mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); @@ -289,6 +312,7 @@ func_decl * bv_decl_plugin::mk_comp(unsigned bv_size) { func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { switch (k) { case OP_BNEG: return mk_unary(m_bv_neg, k, "bvneg", bv_size); + case OP_BNEG_OVFL: return mk_unary_pred(m_bv_neg_ovfl, k, "bvnego", bv_size); case OP_BADD: return mk_binary(m_bv_add, k, "bvadd", bv_size, true); case OP_BSUB: return mk_binary(m_bv_sub, k, "bvsub", bv_size, false); case OP_BMUL: return mk_binary(m_bv_mul, k, "bvmul", bv_size, true); @@ -327,9 +351,16 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { case OP_BREDOR: return mk_reduction(m_bv_redor, k, "bvredor", bv_size); case OP_BREDAND: return mk_reduction(m_bv_redand, k, "bvredand", bv_size); case OP_BCOMP: return mk_comp(bv_size); - case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumul_noovfl", bv_size); - case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmul_noovfl", bv_size); - case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_no_ovfl, k, "bvumul_noovfl", bv_size); + case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_no_ovfl, k, "bvsmul_noovfl", bv_size); + case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_no_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumulo", bv_size); + case OP_BSMUL_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmulo", bv_size); + case OP_BSDIV_OVFL: return mk_pred(m_bv_sdiv_ovfl, k, "bvsdivo", bv_size); + case OP_BUADD_OVFL: return mk_pred(m_bv_uadd_ovfl, k, "bvuaddo", bv_size); + case OP_BSADD_OVFL: return mk_pred(m_bv_sadd_ovfl, k, "bvsaddo", bv_size); + case OP_BUSUB_OVFL: return mk_pred(m_bv_usub_ovfl, k, "bvusubo", bv_size); + case OP_BSSUB_OVFL: return mk_pred(m_bv_ssub_ovfl, k, "bvssubo", bv_size); case OP_BSHL: return mk_binary(m_bv_shl, k, "bvshl", bv_size, false); case OP_BLSHR: return mk_binary(m_bv_lshr, k, "bvlshr", bv_size, false); @@ -681,10 +712,18 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const op_names.push_back(builtin_name("bit1",OP_BIT1)); op_names.push_back(builtin_name("bit0",OP_BIT0)); op_names.push_back(builtin_name("bvneg",OP_BNEG)); + op_names.push_back(builtin_name("bvnego", OP_BNEG_OVFL)); op_names.push_back(builtin_name("bvadd",OP_BADD)); + op_names.push_back(builtin_name("bvuaddo",OP_BUADD_OVFL)); + op_names.push_back(builtin_name("bvsaddo",OP_BSADD_OVFL)); op_names.push_back(builtin_name("bvsub",OP_BSUB)); + op_names.push_back(builtin_name("bvusubo",OP_BUSUB_OVFL)); + op_names.push_back(builtin_name("bvssubo",OP_BSSUB_OVFL)); op_names.push_back(builtin_name("bvmul",OP_BMUL)); + op_names.push_back(builtin_name("bvumulo",OP_BUMUL_OVFL)); + op_names.push_back(builtin_name("bvsmulo",OP_BSMUL_OVFL)); op_names.push_back(builtin_name("bvsdiv",OP_BSDIV)); + op_names.push_back(builtin_name("bvsdivo",OP_BSDIV_OVFL)); op_names.push_back(builtin_name("bvudiv",OP_BUDIV)); op_names.push_back(builtin_name("bvsrem",OP_BSREM)); op_names.push_back(builtin_name("bvurem",OP_BUREM)); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index fc7e35245..51faca7ed 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -93,6 +93,19 @@ enum bv_op_kind { OP_BSMUL_NO_OVFL, // no signed multiplication overflow predicate OP_BSMUL_NO_UDFL, // no signed multiplication underflow predicate + OP_BUMUL_OVFL, // unsigned multiplication overflow predicate (negation of OP_BUMUL_NO_OVFL) + OP_BSMUL_OVFL, // signed multiplication over/underflow predicate + + OP_BSDIV_OVFL, // signed division overflow perdicate + + OP_BNEG_OVFL, // negation overflow predicate + + OP_BUADD_OVFL, // unsigned addition overflow predicate + OP_BSADD_OVFL, // signed addition overflow predicate + + OP_BUSUB_OVFL, // unsigned subtraction overflow predicate + OP_BSSUB_OVFL, // signed subtraction overflow predicate + OP_BIT2BOOL, // predicate OP_MKBV, // bools to bv OP_INT2BV, @@ -189,9 +202,22 @@ protected: ptr_vector m_bv_redand; ptr_vector m_bv_comp; - ptr_vector m_bv_mul_ovfl; - ptr_vector m_bv_smul_ovfl; - ptr_vector m_bv_smul_udfl; + ptr_vector m_bv_mul_no_ovfl; + ptr_vector m_bv_smul_no_ovfl; + ptr_vector m_bv_smul_no_udfl; + + ptr_vector m_bv_mul_ovfl; + ptr_vector m_bv_smul_ovfl; + + ptr_vector m_bv_sdiv_ovfl; + + ptr_vector m_bv_neg_ovfl; + + ptr_vector m_bv_uadd_ovfl; + ptr_vector m_bv_sadd_ovfl; + + ptr_vector m_bv_usub_ovfl; + ptr_vector m_bv_ssub_ovfl; ptr_vector m_bv_shl; ptr_vector m_bv_lshr; @@ -213,6 +239,7 @@ protected: func_decl * mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); + func_decl * mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_comp(unsigned bv_size); bool get_bv_size(sort * t, int & result); @@ -490,9 +517,19 @@ public: app * mk_bv2int(expr* e); + // TODO: all these binary ops commute (right?) but it'd be more logical to swap `n` & `m` in the `return` app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } + app * mk_bvsmul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_OVFL, n, m); } + app * mk_bvumul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_OVFL, n, m); } + app * mk_bvsdiv_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSDIV_OVFL, m, n); } + app * mk_bvneg_ovfl(expr* m) { return m_manager.mk_app(get_fid(), OP_BNEG_OVFL, m); } + app * mk_bvuadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUADD_OVFL, n, m); } + app * mk_bvsadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSADD_OVFL, n, m); } + app * mk_bvusub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUSUB_OVFL, m, n); } + app * mk_bvssub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSSUB_OVFL, m, n); } + app * mk_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); } private: diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 5f76d3fdd..751608e12 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -93,6 +93,10 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons case OP_BNEG: SASSERT(num_args == 1); return mk_uminus(args[0], result); + case OP_BNEG_OVFL: + SASSERT(num_args == 1); + return mk_bvneg_overflow(args[0], result); + case OP_BSHL: SASSERT(num_args == 2); return mk_bv_shl(args[0], args[1], result); @@ -199,6 +203,20 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons return mk_bvsmul_no_overflow(num_args, args, false, result); case OP_BUMUL_NO_OVFL: return mk_bvumul_no_overflow(num_args, args, result); + case OP_BSMUL_OVFL: + return mk_bvsmul_overflow(num_args, args, result); + case OP_BUMUL_OVFL: + return mk_bvumul_overflow(num_args, args, result); + case OP_BSDIV_OVFL: + return mk_bvsdiv_overflow(num_args, args, result); + case OP_BUADD_OVFL: + return mk_bvuadd_overflow(num_args, args, result); + case OP_BSADD_OVFL: + return mk_bvsadd_over_underflow(num_args, args, result); + case OP_BUSUB_OVFL: + return mk_bvusub_underflow(num_args, args, result); + case OP_BSSUB_OVFL: + return mk_bvssub_overflow(num_args, args, result); default: return BR_FAILED; } @@ -2921,6 +2939,21 @@ br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ return BR_DONE; } +br_status bv_rewriter::mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_or( + m.mk_not(m_util.mk_bvsmul_no_ovfl(args[0], args[1])), + m.mk_not(m_util.mk_bvsmul_no_udfl(args[0], args[1])) + ); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_not(m_util.mk_bvumul_no_ovfl(args[0], args[1])); + return BR_REWRITE2; +} + br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; @@ -2980,5 +3013,95 @@ br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, return BR_FAILED; } +br_status bv_rewriter::mk_bvneg_overflow(expr * const arg, expr_ref & result) { + unsigned int sz = get_bv_size(arg); + auto maxUnsigned = mk_numeral(rational::power_of_two(sz)-1, sz); + result = m.mk_eq(arg, maxUnsigned); + return BR_REWRITE3; +} + +br_status bv_rewriter::mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto a1 = mk_zero_extend(1, args[0]); + auto a2 = mk_zero_extend(1, args[1]); + auto r = mk_bv_add(a1, a2); + auto extract = m_mk_extract(sz, sz, r); + result = m.mk_eq(extract, mk_one(1)); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(zero, args[0]); + auto l2 = m_util.mk_slt(zero, args[1]); + auto args_pos = m.mk_and(l1, l2); + auto non_pos_sum = m_util.mk_sle(r, zero); + result = m.mk_and(args_pos, non_pos_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(args[0], zero); + auto l2 = m_util.mk_slt(args[1], zero); + auto args_neg = m.mk_and(l1, l2); + expr_ref non_neg_sum{m}; + auto res_rewrite = mk_sge(r, zero, non_neg_sum); + SASSERT(res_rewrite != BR_FAILED); (void)res_rewrite; + result = m.mk_and(args_neg, non_neg_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + expr_ref l1{m}; + expr_ref l2{m}; + (void)mk_bvsadd_overflow(2, args, l1); + (void)mk_bvsadd_underflow(2, args, l2); + result = m.mk_or(l1, l2); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + br_status status = mk_ult(args[0], args[1], result); + SASSERT(status != BR_FAILED); + return status; +} + +br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[0]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + expr_ref bvsaddo {m}; + expr * args2[2] = { args[0], m_util.mk_bv_neg(args[1]) }; + auto bvsaddo_stat = mk_bvsadd_overflow(2, args2, bvsaddo); + SASSERT(bvsaddo_stat != BR_FAILED); (void)bvsaddo_stat; + auto first_arg_ge_zero = m_util.mk_sle(mk_zero(sz), args[0]); + result = m.mk_ite(m.mk_eq(args[1], minSigned), first_arg_ge_zero, bvsaddo); + return BR_REWRITE_FULL; +} +br_status bv_rewriter::mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[1]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + auto minusOne = mk_numeral(rational::power_of_two(sz) - 1, sz); + result = m.mk_and(m.mk_eq(args[0], minSigned), m.mk_eq(args[1], minusOne)); + return BR_REWRITE_FULL; +} template class poly_rewriter; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 25bd65b61..09e7996c2 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -139,6 +139,22 @@ class bv_rewriter : public poly_rewriter { br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result); br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvneg_overflow(expr * const arg, expr_ref & result); + + br_status mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result); + bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); From f6ab5a61ac8306a78e09a8a55110c6a125bc8652 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 May 2023 13:48:28 -0700 Subject: [PATCH 207/220] reformat code to remove brackets --- src/smt/proto_model/proto_model.cpp | 46 ++++++++++++----------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index d61bcf028..4470f9cb8 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -288,42 +288,33 @@ bool proto_model::is_finite(sort * s) const { } expr * proto_model::get_some_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_value(s); + else // there is no factory for the family id, then assume s is uninterpreted. - return m_user_sort_factory->get_some_value(s); - } + return m_user_sort_factory->get_some_value(s); } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_values(s, v1, v2); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_values(s, v1, v2); - } - else { - return false; - } + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_values(s, v1, v2); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_values(s, v1, v2); + else + return false; } expr * proto_model::get_fresh_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_fresh_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_fresh_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_fresh_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_fresh_value(s); + else // Use user_sort_factory if the theory has no support for model construnction. // This is needed when dummy theories are used for arithmetic or arrays. - return m_user_sort_factory->get_fresh_value(s); - } + return m_user_sort_factory->get_fresh_value(s); } void proto_model::register_value(expr * n) { @@ -354,6 +345,7 @@ void proto_model::compress() { void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { + verbose_stream() << "complete " << f->get_name() << " " << use_fresh << "\n"; expr * else_value; if (use_fresh) { else_value = get_fresh_value(f->get_range()); From 046b80f6a43af5ceff96dfc7114088aa625319b2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 May 2023 12:30:57 -0700 Subject: [PATCH 208/220] remove output Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/proto_model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 4470f9cb8..f79f7851f 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -345,7 +345,6 @@ void proto_model::compress() { void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { - verbose_stream() << "complete " << f->get_name() << " " << use_fresh << "\n"; expr * else_value; if (use_fresh) { else_value = get_fresh_value(f->get_range()); From ba911009e4fe9a823564bdf924cf559065739842 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 May 2023 16:54:40 -0700 Subject: [PATCH 209/220] disable publish Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 7d3ec1085..9a3fcae8c 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -558,7 +558,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(1,0) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From e417f7d78509b2d0c9ebc911fee7632e6ef546b6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 12 May 2023 12:59:04 -0700 Subject: [PATCH 210/220] updated release notes for 12.2 Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 4 ++++ scripts/release.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6cac7cc49..68228df40 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -27,6 +27,10 @@ Version 4.12.2 and `elim-predicates` that go beyond incremental pre-processing used internally. The advantage of using `solve-eqs` during pre-processing can be significant. Incremental pre-processing simplification using `solve-eqs` and other simplifiers that change interpretations was not possible before. +- Optimize added to JS API, thanks to gbagan +- SMTLIB2 proposal for bit-vector overflow predicates added, thanks to aehyvari +- bug fixes, thanks to Clemens Eisenhofer, hgvk94, Lev Nachmanson, and others + Version 4.12.1 ============== diff --git a/scripts/release.yml b/scripts/release.yml index 9a3fcae8c..7d3ec1085 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -558,7 +558,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,0) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From f928b4460665410c83fbe2db2f202cba868fec56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 12 May 2023 14:53:52 -0700 Subject: [PATCH 211/220] update version number Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- RELEASE_NOTES.md | 3 +++ scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9299efe25..9164e8e28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.2.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.3.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 68228df40..9e007d1a1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,9 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.3 +============== + Version 4.12.2 ============== diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 44e436daf..876e0fd21 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, 12, 2, 0) # express a default build version or pick up ci build version + set_version(4, 12, 3, 0) # 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 4aeb44b35..f4573877e 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '2' + Patch: '3' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) diff --git a/scripts/release.yml b/scripts/release.yml index 7d3ec1085..a1306d87f 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.2' + ReleaseVersion: '4.12.3' stages: From 520e692a43c41e8981eb091494bef0297ecbe3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20K=C5=82oczko?= <31284574+kloczek@users.noreply.github.com> Date: Sat, 13 May 2023 17:37:35 +0100 Subject: [PATCH 212/220] Fix building with gcc 13 (#6723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial fix to build with gcc 13 reported in #6722. Signed-off-by: Tomasz Kłoczko --- src/util/tptr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/tptr.h b/src/util/tptr.h index 6213b2efa..2a35af535 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -20,6 +20,7 @@ Revision History: #pragma once #include "util/machine.h" +#include #define TAG_SHIFT PTR_ALIGNMENT #define ALIGNMENT_VALUE (1 << PTR_ALIGNMENT) From c9d8e646ed505a9cd4c84b2cb31d846b8588109f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sat, 13 May 2023 18:37:57 +0200 Subject: [PATCH 213/220] fix missing include (#6720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix missing include in src/util/tptr.h that causes build failure with GCC 13: ``` In file included from /tmp/z3/src/util/region.cpp:53: /tmp/z3/src/util/region.cpp: In member function ‘void* region::allocate(size_t)’: /tmp/z3/src/util/tptr.h:29:62: error: ‘uintptr_t’ does not name a type 29 | #define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ | ^~~~~~~~~ /tmp/z3/src/util/region.cpp:82:22: note: in expansion of macro ‘ALIGN’ 82 | m_curr_ptr = ALIGN(char *, new_curr_ptr); | ^~~~~ /tmp/z3/src/util/region.cpp:57:1: note: ‘uintptr_t’ is defined in header ‘’; did you forget to ‘#include ’? 56 | #include "util/page.h" +++ |+#include 57 | ``` --- src/util/tptr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/tptr.h b/src/util/tptr.h index 2a35af535..50e9417fe 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -19,6 +19,7 @@ Revision History: #pragma once +#include #include "util/machine.h" #include From 06ea765b8229ea784fe6f805379c9e508eb9fed6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 13 May 2023 09:46:49 -0700 Subject: [PATCH 214/220] fix #6721 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/elim_bounds.cpp | 1 + src/ast/rewriter/push_app_ite.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp index de23537ad..27c34463d 100644 --- a/src/ast/rewriter/elim_bounds.cpp +++ b/src/ast/rewriter/elim_bounds.cpp @@ -22,6 +22,7 @@ Revision History: #include "ast/used_vars.h" #include "util/obj_hashtable.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/elim_bounds.h" #include "ast/ast_pp.h" diff --git a/src/ast/rewriter/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp index 0282aa602..b993c29a5 100644 --- a/src/ast/rewriter/push_app_ite.cpp +++ b/src/ast/rewriter/push_app_ite.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "ast/rewriter/push_app_ite.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" From a68f91f0a6bc18a2896e12a842af7beb27ee277f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 May 2023 14:07:12 +0100 Subject: [PATCH 215/220] fix #6729 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 6fd518cb0..24df51845 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5024,12 +5024,14 @@ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { * (re.range c_1 c_n) */ br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) { - zstring s; + zstring slo, shi; unsigned len = 0; bool is_empty = false; - if (str().is_string(lo, s) && s.length() != 1) + if (str().is_string(lo, slo) && slo.length() != 1) is_empty = true; - if (str().is_string(hi, s) && s.length() != 1) + if (str().is_string(hi, shi) && shi.length() != 1) + is_empty = true; + if (slo.length() == 1 && shi.length() == 1 && slo[0] > shi[0]) is_empty = true; len = min_length(lo).second; if (len > 1) @@ -5246,7 +5248,17 @@ br_status seq_rewriter::reduce_re_is_empty(expr* r, expr_ref& result) { else if (re().is_range(r, r1, r2) && str().is_string(r1, s1) && str().is_string(r2, s2) && s1.length() == 1 && s2.length() == 1) { - result = m().mk_bool_val(s1[0] <= s2[0]); + result = m().mk_bool_val(s1[0] > s2[0]); + return BR_DONE; + } + else if (re().is_range(r, r1, r2) && + str().is_string(r1, s1) && s1.length() != 1) { + result = m().mk_true(); + return BR_DONE; + } + else if (re().is_range(r, r1, r2) && + str().is_string(r2, s2) && s2.length() != 1) { + result = m().mk_true(); return BR_DONE; } else if ((re().is_loop(r, r1, lo) || @@ -5307,6 +5319,7 @@ br_status seq_rewriter::mk_le_core(expr * l, expr * r, expr_ref & result) { } br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) { + TRACE("seq", tout << mk_pp(l, m()) << " == " << mk_pp(r, m()) << "\n"); expr_ref_vector res(m()); expr_ref_pair_vector new_eqs(m()); if (m_util.is_re(l)) { From 7963ecaf632d2151c3a74445b52a1eaebd9ed5c5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 May 2023 16:21:34 +0100 Subject: [PATCH 216/220] stubs for #6097 Signed-off-by: Nikolaj Bjorner --- src/api/java/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 4b13a25b1..50fb38b7b 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -179,6 +179,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES Tactic.java TupleSort.java UninterpretedSort.java + UserPropagator.java Version.java Z3Exception.java Z3Object.java From b93529997ebc45770a127c8fb349bb728c194b24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 May 2023 16:25:54 +0100 Subject: [PATCH 217/220] more stubs #6097 Signed-off-by: Nikolaj Bjorner --- src/api/java/UserPropagator.java | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/api/java/UserPropagator.java diff --git a/src/api/java/UserPropagator.java b/src/api/java/UserPropagator.java new file mode 100644 index 000000000..7b3ba5ff9 --- /dev/null +++ b/src/api/java/UserPropagator.java @@ -0,0 +1,55 @@ +/** +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + UserPropagator.java + +Abstract: + + User Propagator plugin + +**/ + + +package com.microsoft.z3; + +import com.microsoft.z3.enumerations.Z3_lbool; +import java.util.*; + +/** + * User Propagator + **/ +@SuppressWarnings("unchecked") +public class UserPropagator implements AutoCloseable { + + Solver solver; + Context ctx; + + /** + * Create a UserPropagator from a solver. + **/ + public UserPropagator(Solver s) + { + this.solver = s; + } + + /** + * Create a UserPropagator from a context. + **/ + public UserPropagator(Context _ctx) + { + this.ctx = _ctx; + } + + /** + * Disposes of the propagator. + **/ + @Override + public void close() + { + this.solver = null; + this.ctx = null; + } +} + From 2c21072c99b3deb2746838158fbaf39b1e27f198 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 May 2023 18:23:10 +0100 Subject: [PATCH 218/220] remove stub class, it may as well go into NativeStatic.txt as C++ Signed-off-by: Nikolaj Bjorner --- src/api/java/CMakeLists.txt | 1 - src/api/java/UserPropagator.java | 55 -------------------------------- 2 files changed, 56 deletions(-) delete mode 100644 src/api/java/UserPropagator.java diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 50fb38b7b..4b13a25b1 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -179,7 +179,6 @@ set(Z3_JAVA_JAR_SOURCE_FILES Tactic.java TupleSort.java UninterpretedSort.java - UserPropagator.java Version.java Z3Exception.java Z3Object.java diff --git a/src/api/java/UserPropagator.java b/src/api/java/UserPropagator.java deleted file mode 100644 index 7b3ba5ff9..000000000 --- a/src/api/java/UserPropagator.java +++ /dev/null @@ -1,55 +0,0 @@ -/** -Copyright (c) 2023 Microsoft Corporation - -Module Name: - - UserPropagator.java - -Abstract: - - User Propagator plugin - -**/ - - -package com.microsoft.z3; - -import com.microsoft.z3.enumerations.Z3_lbool; -import java.util.*; - -/** - * User Propagator - **/ -@SuppressWarnings("unchecked") -public class UserPropagator implements AutoCloseable { - - Solver solver; - Context ctx; - - /** - * Create a UserPropagator from a solver. - **/ - public UserPropagator(Solver s) - { - this.solver = s; - } - - /** - * Create a UserPropagator from a context. - **/ - public UserPropagator(Context _ctx) - { - this.ctx = _ctx; - } - - /** - * Disposes of the propagator. - **/ - @Override - public void close() - { - this.solver = null; - this.ctx = null; - } -} - From 11264c38d84bef9305df3dd34e5165140bec80f8 Mon Sep 17 00:00:00 2001 From: ditto <819045949@qq.com> Date: Thu, 25 May 2023 01:27:28 +0800 Subject: [PATCH 219/220] Java user propagator interface (#6733) * Java API: user propagator interface * Java API: improved user propagator interface * Java API: Add UserPropagatorBase.java * Remove redundant header file * Initialize `JavaInfo` object and error handling * Native.UserPropagatorBase implements AutoCloseable * Add Override annotation --- scripts/update_api.py | 65 ++++++++++++ src/api/java/CMakeLists.txt | 1 + src/api/java/Context.java | 27 +++-- src/api/java/NativeStatic.txt | 153 +++++++++++++++++++++++++++ src/api/java/UserPropagatorBase.java | 97 +++++++++++++++++ 5 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 src/api/java/UserPropagatorBase.java diff --git a/scripts/update_api.py b/scripts/update_api.py index 4295b8961..23d044832 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -633,7 +633,72 @@ def mk_java(java_src, java_dir, package_name): java_native.write(' }\n') java_native.write(' }\n') java_native.write(' }\n') + java_native.write(""" + public static native long propagateInit(Object o, long ctx, long solver); + public static native void propagateRegisterCreated(Object o, long ctx, long solver); + public static native void propagateRegisterFixed(Object o, long ctx, long solver); + public static native void propagateRegisterEq(Object o, long ctx, long solver); + public static native void propagateRegisterDecide(Object o, long ctx, long solver); + public static native void propagateRegisterFinal(Object o, long ctx, long solver); + public static native void propagateConflict(Object o, long ctx, long solver, long javainfo, int num_fixed, long[] fixed, long num_eqs, long[] eq_lhs, long[] eq_rhs, long conseq); + public static native void propagateAdd(Object o, long ctx, long solver, long javainfo, long e); + public static native void propagateNextSplit(Object o, long ctx, long solver, long javainfo, long e, long idx, long phase); + public static native void propagateDestroy(Object o, long ctx, long solver, long javainfo); + public static abstract class UserPropagatorBase implements AutoCloseable { + protected long ctx; + protected long solver; + protected long javainfo; + + public UserPropagatorBase(long _ctx, long _solver) { + ctx = _ctx; + solver = _solver; + javainfo = propagateInit(this, ctx, solver); + } + + @Override + public void close() { + Native.propagateDestroy(this, ctx, solver, javainfo); + javainfo = 0; + solver = 0; + ctx = 0; + } + + protected final void registerCreated() { + Native.propagateRegisterCreated(this, ctx, solver); + } + + protected final void registerFixed() { + Native.propagateRegisterFixed(this, ctx, solver); + } + + protected final void registerEq() { + Native.propagateRegisterEq(this, ctx, solver); + } + + protected final void registerDecide() { + Native.propagateRegisterDecide(this, ctx, solver); + } + + protected final void registerFinal() { + Native.propagateRegisterFinal(this, ctx, solver); + } + + protected abstract void pushWrapper(); + + protected abstract void popWrapper(int number); + + protected abstract void finWrapper(); + + protected abstract void eqWrapper(long lx, long ly); + + protected abstract UserPropagatorBase freshWrapper(long lctx); + + protected abstract void createdWrapper(long le); + + protected abstract void fixedWrapper(long lvar, long lvalue); + } + """) java_native.write('\n') for name, result, params in _dotnet_decls: java_native.write(' protected static native %s INTERNAL%s(' % (type2java(result), java_method_name(name))) diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 4b13a25b1..bd4338f7b 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -179,6 +179,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES Tactic.java TupleSort.java UninterpretedSort.java + UserPropagatorBase.java Version.java Z3Exception.java Z3Object.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 7aaef4801..b5b22405c 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -452,6 +452,21 @@ public class Context implements AutoCloseable { return new FuncDecl<>(this, name, domain, range); } + public final FuncDecl mkPropagateFunction(Symbol name, Sort[] domain, R range) + { + checkContextMatch(name); + checkContextMatch(domain); + checkContextMatch(range); + long f = Native.solverPropagateDeclare( + this.nCtx(), + name.getNativeObject(), + AST.arrayLength(domain), + AST.arrayToNative(domain), + range.getNativeObject()); + return new FuncDecl<>(this, f); + } + + /** * Creates a new function declaration. **/ @@ -2018,11 +2033,11 @@ public class Context implements AutoCloseable { { StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); ++i) { - int code = s.codePointAt(i); - if (code <= 32 || 127 < code) - buf.append(String.format("\\u{%x}", code)); - else - buf.append(s.charAt(i)); + int code = s.codePointAt(i); + if (code <= 32 || 127 < code) + buf.append(String.format("\\u{%x}", code)); + else + buf.append(s.charAt(i)); } return (SeqExpr) Expr.create(this, Native.mkString(nCtx(), buf.toString())); } @@ -2288,7 +2303,7 @@ public class Context implements AutoCloseable { public final ReExpr mkDiff(Expr> a, Expr> b) { checkContextMatch(a, b); - return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); } diff --git a/src/api/java/NativeStatic.txt b/src/api/java/NativeStatic.txt index 4693272d5..f68893e6b 100644 --- a/src/api/java/NativeStatic.txt +++ b/src/api/java/NativeStatic.txt @@ -77,3 +77,156 @@ DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_setInternalErrorHand Z3_set_error_handler((Z3_context)a0, Z3JavaErrorHandler); } + +#include + +struct JavaInfo { + JNIEnv *jenv = nullptr; + jobject jobj = nullptr; + + jmethodID push = nullptr; + jmethodID pop = nullptr; + jmethodID fresh = nullptr; + jmethodID created = nullptr; + jmethodID fixed = nullptr; + jmethodID eq = nullptr; + jmethodID final = nullptr; + + Z3_solver_callback cb = nullptr; +}; + +struct ScopedCB { + JavaInfo *info; + ScopedCB(JavaInfo *_info, Z3_solver_callback cb): info(_info) { + info->cb = cb; + } + ~ScopedCB() { + info->cb = nullptr; + } +}; + +static void push_eh(void* _p, Z3_solver_callback cb) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->push); +} + +static void pop_eh(void* _p, Z3_solver_callback cb, unsigned int number) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->pop, number); +} + +static void* fresh_eh(void* _p, Z3_context new_context) { + JavaInfo *info = static_cast(_p); + return info->jenv->CallObjectMethod(info->jobj, info->fresh, (jlong)new_context); +} + +static void created_eh(void* _p, Z3_solver_callback cb, Z3_ast _e) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->created, (jlong)_e); +} + +static void fixed_eh(void* _p, Z3_solver_callback cb, Z3_ast _var, Z3_ast _value) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->fixed, (jlong)_var, (jlong)_value); +} + +static void eq_eh(void* _p, Z3_solver_callback cb, Z3_ast _x, Z3_ast _y) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->eq, (jlong)_x, (jlong)_y); +} + +static void final_eh(void* _p, Z3_solver_callback cb) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->final); +} + +// TODO: implement decide +static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast* _val, unsigned* bit, Z3_lbool* is_pos) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + +} + +DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + JavaInfo *info = new JavaInfo; + + info->jenv = jenv; + info->jobj = jenv->NewGlobalRef(jobj); + jclass jcls = jenv->GetObjectClass(info->jobj); + info->push = jenv->GetMethodID(jcls, "pushWrapper", "()V"); + info->pop = jenv->GetMethodID(jcls, "popWrapper", "(I)V"); + info->fresh = jenv->GetMethodID(jcls, "freshWrapper", "(J)Lcom/microsoft/z3/Native$UserPropagatorBase;"); + info->created = jenv->GetMethodID(jcls, "createdWrapper", "(J)V"); + info->fixed = jenv->GetMethodID(jcls, "fixedWrapper", "(JJ)V"); + info->eq = jenv->GetMethodID(jcls, "eqWrapper", "(JJ)V"); + info->final = jenv->GetMethodID(jcls, "finWrapper", "()V"); + + if (!info->push || !info->pop || !info->fresh || !info->created || !info->fixed || !info->eq || !info->final) { + assert(false); + } + + Z3_solver_propagate_init((Z3_context)ctx, (Z3_solver)solver, info, push_eh, pop_eh, fresh_eh); + + return (jlong)info; +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateDestroy(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo) { + JavaInfo *info = (JavaInfo*)javainfo; + info->jenv->DeleteGlobalRef(info->jobj); + delete info; +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterCreated(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_created((Z3_context)ctx, (Z3_solver)solver, created_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterFinal(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_final((Z3_context)ctx, (Z3_solver)solver, final_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterFixed(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_fixed((Z3_context)ctx, (Z3_solver)solver, fixed_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterEq(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_eq((Z3_context)ctx, (Z3_solver)solver, eq_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterDecide(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_decide((Z3_context)ctx, (Z3_solver)solver, decide_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateConflict(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong num_fixed, jlongArray fixed, jlong num_eqs, jlongArray eq_lhs, jlongArray eq_rhs, jlong conseq) { + JavaInfo *info = (JavaInfo*)javainfo; + GETLONGAELEMS(Z3_ast, fixed, _fixed); + GETLONGAELEMS(Z3_ast, eq_lhs, _eq_lhs); + GETLONGAELEMS(Z3_ast, eq_rhs, _eq_rhs); + Z3_solver_propagate_consequence((Z3_context)ctx, info->cb, num_fixed, _fixed, num_eqs, _eq_lhs, _eq_rhs, (Z3_ast)conseq); + RELEASELONGAELEMS(fixed, _fixed); + RELEASELONGAELEMS(eq_lhs, _eq_lhs); + RELEASELONGAELEMS(eq_rhs, _eq_rhs); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateAdd(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e) { + JavaInfo *info = (JavaInfo*)javainfo; + Z3_solver_callback cb = info->cb; + if (cb) + Z3_solver_propagate_register_cb((Z3_context)ctx, cb, (Z3_ast)e); + else if (solver) + Z3_solver_propagate_register((Z3_context)ctx, (Z3_solver)solver, (Z3_ast)e); + else { + assert(false); + } +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateNextSplit(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, long e, long idx, long phase) { + JavaInfo *info = (JavaInfo*)javainfo; + Z3_solver_callback cb = info->cb; + Z3_solver_next_split((Z3_context)ctx, cb, (Z3_ast)e, idx, Z3_lbool(phase)); +} diff --git a/src/api/java/UserPropagatorBase.java b/src/api/java/UserPropagatorBase.java new file mode 100644 index 000000000..90243ba68 --- /dev/null +++ b/src/api/java/UserPropagatorBase.java @@ -0,0 +1,97 @@ +package com.microsoft.z3; + +import com.microsoft.z3.Context; +import com.microsoft.z3.enumerations.Z3_lbool; + +public abstract class UserPropagatorBase extends Native.UserPropagatorBase { + private Context ctx; + private Solver solver; + + public UserPropagatorBase(Context _ctx, Solver _solver) { + super(_ctx.nCtx(), _solver.getNativeObject()); + ctx = _ctx; + solver = _solver; + } + + public final Context getCtx() { + return ctx; + } + + public final Solver getSolver() { + return solver; + } + + @Override + protected final void pushWrapper() { + push(); + } + + @Override + protected final void popWrapper(int number) { + pop(number); + } + + @Override + protected final void finWrapper() { + fin(); + } + + @Override + protected final void eqWrapper(long lx, long ly) { + Expr x = new Expr(ctx, lx); + Expr y = new Expr(ctx, ly); + eq(x, y); + } + + @Override + protected final UserPropagatorBase freshWrapper(long lctx) { + return fresh(new Context(lctx)); + } + + @Override + protected final void createdWrapper(long last) { + created(new Expr(ctx, last)); + } + + @Override + protected final void fixedWrapper(long lvar, long lvalue) { + Expr var = new Expr(ctx, lvar); + Expr value = new Expr(ctx, lvalue); + fixed(var, value); + } + + public abstract void push(); + + public abstract void pop(int number); + + public abstract UserPropagatorBase fresh(Context ctx); + + public void created(Expr ast) {} + + public void fixed(Expr var, Expr value) {} + + public void eq(Expr x, Expr y) {} + + public void fin() {} + + public final void add(Expr expr) { + Native.propagateAdd(this, ctx.nCtx(), solver.getNativeObject(), javainfo, expr.getNativeObject()); + } + + public final void conflict(Expr[] fixed) { + conflict(fixed, new Expr[0], new Expr[0]); + } + + public final void conflict(Expr[] fixed, Expr[] lhs, Expr[] rhs) { + AST conseq = ctx.mkBool(false); + Native.propagateConflict( + this, ctx.nCtx(), solver.getNativeObject(), javainfo, + fixed.length, AST.arrayToNative(fixed), lhs.length, AST.arrayToNative(lhs), AST.arrayToNative(rhs), conseq.getNativeObject()); + } + + public final void nextSplit(Expr e, long idx, Z3_lbool phase) { + Native.propagateNextSplit( + this, ctx.nCtx(), solver.getNativeObject(), javainfo, + e.getNativeObject(), idx, phase.toInt()); + } +} \ No newline at end of file From 5e1869d8eb32f0d2d1641f8e9d7fad9fc7a79cf2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 May 2023 09:48:58 +0100 Subject: [PATCH 220/220] fix #6734 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index d84535a9c..82f56ab76 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3024,7 +3024,8 @@ namespace smt { SASSERT(is_well_sorted(m, e)); TRACE("begin_assert_expr", tout << mk_pp(e, m) << " " << mk_pp(pr, m) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m) << "\n";); - pop_to_base_lvl(); + if (!m_searching) + pop_to_base_lvl(); if (pr == nullptr) m_asserted_formulas.assert_expr(e); else