From cc4ac0e65a2713b366cca02431840117904672c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 13 Jun 2023 16:39:53 -0700 Subject: [PATCH 01/41] add guard for eq adapter --- src/smt/theory_lra.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c6bd12f03..8bb80db58 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1017,12 +1017,16 @@ public: } void internalize_eq_eh(app * atom, bool_var) { + if (!ctx().get_fparams().m_arith_eager_eq_axioms) + return; expr* lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(atom, lhs, rhs)); enode * n1 = get_enode(lhs); enode * n2 = get_enode(rhs); - TRACE("arith_verbose", tout << mk_pp(atom, m) << " " << is_arith(n1) << " " << is_arith(n2) << "\n";); - if (is_arith(n1) && is_arith(n2) && n1 != n2) + + if (is_arith(n1) && is_arith(n2) && + n1->get_th_var(get_id()) != null_theory_var && + n2->get_th_var(get_id()) != null_theory_var && n1 != n2) m_arith_eq_adapter.mk_axioms(n1, n2); } From b93171de78fc80cb2f79fcc8011a541d7dbf42d9 Mon Sep 17 00:00:00 2001 From: tcely Date: Fri, 16 Jun 2023 11:46:40 -0400 Subject: [PATCH 02/41] pattern_inference.h: include rewriter_def.h (#6765) Needed to use the `rewriter_tpl` constructor. --- src/ast/pattern/pattern_inference.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index da905dca4..c5c8e2e74 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -20,6 +20,7 @@ Revision History: #include "ast/ast.h" #include "ast/rewriter/rewriter.h" +#include "ast/rewriter/rewriter_def.h" #include "params/pattern_inference_params.h" #include "util/vector.h" #include "util/uint_set.h" From 5f22e983965f66c916fcae5301b62c045b1fa41f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 18 Jun 2023 10:05:34 -0700 Subject: [PATCH 03/41] fix #6766 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/proof_cmds.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index 9c9b9ed62..49a8da09c 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -260,6 +260,8 @@ public: void add_literal(expr* e) override { if (m.is_proof(e)) m_proof_hint = to_app(e); + else if (!m.is_bool(e)) + throw default_exception("literal should be either a Proof or Bool"); else m_lits.push_back(e); } From df77541aae7d76207e61ea8b4037c3c2b6365759 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 18 Jun 2023 16:21:31 -0700 Subject: [PATCH 04/41] #6758 check-assumptions with compound formulas create fresh proxy variables both during compilation to internal format and for the assumptions. These fresh variables may occur in lemmas that are created during search. The lemmas are garbage for future check-sats, but the solver needs to be allowed to invoke GC. Adding a GC call before a check-sat with assumptions allows removing some lemmas every time a new assumptions are used. Eager GC when using assumptions is used elsewhere, for example in cube&conquer scenarios where lemmas learned from one set of assumptions are less likely to be useful for other assumptions. With the GC invocation memory grows at a lesser pace. However, it is not entirely free of memory increases. To avoid memory bloat, have the solver pre-compile the assumptions by defining them as propositional variables, add assertions that the propositional variables are equivalent to the compound formulas and use the propositional variables as assumptions. The same propositional variables come with no extra overhead when invoking check-assumptions. The lemmas are then over the same fixed vocabulary. It is generally a good idea to recycle useful lemmas during the enumeration pass. --- src/smt/smt_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 4125b24de..e55208575 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3337,6 +3337,7 @@ namespace smt { reset_assumptions(); m_literal2assumption.reset(); m_unsat_core.reset(); + if (!asms.empty()) { // We must give a chance to the theories to propagate before we create a new scope... propagate(); @@ -3346,6 +3347,7 @@ namespace smt { return; if (get_cancel_flag()) return; + del_inactive_lemmas(); push_scope(); vector> asm2proxy; internalize_proxies(asms, asm2proxy); From 4d44e60c33a6fa9dc64905f37a92a3c562dc64b1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 18 Jun 2023 16:36:26 -0700 Subject: [PATCH 05/41] fix #6757 --- src/cmd_context/cmd_context.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 61def4c18..6b42d9ee1 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1394,14 +1394,13 @@ void cmd_context::reset_macros() { } void cmd_context::reset_cmds() { - for (auto& kv : m_cmds) { - kv.m_value->reset(*this); + for (auto& [k,v] : m_cmds) { + v->reset(*this); } } void cmd_context::finalize_cmds() { - for (auto& kv : m_cmds) { - cmd * c = kv.m_value; + for (auto& [k,c] : m_cmds) { c->finalize(*this); dealloc(c); } @@ -1433,6 +1432,7 @@ void cmd_context::reset(bool finalize) { m_builtin_decls.reset(); m_extra_builtin_decls.reset(); m_check_logic.reset(); + m_proof_cmds = nullptr; reset_object_refs(); reset_cmds(); reset_psort_decls(); From 32ec02778e20e77f5e7790399efac92969c6f6de Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:50:14 -0700 Subject: [PATCH 06/41] use heap to track infeasible columns. (#6771) * use heap to track infeasible columns * fix the formatting Signed-off-by: Lev Nachmanson --------- Signed-off-by: Lev Nachmanson --- src/math/lp/core_solver_pretty_printer_def.h | 4 +- src/math/lp/lar_core_solver_def.h | 12 ++- src/math/lp/lar_solver.cpp | 30 ++++--- src/math/lp/lar_solver.h | 15 ++-- src/math/lp/lp_core_solver_base.cpp | 4 +- src/math/lp/lp_core_solver_base.h | 57 +++++++------- src/math/lp/lp_core_solver_base_def.h | 6 +- src/math/lp/lp_primal_core_solver.h | 78 ++++++++++--------- .../lp/lp_primal_core_solver_tableau_def.h | 4 +- src/math/lp/lp_settings.h | 2 + src/math/lp/nla_core.cpp | 2 +- src/math/lp/nla_tangent_lemmas.cpp | 4 +- 12 files changed, 116 insertions(+), 102 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 27aa6c75d..b8048b241 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -279,9 +279,9 @@ template void core_solver_pretty_printer::print() print_row(i); } m_out << std::endl; - if (m_core_solver.inf_set().size()) { + if (m_core_solver.inf_heap().size()) { m_out << "inf columns: "; - print_vector(m_core_solver.inf_set(), m_out); + print_vector(m_core_solver.inf_heap(), m_out); m_out << std::endl; } } diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 550b6fe36..942c87b3b 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -85,8 +85,8 @@ unsigned lar_core_solver::get_number_of_non_ints() const { void lar_core_solver::solve() { TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); - lp_assert(m_r_solver.inf_set_is_correct()); - TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.inf_set_size() << ", int_infs = " << get_number_of_non_ints() << std::endl;); + lp_assert(m_r_solver.inf_heap_is_correct()); + TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.inf_heap_size() << ", int_infs = " << get_number_of_non_ints() << std::endl;); if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { m_r_solver.set_status(lp_status::OPTIMAL); TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); @@ -117,11 +117,9 @@ void lar_core_solver::solve() { } lp_assert(r_basis_is_OK()); lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); - lp_assert(m_r_solver.inf_set_is_correct()); - - TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); -} - + lp_assert(m_r_solver.inf_heap_is_correct()); + TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); } +} // namespace lp diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 62712914c..5f544dff1 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -244,6 +244,14 @@ namespace lp { set.erase(j); } + void lar_solver::clean_popped_elements_for_heap(unsigned n, lpvar_heap& heap) { + vector to_remove; + for (unsigned j : heap) + if (j >= n) + to_remove.push_back(j); + for (unsigned j : to_remove) + heap.erase(j); + } void lar_solver::pop(unsigned k) { @@ -271,7 +279,7 @@ 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(); + clean_inf_heap_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()); @@ -328,7 +336,7 @@ namespace lp { void lar_solver::set_costs_to_zero(const lar_term& term) { auto& rslv = m_mpq_lar_core_solver.m_r_solver; - auto& jset = m_mpq_lar_core_solver.m_r_solver.inf_set(); // hijack this set that should be empty right now + auto& jset = m_mpq_lar_core_solver.m_r_solver.inf_heap(); // hijack this set that should be empty right now lp_assert(jset.empty()); for (lar_term::ival p : term) { @@ -674,9 +682,9 @@ namespace lp { void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { if (costs_are_used()) { - bool was_infeas = m_mpq_lar_core_solver.m_r_solver.inf_set_contains(j); + bool was_infeas = m_mpq_lar_core_solver.m_r_solver.inf_heap_contains(j); m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); - if (was_infeas != m_mpq_lar_core_solver.m_r_solver.inf_set_contains(j)) + if (was_infeas != m_mpq_lar_core_solver.m_r_solver.inf_heap_contains(j)) m_basic_columns_with_changed_cost.insert(j); } else { @@ -1293,12 +1301,12 @@ namespace lp { lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); } - void lar_solver::clean_inf_set_of_r_solver_after_pop() { + void lar_solver::clean_inf_heap_of_r_solver_after_pop() { vector became_feas; - clean_popped_elements(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.inf_set()); + clean_popped_elements_for_heap(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.inf_heap()); std::unordered_set basic_columns_with_changed_cost; m_inf_index_copy.reset(); - for (auto j : m_mpq_lar_core_solver.m_r_solver.inf_set()) + for (auto j : m_mpq_lar_core_solver.m_r_solver.inf_heap()) m_inf_index_copy.push_back(j); for (auto j : m_inf_index_copy) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { @@ -1316,16 +1324,16 @@ namespace lp { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); m_mpq_lar_core_solver.m_r_solver.m_d[j] -= m_mpq_lar_core_solver.m_r_solver.m_costs[j]; m_mpq_lar_core_solver.m_r_solver.m_costs[j] = zero_of_type(); - m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); + m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_heap(j); } became_feas.clear(); - for (unsigned j : m_mpq_lar_core_solver.m_r_solver.inf_set()) { + for (unsigned j : m_mpq_lar_core_solver.m_r_solver.inf_heap()) { lp_assert(m_mpq_lar_core_solver.m_r_heading[j] >= 0); if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) became_feas.push_back(j); } for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); + m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_heap(j); } @@ -1504,7 +1512,7 @@ namespace lp { m_mpq_lar_core_solver.m_r_x.resize(j + 1); m_mpq_lar_core_solver.m_r_lower_bounds.increase_size_by_one(); m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one(); - m_mpq_lar_core_solver.m_r_solver.inf_set_increase_size_by_one(); + m_mpq_lar_core_solver.m_r_solver.inf_heap_increase_size_by_one(); m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1); m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 5b421d70d..f2017acd7 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -94,7 +94,7 @@ class lar_solver : public column_namer { // these are basic columns with the value changed, so the corresponding row in the tableau // does not sum to zero anymore u_set m_incorrect_columns; - // copy of m_r_solver.inf_set() + // copy of m_r_solver.inf_heap() unsigned_vector m_inf_index_copy; stacked_value m_term_count; vector m_terms; @@ -178,7 +178,9 @@ class lar_solver : public column_namer { bp); } + static void clean_popped_elements_for_heap(unsigned n, lpvar_heap& set); static void clean_popped_elements(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; @@ -230,7 +232,7 @@ class lar_solver : public column_namer { void remove_last_column_from_basis_tableau(unsigned j); void remove_last_column_from_tableau(); void pop_tableau(); - void clean_inf_set_of_r_solver_after_pop(); + void clean_inf_heap_of_r_solver_after_pop(); inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; @@ -386,14 +388,15 @@ public: return tv::is_term(idx)? m_term_register.local_to_external(idx) : m_var_register.local_to_external(idx); } + bool column_corresponds_to_term(unsigned) const; inline unsigned row_count() const { return A_r().row_count(); } bool var_is_registered(var_index vj) const; - void clear_inf_set() { - m_mpq_lar_core_solver.m_r_solver.inf_set().clear(); + void clear_inf_heap() { + m_mpq_lar_core_solver.m_r_solver.inf_heap().clear(); } - inline void remove_column_from_inf_set(unsigned j) { - m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); + inline void remove_column_from_inf_heap(unsigned j) { + m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_heap(j); } template void change_basic_columns_dependend_on_a_given_nb_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 f1ae95ea0..28f92d8e2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -63,8 +63,8 @@ template bool lp::lp_core_solver_base >::colu 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_heap_is_correct() const; +template bool lp::lp_core_solver_base::inf_heap_is_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 fb0c28507..f38587573 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -28,9 +28,13 @@ Revision History: #include "math/lp/permutation_matrix.h" #include "math/lp/column_namer.h" #include "math/lp/u_set.h" - +#include "util/heap.h" namespace lp { +struct lpvar_lt { + bool operator()(lpvar v1, lpvar v2) const { return v1 < v2; } +}; +typedef heap lpvar_heap; template X dot_product(const vector & a, const vector & b) { lp_assert(a.size() == b.size()); @@ -51,24 +55,24 @@ private: public: bool current_x_is_feasible() const { TRACE("feas", - if (m_inf_set.size()) { - tout << "column " << *m_inf_set.begin() << " is infeasible" << std::endl; - print_column_info(*m_inf_set.begin(), tout); + if (m_inf_heap.size()) { + tout << "column " << *m_inf_heap.begin() << " is infeasible" << std::endl; + print_column_info(*m_inf_heap.begin(), tout); } else { tout << "x is feasible\n"; } ); - return m_inf_set.size() == 0; + return m_inf_heap.empty(); } - bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } + bool current_x_is_infeasible() const { return m_inf_heap.size() != 0; } private: - u_set m_inf_set; + lpvar_heap m_inf_heap; 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(); } + const lpvar_heap& inf_heap() const { return m_inf_heap; } + lpvar_heap& inf_heap() { return m_inf_heap; } + void inf_heap_increase_size_by_one() { m_inf_heap.reserve(m_inf_heap.size() + 1); } + bool inf_heap_contains(unsigned j) const { return m_inf_heap.contains(j); } + unsigned inf_heap_size() const { return m_inf_heap.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 @@ -255,7 +259,7 @@ public: bool calc_current_x_is_feasible_include_non_basis() const; - bool inf_set_is_correct() const; + bool inf_heap_is_correct() const; bool column_is_dual_feasible(unsigned j) const; @@ -526,8 +530,8 @@ public: swap(m_basis_heading, m_basis[i], m_basis[ii]); } - bool column_is_in_inf_set(unsigned j) const { - return m_inf_set.contains(j); + bool column_is_in_inf_heap(unsigned j) const { + return m_inf_heap.contains(j); } bool column_is_base(unsigned j) const { @@ -560,29 +564,26 @@ public: void track_column_feasibility(unsigned j) { if (column_is_feasible(j)) - remove_column_from_inf_set(j); + remove_column_from_inf_heap(j); else - insert_column_into_inf_set(j); + insert_column_into_inf_heap(j); } - void insert_column_into_inf_set(unsigned j) { + void insert_column_into_inf_heap(unsigned j) { TRACE("lar_solver", tout << "j = " << j << "\n";); - m_inf_set.insert(j); + if (!m_inf_heap.contains(j)) + m_inf_heap.insert(j); lp_assert(!column_is_feasible(j)); } - void remove_column_from_inf_set(unsigned j) { + void remove_column_from_inf_heap(unsigned j) { TRACE("lar_solver", tout << "j = " << j << "\n";); - m_inf_set.erase(j); + if (m_inf_heap.contains(j)) + m_inf_heap.erase(j); lp_assert(column_is_feasible(j)); } - void resize_inf_set(unsigned size) { + void clear_inf_heap() { TRACE("lar_solver",); - m_inf_set.resize(size); - } - - void clear_inf_set() { - TRACE("lar_solver",); - m_inf_set.clear(); + m_inf_heap.clear(); } bool costs_on_nbasis_are_zeros() const { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 8619c926e..319966091 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -42,7 +42,7 @@ lp_core_solver_base(static_matrix & A, m_total_iterations(0), m_iters_with_no_cost_growing(0), m_status(lp_status::FEASIBLE), - m_inf_set(A.column_count()), + m_inf_heap(std::max(static_cast(1024), A.column_count())), m_pivot_row(A.column_count()), m_A(A), m_basis(basis), @@ -250,9 +250,9 @@ template bool lp_core_solver_base::calc_current_x return true; } -template bool lp_core_solver_base::inf_set_is_correct() const { +template bool lp_core_solver_base::inf_heap_is_correct() const { for (unsigned j = 0; j < this->m_n(); j++) { - bool belongs_to_set = m_inf_set.contains(j); + bool belongs_to_set = m_inf_heap.contains(j); bool is_feas = column_is_feasible(j); if (is_feas == belongs_to_set) { TRACE("lp_core", tout << "incorrectly set column in inf set "; print_column_info(j, tout) << "\n";); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 207428985..b2e402c08 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -33,6 +33,8 @@ Revision History: #include #include #include +#include "util/heap.h" + namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq @@ -152,19 +154,28 @@ public: UNREACHABLE(); // unreachable return false; } - - unsigned get_number_of_basic_vars_that_might_become_inf( - unsigned j) const { // consider looking at the signs here: todo +/** + * Return the number of base non-free variables depending on the column j, + * different from bj, + * but take the min with the (bound+1). + * This function is used to select the pivot variable. +*/ + unsigned get_num_of_not_free_basic_dependent_vars( + unsigned j, unsigned bound, unsigned bj) 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++; + unsigned basic_for_row = this->m_basis[cc.var()]; + if (basic_for_row == bj) + continue; + + // std::cout << this->m_A.m_rows[cc.var()] << std::endl; + if (this->m_column_types[basic_for_row] != column_type::free_column) + if (r++ > bound) return r; } return r; } - int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T &a_ent) { + int find_beneficial_entering_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); @@ -190,15 +201,15 @@ public: return j; } - int find_beneficial_column_in_row_tableau_rows(int i, T &a_ent) { + int find_beneficial_entering_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); + return find_beneficial_entering_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 min_non_free_so_far = -1; + unsigned best_col_sz = -1; 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++) { @@ -213,17 +224,18 @@ public: 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(); + unsigned not_free = get_num_of_not_free_basic_dependent_vars(j, min_non_free_so_far, bj); + unsigned col_sz = this->m_A.m_columns[j].size(); + if (not_free < min_non_free_so_far || (not_free == min_non_free_so_far && col_sz < best_col_sz)) { + min_non_free_so_far = not_free; + best_col_sz = 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(); + } else if (not_free == min_non_free_so_far && + col_sz == best_col_sz) { + if (this->m_settings.random_next(++nchoices) == 0){ + choice = k; + } } } @@ -282,7 +294,7 @@ public: 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(); @@ -291,14 +303,6 @@ public: 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); - - // 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(); @@ -356,15 +360,13 @@ public: } int find_smallest_inf_column() { - int j = -1; - for (unsigned k : this->inf_set()) { - if (k < static_cast(j)) { - j = k; - } - } - return j; + if (this->inf_heap().empty()) + return -1; + return this->inf_heap().min_value(); } + + const X &get_val_for_leaving(unsigned j) const { lp_assert(!this->column_is_feasible(j)); switch (this->m_column_types[j]) { @@ -405,7 +407,7 @@ public: } } T a_ent; - int entering = find_beneficial_column_in_row_tableau_rows( + int entering = find_beneficial_entering_tableau_rows( this->m_basis_heading[leaving], a_ent); if (entering == -1) { this->set_status(lp_status::INFEASIBLE); @@ -414,7 +416,7 @@ public: 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); + this->remove_column_from_inf_heap(leaving); advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); if (this->current_x_is_feasible()) this->set_status(lp_status::OPTIMAL); 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 898abb152..0c4f6216e 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -30,7 +30,7 @@ template void lp_primal_core_solver::one_iteratio else { advance_on_entering_tableau(entering); } - lp_assert(this->inf_set_is_correct()); + lp_assert(this->inf_heap_is_correct()); } template void lp_primal_core_solver::advance_on_entering_tableau(int entering) { @@ -256,7 +256,7 @@ template void lp_primal_core_solver::init_run_tab 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; - lp_assert(this->inf_set_is_correct()); + lp_assert(this->inf_heap_is_correct()); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) return; if (this->m_settings.backup_costs) diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index c213333e0..727bc3531 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -218,6 +218,8 @@ public: unsigned hnf_cut_period() const { return m_hnf_cut_period; } void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; } unsigned random_next() { return m_rand(); } + unsigned random_next(unsigned u ) { return m_rand(u); } + void set_random_seed(unsigned s) { m_rand.set_seed(s); } bool bound_progation() const { diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4d1cc6edb..4380b940b 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1436,7 +1436,7 @@ void core::patch_monomials() { } else { m_lar_solver.pop(); restore_tableau(); - m_lar_solver.clear_inf_set(); + m_lar_solver.clear_inf_heap(); } SASSERT(m_lar_solver.ax_is_correct()); } diff --git a/src/math/lp/nla_tangent_lemmas.cpp b/src/math/lp/nla_tangent_lemmas.cpp index 299d8031f..4ba9eeccc 100644 --- a/src/math/lp/nla_tangent_lemmas.cpp +++ b/src/math/lp/nla_tangent_lemmas.cpp @@ -75,8 +75,8 @@ private: c().negate_relation(lemma, m_jy, m_y.rat_sign()*pl.y); #if Z3DEBUG SASSERT(c().val(m_x) == m_xy.x && c().val(m_y) == m_xy.y); - int mult_sign = nla::rat_sign(pl.x - m_xy.x)*nla::rat_sign(pl.y - m_xy.y); - SASSERT((mult_sign == 1) == m_below); + // int mult_sign = nla::rat_sign(pl.x - m_xy.x)*nla::rat_sign(pl.y - m_xy.y); + SASSERT((nla::rat_sign(pl.x - m_xy.x)*nla::rat_sign(pl.y - m_xy.y) == 1) == m_below); // If "mult_sign is 1" then (a - x)(b-y) > 0 and ab - bx - ay + xy > 0 // or -ab + bx + ay < xy or -ay - bx + xy > -ab // val(j) stands for xy. So, finally we have -ay - bx + j > - ab From eb1caee18aedf3e8994d6f0b327960e24d5f9ffe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 20 Jun 2023 16:09:34 -0700 Subject: [PATCH 07/41] compile constants into different variables instead of reusing a single variable 1 and coefficients. It delays introducing large coefficients and allows more efficient bounds propagation Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 98 +++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8bb80db58..5372a3b33 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -90,13 +90,11 @@ class theory_lra::imp { expr_ref_vector m_terms; vector m_coeffs; svector m_vars; - rational m_offset; ptr_vector m_to_ensure_enode, m_to_ensure_var; internalize_state(ast_manager& m): m_terms(m) {} void reset() { m_terms.reset(); m_coeffs.reset(); - m_offset.reset(); m_vars.reset(); m_to_ensure_enode.reset(); m_to_ensure_var.reset(); @@ -123,7 +121,6 @@ class theory_lra::imp { expr_ref_vector& terms() { return m_st.m_terms; } vector& coeffs() { return m_st.m_coeffs; } svector& vars() { return m_st.m_vars; } - rational& offset() { return m_st.m_offset; } ptr_vector& to_ensure_enode() { return m_st.m_to_ensure_enode; } ptr_vector& to_ensure_var() { return m_st.m_to_ensure_var; } void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } @@ -345,18 +342,33 @@ class theory_lra::imp { st.push(rhs, rational::minus_one()); linearize(st); } + + theory_var internalize_numeral(app* n, rational const& val) { + + if (!ctx().e_internalized(n)) + mk_enode(n); + theory_var v = mk_var(n); + lpvar vi = get_lpvar(v); + if (vi == UINT_MAX) { + vi = lp().add_var(v, a.is_int(n)); + add_def_constraint_and_equality(vi, lp::GE, val); + add_def_constraint_and_equality(vi, lp::LE, val); + register_fixed_var(v, val); + } + return v; + } + void linearize(scoped_internalize_state& st) { expr_ref_vector & terms = st.terms(); svector& vars = st.vars(); vector& coeffs = st.coeffs(); - rational& offset = st.offset(); rational r; expr* n1, *n2; unsigned index = 0; while (index < terms.size()) { SASSERT(index >= vars.size()); - expr* n = terms[index].get(); + expr* n = terms.get(index); st.to_ensure_enode().push_back(n); if (a.is_add(n)) { for (expr* arg : *to_app(n)) { @@ -394,7 +406,9 @@ class theory_lra::imp { ++index; } else if (a.is_numeral(n, r)) { - offset += coeffs[index]*r; + theory_var v = internalize_numeral(to_app(n), r); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); ++index; } else if (a.is_uminus(n, n1)) { @@ -781,16 +795,9 @@ class theory_lra::imp { } bool is_unit_var(scoped_internalize_state& st) { - return st.offset().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); + return st.vars().size() == 1 && st.coeffs()[0].is_one(); } - bool is_one(scoped_internalize_state& st) { - return st.offset().is_one() && st.vars().empty(); - } - - bool is_zero(scoped_internalize_state& st) { - return st.offset().is_zero() && st.vars().empty(); - } theory_var internalize_def(app* term, scoped_internalize_state& st) { TRACE("arith", tout << expr_ref(term, m) << "\n";); @@ -841,46 +848,29 @@ class theory_lra::imp { theory_var v = mk_var(term); TRACE("arith", tout << mk_bounded_pp(term, m) << " v" << v << "\n";); - if (is_unit_var(st) && v == st.vars()[0]) { + if (is_unit_var(st) && v == st.vars()[0]) return st.vars()[0]; - } - else if (is_one(st) && a.is_numeral(term)) { - return lp().local_to_external(get_one(a.is_int(term))); - } - else if (is_zero(st) && a.is_numeral(term)) { - return lp().local_to_external(get_zero(a.is_int(term))); - } - else { - init_left_side(st); - lpvar vi = get_lpvar(v); - if (vi == UINT_MAX) { - if (m_left_side.empty()) { - vi = lp().add_var(v, a.is_int(term)); - add_def_constraint_and_equality(vi, lp::GE, st.offset()); - add_def_constraint_and_equality(vi, lp::LE, st.offset()); - register_fixed_var(v, st.offset()); - return v; - } - if (!st.offset().is_zero()) { - m_left_side.push_back(std::make_pair(st.offset(), get_one(a.is_int(term)))); - } - if (m_left_side.empty()) { - vi = lp().add_var(v, a.is_int(term)); - add_def_constraint_and_equality(vi, lp::GE, rational(0)); - add_def_constraint_and_equality(vi, lp::LE, rational(0)); - } - else { - vi = lp().add_term(m_left_side, v); - SASSERT(lp::tv::is_term(vi)); - TRACE("arith_verbose", - tout << "v" << v << " := " << mk_pp(term, m) - << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); - } - } - return v; + init_left_side(st); + lpvar vi = get_lpvar(v); + + if (vi == UINT_MAX) { + if (m_left_side.empty()) { + vi = lp().add_var(v, a.is_int(term)); + add_def_constraint_and_equality(vi, lp::GE, rational(0)); + add_def_constraint_and_equality(vi, lp::LE, rational(0)); + } + else { + vi = lp().add_term(m_left_side, v); + SASSERT(lp::tv::is_term(vi)); + TRACE("arith_verbose", + tout << "v" << v << " := " << mk_pp(term, m) + << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; + lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); + } } + + return v; } @@ -3211,11 +3201,13 @@ public: void fixed_var_eh(theory_var v, lp::tv t, lp::constraint_index ci1, lp::constraint_index ci2, rational const& bound) { theory_var w = null_theory_var; enode* x = get_enode(v); - if (bound.is_zero()) + if (m_value2var.find(bound, w)) + ; + else if (bound.is_zero()) w = lp().local_to_external(get_zero(a.is_int(x->get_expr()))); else if (bound.is_one()) w = lp().local_to_external(get_one(a.is_int(x->get_expr()))); - else if (!m_value2var.find(bound, w)) + else return; enode* y = get_enode(w); if (x->get_sort() != y->get_sort()) From 3517361a736a96f342e53a0cbd089b297332ca55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Tue, 20 Jun 2023 16:10:37 -0700 Subject: [PATCH 08/41] Adding some options in support of F* (#6774) * patterns: add option for pattern decomposition (pi.decompose_patterns) True by default, retaining current behavior. * rewriter: add option for sorting of disjunctions (rewriter.sort_disjunctions) True by default, retaining current behavior. --- src/ast/pattern/pattern_inference.cpp | 4 ++++ src/ast/pattern/pattern_inference.h | 1 + src/ast/rewriter/bool_rewriter.cpp | 5 +++-- src/ast/rewriter/bool_rewriter.h | 1 + src/params/bool_rewriter_params.pyg | 1 + src/params/pattern_inference_params.cpp | 2 ++ src/params/pattern_inference_params.h | 1 + src/params/pattern_inference_params_helper.pyg | 1 + 8 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 2fd2b4c82..4391d8bf8 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -109,6 +109,7 @@ pattern_inference_cfg::pattern_inference_cfg(ast_manager & m, pattern_inference_ m_le(), m_nested_arith_only(true), m_block_loop_patterns(params.m_pi_block_loop_patterns), + m_decompose_patterns(params.m_pi_decompose_patterns), m_candidates(m), m_pattern_weight_lt(m_candidates_info), m_collect(m, *this), @@ -407,6 +408,9 @@ bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) app* pattern_inference_cfg::mk_pattern(app* candidate) { + if (!m_decompose_patterns) + return m.mk_pattern(candidate); + auto has_var_arg = [&](expr* e) { if (!is_app(e)) return false; diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index c5c8e2e74..8d179ba33 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -70,6 +70,7 @@ class pattern_inference_cfg : public default_rewriter_cfg { expr * const * m_no_patterns; bool m_nested_arith_only; bool m_block_loop_patterns; + bool m_decompose_patterns; struct info { uint_set m_free_vars; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 95c0950d8..16ecf6473 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -26,6 +26,7 @@ Notes: void bool_rewriter::updt_params(params_ref const & _p) { bool_rewriter_params p(_p); m_flat_and_or = p.flat_and_or(); + m_sort_disjunctions = p.sort_disjunctions(); m_elim_and = p.elim_and(); m_elim_ite = p.elim_ite(); m_local_ctx = p.local_ctx(); @@ -291,7 +292,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args if (st != BR_FAILED) return st; #endif - if (s) { + if (m_sort_disjunctions && s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); result = m().mk_or(sz, buffer.data()); @@ -329,7 +330,7 @@ br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, } } if (mk_nflat_or_core(flat_args.size(), flat_args.data(), result) == BR_FAILED) { - if (!ordered) { + if (m_sort_disjunctions && !ordered) { ast_lt lt; std::sort(flat_args.begin(), flat_args.end(), lt); } diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 0693e94ba..1c1e7c60e 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -53,6 +53,7 @@ class bool_rewriter { ast_manager & m_manager; hoist_rewriter m_hoist; bool m_flat_and_or = false; + bool m_sort_disjunctions = true; bool m_local_ctx = false; bool m_elim_and = false; bool m_blast_distinct = false; diff --git a/src/params/bool_rewriter_params.pyg b/src/params/bool_rewriter_params.pyg index c8d7ddbb7..87578470e 100644 --- a/src/params/bool_rewriter_params.pyg +++ b/src/params/bool_rewriter_params.pyg @@ -4,6 +4,7 @@ def_module_params(module_name='rewriter', params=(("ite_extra_rules", BOOL, True, "extra ite simplifications, these additional simplifications may reduce size locally but increase globally"), ("flat", BOOL, True, "create nary applications for +,*,bvadd,bvmul,bvand,bvor,bvxor"), ("flat_and_or", BOOL, True, "create nary applications for and,or"), + ("sort_disjunctions", BOOL, True, "sort subterms in disjunctions"), ("elim_and", BOOL, False, "conjunctions are rewritten using negation and disjunctions"), ('elim_ite', BOOL, True, "eliminate ite in favor of and/or"), ("local_ctx", BOOL, False, "perform local (i.e., cheap) context simplifications"), diff --git a/src/params/pattern_inference_params.cpp b/src/params/pattern_inference_params.cpp index 26f606b63..aac574c6e 100644 --- a/src/params/pattern_inference_params.cpp +++ b/src/params/pattern_inference_params.cpp @@ -23,6 +23,7 @@ void pattern_inference_params::updt_params(params_ref const & _p) { pattern_inference_params_helper p(_p); m_pi_max_multi_patterns = p.max_multi_patterns(); m_pi_block_loop_patterns = p.block_loop_patterns(); + m_pi_decompose_patterns = p.decompose_patterns(); m_pi_arith = static_cast(p.arith()); m_pi_use_database = p.use_database(); m_pi_arith_weight = p.arith_weight(); @@ -36,6 +37,7 @@ void pattern_inference_params::updt_params(params_ref const & _p) { void pattern_inference_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pi_max_multi_patterns); DISPLAY_PARAM(m_pi_block_loop_patterns); + DISPLAY_PARAM(m_pi_decompose_patterns); DISPLAY_PARAM(m_pi_arith); DISPLAY_PARAM(m_pi_use_database); DISPLAY_PARAM(m_pi_arith_weight); diff --git a/src/params/pattern_inference_params.h b/src/params/pattern_inference_params.h index d05100759..b037411ec 100644 --- a/src/params/pattern_inference_params.h +++ b/src/params/pattern_inference_params.h @@ -29,6 +29,7 @@ enum arith_pattern_inference_kind { struct pattern_inference_params { unsigned m_pi_max_multi_patterns; bool m_pi_block_loop_patterns; + bool m_pi_decompose_patterns; arith_pattern_inference_kind m_pi_arith; bool m_pi_use_database; unsigned m_pi_arith_weight; diff --git a/src/params/pattern_inference_params_helper.pyg b/src/params/pattern_inference_params_helper.pyg index 52c6c653e..cb1f02877 100644 --- a/src/params/pattern_inference_params_helper.pyg +++ b/src/params/pattern_inference_params_helper.pyg @@ -4,6 +4,7 @@ def_module_params(class_name='pattern_inference_params_helper', export=True, params=(('max_multi_patterns', UINT, 0, 'when patterns are not provided, the prover uses a heuristic to infer them, this option sets the threshold on the number of extra multi-patterns that can be created; by default, the prover creates at most one multi-pattern when there is no unary pattern'), ('block_loop_patterns', BOOL, True, 'block looping patterns during pattern inference'), + ('decompose_patterns', BOOL, True, 'allow decomposition of patterns into multipatterns'), ('arith', UINT, 1, '0 - do not infer patterns with arithmetic terms, 1 - use patterns with arithmetic terms if there is no other pattern, 2 - always use patterns with arithmetic terms'), ('use_database', BOOL, False, 'use pattern database'), ('arith_weight', UINT, 5, 'default weight for quantifiers where the only available pattern has nested arithmetic terms'), From 1b263f85e4e8f693b455ff3419fe9e914ff78fe6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 21 Jun 2023 09:36:20 -0700 Subject: [PATCH 09/41] compile numeral constants into separate variables in the new core Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_internalize.cpp | 83 ++++++++++++++----------------- src/sat/smt/arith_solver.h | 4 +- 2 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 60ca9651a..65ed5ac42 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -160,7 +160,6 @@ namespace arith { expr_ref_vector& terms = st.terms(); svector& vars = st.vars(); vector& coeffs = st.coeffs(); - rational& offset = st.offset(); rational r; expr* n1, * n2; unsigned index = 0; @@ -204,7 +203,9 @@ namespace arith { ++index; } else if (a.is_numeral(n, r)) { - offset += coeffs[index] * r; + theory_var v = internalize_numeral(to_app(n), r); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); ++index; } else if (a.is_uminus(n, n1)) { @@ -457,6 +458,19 @@ namespace arith { return v; } + theory_var solver::internalize_numeral(app* n, rational const& val) { + theory_var v = mk_evar(n); + lpvar vi = get_lpvar(v); + if (vi == UINT_MAX) { + vi = lp().add_var(v, a.is_int(n)); + add_def_constraint_and_equality(vi, lp::GE, val); + add_def_constraint_and_equality(vi, lp::LE, val); + register_fixed_var(v, val); + } + return v; + } + + theory_var solver::internalize_mul(app* t) { SASSERT(a.is_mul(t)); internalize_args(t, true); @@ -484,57 +498,32 @@ namespace arith { theory_var v = mk_evar(term); TRACE("arith", tout << mk_bounded_pp(term, m) << " v" << v << "\n";); - if (is_unit_var(st) && v == st.vars()[0]) { + if (is_unit_var(st) && v == st.vars()[0]) return st.vars()[0]; - } - else if (is_one(st) && a.is_numeral(term)) { - return lp().local_to_external(get_one(a.is_int(term))); - } - else if (is_zero(st) && a.is_numeral(term)) { - return lp().local_to_external(get_zero(a.is_int(term))); - } - else { - init_left_side(st); - lpvar vi = get_lpvar(v); - if (vi == UINT_MAX) { - if (m_left_side.empty()) { - vi = lp().add_var(v, a.is_int(term)); - add_def_constraint_and_equality(vi, lp::GE, st.offset()); - add_def_constraint_and_equality(vi, lp::LE, st.offset()); - register_fixed_var(v, st.offset()); - return v; - } - if (!st.offset().is_zero()) { - m_left_side.push_back(std::make_pair(st.offset(), get_one(a.is_int(term)))); - } - if (m_left_side.empty()) { - vi = lp().add_var(v, a.is_int(term)); - add_def_constraint_and_equality(vi, lp::GE, rational(0)); - add_def_constraint_and_equality(vi, lp::LE, rational(0)); - } - else { - vi = lp().add_term(m_left_side, v); - SASSERT(lp::tv::is_term(vi)); - TRACE("arith_verbose", - tout << "v" << v << " := " << mk_pp(term, m) - << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); - } + + init_left_side(st); + lpvar vi = get_lpvar(v); + + if (vi == UINT_MAX) { + if (m_left_side.empty()) { + vi = lp().add_var(v, a.is_int(term)); + add_def_constraint_and_equality(vi, lp::GE, rational(0)); + add_def_constraint_and_equality(vi, lp::LE, rational(0)); + } + else { + vi = lp().add_term(m_left_side, v); + SASSERT(lp::tv::is_term(vi)); + TRACE("arith_verbose", + tout << "v" << v << " := " << mk_pp(term, m) + << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; + lp().print_term(lp().get_term(lp::tv::raw(vi)), tout) << "\n";); } - return v; } + return v; } bool solver::is_unit_var(scoped_internalize_state& st) { - return st.offset().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); - } - - bool solver::is_one(scoped_internalize_state& st) { - return st.offset().is_one() && st.vars().empty(); - } - - bool solver::is_zero(scoped_internalize_state& st) { - return st.offset().is_zero() && st.vars().empty(); + return st.vars().size() == 1 && st.coeffs()[0].is_one(); } void solver::init_left_side(scoped_internalize_state& st) { diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 68d5f8025..522867a0d 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -145,13 +145,11 @@ namespace arith { expr_ref_vector m_terms; vector m_coeffs; svector m_vars; - rational m_offset; ptr_vector m_to_ensure_enode, m_to_ensure_var; internalize_state(ast_manager& m) : m_terms(m) {} void reset() { m_terms.reset(); m_coeffs.reset(); - m_offset.reset(); m_vars.reset(); m_to_ensure_enode.reset(); m_to_ensure_var.reset(); @@ -178,7 +176,6 @@ namespace arith { expr_ref_vector& terms() { return m_st.m_terms; } vector& coeffs() { return m_st.m_coeffs; } svector& vars() { return m_st.m_vars; } - rational& offset() { return m_st.m_offset; } ptr_vector& to_ensure_enode() { return m_st.m_to_ensure_enode; } ptr_vector& to_ensure_var() { return m_st.m_to_ensure_var; } void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } @@ -290,6 +287,7 @@ namespace arith { void ensure_arg_vars(app* t); theory_var internalize_power(app* t, app* n, unsigned p); theory_var internalize_mul(app* t); + theory_var internalize_numeral(app* t, rational const& v); theory_var internalize_def(expr* term); theory_var internalize_def(expr* term, scoped_internalize_state& st); theory_var internalize_linearized_def(expr* term, scoped_internalize_state& st); From 7c380fd6a0e667cbeafb6ea88eec6cf1a09c5925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Fri, 23 Jun 2023 11:45:29 -0700 Subject: [PATCH 10/41] bool_rewriter: fix possible segfault when disabling rewriter.sort_disjunctions (#6779) After introducing the rewriter.sort_disjunctions option (#6774), I noticed a segfault in a Z3 run that was working fine for me before the PR. I traced the difference to a slight discrepancy between the first patch I submitted and the one we ended up merging: my first version would skip sorting the disjuncts in mk_nflat_core, but still return BR_DONE, while the patch in master returns BR_FAILED instead. This patch fixes that problem, and it makes slightly more sense to me to return a BR_DONE since, if `s` is true, some disjunct (e.g. a `false` or a repeat) might have been simplified away. However I don't fully understand this code. ... and I can't say I understand why the segfault happens. Perhaps that is a separate issue? This is the file to reproduce: https://gist.github.com/mtzguido/b7360c74d3d2e42d89f1bd9149ad26f6 Here's a stack trace of the failure, mk_nflat_or_core is not involved. ``` (gdb) where #0 0x0000555555b98497 in smt::context::get_lit_assignment(unsigned int) const () #1 0x0000555555b984cb in smt::context::get_assignment(sat::literal) const () #2 0x0000555555b98504 in smt::context::get_assignment(unsigned int) const () #3 0x0000555555ca83b8 in smt::context::get_assignment_core(expr*) const () #4 0x0000555555c9af5a in smt::context::get_assignment(expr*) const () #5 0x0000555555d7bd1d in (anonymous namespace)::has_child_assigned_to(smt::context&, app*, lbool, expr*&, unsigned int) () #6 0x0000555555d7c413 in (anonymous namespace)::rel_case_split_queue::next_case_split_core(ptr_vector&, unsigned int&, unsigned int&, lbool&) () #7 0x0000555555d7c589 in (anonymous namespace)::rel_case_split_queue::next_case_split(unsigned int&, lbool&) () #8 0x0000555555c9c1b7 in smt::context::decide() () #9 0x0000555555ca39fd in smt::context::bounded_search() () #10 0x0000555555ca30c2 in smt::context::search() () #11 0x0000555555ca273d in smt::context::check(unsigned int, expr* const*, bool) () #12 0x0000555555cb166a in smt::kernel::check(unsigned int, expr* const*) () #13 0x0000555555cb9695 in (anonymous namespace)::smt_solver::check_sat_core2(unsigned int, expr* const*) () #14 0x00005555560dc0c6 in solver_na2as::check_sat_core(unsigned int, expr* const*) () #15 0x00005555560d73f3 in combined_solver::check_sat_core(unsigned int, expr* const*) () #16 0x00005555560d34e3 in solver::check_sat(unsigned int, expr* const*) () #17 0x0000555556097b26 in cmd_context::check_sat(unsigned int, expr* const*) () #18 0x0000555556082ff0 in smt2::parser::parse_check_sat() () #19 0x0000555556084dc0 in smt2::parser::parse_cmd() () #20 0x00005555560861b6 in smt2::parser::operator()() () #21 0x00005555560757e6 in parse_smt2_commands(cmd_context&, std::basic_istream >&, bool, params_ref const&, char const*) () #22 0x00005555555e8f68 in read_smtlib2_commands(char const*) () #23 0x00005555555ee6f6 in main () (gdb) ``` --- src/ast/rewriter/bool_rewriter.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 16ecf6473..9806424a3 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -184,7 +184,7 @@ br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args } br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) { - bool s = false; + bool s = false; // whether we have canceled some disjuncts or found some out or order ptr_buffer buffer; expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; @@ -292,9 +292,11 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args if (st != BR_FAILED) return st; #endif - if (m_sort_disjunctions && s) { - ast_lt lt; - std::sort(buffer.begin(), buffer.end(), lt); + if (s) { + if (m_sort_disjunctions) { + ast_lt lt; + std::sort(buffer.begin(), buffer.end(), lt); + } result = m().mk_or(sz, buffer.data()); return BR_DONE; } From 7c4d098f654fb6fc021bb19917bd81facafbff7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:20:56 +0100 Subject: [PATCH 11/41] Bump docker/build-push-action from 4.0.0 to 4.1.1 (#6772) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.0.0 to 4.1.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4.0.0...v4.1.1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index d9baccd39..301ee2c87 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v4.0.0 + uses: docker/build-push-action@v4.1.1 with: context: . push: true From b451735aa0b2cd6c5d109b1fab77c44cd490e561 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 Jun 2023 21:08:06 -0700 Subject: [PATCH 12/41] fix #6778 Signed-off-by: Nikolaj Bjorner --- src/api/z3_replayer.cpp | 2 +- src/smt/smt_solver.cpp | 1 + src/solver/tactic2solver.cpp | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index ad5bc9523..aebc2f9a5 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -72,7 +72,7 @@ struct z3_replayer::imp { void check_arg(unsigned pos, value_kind k) const { if (pos >= m_args.size()) { - TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";); + TRACE("z3_replayer", tout << pos << " too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";); throw z3_replayer_exception("invalid argument reference"); } if (m_args[pos].m_kind != k) { diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 4be78b20a..d415812b0 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -267,6 +267,7 @@ namespace { }; void get_unsat_core(expr_ref_vector & r) override { + unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index cc3ac9336..8ecfb3680 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -25,6 +25,9 @@ Notes: #include "solver/tactic2solver.h" #include "solver/solver_na2as.h" #include "solver/mus.h" +#include "smt/params/smt_params.h" +#include "smt/params/smt_params_helper.hpp" + /** \brief Simulates the incremental solver interface using a tactic. @@ -48,6 +51,7 @@ class tactic2solver : public solver_na2as { bool m_produce_proofs; bool m_produce_unsat_cores; statistics m_stats; + bool m_minimizing = false; public: tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); @@ -173,6 +177,7 @@ tactic2solver::~tactic2solver() { void tactic2solver::updt_params(params_ref const & p) { solver::updt_params(p); + m_produce_unsat_cores |= p.get_bool("unsat_core", false); } void tactic2solver::collect_param_descrs(param_descrs & r) { @@ -309,6 +314,16 @@ void tactic2solver::collect_statistics(statistics & st) const { void tactic2solver::get_unsat_core(expr_ref_vector & r) { if (m_result.get()) { m_result->get_unsat_core(r); + if (!m_minimizing && smt_params_helper(get_params()).core_minimize()) { + flet minimizing(m_minimizing, true); + mus mus(*this); + mus.add_soft(r.size(), r.data()); + expr_ref_vector r2(m); + if (l_true == mus.get_mus(r2)) { + r.reset(); + r.append(r2); + } + } } } From 7221c8415667c1a2fbc24a2f8ea0b9de23ff6110 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 Jun 2023 21:21:06 -0700 Subject: [PATCH 13/41] fix #6783 Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4380b940b..4b38dcd52 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -166,7 +166,9 @@ bool core::check_monic(const monic& m) const { if (!is_relevant(m.var())) return true; #endif - SASSERT((!m_lar_solver.column_is_int(m.var())) || m_lar_solver.get_column_value(m.var()).is_int()); + if (m_lar_solver.column_is_int(m.var()) && m_lar_solver.get_column_value(m.var()).is_int()) + return true; + bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x; CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';); return ret; From d42693d5b565e3fd89f22df6fc5b3c8fc1436138 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Mon, 26 Jun 2023 19:59:57 +0200 Subject: [PATCH 14/41] Equalities in C# UP-Propagation (#6786) * Query Boolean Assignment in the UP * UP's decide ref arguments => next_split * Fixed wrapper * More fixes * Equalities in C# UP-Propagation --------- Co-authored-by: Nikolaj Bjorner --- src/api/dotnet/UserPropagator.cs | 80 +++++++++++++++++++++++++++++++- src/api/java/NativeStatic.txt | 1 + 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/api/dotnet/UserPropagator.cs b/src/api/dotnet/UserPropagator.cs index af469ddff..68f2b0127 100644 --- a/src/api/dotnet/UserPropagator.cs +++ b/src/api/dotnet/UserPropagator.cs @@ -254,9 +254,19 @@ namespace Microsoft.Z3 /// Propagate consequence /// public void Propagate(IEnumerable terms, Expr conseq) + { + Propagate(terms, new EqualityPairs(), conseq); + } + + /// + /// Propagate consequence + /// + public void Propagate(IEnumerable terms, EqualityPairs equalities, Expr conseq) { var nTerms = Z3Object.ArrayToNative(terms.ToArray()); - Native.Z3_solver_propagate_consequence(ctx.nCtx, this.callback, (uint)nTerms.Length, nTerms, 0u, null, null, conseq.NativeObject); + var nLHS = Z3Object.ArrayToNative(equalities.LHS.ToArray()); + var nRHS = Z3Object.ArrayToNative(equalities.RHS.ToArray()); + Native.Z3_solver_propagate_consequence(ctx.nCtx, this.callback, (uint)nTerms.Length, nTerms, (uint)equalities.Count, nLHS, nRHS, conseq.NativeObject); } @@ -375,4 +385,72 @@ namespace Microsoft.Z3 } } } + + /// + /// A list of equalities used as justifications for propagation + /// + public class EqualityPairs { + + readonly List lhsList = new List(); + readonly List rhsList = new List(); + + /// + /// The left hand sides of the equalities + /// + public Expr[] LHS => lhsList.ToArray(); + + /// + /// The right hand sides of the equalities + /// + public Expr[] RHS => rhsList.ToArray(); + + /// + /// The number of equalities + /// + public int Count => lhsList.Count; + + /// + /// Adds an equality to the list. The sorts of the arguments have to be the same. + /// The left hand side of the equality + /// The right hand side of the equality + /// + public void Add(Expr lhs, Expr rhs) { + lhsList.Add(lhs); + rhsList.Add(rhs); + } + + /// + /// Checks if two equality lists are equal. + /// The function does not take symmetries, shuffling, or duplicates into account. + /// + public override bool Equals(object obj) { + if (ReferenceEquals(this, obj)) + return true; + if (!(obj is EqualityPairs other)) + return false; + if (lhsList.Count != other.lhsList.Count) + return false; + for (int i = 0; i < lhsList.Count; i++) { + if (!lhsList[i].Equals(other.lhsList[i])) + return false; + } + return true; + } + + /// + /// Gets a hash code for the list of equalities + /// + public override int GetHashCode() { + int hash = lhsList.Count; + unchecked { + for (int i = 0; i < lhsList.Count; i++) { + hash ^= lhsList[i].GetHashCode(); + hash *= 17; + hash ^= rhsList[i].GetHashCode(); + hash *= 29; + } + return hash; + } + } + } } diff --git a/src/api/java/NativeStatic.txt b/src/api/java/NativeStatic.txt index f076a817c..9507130fd 100644 --- a/src/api/java/NativeStatic.txt +++ b/src/api/java/NativeStatic.txt @@ -226,6 +226,7 @@ DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateAdd(JNIEnv } } + DLL_VIS JNIEXPORT bool JNICALL Java_com_microsoft_z3_Native_propagateNextSplit(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e, long idx, int phase) { JavaInfo *info = (JavaInfo*)javainfo; Z3_solver_callback cb = info->cb; From b2c035ea3f2b6618af7966ec62ac8e48abe3408b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 26 Jun 2023 18:46:03 -0700 Subject: [PATCH 15/41] missing negation Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4b38dcd52..3c1f9da57 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -166,7 +166,7 @@ bool core::check_monic(const monic& m) const { if (!is_relevant(m.var())) return true; #endif - if (m_lar_solver.column_is_int(m.var()) && m_lar_solver.get_column_value(m.var()).is_int()) + if (m_lar_solver.column_is_int(m.var()) && !m_lar_solver.get_column_value(m.var()).is_int()) return true; bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x; From 30a2ced9aa7f97e7c896072c11a64549b4dde899 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:53:27 -0700 Subject: [PATCH 16/41] patching merge (#6780) * patching merge * fix the format and some warnings Signed-off-by: Lev Nachmanson * a fix in the delta calculation * test patching * try a new version of get_patching_deltas Signed-off-by: Lev Nachmanson * remove dead code from lp_tst and try optimizing patching * add comments, replace VERIFY with lp_assert Signed-off-by: Lev Nachmanson * cleanup --------- Signed-off-by: Lev Nachmanson --- src/math/lp/.clang-format | 4 + src/math/lp/explanation.h | 9 + src/math/lp/int_solver.cpp | 406 ++++-- src/math/lp/int_solver.h | 19 +- src/math/lp/lar_core_solver.h | 2 + src/math/lp/lar_solver.cpp | 78 +- src/math/lp/lar_solver.h | 435 +++--- src/math/lp/lp_bound_propagator.h | 405 +++-- src/math/lp/lp_primal_core_solver.h | 1119 +++++++------- .../lp/lp_primal_core_solver_tableau_def.h | 7 +- src/math/lp/lp_types.h | 5 +- src/test/lp/.clang-format | 3 + src/test/lp/lp.cpp | 1297 ++++++++--------- 13 files changed, 1989 insertions(+), 1800 deletions(-) create mode 100644 src/math/lp/.clang-format create mode 100644 src/test/lp/.clang-format diff --git a/src/math/lp/.clang-format b/src/math/lp/.clang-format new file mode 100644 index 000000000..f5c8a41b2 --- /dev/null +++ b/src/math/lp/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 0 +NamespaceIndentation: All \ No newline at end of file diff --git a/src/math/lp/explanation.h b/src/math/lp/explanation.h index d2e7edc33..b7f721a30 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -48,6 +48,15 @@ public: SASSERT(m_vector.empty()); m_set.insert(j); } + + void remove(constraint_index j) { + m_set.remove(j); + unsigned i = 0; + for (auto& p : m_vector) + if (p.first != j) + m_vector[i++] = p; + m_vector.shrink(i); + } void add_expl(const explanation& e) { if (e.m_vector.empty()) { diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 6c34ce16f..af4488162 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -2,8 +2,7 @@ Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson */ - -#include +// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" #include "math/lp/lp_utils.h" @@ -14,56 +13,216 @@ namespace lp { -int_solver::patcher::patcher(int_solver& lia): - lia(lia), - lra(lia.lra), - lrac(lia.lrac), - m_num_nbasic_patches(0), - m_patch_cost(0), - m_next_patch(0), - m_delay(0) -{} + int_solver::patcher::patcher(int_solver& lia): + lia(lia), + lra(lia.lra), + lrac(lia.lrac) + {} + + void int_solver::patcher::remove_fixed_vars_from_base() { + unsigned num = lra.A_r().column_count(); + for (unsigned v = 0; v < num; v++) { + if (!lia.is_base(v) || !lia.is_fixed(v)) + continue; + auto const & r = lra.basic2row(v); + for (auto const& c : r) { + if (c.var() != v && !lia.is_fixed(c.var())) { + lra.pivot(c.var(), v); + break; + } + } + } + } -bool int_solver::patcher::should_apply() { -#if 1 - return true; -#else - if (m_delay == 0) { + + unsigned int_solver::patcher::count_non_int() { + unsigned non_int = 0; + for (auto j : lra.r_basis()) + if (lra.column_is_int(j) && !lra.column_value_is_int(j)) + ++non_int; + return non_int; + } + + lia_move int_solver::patcher::patch_basic_columns() { + remove_fixed_vars_from_base(); + lia.settings().stats().m_patches++; + lp_assert(lia.is_feasible()); + + // unsigned non_int_before, non_int_after; + + // non_int_before = count_non_int(); + + + // unsigned num = lra.A_r().column_count(); + for (unsigned j : lra.r_basis()) + if (!lra.get_value(j).is_int()) + patch_basic_column(j); + // non_int_after = count_non_int(); + // verbose_stream() << non_int_before << " -> " << non_int_after << "\n"; + + if (!lia.has_inf_int()) { + lia.settings().stats().m_patches_success++; + return lia_move::sat; + } + return lia_move::undef; + } + + // clang-format on + /** + * \brief find integral and minimal, in the absolute values, deltas such that x - alpha*delta is integral too. + */ + bool get_patching_deltas(const rational& x, const rational& alpha, + rational& delta_0, rational& delta_1) { + auto a1 = numerator(alpha); + auto a2 = denominator(alpha); + auto x1 = numerator(x); + auto x2 = denominator(x); + if (!divides(x2, a2)) + return false; + + // delta has to be integral. + // We need to find delta such that x1/x2 + (a1/a2)*delta is integral (we are going to flip the delta sign later). + // Then a2*x1/x2 + a1*delta is integral, but x2 and x1 are coprime: + // that means that t = a2/x2 is + // integral. We established that a2 = x2*t Then x1 + a1*delta*(x2/a2) = x1 + // + a1*(delta/t) is integral. Taking into account that t and a1 are + // coprime we have delta = t*k, where k is an integer. + rational t = a2 / x2; + // std::cout << "t = " << t << std::endl; + // Now we have x1/x2 + (a1/x2)*k is integral, or (x1 + a1*k)/x2 is integral. + // It is equivalent to x1 + a1*k = x2*m, where m is an integer + // We know that a2 and a1 are coprime, and x2 divides a2, so x2 and a1 are + // coprime. We can find u and v such that u*a1 + v*x2 = 1. + rational u, v; + gcd(a1, x2, u, v); + lp_assert(gcd(a1, x2, u, v).is_one()); + // std::cout << "u = " << u << ", v = " << v << std::endl; + // std::cout << "x= " << (x1 / x2) << std::endl; + // std::cout << "x + (a1 / a2) * (-u * t) * x1 = " + // << x + (a1 / a2) * (-u * t) * x1 << std::endl; + lp_assert((x + (a1 / a2) * (-u * t) * x1).is_int()); + // 1 = (u- l*x2 ) * a1 + (v + l*a1)*x2, for every integer l. + rational l_low, l_high; + auto sign = u.is_pos() ? 1 : -1; + auto p = sign * floor(abs(u / x2)); + auto p_ = p + sign; + lp_assert(p * x2 <= u && u <= p_ * x2 || p * x2 >= u && u >= p_ * x2); + // std::cout << "u = " << u << ", v = " << v << std::endl; + // std::cout << "p = " << p << ", p_ = " << p_ << std::endl; + // std::cout << "u - p*x2 = " << u - p * x2 << ", u - p_*x2 = " << u - p_ * x2 + // << std::endl; + mpq d_0 = (u - p * x2) * t * x1; + mpq d_1 = (u - p_ * x2) * t * x1; + if (abs(d_0) < abs(d_1)) { + delta_0 = d_0; + delta_1 = d_1; + } else { + delta_0 = d_1; + delta_1 = d_0; + } + return true; + // std::cout << "delta_0 = " << delta_0 << std::endl; + // std::cout << "delta_1 = " << delta_1 << std::endl; + } + // clang-format off + /** + * \brief try to patch the basic column v + */ + bool int_solver::patcher::patch_basic_column_on_row_cell(unsigned v, row_cell const& c) { + if (v == c.var()) + return false; + if (!lra.column_is_int(c.var())) // could use real to patch integer + return false; + if (c.coeff().is_int()) + return false; + mpq a = fractional_part(c.coeff()); + mpq r = fractional_part(lra.get_value(v)); + lp_assert(0 < r && r < 1); + lp_assert(0 < a && a < 1); + mpq delta_0, delta_1; + if (!get_patching_deltas(r, a, delta_0, delta_1)) + return false; + + if (try_patch_column(v, c.var(), delta_0)) + return true; + + if (try_patch_column(v, c.var(), delta_1)) + return true; + + return false; + } + + bool int_solver::patcher::try_patch_column(unsigned v, unsigned j, mpq const& delta) { + const auto & A = lra.A_r(); + if (delta < 0) { + if (lia.has_lower(j) && lia.get_value(j) + impq(delta) < lra.get_lower_bound(j)) + return false; + } + else { + if (lia.has_upper(j) && lia.get_value(j) + impq(delta) > lra.get_upper_bound(j)) + return false; + } + for (auto const& c : A.column(j)) { + unsigned row_index = c.var(); + unsigned i = lrac.m_r_basis[row_index]; + auto old_val = lia.get_value(i); + auto new_val = old_val - impq(c.coeff()*delta); + if (lia.has_lower(i) && new_val < lra.get_lower_bound(i)) + return false; + if (lia.has_upper(i) && new_val > lra.get_upper_bound(i)) + return false; + if (old_val.is_int() && !new_val.is_int()){ + return false; // do not waste resources on this case + } + lp_assert(i != v || new_val.is_int()) + } + + lra.set_value_for_nbasic_column(j, lia.get_value(j) + impq(delta)); + return true; } - --m_delay; - return false; -#endif -} + + void int_solver::patcher::patch_basic_column(unsigned v) { + SASSERT(!lia.is_fixed(v)); + for (auto const& c : lra.basic2row(v)) + if (patch_basic_column_on_row_cell(v, c)) + return; + } -lia_move int_solver::patcher::operator()() { - return patch_nbasic_columns(); -} + lia_move int_solver::patcher::patch_nbasic_columns() { + remove_fixed_vars_from_base(); + lia.settings().stats().m_patches++; + lp_assert(lia.is_feasible()); + m_patch_success = 0; + m_patch_fail = 0; + m_num_ones = 0; + m_num_divides = 0; + //unsigned non_int_before = count_non_int(); -lia_move int_solver::patcher::patch_nbasic_columns() { - lia.settings().stats().m_patches++; - lp_assert(lia.is_feasible()); - m_num_nbasic_patches = 0; - m_patch_cost = 0; - for (unsigned j : lia.lrac.m_r_nbasis) { - patch_nbasic_column(j); + unsigned num = lra.A_r().column_count(); + for (unsigned v = 0; v < num; v++) { + if (lia.is_base(v)) + continue; + patch_nbasic_column(v); + } + unsigned num_fixed = 0; + for (unsigned v = 0; v < num; v++) + if (lia.is_fixed(v)) + ++num_fixed; + + lp_assert(lia.is_feasible()); + //verbose_stream() << "patch " << m_patch_success << " fails " << m_patch_fail << " ones " << m_num_ones << " divides " << m_num_divides << " num fixed " << num_fixed << "\n"; + //lra.display(verbose_stream()); + //exit(0); + //unsigned non_int_after = count_non_int(); + + // verbose_stream() << non_int_before << " -> " << non_int_after << "\n"; + if (!lia.has_inf_int()) { + lia.settings().stats().m_patches_success++; + return lia_move::sat; + } + return lia_move::undef; } - lp_assert(lia.is_feasible()); - if (!lia.has_inf_int()) { - lia.settings().stats().m_patches_success++; - m_delay = 0; - m_next_patch = 0; - return lia_move::sat; - } - if (m_patch_cost > 0 && m_num_nbasic_patches * 10 < m_patch_cost) { - m_delay = std::min(20u, m_next_patch++); - } - else { - m_delay = 0; - m_next_patch = 0; - } - return lia_move::undef; -} void int_solver::patcher::patch_nbasic_column(unsigned j) { impq & val = lrac.m_r_x[j]; @@ -71,17 +230,48 @@ void int_solver::patcher::patch_nbasic_column(unsigned j) { impq l, u; mpq m; bool has_free = lia.get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m); - m_patch_cost += lra.A_r().number_of_non_zeroes_in_column(j); - if (!has_free) { + if (!has_free) return; - } bool m_is_one = m.is_one(); bool val_is_int = lia.value_is_int(j); +#if 0 +const auto & A = lra.A_r(); +#endif // check whether value of j is already a multiple of m. if (val_is_int && (m_is_one || (val.x / m).is_int())) { + if (m_is_one) + ++m_num_ones; + else + ++m_num_divides; +#if 0 + for (auto c : A.column(j)) { + unsigned row_index = c.var(); + unsigned i = lrac.m_r_basis[row_index]; + if (!lia.get_value(i).is_int() || + (lia.has_lower(i) && lia.get_value(i) < lra.get_lower_bound(i)) || + (lia.has_upper(i) && lia.get_value(i) > lra.get_upper_bound(i))) { + verbose_stream() << "skip " << j << " " << m << ": "; + lia.display_row(verbose_stream(), A.m_rows[row_index]); + verbose_stream() << "\n"; + } + } +#endif return; } + +#if 0 + if (!m_is_one) { + // lia.display_column(verbose_stream(), j); + for (auto c : A.column(j)) { + continue; + unsigned row_index = c.var(); + lia.display_row(verbose_stream(), A.m_rows[row_index]); + verbose_stream() << "\n"; + } + } +#endif + TRACE("patch_int", tout << "TARGET j" << j << " -> ["; if (inf_l) tout << "-oo"; else tout << l; @@ -89,9 +279,33 @@ void int_solver::patcher::patch_nbasic_column(unsigned j) { if (inf_u) tout << "oo"; else tout << u; tout << "]"; tout << ", m: " << m << ", val: " << val << ", is_int: " << lra.column_is_int(j) << "\n";); + +#if 0 + verbose_stream() << "path " << m << " "; + if (!inf_l) verbose_stream() << "infl " << l.x << " "; + if (!inf_u) verbose_stream() << "infu " << u.x << " "; + verbose_stream() << "\n"; if (m.is_big() || (!inf_l && l.x.is_big()) || (!inf_u && u.x.is_big())) { return; } +#endif + +#if 0 + verbose_stream() << "TARGET v" << j << " -> ["; + if (inf_l) verbose_stream() << "-oo"; else verbose_stream() << ceil(l.x) << " " << l << "\n"; + verbose_stream() << ", "; + if (inf_u) verbose_stream() << "oo"; else verbose_stream() << floor(u.x) << " " << u << "\n"; + verbose_stream() << "]"; + verbose_stream() << ", m: " << m << ", val: " << val << ", is_int: " << lra.column_is_int(j) << "\n"; +#endif + +#if 0 + if (!inf_l) + l = impq(ceil(l)); + if (!inf_u) + u = impq(floor(u)); +#endif + if (!inf_l) { l = impq(m_is_one ? ceil(l) : m * ceil(l / m)); if (inf_u || l <= u) { @@ -99,8 +313,23 @@ void int_solver::patcher::patch_nbasic_column(unsigned j) { lra.set_value_for_nbasic_column(j, l); } else { - --m_num_nbasic_patches; + //verbose_stream() << "fail: " << j << " " << m << "\n"; + ++m_patch_fail; TRACE("patch_int", tout << "not patching " << l << "\n";); +#if 0 + verbose_stream() << "not patched\n"; + for (auto c : A.column(j)) { + unsigned row_index = c.var(); + unsigned i = lrac.m_r_basis[row_index]; + if (!lia.get_value(i).is_int() || + (lia.has_lower(i) && lia.get_value(i) < lra.get_lower_bound(i)) || + (lia.has_upper(i) && lia.get_value(i) > lra.get_upper_bound(i))) { + lia.display_row(verbose_stream(), A.m_rows[row_index]); + verbose_stream() << "\n"; + } + } +#endif + return; } } else if (!inf_u) { @@ -112,7 +341,21 @@ void int_solver::patcher::patch_nbasic_column(unsigned j) { lra.set_value_for_nbasic_column(j, impq(0)); TRACE("patch_int", tout << "patching with 0\n";); } - ++m_num_nbasic_patches; + ++m_patch_success; +#if 0 + verbose_stream() << "patched " << j << "\n"; + for (auto c : A.column(j)) { + unsigned row_index = c.var(); + unsigned i = lrac.m_r_basis[row_index]; + if (!lia.get_value(i).is_int() || + (lia.has_lower(i) && lia.get_value(i) < lra.get_lower_bound(i)) || + (lia.has_upper(i) && lia.get_value(i) > lra.get_upper_bound(i))) { + lia.display_row(verbose_stream(), A.m_rows[row_index]); + verbose_stream() << "\n"; + } + } +#endif + } int_solver::int_solver(lar_solver& lar_slv) : @@ -326,7 +569,7 @@ static void set_upper(impq & u, bool & inf_u, impq const & v) { // this function assumes that all basic columns dependend on j are feasible bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m) { - if (lrac.m_r_heading[j] >= 0) // the basic var + if (lrac.m_r_heading[j] >= 0 || is_fixed(j)) // basic or fixed var return false; TRACE("random_update", display_column(tout, j) << ", is_int = " << column_is_int(j) << "\n";); @@ -361,10 +604,9 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq unsigned i = lrac.m_r_basis[row_index]; impq const & xi = get_value(i); lp_assert(lrac.m_r_solver.column_is_feasible(i)); - if (column_is_int(i) && !a.is_int()) + if (column_is_int(i) && !a.is_int() && xi.is_int()) m = lcm(m, denominator(a)); - - + if (!inf_l && !inf_u) { if (l == u) continue; @@ -372,15 +614,15 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq if (a.is_neg()) { if (has_lower(i)) - set_lower(l, inf_l, delta(a, xi, lrac.m_r_lower_bounds()[i])); + set_lower(l, inf_l, delta(a, xi, lra.get_lower_bound(i))); if (has_upper(i)) - set_upper(u, inf_u, delta(a, xi, lrac.m_r_upper_bounds()[i])); + set_upper(u, inf_u, delta(a, xi, lra.get_upper_bound(i))); } else { if (has_upper(i)) - set_lower(l, inf_l, delta(a, xi, lrac.m_r_upper_bounds()[i])); + set_lower(l, inf_l, delta(a, xi, lra.get_upper_bound(i))); if (has_lower(i)) - set_upper(u, inf_u, delta(a, xi, lrac.m_r_lower_bounds()[i])); + set_upper(u, inf_u, delta(a, xi, lra.get_lower_bound(i))); } } @@ -479,14 +721,11 @@ bool int_solver::at_upper(unsigned j) const { } std::ostream & int_solver::display_row(std::ostream & out, lp::row_strip const & row) const { -bool first = true; + bool first = true; auto & rslv = lrac.m_r_solver; -for (const auto &c : row) - { - if (is_fixed(c.var())) - { - if (!get_value(c.var()).is_zero()) - { + for (const auto &c : row) { + if (is_fixed(c.var())) { + if (!get_value(c.var()).is_zero()) { impq val = get_value(c.var()) * c.coeff(); if (!first && val.is_pos()) out << "+"; @@ -505,17 +744,11 @@ for (const auto &c : row) } else if (c.coeff().is_minus_one()) out << "-"; - else - { - if (c.coeff().is_pos()) - { - if (!first) - out << "+"; - } + else { + if (c.coeff().is_pos() && !first) + out << "+"; if (c.coeff().is_big()) - { out << " b*"; - } else out << c.coeff(); } @@ -523,8 +756,7 @@ for (const auto &c : row) first = false; } out << "\n"; - for (const auto &c : row) - { + for (const auto &c : row) { if (is_fixed(c.var())) continue; rslv.print_column_info(c.var(), out); @@ -533,14 +765,13 @@ for (const auto &c : row) } return out; } + std::ostream& int_solver::display_row_info(std::ostream & out, unsigned row_index) const { auto & rslv = lrac.m_r_solver; auto const& row = rslv.m_A.m_rows[row_index]; return display_row(out, row); } - - bool int_solver::shift_var(unsigned j, unsigned range) { if (is_fixed(j) || is_base(j)) return false; @@ -549,11 +780,13 @@ bool int_solver::shift_var(unsigned j, unsigned range) { bool inf_l = false, inf_u = false; impq l, u; mpq m; - VERIFY(get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m) || settings().get_cancel_flag()); + if (!get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m)) + return false; if (settings().get_cancel_flag()) return false; const impq & x = get_value(j); // x, the value of j column, might be shifted on a multiple of m + if (inf_l && inf_u) { impq new_val = m * impq(random() % (range + 1)) + x; lra.set_value_for_nbasic_column(j, new_val); @@ -570,6 +803,7 @@ bool int_solver::shift_var(unsigned j, unsigned range) { if (!inf_l && !inf_u && l >= u) return false; + if (inf_u) { SASSERT(!inf_l); impq new_val = x + m * impq(random() % (range + 1)); @@ -640,9 +874,6 @@ int int_solver::select_int_infeasible_var() { 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; @@ -650,11 +881,10 @@ int int_solver::select_int_infeasible_var() { // 1. small box // 2. small value // 3. any value - for (; k < lra.r_basis().size(); k++) { - j = lra.r_basis()[k]; + for (unsigned j : lra.r_basis()) { if (!column_is_int_inf(j)) continue; - usage = lra.usage_in_terms(j); + unsigned 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) { @@ -688,12 +918,12 @@ int int_solver::select_int_infeasible_var() { continue; SASSERT(st == not_found || st == any_value); st = any_value; - if (n == 0 /*|| usage > prev_usage*/) { + if (n == 0 || usage > prev_usage) { result = j; prev_usage = usage; n = 1; } - else if (usage > 0 && /*usage == prev_usage && */ (random() % (++n) == 0)) + else if (usage > 0 && usage == prev_usage && (random() % (++n) == 0)) result = j; } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 822e1cf1e..fad040965 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lp_settings.h" #include "math/lp/static_matrix.h" @@ -44,17 +45,23 @@ class int_solver { int_solver& lia; lar_solver& lra; lar_core_solver& lrac; - unsigned m_num_nbasic_patches; - unsigned m_patch_cost; - unsigned m_next_patch; - unsigned m_delay; + unsigned m_patch_success = 0; + unsigned m_patch_fail = 0; + unsigned m_num_ones = 0; + unsigned m_num_divides = 0; public: patcher(int_solver& lia); - bool should_apply(); - lia_move operator()(); + bool should_apply() const { return true; } + lia_move operator()() { return patch_basic_columns(); } void patch_nbasic_column(unsigned j); + bool patch_basic_column_on_row_cell(unsigned v, row_cell const& c); + void patch_basic_column(unsigned j); + bool try_patch_column(unsigned v, unsigned j, mpq const& delta); + unsigned count_non_int(); private: + void remove_fixed_vars_from_base(); lia_move patch_nbasic_columns(); + lia_move patch_basic_columns(); }; lar_solver& lra; diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 06ef4d50b..10cd53141 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -93,6 +93,8 @@ public: void solve(); + void pivot(int entering, int leaving) { m_r_solver.pivot(entering, leaving); } + bool lower_bounds_are_set() const { return true; } const indexed_vector & get_pivot_row() const { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 5f544dff1..9fa9bc540 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2,7 +2,7 @@ Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner, Lev Nachmanson */ - +// clang-format off #include "math/lp/lar_solver.h" #include "smt/params/smt_params_helper.hpp" @@ -41,7 +41,6 @@ namespace lp { for (auto t : m_terms) delete t; } - bool lar_solver::sizes_are_correct() const { lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); @@ -50,7 +49,6 @@ namespace lp { return true; } - std::ostream& lar_solver::print_implied_bound(const implied_bound& be, std::ostream& out) const { out << "implied bound\n"; unsigned v = be.m_j; @@ -1354,8 +1352,8 @@ namespace lp { } bool lar_solver::term_is_int(const vector>& coeffs) const { - for (auto const& p : coeffs) - if (!(column_is_int(p.second) && p.first.is_int())) + for (auto const& [coeff, v] : coeffs) + if (!(column_is_int(v) && coeff.is_int())) return false; return true; } @@ -1386,7 +1384,7 @@ 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 lar_solver::is_fixed_at_bound(column_index const& j) { + bool lar_solver::is_fixed_at_bound(column_index const& j, vector>& bounds) { if (column_is_fixed(j)) return false; mpq val; @@ -1395,50 +1393,56 @@ namespace lp { 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); + push(); + k = column_is_int(j) ? LE : LT; + auto ci = mk_var_bound(j, k, column_is_int(j) ? val - 1 : val); + update_column_type_and_bound(j, k, column_is_int(j) ? val - 1 : val, ci); auto st = find_feasible_solution(); + bool infeasible = st == lp_status::INFEASIBLE; + if (infeasible) { + explanation exp; + get_infeasibility_explanation(exp); + unsigned_vector cis; + exp.remove(ci); + verbose_stream() << "tight upper bound " << j << " " << val << "\n"; + bounds.push_back({exp, j, true, val}); + } pop(1); - return st == lp_status::INFEASIBLE; + return 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); + k = column_is_int(j) ? GE : GT; + auto ci = mk_var_bound(j, k, column_is_int(j) ? val + 1 : val); + update_column_type_and_bound(j, k, column_is_int(j) ? val + 1 : val, ci); auto st = find_feasible_solution(); + bool infeasible = st == lp_status::INFEASIBLE; + if (infeasible) { + explanation exp; + get_infeasibility_explanation(exp); + exp.remove(ci); + verbose_stream() << "tight lower bound " << j << " " << val << "\n"; + bounds.push_back({exp, j, false, val}); + } pop(1); - return st == lp_status::INFEASIBLE; + return infeasible; } return false; } - bool lar_solver::has_fixed_at_bound() { + bool lar_solver::has_fixed_at_bound(vector>& bounds) { 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; + if (is_fixed_at_bound(ci, bounds)) verbose_stream() << "fixed " << j << "\n"; - } } - verbose_stream() << "num fixed " << num_fixed << "\n"; - if (num_fixed > 0) + verbose_stream() << "num fixed " << bounds.size() << "\n"; + if (!bounds.empty()) find_feasible_solution(); - return num_fixed > 0; + return !bounds.empty(); } @@ -1617,6 +1621,18 @@ namespace lp { return ret; } + /** + * \brief ensure there is a column index corresponding to vi + * If vi is already a column, just return vi + * If vi is for a term, then create a row that uses the term. + */ + var_index lar_solver::ensure_column(var_index vi) { + if (lp::tv::is_term(vi)) + return to_column(vi); + else + return vi; + } + void lar_solver::add_row_from_term_no_constraint(const lar_term* term, unsigned term_ext_index) { TRACE("dump_terms", print_term(*term, tout) << std::endl;); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index f2017acd7..9c45fdb80 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -18,29 +18,30 @@ --*/ #pragma once -#include "util/vector.h" -#include -#include "util/debug.h" -#include "util/buffer.h" +#include +#include +#include +#include #include #include -#include -#include -#include -#include +#include + +#include "math/lp/bound_analyzer_on_row.h" +#include "math/lp/implied_bound.h" +#include "math/lp/int_solver.h" #include "math/lp/lar_constraints.h" #include "math/lp/lar_core_solver.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/random_updater.h" -#include "util/stacked_value.h" -#include "math/lp/stacked_vector.h" -#include "math/lp/implied_bound.h" -#include "math/lp/bound_analyzer_on_row.h" -#include "math/lp/int_solver.h" -#include "math/lp/nra_solver.h" -#include "math/lp/lp_types.h" #include "math/lp/lp_bound_propagator.h" +#include "math/lp/lp_primal_core_solver.h" +#include "math/lp/lp_types.h" +#include "math/lp/nra_solver.h" +#include "math/lp/numeric_pair.h" +#include "math/lp/random_updater.h" +#include "math/lp/stacked_vector.h" +#include "util/buffer.h" +#include "util/debug.h" +#include "util/stacked_value.h" +#include "util/vector.h" namespace lp { @@ -48,10 +49,9 @@ class int_branch; class int_solver; class lar_solver : public column_namer { struct term_hasher { - std::size_t operator()(const lar_term &t) const - { - using std::size_t; + std::size_t operator()(const lar_term& t) const { using std::hash; + using std::size_t; using std::string; size_t seed = 0; int i = 0; @@ -66,110 +66,107 @@ class lar_solver : public column_namer { }; struct term_comparer { - bool operator()(const lar_term &a, const lar_term& b) const - { - return a == b; + bool operator()(const lar_term& a, const lar_term& b) const { + return a == b; } }; - + //////////////////// fields ////////////////////////// - lp_settings m_settings; - lp_status m_status = lp_status::UNKNOWN; - stacked_value m_simplex_strategy; + lp_settings m_settings; + lp_status m_status = lp_status::UNKNOWN; + stacked_value m_simplex_strategy; // such can be found at the initialization step: u < l - stacked_value m_crossed_bounds_column; - lar_core_solver m_mpq_lar_core_solver; - int_solver * m_int_solver = nullptr; - bool m_need_register_terms = false; - var_register m_var_register; - var_register m_term_register; - stacked_vector m_columns_to_ul_pairs; - constraint_set m_constraints; + stacked_value m_crossed_bounds_column; + lar_core_solver m_mpq_lar_core_solver; + int_solver* m_int_solver = nullptr; + bool m_need_register_terms = false; + var_register m_var_register; + var_register m_term_register; + stacked_vector m_columns_to_ul_pairs; + constraint_set m_constraints; // the set of column indices j such that bounds have changed for j - u_set m_columns_with_changed_bounds; - u_set m_rows_with_changed_bounds; - unsigned_vector m_row_bounds_to_replay; - - u_set m_basic_columns_with_changed_cost; + u_set m_columns_with_changed_bounds; + u_set m_rows_with_changed_bounds; + unsigned_vector m_row_bounds_to_replay; + + u_set m_basic_columns_with_changed_cost; // these are basic columns with the value changed, so the corresponding row in the tableau // does not sum to zero anymore - u_set m_incorrect_columns; + u_set m_incorrect_columns; // copy of m_r_solver.inf_heap() - unsigned_vector m_inf_index_copy; - stacked_value m_term_count; - vector m_terms; - indexed_vector m_column_buffer; + unsigned_vector m_inf_index_copy; + stacked_value m_term_count; + vector m_terms; + indexed_vector m_column_buffer; std::unordered_map, term_hasher, term_comparer> - m_normalized_terms_to_columns; - vector m_backup_x; - stacked_vector m_usage_in_terms; + m_normalized_terms_to_columns; + vector m_backup_x; + stacked_vector m_usage_in_terms; // ((x[j], is_int(j))->j) for fixed j, used in equalities propagation // maps values to integral fixed vars - map, default_eq> m_fixed_var_table_int; + map, default_eq> m_fixed_var_table_int; // maps values to non-integral fixed vars - map, default_eq> m_fixed_var_table_real; + map, default_eq> m_fixed_var_table_real; // end of fields ////////////////// methods //////////////////////////////// - - static bool valid_index(unsigned j) { return static_cast(j) >= 0;} - const lar_term & get_term(unsigned j) const; + + static bool valid_index(unsigned j) { return static_cast(j) >= 0; } + const lar_term& get_term(unsigned j) const; bool row_has_a_big_num(unsigned i) const; // init region bool strategy_is_undecided() const; void register_new_ext_var_index(unsigned ext_v, bool is_int); - bool term_is_int(const lar_term * t) const; - bool term_is_int(const vector> & coeffs) const; + 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_mpq(bool register_in_basis); mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&); // terms - bool all_vars_are_registered(const vector> & coeffs); - var_index add_term_undecided(const vector> & coeffs); - bool term_coeffs_are_ok(const vector> & coeffs); + bool all_vars_are_registered(const vector>& coeffs); + 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_from_term_no_constraint(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); + bool compare_values(impq const& lhs, lconstraint_kind k, const mpq& rhs); inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.clear(); } inline void increase_by_one_columns_with_changed_bounds() { m_columns_with_changed_bounds.increase_size_by_one(); } inline void insert_to_columns_with_changed_bounds(unsigned j) { m_columns_with_changed_bounds.insert(j); } - - void update_column_type_and_bound_check_on_equal(unsigned j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index, unsigned&); - void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); - void update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); + + void update_column_type_and_bound_check_on_equal(unsigned j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index, unsigned&); + void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); + void update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); void register_in_fixed_var_table(unsigned, unsigned&); void remove_non_fixed_from_fixed_var_table(); - constraint_index add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side); + constraint_index add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq& right_side); inline void set_infeasible_column(unsigned j) { set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; } constraint_index add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, - lconstraint_kind kind, const mpq & right_side); + lconstraint_kind kind, const mpq& right_side); unsigned row_of_basic_column(unsigned) const; void decide_on_strategy_and_adjust_initial_state(); void adjust_initial_state(); void adjust_initial_state_for_tableau_rows(); bool sizes_are_correct() const; - bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; - + bool implied_bound_is_correctly_explained(implied_bound const& be, const vector>& explanation) const; void substitute_basis_var_in_terms_for_row(unsigned i); - + template - 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)) + 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, @@ -177,29 +174,28 @@ class lar_solver : public column_namer { row_index, bp); } - + static void clean_popped_elements_for_heap(unsigned n, lpvar_heap& set); static void clean_popped_elements(unsigned n, u_set& set); - - bool maximize_term_on_tableau(const lar_term & term, - impq &term_max); + bool maximize_term_on_tableau(const lar_term& term, + impq& term_max); bool costs_are_zeros_for_r_solver() const; bool reduced_costs_are_zeroes_for_r_solver() const; - void set_costs_to_zero(const lar_term & term); - void prepare_costs_for_r_solver(const lar_term & term); - bool maximize_term_on_corrected_r_solver(lar_term & term, impq &term_max); + void set_costs_to_zero(const lar_term& term); + void prepare_costs_for_r_solver(const lar_term& term); + bool maximize_term_on_corrected_r_solver(lar_term& term, impq& term_max); void pop_core_solver_params(); void pop_core_solver_params(unsigned k); void set_upper_bound_witness(var_index j, constraint_index ci); 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 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_tableau(unsigned j); bool use_tableau_costs() const; 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); + void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta); void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); } void insert_row_with_changed_bounds(unsigned rid); @@ -211,19 +207,19 @@ class lar_solver : public column_namer { numeric_pair get_basic_var_value_from_row(unsigned i); 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; - 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 constraint_holds(const lar_base_constraint& constr, std::unordered_map& var_map) 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; bool explanation_is_correct(explanation&) const; bool inf_explanation_is_correct() const; - mpq sum_of_right_sides_of_explanation(explanation &) const; + mpq sum_of_right_sides_of_explanation(explanation&) const; void get_infeasibility_explanation_for_inf_sign( - explanation & exp, - const vector> & inf_row, + explanation& exp, + const vector>& inf_row, 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); + 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); 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); @@ -235,22 +231,22 @@ class lar_solver : public column_namer { void clean_inf_heap_of_r_solver_after_pop(); 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; } + + 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; } var_index to_column(unsigned ext_j) const; void fix_terms_with_rounded_columns(); 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; + bool sum_first_coords(const lar_term& t, mpq& val) const; void register_normalized_term(const lar_term&, lpvar); void deregister_normalized_term(const lar_term&); mutable std::unordered_set m_set_of_different_pairs; - mutable std::unordered_set m_set_of_different_singles; + mutable std::unordered_set m_set_of_different_singles; mutable mpq m_delta; -public: + public: // this function just looks at the status bool is_feasible() const; @@ -258,7 +254,6 @@ public: return m_fixed_var_table_int; } - const map, default_eq>& fixed_var_table_real() const { return m_fixed_var_table_real; } @@ -267,11 +262,12 @@ public: return m_fixed_var_table_real; } - bool find_in_fixed_tables(const rational& mpq, bool is_int, unsigned & j) const { - return is_int? fixed_var_table_int().find(mpq, j) : fixed_var_table_real().find(mpq, j); + bool find_in_fixed_tables(const rational& mpq, bool is_int, unsigned& j) const { + return is_int ? fixed_var_table_int().find(mpq, j) : fixed_var_table_real().find(mpq, j); } - - template void remove_non_fixed_from_table(T&); + + template + void remove_non_fixed_from_table(T&); unsigned external_to_column_index(unsigned) const; @@ -284,38 +280,38 @@ public: inline void set_column_value_test(unsigned j, const impq& v) { set_column_value(j, v); } - + 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); + lp_status maximize_term(unsigned j_or_term, impq& term_max); - inline core_solver_pretty_printer pp(std::ostream& out) const { - return core_solver_pretty_printer(m_mpq_lar_core_solver.m_r_solver, out); + inline core_solver_pretty_printer pp(std::ostream& out) const { + return core_solver_pretty_printer(m_mpq_lar_core_solver.m_r_solver, out); } - void get_infeasibility_explanation(explanation &) const; + void get_infeasibility_explanation(explanation&) const; inline void backup_x() { m_backup_x = m_mpq_lar_core_solver.m_r_x; } inline void restore_x() { m_mpq_lar_core_solver.m_r_x = m_backup_x; } template - void explain_implied_bound(const implied_bound & ib, lp_bound_propagator & bp) { + void explain_implied_bound(const implied_bound& ib, lp_bound_propagator& bp) { unsigned i = ib.m_row_or_term_index; int bound_sign = (ib.m_is_lower_bound ? 1 : -1); int j_sign = (ib.m_coeff_before_j_is_pos ? 1 : -1) * bound_sign; unsigned bound_j = ib.m_j; - if (tv::is_term(bound_j)) + if (tv::is_term(bound_j)) bound_j = m_var_register.external_to_local(bound_j); for (auto const& r : get_row(i)) { unsigned j = r.var(); - if (j == bound_j) + if (j == bound_j) continue; mpq const& a = r.coeff(); int a_sign = is_pos(a) ? 1 : -1; int sign = j_sign * a_sign; - const ul_pair & ul = m_columns_to_ul_pairs[j]; + const ul_pair& ul = m_columns_to_ul_pairs[j]; auto witness = sign > 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); lp_assert(is_valid(witness)); bp.consume(a, witness); @@ -329,13 +325,13 @@ public: } // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); } - constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side); + constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq& right_side); void activate_check_on_equal(constraint_index, var_index&); void activate(constraint_index); - void random_update(unsigned sz, var_index const * vars); + void random_update(unsigned sz, var_index const* vars); void mark_rows_for_bound_prop(lpvar j); template - void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { + void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { unsigned num_prop = 0; for (unsigned i : m_rows_with_changed_bounds) { num_prop += calculate_implied_bounds_for_row(i, bp); @@ -349,7 +345,7 @@ public: bp.clear_for_eq(); for (unsigned i : m_rows_with_changed_bounds) { unsigned offset_eqs = stats().m_offset_eqs; - bp.cheap_eq_tree(i); + bp.cheap_eq_tree(i); if (settings().get_cancel_flag()) return; if (stats().m_offset_eqs > offset_eqs) @@ -360,75 +356,81 @@ public: } 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)) + 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_at_bound(column_index const& j); - bool has_fixed_at_bound(); - - bool is_fixed(column_index const& j) const { return column_is_fixed(j); } + bool is_fixed_at_bound(column_index const& j, vector>& bounds); + bool has_fixed_at_bound(vector>& bounds); + + 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); - bool compare_values(var_index j, lconstraint_kind kind, const mpq & right_side); - var_index add_term(const vector> & coeffs, unsigned ext_i); + bool compare_values(var_index j, lconstraint_kind kind, const mpq& right_side); + var_index add_term(const vector>& coeffs, unsigned ext_i); void register_existing_terms(); - constraint_index add_var_bound(var_index, lconstraint_kind, const mpq &); - constraint_index add_var_bound_check_on_equal(var_index, lconstraint_kind, const mpq &, var_index&); - + var_index ensure_column(var_index vi); + constraint_index add_var_bound(var_index, lconstraint_kind, const mpq&); + constraint_index add_var_bound_check_on_equal(var_index, lconstraint_kind, const mpq&, var_index&); + var_index add_var(unsigned ext_j, bool is_integer); void set_cut_strategy(unsigned cut_frequency); inline unsigned column_count() const { return A_r().column_count(); } inline var_index local_to_external(var_index idx) const { - return tv::is_term(idx)? - m_term_register.local_to_external(idx) : m_var_register.local_to_external(idx); + return tv::is_term(idx) ? m_term_register.local_to_external(idx) : m_var_register.local_to_external(idx); } - bool column_corresponds_to_term(unsigned) const; + const lar_term& column_to_term(unsigned j) const { + SASSERT(column_corresponds_to_term(j)); + return get_term(column2tv(to_column_index(j))); + } + inline unsigned row_count() const { return A_r().row_count(); } bool var_is_registered(var_index vj) const; void clear_inf_heap() { - m_mpq_lar_core_solver.m_r_solver.inf_heap().clear(); + m_mpq_lar_core_solver.m_r_solver.inf_heap().clear(); } - inline void remove_column_from_inf_heap(unsigned j) { + + inline void remove_column_from_inf_set(unsigned j) { m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_heap(j); } + + void pivot(int entering, int leaving) { + m_mpq_lar_core_solver.pivot(entering, leaving); + } + template void change_basic_columns_dependend_on_a_given_nb_column_report(unsigned j, - const numeric_pair & delta, + const numeric_pair& delta, const ChangeReport& after) { - - 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); - after(bj); - 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); + after(bj); + 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;); + } + } template void set_value_for_nbasic_column_report(unsigned j, - const impq & new_val, + const impq& new_val, const ChangeReport& after) { - lp_assert(!is_base(j)); - auto & x = m_mpq_lar_core_solver.m_r_x[j]; + auto& x = m_mpq_lar_core_solver.m_r_x[j]; auto delta = new_val - x; x = new_val; after(j); change_basic_columns_dependend_on_a_given_nb_column_report(j, delta, after); } - + template bool try_to_patch(lpvar j, const mpq& val, const Blocker& is_blocked, @@ -445,8 +447,8 @@ public: impq delta = get_column_value(j) - ival; for (auto c : A_r().column(j)) { unsigned row_index = c.var(); - const mpq & a = c.coeff(); - unsigned rj = m_mpq_lar_core_solver.m_r_basis[row_index]; + const mpq& a = c.coeff(); + unsigned rj = m_mpq_lar_core_solver.m_r_basis[row_index]; impq rj_new_val = a * delta + get_column_value(rj); // if (column_is_int(rj) && !rj_new_val.is_int()) // return false; @@ -463,65 +465,62 @@ public: inline bool column_has_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_solver.column_has_lower_bound(j); - } + } - inline - constraint_index get_column_upper_bound_witness(unsigned j) const { + inline constraint_index get_column_upper_bound_witness(unsigned j) const { if (tv::is_term(j)) { j = m_var_register.external_to_local(j); } return m_columns_to_ul_pairs()[j].upper_bound_witness(); } - inline - const impq& get_upper_bound(column_index j) const { + inline const impq& get_upper_bound(column_index j) const { return m_mpq_lar_core_solver.m_r_solver.m_upper_bounds[j]; } - inline - const impq& get_lower_bound(column_index j) const { + inline const impq& get_lower_bound(column_index j) const { return m_mpq_lar_core_solver.m_r_solver.m_lower_bounds[j]; } - bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; bool has_value(var_index var, mpq& value) const; - bool fetch_normalized_term_column(const lar_term& t, std::pair& ) const; + bool fetch_normalized_term_column(const lar_term& t, std::pair&) const; unsigned map_term_index_to_column_index(unsigned j) const; bool column_is_fixed(unsigned j) const; bool column_is_free(unsigned j) const; unsigned column_to_reported_index(unsigned j) const; - lp_settings & settings(); - lp_settings const & settings() const; + lp_settings& settings(); + lp_settings const& settings() const; statistics& stats(); - + void updt_params(params_ref const& p); column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; } - const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } - const impq & get_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_upper_bounds()[j]; } + const impq& get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } + const impq& get_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_upper_bounds()[j]; } std::ostream& print_terms(std::ostream& out) const; - std::ostream& print_term(lar_term const& term, std::ostream & out) const; - static std::ostream& print_term_as_indices(lar_term const& term, std::ostream & out); - std::ostream& print_constraint_indices_only(const lar_base_constraint * c, std::ostream & out) const; - std::ostream& print_implied_bound(const implied_bound& be, std::ostream & out) const; + std::ostream& print_term(lar_term const& term, std::ostream& out) const; + static std::ostream& print_term_as_indices(lar_term const& term, std::ostream& out); + std::ostream& print_constraint_indices_only(const lar_base_constraint* c, std::ostream& out) const; + std::ostream& print_implied_bound(const implied_bound& be, std::ostream& out) const; std::ostream& print_values(std::ostream& out) const; std::ostream& display(std::ostream& out) const; bool init_model() const; mpq get_value(column_index const& j) const; mpq get_tv_value(tv const& t) const; - const impq & get_tv_ivalue(tv const& t) const; - void get_model(std::unordered_map & variable_values) const; + const impq& get_tv_ivalue(tv const& t) const; + void get_model(std::unordered_map& variable_values) const; void get_rid_of_inf_eps(); - void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; + void get_model_do_not_care_about_diff_vars(std::unordered_map& variable_values) const; std::string get_variable_name(var_index vi) const override; void set_variable_name(var_index vi, std::string); inline unsigned number_of_vars() const { return m_var_register.size(); } inline bool is_base(unsigned j) const { return m_mpq_lar_core_solver.m_r_heading[j] >= 0; } - inline const impq & column_lower_bound(unsigned j) const { + inline const impq& column_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.lower_bound(j); } - inline const impq & column_upper_bound(unsigned j) const { + inline const impq& column_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.upper_bound(j); } @@ -534,9 +533,9 @@ public: } std::pair add_equality(lpvar j, lpvar k); - - inline void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index & lc, constraint_index & uc) const { - const ul_pair & ul = m_columns_to_ul_pairs[j]; + + inline void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index& lc, constraint_index& uc) const { + const ul_pair& ul = m_columns_to_ul_pairs[j]; lc = ul.lower_bound_witness(); uc = ul.upper_bound_witness(); } @@ -553,19 +552,19 @@ public: inline tv column2tv(column_index const& c) const { return tv::raw(column_to_reported_index(c)); } - + inline std::ostream& print_column_info(unsigned j, std::ostream& out) const { m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out); if (tv::is_term(j)) { print_term_as_indices(get_term(j), out) << "\n"; - - } else if (column_corresponds_to_term(j)) { + + } else if (column_corresponds_to_term(j)) { const lar_term& t = get_term(m_var_register.local_to_external(j)); print_term_as_indices(t, out) << "\n"; } return out; } - + void subst_known_terms(lar_term*); inline std::ostream& print_column_bound_info(unsigned j, std::ostream& out) const { @@ -576,67 +575,68 @@ public: inline bool has_inf_int() const { for (unsigned j = 0; j < column_count(); j++) { - if (column_is_int(j) && ! column_value_is_int(j)) + if (column_is_int(j) && !column_value_is_int(j)) return true; } return false; } - inline const vector & terms() const { return m_terms; } + inline const vector& terms() const { return m_terms; } inline lar_term const& term(unsigned i) const { return *m_terms[i]; } - inline void set_int_solver(int_solver * int_slv) { m_int_solver = int_slv; } - inline int_solver * get_int_solver() { return m_int_solver; } - inline const int_solver * get_int_solver() const { return m_int_solver; } - inline const lar_term & get_term(tv const& t) const { lp_assert(t.is_term()); return *m_terms[t.id()]; } - lp_status find_feasible_solution(); + inline void set_int_solver(int_solver* int_slv) { m_int_solver = int_slv; } + inline int_solver* get_int_solver() { return m_int_solver; } + inline const int_solver* get_int_solver() const { return m_int_solver; } + inline const lar_term& get_term(tv const& t) const { + lp_assert(t.is_term()); + return *m_terms[t.id()]; + } + lp_status find_feasible_solution(); void move_non_basic_columns_to_bounds(bool); bool move_non_basic_column_to_bounds(unsigned j, bool); inline bool r_basis_has_inf_int() const { for (unsigned j : r_basis()) { - if (column_is_int(j) && ! column_value_is_int(j)) + if (column_is_int(j) && !column_value_is_int(j)) return true; } return false; } void round_to_integer_solution(); - inline const row_strip & get_row(unsigned i) const { return A_r().m_rows[i]; } - inline const row_strip & basic2row(unsigned i) const { return A_r().m_rows[row_of_basic_column(i)]; } - inline const column_strip & get_column(unsigned i) const { return A_r().m_columns[i]; } + inline const row_strip& get_row(unsigned i) const { return A_r().m_rows[i]; } + inline const row_strip& basic2row(unsigned i) const { return A_r().m_rows[row_of_basic_column(i)]; } + inline const column_strip& get_column(unsigned i) const { return A_r().m_columns[i]; } bool row_is_correct(unsigned i) const; bool ax_is_correct() const; - bool get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq &rs, constraint_index& ci, bool &upper_bound) const; + bool get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq& rs, constraint_index& ci, bool& upper_bound) const; bool var_is_int(var_index v) const; - inline const vector & r_heading() const { return m_mpq_lar_core_solver.m_r_heading; } - inline const vector & r_basis() const { return m_mpq_lar_core_solver.r_basis(); } - inline const vector & r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } - inline bool column_is_real(unsigned j) const { return !column_is_int(j); } + inline const vector& r_heading() const { return m_mpq_lar_core_solver.m_r_heading; } + inline const vector& r_basis() const { return m_mpq_lar_core_solver.r_basis(); } + inline const vector& r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } + inline bool column_is_real(unsigned j) const { return !column_is_int(j); } lp_status get_status() const; - bool has_changed_columns() const { return !m_columns_with_changed_bounds.empty(); } + bool has_changed_columns() const { return !m_columns_with_changed_bounds.empty(); } void set_status(lp_status s); lp_status solve(); - void fill_explanation_from_crossed_bounds_column(explanation & evidence) const; + void fill_explanation_from_crossed_bounds_column(explanation& evidence) const; bool term_is_used_as_row(unsigned term) const; bool tighten_term_bounds_by_delta(tv const& t, const impq&); lar_solver(); void set_track_pivoted_rows(bool v); - bool get_track_pivoted_rows() const; + bool get_track_pivoted_rows() const; ~lar_solver() override; const vector& r_x() const { return m_mpq_lar_core_solver.m_r_x; } bool column_is_int(unsigned j) const; inline bool column_value_is_int(unsigned j) const { return m_mpq_lar_core_solver.m_r_x[j].is_int(); } - inline static_matrix & A_r() { return m_mpq_lar_core_solver.m_r_A; } - inline const static_matrix & A_r() const { return m_mpq_lar_core_solver.m_r_A; } + inline static_matrix& A_r() { return m_mpq_lar_core_solver.m_r_A; } + inline const static_matrix& A_r() const { return m_mpq_lar_core_solver.m_r_A; } // columns bool column_is_int(column_index const& j) const { return column_is_int((unsigned)j); } -// const impq& get_ivalue(column_index const& j) const { return get_column_value(j); } + // const impq& get_ivalue(column_index const& j) const { return get_column_value(j); } const impq& get_column_value(column_index const& j) const { return m_mpq_lar_core_solver.m_r_x[j]; } - inline - var_index external_to_local(unsigned j) const { + inline var_index external_to_local(unsigned j) const { var_index local_j; if (m_var_register.external_is_used(j, local_j) || m_term_register.external_is_used(j, local_j)) { return local_j; - } - else { + } else { return -1; } } @@ -647,6 +647,5 @@ public: } friend int_solver; friend int_branch; - }; -} +} // namespace lp diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index dba93398e..a909e8472 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -4,39 +4,40 @@ Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) */ +//clang-format off #pragma once -#include "math/lp/lp_settings.h" #include + +#include "math/lp/lp_settings.h" +#include "util/uint_set.h" namespace lp { template class lp_bound_propagator { - class edge; // forward definition + class edge; // forward definition // vertex represents a column - // The set of vertices is organised in a tree. + // The set of vertices is organized in a tree. // The edges of the tree are rows, // Vertices with m_neg set to false grow with the same rate as the root. // Vertices with m_neq set to true diminish with the same rate as the roow grows. // When two vertices with the same m_neg have the same value of columns - // then we have an equality betweet the columns. - class vertex { - unsigned m_column; - vector m_edges; - edge m_edge_from_parent; - unsigned m_level; // the distance in hops to the root; - // it is handy to find the common ancestor - public: + // then we have an equality between the columns. + class vertex { + unsigned m_column; + vector m_edges; + edge m_edge_from_parent; + unsigned m_level; // the distance in hops to the root; + // it is handy to find the common ancestor + public: vertex() {} - vertex(unsigned column) : - m_column(column), - m_level(0) - {} + vertex(unsigned column) : m_column(column), + m_level(0) {} unsigned column() const { return m_column; } const vertex* parent() const { return m_edge_from_parent.source(); } vertex* parent() { return m_edge_from_parent.source(); } unsigned level() const { return m_level; } - void set_edge_from_parent(edge &e) { m_edge_from_parent = e; } + void set_edge_from_parent(edge& e) { m_edge_from_parent = e; } const edge& edge_from_parent() const { return m_edge_from_parent; } - + void add_child(int row, vertex* child) { SASSERT(*this != *child); SASSERT(child->parent() == nullptr); @@ -45,7 +46,7 @@ class lp_bound_propagator { child->set_edge_from_parent(e); child->m_level = m_level + 1; } - const vector & edges() const { return m_edges; } + const vector& edges() const { return m_edges; } bool operator==(const vertex& o) const { return m_column == o.m_column; } @@ -58,7 +59,8 @@ class lp_bound_propagator { vertex* m_source; vertex* m_target; int m_row; - public: + + public: edge(vertex* source, vertex* target, int row) : m_source(source), m_target(target), m_row(row) {} edge() : m_source(nullptr), m_target(nullptr), m_row(-1) {} const vertex* source() const { return m_source; } @@ -69,57 +71,58 @@ class lp_bound_propagator { edge reverse() const { return edge(m_target, m_source, m_row); } }; - static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; } - std::ostream& print_vert(std::ostream & out, const vertex* v) const { + static int other(int x, int y, int z) { + SASSERT(x == z || y == z); + return x == z ? y : x; + } + std::ostream& print_vert(std::ostream& out, const vertex* v) const { out << "(c = " << v->column() << ", parent = {"; if (v->parent()) out << "(" << v->parent()->column() << ")"; else - out << "null"; - out << "} , lvl = " << v->level(); - if (m_pol.contains(v->column())) - out << (pol(v) == -1? " -":" +"); + out << "null"; + out << "} , lvl = " << v->level(); + if (m_pol.contains(v->column())) + out << (pol(v) == -1 ? " -" : " +"); else out << " not in m_pol"; out << ')'; return out; } - hashtable m_visited_rows; - hashtable m_visited_columns; - u_map m_vertices; - vertex* m_root = nullptr; + hashtable m_visited_rows; + hashtable m_visited_columns; + u_map m_vertices; + vertex* m_root = nullptr; // At some point we can find a row with a single vertex non fixed vertex // then we can fix the whole tree, // by adjusting the vertices offsets, so they become absolute. // If the tree is fixed then in addition to checking with the m_vals_to_verts // we are going to check with the m_fixed_var_tables. - const vertex* m_fixed_vertex = nullptr; - explanation m_fixed_vertex_explanation; + const vertex* m_fixed_vertex = nullptr; + explanation m_fixed_vertex_explanation; // a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o - map, default_eq> m_vals_to_verts; + map, default_eq> m_vals_to_verts; // a pair (o, j) belongs to m_vals_to_verts_neg iff -x[j] = x[m_root->column()] + o - map, default_eq> m_vals_to_verts_neg; + map, default_eq> m_vals_to_verts_neg; // x[m_root->column()] - m_pol[j].pol()*x[j] == const; // to bind polarity and the vertex in the table - u_map m_pol; + u_map m_pol; // if m_pos.contains(j) then x[j] = x[m_root->column()] + o - uint_set m_pos; - + uint_set m_pos; + // these maps map a column index to the corresponding index in ibounds - std::unordered_map m_improved_lower_bounds; - std::unordered_map m_improved_upper_bounds; - - T& m_imp; - vector m_ibounds; + std::unordered_map m_improved_lower_bounds; + std::unordered_map m_improved_upper_bounds; + T& m_imp; + vector m_ibounds; + map, default_eq> m_val2fixed_row; - map, default_eq> m_val2fixed_row; - - bool is_fixed_row(unsigned r, unsigned & x) { + bool is_fixed_row(unsigned r, unsigned& x) { x = UINT_MAX; - const auto & row = lp().get_row(r); + const auto& row = lp().get_row(r); for (unsigned k = 0; k < row.size(); k++) { const auto& c = row[k]; if (column_is_fixed(c.var())) @@ -130,7 +133,7 @@ class lp_bound_propagator { } return x != UINT_MAX; } - + void try_add_equation_with_internal_fixed_tables(unsigned r1) { SASSERT(m_fixed_vertex); unsigned v1, v2; @@ -154,8 +157,8 @@ class lp_bound_propagator { TRACE("eq", print_row(tout, r1); print_row(tout, r2); tout << v1 << " == " << v2 << " = " << val(v1) << "\n"); add_eq_on_columns(ex, v1, v2, true); } - - void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex *v) { + + void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex* v) { SASSERT(m_fixed_vertex); unsigned v_j = v->column(); unsigned j = null_lpvar; @@ -163,23 +166,24 @@ class lp_bound_propagator { try_add_equation_with_internal_fixed_tables(row_index); return; } - + TRACE("cheap_eq", - tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl; + tout << "v_j = "; + lp().print_column_info(v_j, tout) << std::endl; tout << "v = "; print_vert(tout, v) << std::endl; - tout << "found j " << j << std::endl; lp().print_column_info(j, tout)<< std::endl; + tout << "found j " << j << std::endl; lp().print_column_info(j, tout) << std::endl; tout << "found j = " << j << std::endl;); vector path = connect_in_tree(v, m_fixed_vertex); explanation ex = get_explanation_from_path(path); ex.add_expl(m_fixed_vertex_explanation); explain_fixed_column(j, ex); - add_eq_on_columns(ex, j, v_j, true); + add_eq_on_columns(ex, j, v_j, true); } - void try_add_equation_with_val_table(const vertex *v) { + void try_add_equation_with_val_table(const vertex* v) { SASSERT(m_fixed_vertex); unsigned v_j = v->column(); - const vertex *u = nullptr; + const vertex* u = nullptr; if (!m_vals_to_verts.find(val(v_j), u)) { m_vals_to_verts.insert(val(v_j), v); return; @@ -187,7 +191,7 @@ class lp_bound_propagator { unsigned j = u->column(); if (j == v_j || is_int(j) != is_int(v_j)) return; - + TRACE("cheap_eq", tout << "found j=" << j << " for v="; print_vert(tout, v) << "\n in m_vals_to_verts\n";); vector path = connect_in_tree(u, v); @@ -198,7 +202,7 @@ class lp_bound_propagator { static bool not_set(unsigned j) { return j == UINT_MAX; } static bool is_set(unsigned j) { return j != UINT_MAX; } - + void create_root(unsigned row_index) { SASSERT(!m_root && !m_fixed_vertex); unsigned x, y; @@ -210,31 +214,30 @@ class lp_bound_propagator { } TRACE("cheap_eq", print_row(tout, row_index);); m_root = alloc_v(x); - set_polarity(m_root, 1); // keep m_root in the positive table + set_polarity(m_root, 1); // keep m_root in the positive table if (not_set(y)) { set_fixed_vertex(m_root); explain_fixed_in_row(row_index, m_fixed_vertex_explanation); - } - else { - vertex *v = add_child_with_check(row_index, y, m_root, polarity); + } else { + vertex* v = add_child_with_check(row_index, y, m_root, polarity); if (v) explore_under(v); } explore_under(m_root); } - void explore_under(vertex * v) { + void explore_under(vertex* v) { check_for_eq_and_add_to_val_tables(v); go_over_vertex_column(v); } // In case of only one non fixed column, and the function returns true, // this column would be represened by x. - bool is_tree_offset_row(unsigned row_index, unsigned & x, unsigned & y, int & polarity) const { - x = y = UINT_MAX; + bool is_tree_offset_row(unsigned row_index, unsigned& x, unsigned& y, int& polarity) const { + x = y = UINT_MAX; const row_cell* x_cell = nullptr; const row_cell* y_cell = nullptr; - const auto & row = lp().get_row(row_index); + const auto& row = lp().get_row(row_index); for (unsigned k = 0; k < row.size(); k++) { const auto& c = row[k]; if (column_is_fixed(c.var())) @@ -242,25 +245,21 @@ class lp_bound_propagator { if (not_set(x)) { if (c.coeff().is_one() || c.coeff().is_minus_one()) { x = c.var(); - x_cell = & c; - } - else + x_cell = &c; + } else return false; - } - else if (not_set(y)) { + } else if (not_set(y)) { if (c.coeff().is_one() || c.coeff().is_minus_one()) { y = c.var(); - y_cell = & c; - } - else + y_cell = &c; + } else return false; - } - else + } else return false; } if (is_set(x)) { if (is_set(y)) - polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1; + polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos() ? -1 : 1; else polarity = 1; return true; @@ -268,18 +267,18 @@ class lp_bound_propagator { return false; } - void go_over_vertex_column(vertex * v) { + void go_over_vertex_column(vertex* v) { lpvar j = v->column(); if (!check_insert(m_visited_columns, j)) return; - - for (const auto & c : lp().get_column(j)) { + + for (const auto& c : lp().get_column(j)) { unsigned row_index = c.var(); if (!check_insert(m_visited_rows, row_index)) continue; vertex* u = get_child_from_row(row_index, v); - if (u) - explore_under(u); + if (u) + explore_under(u); } } @@ -295,21 +294,18 @@ class lp_bound_propagator { m_pol.reset(); m_vertices.reset(); } - + struct reset_cheap_eq { lp_bound_propagator& p; - reset_cheap_eq(lp_bound_propagator& p):p(p) {} + reset_cheap_eq(lp_bound_propagator& p) : p(p) {} ~reset_cheap_eq() { p.reset_cheap_eq_eh(); } }; - -public: - - lp_bound_propagator(T& imp): - m_imp(imp) {} + public: + lp_bound_propagator(T& imp) : m_imp(imp) {} const vector& ibounds() const { return m_ibounds; } - + void init() { m_improved_upper_bounds.clear(); m_improved_lower_bounds.clear(); @@ -318,24 +314,24 @@ public: const lar_solver& lp() const { return m_imp.lp(); } lar_solver& lp() { return m_imp.lp(); } - + column_type get_column_type(unsigned j) const { return m_imp.lp().get_column_type(j); } - - const impq & get_lower_bound(unsigned j) const { + + const impq& get_lower_bound(unsigned j) const { return m_imp.lp().get_lower_bound(j); } - const mpq & get_lower_bound_rational(unsigned j) const { + const mpq& get_lower_bound_rational(unsigned j) const { return m_imp.lp().get_lower_bound(j).x; } - - const impq & get_upper_bound(unsigned j) const { + + const impq& get_upper_bound(unsigned j) const { return m_imp.lp().get_upper_bound(j); } - const mpq & get_upper_bound_rational(unsigned j) const { + const mpq& get_upper_bound_rational(unsigned j) const { return m_imp.lp().get_upper_bound(j).x; } @@ -343,40 +339,37 @@ public: bool column_is_fixed(lpvar j) const { return lp().column_is_fixed(j) && get_lower_bound(j).y.is_zero(); } - - void try_add_bound(mpq const& v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { - j = m_imp.lp().column_to_reported_index(j); - lconstraint_kind kind = is_low? GE : LE; + void try_add_bound(mpq const& v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { + j = m_imp.lp().column_to_reported_index(j); + + lconstraint_kind kind = is_low ? GE : LE; if (strict) kind = static_cast(kind / 2); - + if (!m_imp.bound_is_interesting(j, kind, v)) return; - unsigned k; // index to ibounds + unsigned k; // index to ibounds if (is_low) { if (try_get_value(m_improved_lower_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; + auto& found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } - } - else { + } else { m_improved_lower_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); } - } - else { // the upper bound case + } else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; + auto& found_bound = m_ibounds[k]; if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } - } - else { + } else { m_improved_upper_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); @@ -388,18 +381,18 @@ public: m_imp.consume(a, ci); } - const mpq& val(unsigned j) const { - return lp().get_column_value(j).x; + const mpq& val(unsigned j) const { + return lp().get_column_value(j).x; } - + const mpq& val(const vertex* v) const { return val(v->column()); } - - bool tree_contains_r(vertex* root, vertex *v) const { + + bool tree_contains_r(vertex* root, vertex* v) const { if (*root == *v) return true; - for (auto e : root->edges()) + for (auto e : root->edges()) if (tree_contains_r(e.target(), v)) return true; return false; @@ -415,7 +408,7 @@ public: m_pol.insert(j, p); } - void check_and_set_polarity(vertex* v, int polarity, unsigned row_index, vertex*v_parent) { + void check_and_set_polarity(vertex* v, int polarity, unsigned row_index, vertex* v_parent) { int prev_pol; if (!m_pol.find(v->column(), prev_pol)) { set_polarity(v, polarity); @@ -429,106 +422,107 @@ public: m_fixed_vertex_explanation = get_explanation_from_path(path); explain_fixed_in_row(row_index, m_fixed_vertex_explanation); set_fixed_vertex(v); - TRACE("cheap_eq", - tout << "polarity switch: " << polarity << "\nv = "; print_vert(tout , v) << "\nu = "; tout << "fixed vertex explanation\n"; - for (auto p : m_fixed_vertex_explanation) - lp().constraints().display(tout, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci());); - + TRACE("cheap_eq", + tout << "polarity switch: " << polarity << "\nv = "; + print_vert(tout, v) << "\nu = "; tout << "fixed vertex explanation\n"; + for (auto p + : m_fixed_vertex_explanation) + lp() + .constraints() + .display( + tout, [this](lpvar j) { return lp().get_variable_name(j); }, p.ci());); } - - bool tree_contains(vertex *v) const { + + bool tree_contains(vertex* v) const { if (!m_root) return false; return tree_contains_r(m_root, v); } - - vertex * alloc_v(unsigned column) { - vertex * v = alloc(vertex, column); + + vertex* alloc_v(unsigned column) { + vertex* v = alloc(vertex, column); m_vertices.insert(column, v); SASSERT(!tree_contains(v)); return v; } - unsigned column(unsigned row, unsigned index) { return lp().get_row(row)[index].var(); } bool fixed_phase() const { return m_fixed_vertex; } - - + // Returns the vertex to start exploration from, or nullptr. // It is assumed that parent->column() is present in the row vertex* get_child_from_row(unsigned row_index, vertex* parent) { TRACE("cheap_eq_det", print_row(tout, row_index);); - unsigned x, y; int row_polarity; + unsigned x, y; + int row_polarity; if (!is_tree_offset_row(row_index, x, y, row_polarity)) { - TRACE("cheap_eq_det", tout << "not an offset row\n"; ); + TRACE("cheap_eq_det", tout << "not an offset row\n";); return nullptr; } - if (not_set(y)) { // there is only one fixed variable in the row + if (not_set(y)) { // there is only one fixed variable in the row if (!fixed_phase()) { set_fixed_vertex(parent); explain_fixed_in_row(row_index, m_fixed_vertex_explanation); } return nullptr; } - + SASSERT(is_set(x) && is_set(y)); unsigned col = other(x, y, parent->column()); return add_child_with_check(row_index, col, parent, row_polarity); } - vertex * add_child_with_check(unsigned row_index, unsigned col, vertex* parent, int row_polarity) { + vertex* add_child_with_check(unsigned row_index, unsigned col, vertex* parent, int row_polarity) { vertex* vy; if (m_vertices.find(col, vy)) { SASSERT(vy != nullptr); if (!fixed_phase()) { - check_and_set_polarity(vy, pol(parent) * row_polarity, row_index, parent); + check_and_set_polarity(vy, pol(parent) * row_polarity, row_index, parent); } - return nullptr; // it is not a new vertex - } + return nullptr; // it is not a new vertex + } vy = alloc_v(col); parent->add_child(row_index, vy); if (!fixed_phase()) check_and_set_polarity(vy, row_polarity * pol(parent), row_index, parent); - return vy; + return vy; } - bool is_equal(lpvar j, lpvar k) const { + bool is_equal(lpvar j, lpvar k) const { return m_imp.is_equal(col_to_imp(j), col_to_imp(k)); } - void check_for_eq_and_add_to_val_table(vertex* v, map, default_eq>& table) { + void check_for_eq_and_add_to_val_table(vertex* v, map, default_eq>& table) { TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << "\n";); - const vertex *k; // the other vertex + const vertex* k; // the other vertex if (table.find(val(v), k)) { - TRACE("cheap_eq", tout << "found k " ; print_vert(tout, k) << "\n";); + TRACE("cheap_eq", tout << "found k "; print_vert(tout, k) << "\n";); if (k->column() != v->column() && is_int(k->column()) == is_int(v->column()) && !is_equal(k->column(), v->column())) { report_eq(k, v); - } - else { + } else { TRACE("cheap_eq", tout << "no report\n";); } - } - else { + } else { TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";); table.insert(val(v), v); } } - + void check_for_eq_and_add_to_val_tables(vertex* v) { TRACE("cheap_eq_det", print_vert(tout, v) << "\n";); if (!fixed_phase()) { if (pol(v->column()) == -1) check_for_eq_and_add_to_val_table(v, m_vals_to_verts_neg); - else + else check_for_eq_and_add_to_val_table(v, m_vals_to_verts); } } - + void clear_for_eq() { m_visited_rows.reset(); m_visited_columns.reset(); @@ -542,41 +536,40 @@ public: std::ostream& print_path(const vector& path, std::ostream& out) const { out << "path = \n"; - for (const edge& k : path) + for (const edge& k : path) print_edge(k, out) << "\n"; return out; } - + // we have v_i and v_j, indices of vertices at the same offsets void report_eq(const vertex* v_i, const vertex* v_j) { SASSERT(v_i != v_j); SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column())); TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = "; - print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n"); - + print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) << "\n"); + vector path = connect_in_tree(v_i, v_j); lp::explanation exp = get_explanation_from_path(path); add_eq_on_columns(exp, v_i->column(), v_j->column(), false); - } - std::ostream& print_expl(std::ostream & out, const explanation& exp) const { - for (auto p : exp) - lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci()); + std::ostream& print_expl(std::ostream& out, const explanation& exp) const { + for (auto p : exp) + lp().constraints().display( + out, [this](lpvar j) { return lp().get_variable_name(j); }, p.ci()); return out; } - + bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) { SASSERT(j != k); unsigned je = lp().column_to_reported_index(j); unsigned ke = lp().column_to_reported_index(k); - TRACE("cheap_eq", - tout << "reporting eq " << j << ", " << k << "\n"; + TRACE("cheap_eq", + tout << "reporting eq " << j << ", " << k << "\n"; tout << "reported idx " << je << ", " << ke << "\n"; print_expl(tout, exp); - tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n"; - ); - + tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";); + bool added = m_imp.add_eq(je, ke, exp, is_fixed); if (added) { if (is_fixed) @@ -600,75 +593,75 @@ public: bool is_int(lpvar j) const { return lp().column_is_int(j); } - + explanation get_explanation_from_path(vector& path) const { explanation ex; - for (edge &e : path) + for (edge& e : path) explain_fixed_in_row(e.row(), ex); return ex; } void explain_fixed_in_row(unsigned row, explanation& ex) const { TRACE("cheap_eq", tout << lp().get_row(row) << std::endl); - for (const auto & c : lp().get_row(row)) - if (lp().is_fixed(c.var())) + for (const auto& c : lp().get_row(row)) + if (lp().is_fixed(c.var())) explain_fixed_column(c.var(), ex); } - void explain_fixed_column(unsigned j, explanation & ex) const { + void explain_fixed_column(unsigned j, explanation& ex) const { SASSERT(column_is_fixed(j)); - constraint_index lc, uc; + constraint_index lc, uc; lp().get_bound_constraint_witnesses_for_column(j, lc, uc); ex.push_back(lc); - ex.push_back(uc); + ex.push_back(uc); } - + vector connect_in_tree(const vertex* u, const vertex* v) const { vector path; - TRACE("cheap_eq_details", tout << "u = " ; print_vert(tout, u); tout << "\nv = ";print_vert(tout, v) << "\n";); + TRACE("cheap_eq_details", tout << "u = "; print_vert(tout, u); tout << "\nv = "; print_vert(tout, v) << "\n";); vector v_branch; // equalize the levels while (u->level() > v->level()) { path.push_back(u->edge_from_parent().reverse()); u = u->parent(); } - + while (u->level() < v->level()) { v_branch.push_back(v->edge_from_parent()); v = v->parent(); } SASSERT(u->level() == v->level()); - TRACE("cheap_eq_details", tout << "u = " ; print_vert(tout, u); tout << "\nv = "; print_vert(tout, v) << "\n";); + TRACE("cheap_eq_details", tout << "u = "; print_vert(tout, u); tout << "\nv = "; print_vert(tout, v) << "\n";); while (u != v) { path.push_back(u->edge_from_parent().reverse()); v_branch.push_back(v->edge_from_parent()); u = u->parent(); v = v->parent(); } - for (unsigned i = v_branch.size(); i--; ) { - path.push_back(v_branch[i]); + for (unsigned i = v_branch.size(); i--;) { + path.push_back(v_branch[i]); } TRACE("cheap_eq", print_path(path, tout);); return path; } - + bool tree_is_correct() const { std::unordered_set vs; return tree_is_correct(m_root, vs); } - + bool tree_is_correct(vertex* v, std::unordered_set& visited_verts) const { if (fixed_phase()) return true; - if (visited_verts.find(v->column()) != visited_verts.end()) + if (visited_verts.find(v->column()) != visited_verts.end()) return false; visited_verts.insert(v->column()); - for (auto e : v->edges()) + for (auto e : v->edges()) if (!tree_is_correct(e.target(), visited_verts)) return false; return true; } - std::ostream& print_tree(std::ostream & out, vertex* v) const { + std::ostream& print_tree(std::ostream& out, vertex* v) const { print_vert(out, v); out << "\nchildren :\n"; for (auto c : v->edges()) { @@ -683,75 +676,74 @@ public: try_add_equation_with_lp_fixed_tables(row_index, v); try_add_equation_with_val_table(v); } - + void handle_fixed_phase(unsigned row_index) { if (!fixed_phase()) return; const vertex* v = m_root; try_add_equation_with_fixed_tables(row_index, v); - for (auto e: v->edges()) + for (auto e : v->edges()) try_add_equation_with_fixed_tables(row_index, e.target()); } - void cheap_eq_tree(unsigned row_index) { reset_cheap_eq _reset(*this); TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";); - if (!check_insert(m_visited_rows, row_index)) + if (!check_insert(m_visited_rows, row_index)) return; create_root(row_index); if (!m_root) return; - - TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); + + TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); SASSERT(tree_is_correct()); handle_fixed_phase(row_index); - + TRACE("cheap_eq", tout << "done for row_index " << row_index << "\n"; tout << "tree size = " << verts_size();); } - std::ostream& print_row(std::ostream & out, unsigned row_index) const { - unsigned x, y; int polarity; + std::ostream& print_row(std::ostream& out, unsigned row_index) const { + unsigned x, y; + int polarity; if (true || !is_tree_offset_row(row_index, x, y, polarity)) return lp().get_int_solver()->display_row_info(out, row_index); - + bool first = true; - for (const auto &c: lp().A_r().m_rows[row_index]) { + for (const auto& c : lp().A_r().m_rows[row_index]) { if (lp().column_is_fixed(c.var())) continue; if (c.coeff().is_one()) { - if (!first) - out << "+"; - } - else if (c.coeff().is_minus_one()) - out << "-"; + if (!first) + out << "+"; + } else if (c.coeff().is_minus_one()) + out << "-"; out << lp().get_variable_name(c.var()) << " "; - first = false; + first = false; } out << "\n"; return out; } - - void set_fixed_vertex(vertex *v) { + + void set_fixed_vertex(vertex* v) { TRACE("cheap_eq", if (v) print_vert(tout, v); else tout << "set m_fixed_vertex to nullptr"; tout << "\n";); SASSERT(!m_fixed_vertex || v == nullptr); m_fixed_vertex = v; } - + unsigned verts_size() const { return subtree_size(m_root); } unsigned subtree_size(vertex* v) const { - unsigned r = 1; // 1 for v + unsigned r = 1; // 1 for v for (auto e : v->edges()) r += subtree_size(e.target()); return r; } - - void delete_tree(vertex * v) { + + void delete_tree(vertex* v) { for (auto p : v->edges()) delete_tree(p.target()); dealloc(v); @@ -763,7 +755,6 @@ public: return false; table.insert(j); return true; - } - + } }; -} +} // namespace lp diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index b2e402c08..470405581 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -17,7 +17,7 @@ Revision History: --*/ - +// clang-format off #pragma once #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" @@ -37,123 +37,108 @@ Revision History: 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 -template -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; + // 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 { + 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_tableau(); + 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; - } + 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; - } - UNREACHABLE(); // 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: + return !is_pos(rc.coeff()) || this->x_above_lower_bound(j); + case column_type::upper_bound: + return is_pos(rc.coeff()) || 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; + } + UNREACHABLE(); // unreachable + return false; + } - return true; - case column_type::upper_bound: - if (is_pos(rc.coeff())) { - return true; - } + 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 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; + } + UNREACHABLE(); // unreachable + return false; + } - 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; - } - 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); - } - - 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); - default: - return false; - } - UNREACHABLE(); // unreachable - return false; - } /** * Return the number of base non-free variables depending on the column j, * different from bj, @@ -174,503 +159,495 @@ public: } return r; } - - int find_beneficial_entering_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_entering_tableau_rows(int i, T &a_ent) { - if (m_bland_mode_tableau) - return find_beneficial_entering_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 min_non_free_so_far = -1; - unsigned best_col_sz = -1; - 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 not_free = get_num_of_not_free_basic_dependent_vars(j, min_non_free_so_far, bj); - unsigned col_sz = this->m_A.m_columns[j].size(); - if (not_free < min_non_free_so_far || (not_free == min_non_free_so_far && col_sz < best_col_sz)) { - min_non_free_so_far = not_free; - best_col_sz = this->m_A.m_columns[j].size(); - choice = k; - nchoices = 1; - } else if (not_free == min_non_free_so_far && - col_sz == best_col_sz) { - if (this->m_settings.random_next(++nchoices) == 0){ - choice = k; + // clang-format on + int find_beneficial_entering_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; + } + //clang-format off + int find_beneficial_entering_tableau_rows(int i, T &a_ent) { + if (m_bland_mode_tableau) + return find_beneficial_entering_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 min_non_free_so_far = -1; + unsigned best_col_sz = -1; + 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 not_free = get_num_of_not_free_basic_dependent_vars(j, min_non_free_so_far, bj); + unsigned col_sz = this->m_A.m_columns[j].size(); + if (not_free < min_non_free_so_far || (not_free == min_non_free_so_far && col_sz < best_col_sz)) { + min_non_free_so_far = not_free; + best_col_sz = this->m_A.m_columns[j].size(); + choice = k; + nchoices = 1; + } else if (not_free == min_non_free_so_far && + col_sz == best_col_sz) { + if (this->m_settings.random_next(++nchoices) == 0) { + choice = k; + } + } + } + + 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(); } - if (choice == -1) { - m_inf_row_index_for_tableau = i; - return -1; + 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); } - 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(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_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 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 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); + } - 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); - } + 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_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 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); - - + void get_bound_on_variable_and_update_leaving_precisely( + unsigned j, vector &leavings, T m, X &t, + T &abs_of_d_of_leaving); #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 + void backup_and_normalize_costs(); - void backup_and_normalize_costs(); + void advance_on_entering_and_leaving_tableau(int entering, int leaving, 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(); - } - - void advance_on_entering_tableau(int entering); - - void push_forward_offset_in_non_basis(unsigned &offset_in_nb); - - unsigned get_number_of_non_basic_column_to_try_for_enter(); - - // 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 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); - 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() { - if (this->inf_heap().empty()) - return -1; - return this->inf_heap().min_value(); - } - - - - 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 pivot(int entering, int leaving) { + this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); + this->change_basis(entering, leaving); } - SASSERT(this->column_is_base(leaving)); + 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(); + } - 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; + void advance_on_entering_tableau(int entering); + + void push_forward_offset_in_non_basis(unsigned &offset_in_nb); + + unsigned get_number_of_non_basic_column_to_try_for_enter(); + + // 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 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); + 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() { + if (this->inf_heap().empty()) + return -1; + return this->inf_heap().min_value(); + } + + 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]; } - } else { - m_left_basis_tableau.insert(leaving); - } - } - T a_ent; - int entering = find_beneficial_entering_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_heap(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() { - 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 one_iteration_tableau_rows() { + int leaving = find_smallest_inf_column(); + if (leaving == -1) { + this->set_status(lp_status::OPTIMAL); + return; + } - 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); + 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_entering_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_heap(leaving); + advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); + if (this->current_x_is_feasible()) + this->set_status(lp_status::OPTIMAL); } - } - 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 decide_on_status_when_cannot_find_entering() { + this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL + : lp_status::INFEASIBLE); } - } - 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_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(); } - } - 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); + 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 { - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, - unlimited); + theta = zero_of_type(); + unlimited = false; } - } else { - if (m > 0) { - limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, - unlimited); + 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 { - limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, - unlimited); + theta = zero_of_type(); + unlimited = false; } - } - break; - default: - UNREACHABLE(); + return true; } - if (!unlimited && theta < zero_of_type()) { - theta = zero_of_type(); + + 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); + } } - } - 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_run_tableau(); - void update_x_tableau(unsigned entering, const X &delta); - // 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 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); + } } - } - 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); - } + 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]; - friend core_solver_pretty_printer; + 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 { + 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: + UNREACHABLE(); + } + if (!unlimited && theta < zero_of_type()) { + theta = zero_of_type(); + } + } + + 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_run_tableau(); + void update_x_tableau(unsigned entering, const X &delta); + // 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), + m_bland_mode_threshold(1000) { + this->set_status(lp_status::UNKNOWN); + } + + friend core_solver_pretty_printer; }; -} // namespace lp +} // namespace lp 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 0c4f6216e..dbcced4ab 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -59,6 +59,7 @@ template void lp_primal_core_solver::advance_on_e } 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(); + unsigned n = 0; 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)) @@ -71,8 +72,9 @@ template void lp_primal_core_solver::advance_on_e entering_iter = non_basis_iter; if (number_of_benefitial_columns_to_go_over) number_of_benefitial_columns_to_go_over--; + n = 1; } - else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { + else if (t == j_nz && this->m_settings.random_next(++n) == 0) { entering_iter = non_basis_iter; } }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); @@ -166,7 +168,8 @@ template void lp_primal_core_solver::advance_on_en } this->update_basis_and_x_tableau(entering, leaving, t); this->iters_with_no_cost_growing() = 0; - } else { + } + else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); } diff --git a/src/math/lp/lp_types.h b/src/math/lp/lp_types.h index 3f9c107c5..e4ee535aa 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -20,8 +20,9 @@ Revision History: #pragma once #include - - +// clang-format off +#include +#include "util/debug.h" namespace nla { class core; } diff --git a/src/test/lp/.clang-format b/src/test/lp/.clang-format new file mode 100644 index 000000000..d70ca6b63 --- /dev/null +++ b/src/test/lp/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 0 diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 9120d64cf..750eaefb1 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -19,81 +19,81 @@ --*/ #include + +#include "util/rational.h" #ifndef _WINDOWS #include #endif -#include -#include -#include -#include -#include +#include #include +#include + +#include #include #include -#include +#include +#include +#include #include -#include "math/lp/lp_utils.h" -#include "test/lp/smt_reader.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 "util/stacked_value.h" -#include "math/lp/u_set.h" -#include "util/stopwatch.h" -#include -#include "test/lp/gomory_test.h" -#include "math/lp/matrix.h" -#include "math/lp/hnf.h" -#include "math/lp/general_matrix.h" -#include "math/lp/lp_bound_propagator.h" -#include "math/lp/nla_solver.h" -#include "math/lp/horner.h" -#include "math/lp/cross_nested.h" -#include "math/lp/int_cube.h" -#include "math/lp/emonics.h" -#include "math/lp/static_matrix.h" -bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -size_t number_of_whites(const std::string & s) { +#include "math/lp/cross_nested.h" +#include "math/lp/emonics.h" +#include "math/lp/general_matrix.h" +#include "math/lp/hnf.h" +#include "math/lp/horner.h" +#include "math/lp/indexed_value.h" +#include "math/lp/int_cube.h" +#include "math/lp/lar_solver.h" +#include "math/lp/lp_bound_propagator.h" +#include "math/lp/lp_utils.h" +#include "math/lp/matrix.h" +#include "math/lp/nla_solver.h" +#include "math/lp/numeric_pair.h" +#include "math/lp/static_matrix.h" +#include "math/lp/u_set.h" +#include "test/lp/argument_parser.h" +#include "test/lp/gomory_test.h" +#include "test/lp/smt_reader.h" +#include "test/lp/test_file_reader.h" +#include "util/stacked_value.h" +#include "util/stopwatch.h" +void test_patching(); +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; + 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 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; - + 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 +// 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(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)); -} +// 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 string_split(const std::string &source, + const char *delimiter, bool keep_empty) { vector results; size_t prev = 0; size_t next = 0; @@ -118,7 +118,6 @@ vector split_and_trim(const std::string &line) { return ret; } - namespace nla { void test_horner(); void test_monics(); @@ -131,7 +130,7 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial(); void test_basic_lemma_for_mon_neutral_from_monomial_to_factors(); void test_basic_lemma_for_mon_neutral_from_factors_to_monomial(); -void test_cn_on_expr(nex_sum *t, cross_nested& cn) { +void test_cn_on_expr(nex_sum *t, cross_nested &cn) { t = to_sum(cn.get_nex_creator().simplify(t)); TRACE("nla_test", tout << "t=" << *t << '\n';); cn.run(t); @@ -147,35 +146,34 @@ void test_nex_order() { r.set_number_of_vars(3); for (unsigned j = 0; j < r.get_number_of_vars(); j++) r.set_var_weight(j, 10 - j); - nex_var* a = r.mk_var(0); - nex_var* b = r.mk_var(1); - nex_var* c = r.mk_var(2); + nex_var *a = r.mk_var(0); + nex_var *b = r.mk_var(1); + nex_var *c = r.mk_var(2); ENSURE(r.gt(a, b)); ENSURE(r.gt(b, c)); ENSURE(r.gt(a, c)); - - - nex* ab = r.mk_mul(a, b); - nex* ba = r.mk_mul(b, a); - nex* ac = r.mk_mul(a, c); + nex *ab = r.mk_mul(a, b); + nex *ba = r.mk_mul(b, a); + nex *ac = r.mk_mul(a, c); ENSURE(r.gt(ab, ac)); ENSURE(!r.gt(ac, ab)); - nex* _3ac = r.mk_mul(rational(3), a, c); - nex* _2ab = r.mk_mul(rational(2), a, b); + nex *_3ac = r.mk_mul(rational(3), a, c); + nex *_2ab = r.mk_mul(rational(2), a, b); ENSURE(r.gt(ab, _3ac)); ENSURE(!r.gt(_3ac, ab)); ENSURE(!r.gt(a, ab)); ENSURE(r.gt(ab, a)); ENSURE(r.gt(_2ab, _3ac)); ENSURE(!r.gt(_3ac, _2ab)); - nex* _2a = r.mk_mul(rational(2), a); + nex *_2a = r.mk_mul(rational(2), a); ENSURE(!r.gt(_2a, _2ab)); ENSURE(r.gt(_2ab, _2a)); ENSURE(nex_creator::equal(ab, ba)); - nex_sum * five_a_pl_one = r.mk_sum(r.mk_mul(rational(5), a), r.mk_scalar(rational(1))); - nex_mul * poly = r.mk_mul(five_a_pl_one, b); - nex * p = r.simplify(poly); + nex_sum *five_a_pl_one = + r.mk_sum(r.mk_mul(rational(5), a), r.mk_scalar(rational(1))); + nex_mul *poly = r.mk_mul(five_a_pl_one, b); + nex *p = r.simplify(poly); std::cout << "poly = " << *poly << " , p = " << *p << "\n"; #endif } @@ -184,216 +182,208 @@ void test_simplify() { #ifdef Z3DEBUG nex_creator r; cross_nested cn( - [](const nex* n) { - TRACE("nla_cn_test", tout << *n << "\n";); - return false; - } , - [](unsigned) { return false; }, - []() { return 1; }, // for random - r); + [](const nex *n) { + TRACE("nla_cn_test", tout << *n << "\n";); + return false; + }, + [](unsigned) { return false; }, []() { return 1; }, // for random + r); enable_trace("nla_cn"); enable_trace("nla_cn_details"); // enable_trace("nla_cn_details_"); enable_trace("nla_test"); - + r.set_number_of_vars(3); for (unsigned j = 0; j < r.get_number_of_vars(); j++) r.set_var_weight(j, j); - nex_var* a = r.mk_var(0); - nex_var* b = r.mk_var(1); - nex_var* c = r.mk_var(2); + nex_var *a = r.mk_var(0); + nex_var *b = r.mk_var(1); + nex_var *c = r.mk_var(2); auto bc = r.mk_mul(b, c); auto a_plus_bc = r.mk_sum(a, bc); auto two_a_plus_bc = r.mk_mul(r.mk_scalar(rational(2)), a_plus_bc); auto simp_two_a_plus_bc = r.simplify(two_a_plus_bc); - TRACE("nla_test", tout << * simp_two_a_plus_bc << "\n";); + TRACE("nla_test", tout << *simp_two_a_plus_bc << "\n";); ENSURE(nex_creator::equal(simp_two_a_plus_bc, two_a_plus_bc)); auto simp_a_plus_bc = r.simplify(a_plus_bc); ENSURE(to_sum(simp_a_plus_bc)->size() > 1); auto three_ab = r.mk_mul(r.mk_scalar(rational(3)), a, b); auto three_ab_square = r.mk_mul(three_ab, three_ab, three_ab); - + TRACE("nla_test", tout << "before simplify " << *three_ab_square << "\n";); three_ab_square = to_mul(r.simplify(three_ab_square)); TRACE("nla_test", tout << *three_ab_square << "\n";); - const rational& s = three_ab_square->coeff(); + const rational &s = three_ab_square->coeff(); ENSURE(s == rational(27)); auto m = r.mk_mul(a, a); TRACE("nla_test_", tout << "m = " << *m << "\n";); /* - auto n = r.mk_mul(b, b, b, b, b, b, b); - n->add_child_in_power(b, 7); - n->add_child(r.mk_scalar(rational(3))); - n->add_child_in_power(r.mk_scalar(rational(2)), 2); - n->add_child(r.mk_scalar(rational(1))); - TRACE("nla_test_", tout << "n = " << *n << "\n";); - m->add_child_in_power(n, 3); - n->add_child_in_power(r.mk_scalar(rational(1, 3)), 2); - TRACE("nla_test_", tout << "m = " << *m << "\n";); - - nex_sum * e = r.mk_sum(a, r.mk_sum(b, m)); - TRACE("nla_test", tout << "before simplify e = " << *e << "\n";); - e = to_sum(r.simplify(e)); - TRACE("nla_test", tout << "simplified e = " << *e << "\n";); - ENSURE(e->children().size() > 2); - nex_sum * e_m = r.mk_sum(); - for (const nex* ex: to_sum(e)->children()) { - nex* ce = r.mk_mul(r.clone(ex), r.mk_scalar(rational(3))); - TRACE("nla_test", tout << "before simpl ce = " << *ce << "\n";); - ce = r.simplify(ce); - TRACE("nla_test", tout << "simplified ce = " << *ce << "\n";); - e_m->add_child(ce); - } - e->add_child(e_m); - TRACE("nla_test", tout << "before simplify sum e = " << *e << "\n";); - e = to_sum(r.simplify(e)); - TRACE("nla_test", tout << "simplified sum e = " << *e << "\n";); + auto n = r.mk_mul(b, b, b, b, b, b, b); + n->add_child_in_power(b, 7); + n->add_child(r.mk_scalar(rational(3))); + n->add_child_in_power(r.mk_scalar(rational(2)), 2); + n->add_child(r.mk_scalar(rational(1))); + TRACE("nla_test_", tout << "n = " << *n << "\n";); + m->add_child_in_power(n, 3); + n->add_child_in_power(r.mk_scalar(rational(1, 3)), 2); + TRACE("nla_test_", tout << "m = " << *m << "\n";); - nex * pr = r.mk_mul(a, b, b); - TRACE("nla_test", tout << "before simplify pr = " << *pr << "\n";); - r.simplify(pr); - TRACE("nla_test", tout << "simplified sum e = " << *pr << "\n";); - */ + nex_sum * e = r.mk_sum(a, r.mk_sum(b, m)); + TRACE("nla_test", tout << "before simplify e = " << *e << "\n";); + e = to_sum(r.simplify(e)); + TRACE("nla_test", tout << "simplified e = " << *e << "\n";); + ENSURE(e->children().size() > 2); + nex_sum * e_m = r.mk_sum(); + for (const nex* ex: to_sum(e)->children()) { + nex* ce = r.mk_mul(r.clone(ex), r.mk_scalar(rational(3))); + TRACE("nla_test", tout << "before simpl ce = " << *ce << "\n";); + ce = r.simplify(ce); + TRACE("nla_test", tout << "simplified ce = " << *ce << "\n";); + e_m->add_child(ce); + } + e->add_child(e_m); + TRACE("nla_test", tout << "before simplify sum e = " << *e << "\n";); + e = to_sum(r.simplify(e)); + TRACE("nla_test", tout << "simplified sum e = " << *e << "\n";); + + nex * pr = r.mk_mul(a, b, b); + TRACE("nla_test", tout << "before simplify pr = " << *pr << "\n";); + r.simplify(pr); + TRACE("nla_test", tout << "simplified sum e = " << *pr << "\n";); + */ #endif } void test_cn_shorter() { -// nex_sum *clone; -// nex_creator cr; -// cross_nested cn( -// [](const nex* n) { -// TRACE("nla_test", tout <<"cn form = " << *n << "\n"; - -// ); -// return false; -// } , -// [](unsigned) { return false; }, -// []{ return 1; }, cr); -// enable_trace("nla_test"); -// enable_trace("nla_cn"); -// enable_trace("nla_cn_test"); -// enable_trace("nla_cn_details"); -// // enable_trace("nla_cn_details_"); -// enable_trace("nla_test_details"); -// cr.set_number_of_vars(20); -// for (unsigned j = 0; j < cr.get_number_of_vars(); j++) -// cr.set_var_weight(j,j); - -// nex_var* a = cr.mk_var(0); -// nex_var* b = cr.mk_var(1); -// nex_var* c = cr.mk_var(2); -// nex_var* d = cr.mk_var(3); -// nex_var* e = cr.mk_var(4); -// nex_var* g = cr.mk_var(6); + // nex_sum *clone; + // nex_creator cr; + // cross_nested cn( + // [](const nex* n) { + // TRACE("nla_test", tout <<"cn form = " << *n << "\n"; -// nex* min_1 = cr.mk_scalar(rational(-1)); -// // test_cn_on_expr(min_1*c*e + min_1*b*d + min_1*a*b + a*c); -// nex_mul* bcg = cr.mk_mul(b, c, g); -// /* -// bcg->add_child(min_1); -// nex* abcd = cr.mk_mul(a, b, c, d); -// nex* eae = cr.mk_mul(e, a, e); -// nex* three_eac = cr.mk_mul(e, a, c); to_mul(three_eac)->coeff() = rational(3); -// nex* _6aad = cr.mk_mul(cr.mk_scalar(rational(6)), a, a, d); -// clone = to_sum(cr.clone(cr.mk_sum(_6aad, abcd, eae, three_eac))); -// clone = to_sum(cr.simplify(clone)); -// TRACE("nla_test", tout << "clone = " << *clone << "\n";); -// // test_cn_on_expr(cr.mk_sum(aad, abcd, aaccd, add, eae, eac, ed), cn); -// test_cn_on_expr(clone, cn); -// */ + // ); + // return false; + // } , + // [](unsigned) { return false; }, + // []{ return 1; }, cr); + // enable_trace("nla_test"); + // enable_trace("nla_cn"); + // enable_trace("nla_cn_test"); + // enable_trace("nla_cn_details"); + // // enable_trace("nla_cn_details_"); + // enable_trace("nla_test_details"); + // cr.set_number_of_vars(20); + // for (unsigned j = 0; j < cr.get_number_of_vars(); j++) + // cr.set_var_weight(j,j); + + // nex_var* a = cr.mk_var(0); + // nex_var* b = cr.mk_var(1); + // nex_var* c = cr.mk_var(2); + // nex_var* d = cr.mk_var(3); + // nex_var* e = cr.mk_var(4); + // nex_var* g = cr.mk_var(6); + + // nex* min_1 = cr.mk_scalar(rational(-1)); + // // test_cn_on_expr(min_1*c*e + min_1*b*d + min_1*a*b + a*c); + // nex_mul* bcg = cr.mk_mul(b, c, g); + // /* + // bcg->add_child(min_1); + // nex* abcd = cr.mk_mul(a, b, c, d); + // nex* eae = cr.mk_mul(e, a, e); + // nex* three_eac = cr.mk_mul(e, a, c); to_mul(three_eac)->coeff() = + // rational(3); nex* _6aad = cr.mk_mul(cr.mk_scalar(rational(6)), a, a, + // d); clone = to_sum(cr.clone(cr.mk_sum(_6aad, abcd, eae, three_eac))); + // clone = to_sum(cr.simplify(clone)); + // TRACE("nla_test", tout << "clone = " << *clone << "\n";); + // // test_cn_on_expr(cr.mk_sum(aad, abcd, aaccd, add, eae, eac, ed), + // cn); test_cn_on_expr(clone, cn); + // */ } void test_cn() { -// #ifdef Z3DEBUG -// test_cn_shorter(); -// nex_creator cr; -// cross_nested cn( -// [](const nex* n) { -// TRACE("nla_test", tout <<"cn form = " << *n << "\n";); -// return false; -// } , -// [](unsigned) { return false; }, -// []{ return 1; }, cr); -// enable_trace("nla_test"); -// enable_trace("nla_cn_test"); -// // enable_trace("nla_cn"); -// // enable_trace("nla_test_details"); -// cr.set_number_of_vars(20); -// for (unsigned j = 0; j < cr.get_number_of_vars(); j++) -// cr.set_var_weight(j, j); - -// nex_var* a = cr.mk_var(0); -// nex_var* b = cr.mk_var(1); -// nex_var* c = cr.mk_var(2); -// nex_var* d = cr.mk_var(3); -// nex_var* e = cr.mk_var(4); -// nex_var* g = cr.mk_var(6); -// nex_sum * a_p_ae_sq = cr.mk_sum(a, cr.mk_mul(a, e, e)); -// a_p_ae_sq = to_sum(cr.simplify(a_p_ae_sq)); -// test_cn_on_expr(a_p_ae_sq, cn); + // #ifdef Z3DEBUG + // test_cn_shorter(); + // nex_creator cr; + // cross_nested cn( + // [](const nex* n) { + // TRACE("nla_test", tout <<"cn form = " << *n << "\n";); + // return false; + // } , + // [](unsigned) { return false; }, + // []{ return 1; }, cr); + // enable_trace("nla_test"); + // enable_trace("nla_cn_test"); + // // enable_trace("nla_cn"); + // // enable_trace("nla_test_details"); + // cr.set_number_of_vars(20); + // for (unsigned j = 0; j < cr.get_number_of_vars(); j++) + // cr.set_var_weight(j, j); -// nex* min_1 = cr.mk_scalar(rational(-1)); -// // test_cn_on_expr(min_1*c*e + min_1*b*d + min_1*a*b + a*c); -// nex* bcd = cr.mk_mul(b, c, d); -// nex_mul* bcg = cr.mk_mul(b, c, g); -// /* -// bcg->add_child(min_1); -// nex_sum* t = cr.mk_sum(bcd, bcg); -// test_cn_on_expr(t, cn); -// nex* abd = cr.mk_mul(a, b, d); -// nex* abc = cr.mk_mul(a, b, c); -// nex* abcd = cr.mk_mul(a, b, c, d); -// nex* aaccd = cr.mk_mul(a, a, c, c, d); -// nex* add = cr.mk_mul(a, d, d); -// nex* eae = cr.mk_mul(e, a, e); -// nex* eac = cr.mk_mul(e, a, c); -// nex* ed = cr.mk_mul(e, d); -// nex* cbd = cr.mk_mul(c, b, d); -// nex* acd = cr.mk_mul(a, c, d); - -// nex* _6aad = cr.mk_mul(cr.mk_scalar(rational(6)), a, a, d); -// nex * clone = cr.clone(cr.mk_sum(_6aad, abcd, aaccd, add, eae, eac, ed)); -// clone = cr.simplify(clone); -// ENSURE(cr.is_simplified(clone)); -// TRACE("nla_test", tout << "clone = " << *clone << "\n";); -// // test_cn_on_expr(cr.mk_sum(aad, abcd, aaccd, add, eae, eac, ed), cn); -// test_cn_on_expr(to_sum(clone), cn); -// TRACE("nla_test", tout << "done\n";); -// test_cn_on_expr(cr.mk_sum(abd, abc, cbd, acd), cn); -// TRACE("nla_test", tout << "done\n";);*/ -// #endif -// // test_cn_on_expr(a*b*b*d*d + a*b*b*c*d + c*b*b*d); -// // TRACE("nla_test", tout << "done\n";); -// // test_cn_on_expr(a*b*d + a*b*c + c*b*d); + // nex_var* a = cr.mk_var(0); + // nex_var* b = cr.mk_var(1); + // nex_var* c = cr.mk_var(2); + // nex_var* d = cr.mk_var(3); + // nex_var* e = cr.mk_var(4); + // nex_var* g = cr.mk_var(6); + // nex_sum * a_p_ae_sq = cr.mk_sum(a, cr.mk_mul(a, e, e)); + // a_p_ae_sq = to_sum(cr.simplify(a_p_ae_sq)); + // test_cn_on_expr(a_p_ae_sq, cn); + + // nex* min_1 = cr.mk_scalar(rational(-1)); + // // test_cn_on_expr(min_1*c*e + min_1*b*d + min_1*a*b + a*c); + // nex* bcd = cr.mk_mul(b, c, d); + // nex_mul* bcg = cr.mk_mul(b, c, g); + // /* + // bcg->add_child(min_1); + // nex_sum* t = cr.mk_sum(bcd, bcg); + // test_cn_on_expr(t, cn); + // nex* abd = cr.mk_mul(a, b, d); + // nex* abc = cr.mk_mul(a, b, c); + // nex* abcd = cr.mk_mul(a, b, c, d); + // nex* aaccd = cr.mk_mul(a, a, c, c, d); + // nex* add = cr.mk_mul(a, d, d); + // nex* eae = cr.mk_mul(e, a, e); + // nex* eac = cr.mk_mul(e, a, c); + // nex* ed = cr.mk_mul(e, d); + // nex* cbd = cr.mk_mul(c, b, d); + // nex* acd = cr.mk_mul(a, c, d); + + // nex* _6aad = cr.mk_mul(cr.mk_scalar(rational(6)), a, a, d); + // nex * clone = cr.clone(cr.mk_sum(_6aad, abcd, aaccd, add, eae, eac, + // ed)); clone = cr.simplify(clone); ENSURE(cr.is_simplified(clone)); + // TRACE("nla_test", tout << "clone = " << *clone << "\n";); + // // test_cn_on_expr(cr.mk_sum(aad, abcd, aaccd, add, eae, eac, ed), + // cn); test_cn_on_expr(to_sum(clone), cn); TRACE("nla_test", tout << + // "done\n";); test_cn_on_expr(cr.mk_sum(abd, abc, cbd, acd), cn); + // TRACE("nla_test", tout << "done\n";);*/ + // #endif + // // test_cn_on_expr(a*b*b*d*d + a*b*b*c*d + c*b*b*d); + // // TRACE("nla_test", tout << "done\n";); + // // test_cn_on_expr(a*b*d + a*b*c + c*b*d); } -} // end of namespace nla +} // end of namespace nla namespace lp { unsigned seed = 1; - random_gen g_rand; -static unsigned my_random() { - return g_rand(); -} -struct simple_column_namer:public column_namer -{ +static unsigned my_random() { return g_rand(); } +struct simple_column_namer : public column_namer { std::string get_variable_name(unsigned j) const override { - return std::string("x") + T_to_string(j); + return std::string("x") + T_to_string(j); } }; - - -vector allocate_basis_heading(unsigned count) { // the rest of initialization will be handled by lu_QR +vector allocate_basis_heading( + unsigned count) { // the rest of initialization will be handled by lu_QR vector basis_heading(count, -1); return basis_heading; } - -void init_basic_part_of_basis_heading(vector & basis, vector & basis_heading) { +void init_basic_part_of_basis_heading(vector &basis, + vector &basis_heading) { lp_assert(basis_heading.size() >= basis.size()); unsigned m = basis.size(); for (unsigned i = 0; i < m; i++) { @@ -402,26 +392,28 @@ void init_basic_part_of_basis_heading(vector & basis, vector & ba } } -void init_non_basic_part_of_basis_heading(vector & basis_heading, vector & non_basic_columns) { +void init_non_basic_part_of_basis_heading(vector &basis_heading, + vector &non_basic_columns) { non_basic_columns.clear(); - for (int j = basis_heading.size(); j--;){ + for (int j = basis_heading.size(); j--;) { if (basis_heading[j] < 0) { non_basic_columns.push_back(j); // the index of column j in m_nbasis is (- basis_heading[j] - 1) - basis_heading[j] = - static_cast(non_basic_columns.size()); + basis_heading[j] = -static_cast(non_basic_columns.size()); } } } -void init_basis_heading_and_non_basic_columns_vector(vector & basis, - vector & basis_heading, - vector & non_basic_columns) { +void init_basis_heading_and_non_basic_columns_vector( + vector &basis, vector &basis_heading, + vector &non_basic_columns) { init_basic_part_of_basis_heading(basis, basis_heading); init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns); } -void change_basis(unsigned entering, unsigned leaving, vector& basis, vector& nbasis, vector & basis_heading) { - int place_in_basis = basis_heading[leaving]; - int place_in_non_basis = - basis_heading[entering] - 1; +void change_basis(unsigned entering, unsigned leaving, vector &basis, + vector &nbasis, vector &basis_heading) { + int place_in_basis = basis_heading[leaving]; + int place_in_non_basis = -basis_heading[entering] - 1; basis_heading[entering] = place_in_basis; basis_heading[leaving] = -place_in_non_basis - 1; basis[place_in_basis] = entering; @@ -430,7 +422,8 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, int perm_id = 0; -bool get_int_from_args_parser(const char * option, argument_parser & args_parser, unsigned & n) { +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()) { n = atoi(s.c_str()); @@ -439,7 +432,8 @@ bool get_int_from_args_parser(const char * option, argument_parser & args_parser return false; } -bool get_double_from_args_parser(const char * option, argument_parser & args_parser, double & n) { +bool get_double_from_args_parser(const char *option, + argument_parser &args_parser, double &n) { std::string s = args_parser.get_option_value(option); if (!s.empty()) { n = atof(s.c_str()); @@ -448,196 +442,18 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par return false; } +void get_time_limit_and_max_iters_from_parser( + argument_parser &args_parser, unsigned &time_limit); // forward definition +int get_random_rows() { return 5 + my_random() % 2; } - - -void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition - - - - - - -int get_random_rows() { - return 5 + my_random() % 2; -} - -int get_random_columns() { - return 5 + my_random() % 3; -} +int get_random_columns() { return 5 + my_random() % 3; } int get_random_int() { - return -1 + my_random() % 2; // (1.0 + RAND_MAX); + return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -#ifndef _WINDOWS -void fill_file_names(vector &file_names, std::set & minimums) { - char *home_dir = getenv("HOME"); - if (home_dir == nullptr) { - std::cout << "cannot find home directory, don't know how to find the files"; - return; - } - std::string home_dir_str(home_dir); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l0redund.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l1.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l2.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l3.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l4.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l4fix.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/plan.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/samp2.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/murtagh.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/l0.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/AFIRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SC50B.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SC50A.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/KB2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SC105.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STOCFOR1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/ADLITTLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BLEND.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCAGR7.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SC205.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHARE2B.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/RECIPELP.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/LOTFI.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/VTP-BASE.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHARE1B.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BOEING2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BORE3D.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCORPION.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/CAPRI.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BRANDY.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCAGR25.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCTAP1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/ISRAEL.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCFXM1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BANDM.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/E226.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/AGG.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GROW7.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/ETAMACRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FINNIS.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCSD1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STANDATA.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STANDGUB.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BEACONFD.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STAIR.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STANDMPS.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GFRD-PNC.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCRS8.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BOEING1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/MODSZK1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/DEGEN2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FORPLAN.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/AGG2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/AGG3.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCFXM2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHELL.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOT4.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCSD6.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP04S.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SEBA.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GROW15.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FFFFF800.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BNL1.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PEROLD.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/QAP8.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCFXM3.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP04L.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GANGES.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCTAP2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GROW22.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP08S.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOT-WE.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/MAROS.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STOCFOR2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/25FV47.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP12S.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCSD8.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FIT1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SCTAP3.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SIERRA.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOTNOV.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/CZPROB.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FIT1D.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOT-JA.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP08L.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/BNL2.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/NESM.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/CYCLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/acc-tight5.mps"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/SHIP12L.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/DEGEN3.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GREENBEA.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/GREENBEB.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/80BAU3B.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/TRUSS.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/D2Q06C.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/WOODW.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/QAP12.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/D6CUBE.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOT.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/DFL001.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/WOOD1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FIT2P.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/PILOT87.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/STOCFOR3.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/QAP15.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/FIT2D.SIF"); - file_names.push_back(home_dir_str + "/projects/lp/src/tests/math/lp/test_files/netlib/MAROS-R7.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/FIT2P.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/DFL001.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/D2Q06C.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/80BAU3B.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/GREENBEB.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/GREENBEA.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/BNL2.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/SHIP08L.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/FIT1D.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/SCTAP3.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/SCSD8.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/SCSD6.SIF"); - minimums.insert("/projects/lp/src/tests/math/lp/test_files/netlib/MAROS-R7.SIF"); -} - -void test_out_dir(std::string out_dir) { - auto *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "creating directory " << out_dir << std::endl; -#ifdef LEAN_WINDOWS - int res = mkdir(out_dir.c_str()); -#else - int res = mkdir(out_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); -#endif - if (res) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - } - return; - } - closedir(out_dir_p); -} - -void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { - // todo: make it system independent - size_t last_slash_pos = a.find_last_of('/'); - if (last_slash_pos >= a.size()) { - std::cout << "cannot find file name in " << a << std::endl; - throw; - } - dir = a.substr(0, last_slash_pos); - // std::cout << "dir = " << dir << std::endl; - fn = a.substr(last_slash_pos + 1); - // std::cout << "fn = " << fn << std::endl; -} - - -#endif - - - -std::string read_line(bool & end, std::ifstream & file) { +std::string read_line(bool &end, std::ifstream &file) { std::string s; if (!getline(file, s)) { end = true; @@ -647,67 +463,108 @@ std::string read_line(bool & end, std::ifstream & file) { return s; } -bool contains(std::string const & s, char const * pattern) { +bool contains(std::string const &s, char const *pattern) { return s.find(pattern) != std::string::npos; } - - -void setup_args_parser(argument_parser & parser) { +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"); parser.add_option_with_help_string("-nla_cn", "test cross nornmal form"); parser.add_option_with_help_string("-nla_sim", "test nex simplify"); - parser.add_option_with_help_string("-nla_blfmz_mf", "test_basic_lemma_for_mon_zero_from_factor_to_monomial"); - parser.add_option_with_help_string("-nla_blfmz_fm", "test_basic_lemma_for_mon_zero_from_monomials_to_factor"); - parser.add_option_with_help_string("-nla_order", "test nla_solver order lemma"); - parser.add_option_with_help_string("-nla_monot", "test nla_solver order lemma"); + parser.add_option_with_help_string( + "-nla_blfmz_mf", "test_basic_lemma_for_mon_zero_from_factor_to_monomial"); + parser.add_option_with_help_string( + "-nla_blfmz_fm", + "test_basic_lemma_for_mon_zero_from_monomials_to_factor"); + parser.add_option_with_help_string("-nla_order", + "test nla_solver order lemma"); + parser.add_option_with_help_string("-nla_monot", + "test nla_solver order lemma"); parser.add_option_with_help_string("-nla_tan", "test_tangent_lemma"); parser.add_option_with_help_string("-nla_bsl", "test_basic_sign_lemma"); parser.add_option_with_help_string("-horner", "test horner's heuristic"); - parser.add_option_with_help_string("-nla_blnt_mf", "test_basic_lemma_for_mon_neutral_from_monomial_to_factors"); - parser.add_option_with_help_string("-nla_blnt_fm", "test_basic_lemma_for_mon_neutral_from_factors_to_monomial"); + parser.add_option_with_help_string( + "-nla_blnt_mf", + "test_basic_lemma_for_mon_neutral_from_monomial_to_factors"); + parser.add_option_with_help_string( + "-nla_blnt_fm", + "test_basic_lemma_for_mon_neutral_from_factors_to_monomial"); parser.add_option_with_help_string("-hnf", "test hermite normal form"); 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("--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 "); + parser.add_option_with_help_string("-xyz_sample", + "run a small interactive scenario"); + 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 "); parser.add_option_with_help_string("--smt", "smt file format"); - parser.add_option_with_after_string_with_help("--filelist", "the file containing the list of files"); - parser.add_option_with_after_string_with_help("--file", "the input file name"); + parser.add_option_with_after_string_with_help( + "--filelist", "the file containing the list of files"); + parser.add_option_with_after_string_with_help("--file", + "the input file name"); parser.add_option_with_after_string_with_help("--random_seed", "random seed"); parser.add_option_with_help_string("--bp", "bound propagation"); - parser.add_option_with_help_string("--min", "will look for the minimum for the given file if --file is used; the default is looking for the max"); - parser.add_option_with_help_string("--max", "will look for the maximum for the given file if --file is used; it is the default behavior"); - parser.add_option_with_after_string_with_help("--max_iters", "maximum total iterations in a core solver stage"); - parser.add_option_with_after_string_with_help("--time_limit", "time limit in seconds"); + parser.add_option_with_help_string( + "--min", + "will look for the minimum for the given file if --file is " + "used; the default is looking for the max"); + parser.add_option_with_help_string( + "--max", + "will look for the maximum for the given file if --file is " + "used; it is the default behavior"); + parser.add_option_with_after_string_with_help( + "--max_iters", "maximum total iterations in a core solver stage"); + 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_after_string_with_help( + "--simplex_strategy", "sets simplex strategy for rational number"); 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_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); + 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_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"); + 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("--randomize_lar", "test randomize functionality"); + parser.add_option_with_after_string_with_help( + "--maxng", "max iterations without progress"); + 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"); - parser.add_option_with_help_string("--eti"," run a small evidence test for total infeasibility scenario"); - parser.add_option_with_help_string("--row_inf", "forces row infeasibility search"); + parser.add_option_with_help_string( + "--eti", " run a small evidence test for total infeasibility scenario"); + parser.add_option_with_help_string("--row_inf", + "forces row infeasibility search"); parser.add_option_with_help_string("-pd", "presolve with double solver"); parser.add_option_with_help_string("--test_int_set", "test int_set"); parser.add_option_with_help_string("--test_mpq", "test rationals"); parser.add_option_with_help_string("--test_mpq_np", "test rationals"); - parser.add_option_with_help_string("--test_mpq_np_plus", "test rationals using plus instead of +="); + parser.add_option_with_help_string("--test_mpq_np_plus", + "test rationals using plus instead of +="); parser.add_option_with_help_string("--maximize_term", "test maximize_term()"); + parser.add_option_with_help_string("--patching", "test patching"); } -struct fff { int a; int b;}; - +struct fff { + int a; + int b; +}; void test_stacked_unsigned() { std::cout << "test stacked unsigned" << std::endl; @@ -719,24 +576,21 @@ void test_stacked_unsigned() { v = 4; v.pop(); lp_assert(v == 2); - v ++; + v++; v++; std::cout << "before push v=" << v << std::endl; v.push(); v++; v.push(); - v+=1; + v += 1; std::cout << "v = " << v << std::endl; v.pop(2); lp_assert(v == 4); - const unsigned & rr = v; - std::cout << rr << std:: endl; - + const unsigned &rr = v; + std::cout << rr << std::endl; } -void test_stacked_value() { - test_stacked_unsigned(); -} +void test_stacked_value() { test_stacked_unsigned(); } void test_stacked_vector() { std::cout << "test_stacked_vector" << std::endl; @@ -751,31 +605,29 @@ void test_stacked_vector() { v.push_back(3); v.push_back(34); v.push(); - v[1]=3; + v[1] = 3; v[2] = 3; v.push(); - v[0]= 7; + v[0] = 7; v[1] = 9; v.pop(2); if (v.size()) - v[v.size() -1 ] = 7; + v[v.size() - 1] = 7; v.push(); v.push_back(33); v[0] = 13; v.pop(); - } - void test_stacked() { test_stacked_value(); test_stacked_vector(); } -char * find_home_dir() { +char *find_home_dir() { #ifdef _WINDOWS #else - char * home_dir = getenv("HOME"); + char *home_dir = getenv("HOME"); if (home_dir == nullptr) { std::cout << "cannot find home directory" << std::endl; return nullptr; @@ -784,9 +636,8 @@ char * find_home_dir() { return nullptr; } - template -void print_chunk(T * arr, unsigned len) { +void print_chunk(T *arr, unsigned len) { for (unsigned i = 0; i < len; i++) { std::cout << arr[i] << ", "; } @@ -794,7 +645,7 @@ void print_chunk(T * arr, unsigned len) { } struct mem_cpy_place_holder { - static void mem_copy_hook(int * destination, unsigned num) { + static void mem_copy_hook(int *destination, unsigned num) { if (destination == nullptr || num == 0) { throw "bad parameters"; } @@ -803,13 +654,14 @@ struct mem_cpy_place_holder { void finalize(unsigned ret) { /* - finalize_util_module(); - finalize_numerics_module(); - */ + finalize_util_module(); + finalize_numerics_module(); + */ // return ret; } -void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit) { +void get_time_limit_and_max_iters_from_parser(argument_parser &args_parser, + unsigned &time_limit) { std::string time_limit_string = args_parser.get_option_value("--time_limit"); if (!time_limit_string.empty()) { time_limit = atoi(time_limit_string.c_str()); @@ -818,21 +670,28 @@ void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, uns } } - -std::string create_output_file_name(bool minimize, std::string file_name, bool use_mpq) { - std::string ret = file_name + "_lp_tst_" + (minimize?"min":"max"); - if (use_mpq) return ret + "_mpq.out"; +std::string create_output_file_name(bool minimize, std::string file_name, + bool use_mpq) { + std::string ret = file_name + "_lp_tst_" + (minimize ? "min" : "max"); + if (use_mpq) + return ret + "_mpq.out"; return ret + ".out"; } -std::string create_output_file_name_for_glpsol(bool minimize, std::string file_name){ - return file_name + (minimize?"_min":"_max") + "_glpk_out"; +std::string create_output_file_name_for_glpsol(bool minimize, + std::string file_name) { + return file_name + (minimize ? "_min" : "_max") + "_glpk_out"; } -int run_glpk(std::string file_name, std::string glpk_out_file_name, bool minimize, unsigned time_limit) { - std::string minmax(minimize?"--min":"--max"); - std::string tmlim = time_limit > 0 ? std::string(" --tmlim ") + std::to_string(time_limit)+ " ":std::string(); - std::string command_line = std::string("glpsol --nointopt --nomip ") + minmax + tmlim + + " -o " + glpk_out_file_name +" " + file_name + " > /dev/null"; +int run_glpk(std::string file_name, std::string glpk_out_file_name, + bool minimize, unsigned time_limit) { + std::string minmax(minimize ? "--min" : "--max"); + std::string tmlim = time_limit > 0 ? std::string(" --tmlim ") + + std::to_string(time_limit) + " " + : std::string(); + std::string command_line = std::string("glpsol --nointopt --nomip ") + + minmax + tmlim + +" -o " + glpk_out_file_name + + " " + file_name + " > /dev/null"; return system(command_line.c_str()); } @@ -857,22 +716,13 @@ std::string get_status(std::string file_name) { throw 0; } - - - struct sort_pred { - bool operator()(const std::pair &left, const std::pair &right) { + bool operator()(const std::pair &left, + const std::pair &right) { return left.second < right.second; } }; - - - - - - - vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); if (!file.is_open()) { @@ -892,17 +742,16 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } - void test_numeric_pair() { numeric_pair a; numeric_pair b(2, lp::mpq(6, 2)); a = b; numeric_pair c(0.1, 0.5); - a += 2*c; + a += 2 * c; a -= c; - lp_assert (a == b + c); + lp_assert(a == b + c); numeric_pair d = a * 2; - std::cout << a << std::endl; + std::cout << a << std::endl; lp_assert(b == b); lp_assert(b < a); lp_assert(b <= a); @@ -913,12 +762,12 @@ void test_numeric_pair() { lp_assert(a < 2 * b); lp_assert(b + b > a); lp_assert(lp::mpq(2.1) * b + b > a); - lp_assert(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); - std::cout << - b * lp::mpq(2.1) - b << std::endl; - lp_assert(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); + lp_assert(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); + std::cout << -b * lp::mpq(2.1) - b << std::endl; + lp_assert(-b * (lp::mpq(2.1) + 1) == -b * lp::mpq(2.1) - b); } -void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { +void get_matrix_dimensions(std::ifstream &f, unsigned &m, unsigned &n) { std::string line; getline(f, line); getline(f, line); @@ -929,13 +778,10 @@ void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { n = atoi(r[1].c_str()); } - void print_st(lp_status status) { std::cout << lp_status_to_string(status) << std::endl; } - - void test_term() { lar_solver solver; unsigned _x = 0; @@ -953,10 +799,10 @@ void test_term() { solver.add_var_bound(x_plus_y, lconstraint_kind::LE, mpq(14, 3)); pairs.pop_back(); pairs.push_back(std::pair(mpq(-1), y)); - unsigned x_minus_y = solver.add_term(pairs, ti++); + unsigned x_minus_y = solver.add_term(pairs, ti++); solver.add_var_bound(x_minus_y, lconstraint_kind::GE, mpq(5, 3)); solver.add_var_bound(x_minus_y, lconstraint_kind::LE, mpq(14, 3)); - auto status = solver.solve(); + auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; if (status != lp_status::OPTIMAL) { @@ -971,35 +817,36 @@ void test_term() { solver.set_int_solver(&i_s); int_cube cuber(i_s); lia_move m = cuber(); - - std::cout <<"\n" << lia_move_to_string(m) << std::endl; + + std::cout << "\n" + << lia_move_to_string(m) << std::endl; model.clear(); solver.get_model(model); - for (auto & t : model) { - std::cout << solver.get_variable_name(t.first) << " = " << t.second.get_double() << ","; + for (auto &t : model) { + std::cout << solver.get_variable_name(t.first) << " = " + << t.second.get_double() << ","; } std::cout << "\ntableu after cube\n"; solver.pp(std::cout).print(); std::cout << "Ax_is_correct = " << solver.ax_is_correct() << "\n"; - } -void test_evidence_for_total_inf_simple(argument_parser & args_parser) { +void test_evidence_for_total_inf_simple(argument_parser &args_parser) { lar_solver solver; var_index x = solver.add_var(0, false); var_index y = solver.add_var(1, false); solver.add_var_bound(x, LE, mpq(-1)); solver.add_var_bound(y, GE, mpq(0)); vector> ls; - + ls.push_back(std::pair(mpq(1), x)); ls.push_back(std::pair(mpq(1), y)); unsigned j = solver.add_term(ls, 1); solver.add_var_bound(j, GE, mpq(1)); ls.pop_back(); - ls.push_back(std::pair(- mpq(1), y)); + ls.push_back(std::pair(-mpq(1), y)); j = solver.add_term(ls, 2); solver.add_var_bound(j, GE, mpq(0)); auto status = solver.solve(); @@ -1009,21 +856,19 @@ void test_evidence_for_total_inf_simple(argument_parser & args_parser) { } void test_bound_propagation_one_small_sample1() { /* - (<= (+ a (* (- 1.0) b)) 0.0) - (<= (+ b (* (- 1.0) x_13)) 0.0) - --> (<= (+ a (* (- 1.0) c)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) c)) 0.0) - the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). - If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). - a - b <= 0.0 - b - c <= 0.0 + the inequality on (<= a c) is obtained from a triangle inequality (<= a b) + (<= b c). If b becomes basic variable, then it is likely the old solver ends + up with a row that implies (<= a c). a - b <= 0.0 b - c <= 0.0 - got to get a <= c - */ - std::function bound_is_relevant = - [&](unsigned j, bool is_lower_bound, bool strict, const rational& bound_val) { - return true; - }; + got to get a <= c + */ + std::function bound_is_relevant = + [&](unsigned j, bool is_lower_bound, bool strict, + const rational &bound_val) { return true; }; lar_solver ls; unsigned a = ls.add_var(0, false); unsigned b = ls.add_var(1, false); @@ -1048,8 +893,8 @@ void test_bound_propagation_one_small_sample1() { // ls.solve(); // my_bound_propagator bp(ls); // ls.propagate_bounds_for_touched_rows(bp); - // std::cout << " bound ev from test_bound_propagation_one_small_sample1" << std::endl; - // for (auto & be : bp.m_ibounds) { + // std::cout << " bound ev from test_bound_propagation_one_small_sample1" << + // std::endl; for (auto & be : bp.m_ibounds) { // std::cout << "bound\n"; // ls.print_implied_bound(be, std::cout); // } // todo: restore test @@ -1058,35 +903,40 @@ void test_bound_propagation_one_small_sample1() { void test_bound_propagation_one_small_samples() { test_bound_propagation_one_small_sample1(); /* - (>= x_46 0.0) - (<= x_29 0.0) - (not (<= x_68 0.0)) - (<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) - (<= (+ (* (/ 1001.0 999.0) x_9) - (* (- 1.0) x_152) - (* (/ 1001.0 999.0) x_151) - (* (/ 1001.0 999.0) x_68)) - (- (/ 1502501.0 999000.0))) - (not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) - (/ 1001.0 2.0))) - (not (<= x_153 0.0))z - (>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) - (/ 5003.0 1998.0)) - --> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) + (>= x_46 0.0) + (<= x_29 0.0) + (not (<= x_68 0.0)) + (<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 + 999.0))) + (<= (+ (* (/ 1001.0 999.0) x_9) + (* (- 1.0) x_152) + (* (/ 1001.0 999.0) x_151) + (* (/ 1001.0 999.0) x_68)) + (- (/ 1502501.0 999000.0))) + (not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) + x_151)) + (/ 1001.0 2.0))) + (not (<= x_153 0.0))z + (>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) + (/ 5003.0 1998.0)) + --> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) - and + and - (<= (+ a (* (- 1.0) b)) 0.0) - (<= (+ b (* (- 1.0) x_13)) 0.0) - --> (<= (+ a (* (- 1.0) x_13)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) x_13)) 0.0) - In the first case, there typically are no atomic formulas for bounding x_10. So there is never some - basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). - Instead the bound on x_10 falls out from a bigger blob of constraints. + In the first case, there typically are no atomic formulas for bounding x_10. + So there is never some basic lemma of the form (>= x46 0), (<= x29 0), (>= + x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). Instead the bound on x_10 falls + out from a bigger blob of constraints. - In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). - If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). - */ + In the second case, the inequality on (<= x19 x13) is obtained from a + triangle inequality (<= x19 x9) (<= x9 x13). If x9 becomes basic variable, + then it is likely the old solver ends up with a row that implies (<= x19 + x13). + */ } void test_bound_propagation_one_row() { lar_solver ls; @@ -1102,7 +952,7 @@ void test_bound_propagation_one_row() { // ls.solve(); // my_bound_propagator bp(ls); // ls.propagate_bounds_for_touched_rows(bp); -} +} void test_bound_propagation_one_row_with_bounded_vars() { lar_solver ls; unsigned x0 = ls.add_var(0, false); @@ -1134,7 +984,7 @@ void test_bound_propagation_one_row_mixed() { // ls.solve(); // my_bound_propagator bp(ls); // ls.propagate_bounds_for_touched_rows(bp); -} +} void test_bound_propagation_two_rows() { lar_solver ls; @@ -1158,7 +1008,7 @@ void test_bound_propagation_two_rows() { // ls.solve(); // my_bound_propagator bp(ls); // ls.propagate_bounds_for_touched_rows(bp); -} +} void test_total_case_u() { std::cout << "test_total_case_u\n"; @@ -1180,14 +1030,15 @@ void test_total_case_u() { // my_bound_propagator bp(ls); // ls.propagate_bounds_for_touched_rows(bp); } -bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const vector & ev) { - for (auto & e : ev) { +bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq &rs, + const vector &ev) { + for (auto &e : ev) { if (e.m_j == j && e.m_bound == rs && e.kind() == kind) return true; } return false; } -void test_total_case_l(){ +void test_total_case_l() { std::cout << "test_total_case_l\n"; lar_solver ls; unsigned x = ls.add_var(0, false); @@ -1218,7 +1069,6 @@ void test_bound_propagation() { test_bound_propagation_two_rows(); test_bound_propagation_one_row_mixed(); test_total_case_l(); - } void test_int_set() { @@ -1236,36 +1086,35 @@ void test_int_set() { s.insert(2); s.clear(); lp_assert(s.size() == 0); - - } void test_rationals_no_numeric_pairs() { stopwatch sw; vector c; - for (unsigned j = 0; j < 10; j ++) - c.push_back(mpq(my_random()%100, 1 + my_random()%100 )); - + for (unsigned j = 0; j < 10; j++) + c.push_back(mpq(my_random() % 100, 1 + my_random() % 100)); + vector x; - for (unsigned j = 0; j < 10; j ++) - x.push_back(mpq(my_random()%100, 1 + my_random()%100 )); + for (unsigned j = 0; j < 10; j++) + x.push_back(mpq(my_random() % 100, 1 + my_random() % 100)); unsigned k = 500000; - mpq r=zero_of_type(); + mpq r = zero_of_type(); sw.start(); - - for (unsigned j = 0; j < k; j++){ + + for (unsigned j = 0; j < k; j++) { mpq val = zero_of_type(); - for (unsigned j=0;j< c.size(); j++){ - val += c[j]*x[j]; + for (unsigned j = 0; j < c.size(); j++) { + val += c[j] * x[j]; } - + r += val; } - + sw.stop(); - std::cout << "operation with rationals no pairs " << sw.get_seconds() << std::endl; + std::cout << "operation with rationals no pairs " << sw.get_seconds() + << std::endl; std::cout << T_to_string(r) << std::endl; } @@ -1273,64 +1122,62 @@ void test_rationals_no_numeric_pairs_plus() { stopwatch sw; vector c; - for (unsigned j = 0; j < 10; j ++) - c.push_back(mpq(my_random()%100, 1 + my_random()%100 )); - + for (unsigned j = 0; j < 10; j++) + c.push_back(mpq(my_random() % 100, 1 + my_random() % 100)); + vector x; - for (unsigned j = 0; j < 10; j ++) - x.push_back(mpq(my_random()%100, 1 + my_random()%100 )); + for (unsigned j = 0; j < 10; j++) + x.push_back(mpq(my_random() % 100, 1 + my_random() % 100)); unsigned k = 500000; - mpq r=zero_of_type(); + mpq r = zero_of_type(); sw.start(); - - for (unsigned j = 0; j < k; j++){ + + for (unsigned j = 0; j < k; j++) { mpq val = zero_of_type(); - for (unsigned j=0;j< c.size(); j++){ - val = val + c[j]*x[j]; + for (unsigned j = 0; j < c.size(); j++) { + val = val + c[j] * x[j]; } - + r = r + val; } - + sw.stop(); - std::cout << "operation with rationals no pairs " << sw.get_seconds() << std::endl; + std::cout << "operation with rationals no pairs " << sw.get_seconds() + << std::endl; std::cout << T_to_string(r) << std::endl; } - - void test_rationals() { stopwatch sw; vector c; - for (unsigned j = 0; j < 10; j ++) - c.push_back(rational(my_random()%100, 1 + my_random()%100)); + for (unsigned j = 0; j < 10; j++) + c.push_back(rational(my_random() % 100, 1 + my_random() % 100)); - - vector> x; - for (unsigned j = 0; j < 10; j ++) - x.push_back(numeric_pair(rational(my_random()%100, 1 + my_random()%100 ))); + for (unsigned j = 0; j < 10; j++) + x.push_back(numeric_pair( + rational(my_random() % 100, 1 + my_random() % 100))); std::cout << "x = "; print_vector(x, std::cout); - + unsigned k = 1000000; - numeric_pair r=zero_of_type>(); + numeric_pair r = zero_of_type>(); sw.start(); - + for (unsigned j = 0; j < k; j++) { for (unsigned i = 0; i < c.size(); i++) { - r+= c[i] * x[i]; + r += c[i] * x[i]; } - } + } sw.stop(); std::cout << "operation with rationals " << sw.get_seconds() << std::endl; std::cout << T_to_string(r) << std::endl; } -void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { +void get_random_interval(bool &neg_inf, bool &pos_inf, int &x, int &y) { int i = my_random() % 10; if (i == 0) { neg_inf = true; @@ -1346,29 +1193,29 @@ void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { if (!neg_inf) { y = x + my_random() % (101 - x); lp_assert(y >= x); - } - else { + } else { y = my_random() % 100; } } - lp_assert((neg_inf || (0 <= x && x <= 100)) && (pos_inf || (0 <= y && y <= 100))); + lp_assert((neg_inf || (0 <= x && x <= 100)) && + (pos_inf || (0 <= y && y <= 100))); } - void test_gomory_cut_0() { gomory_test g( - [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + [](unsigned j) { return "v" + T_to_string(j); } // name_function_p , - [](unsigned j) { //get_value_p + [](unsigned j) { // get_value_p if (j == 1) return mpq(2730, 1727); if (j == 2) return zero_of_type(); - if (j == 3) return mpq(3); + if (j == 3) + return mpq(3); UNREACHABLE(); return zero_of_type(); }, - [](unsigned j) { // at_low_p + [](unsigned j) { // at_low_p if (j == 1) return false; if (j == 2) @@ -1378,7 +1225,7 @@ void test_gomory_cut_0() { UNREACHABLE(); return false; }, - [](unsigned j) { // at_upper + [](unsigned j) { // at_upper if (j == 1) return false; if (j == 2) @@ -1388,9 +1235,9 @@ void test_gomory_cut_0() { UNREACHABLE(); return false; }, - [](unsigned j) { // lower_bound + [](unsigned j) { // lower_bound if (j == 1) { - UNREACHABLE(); //unlimited from below + UNREACHABLE(); // unlimited from below return impq(0); } if (j == 2) @@ -1400,9 +1247,9 @@ void test_gomory_cut_0() { UNREACHABLE(); return impq(0); }, - [](unsigned j) { // upper + [](unsigned j) { // upper if (j == 1) { - UNREACHABLE(); //unlimited from above + UNREACHABLE(); // unlimited from above return impq(0); } if (j == 2) @@ -1412,9 +1259,7 @@ void test_gomory_cut_0() { UNREACHABLE(); return impq(0); }, - [] (unsigned) { return 0; }, - [] (unsigned) { return 0; } - ); + [](unsigned) { return 0; }, [](unsigned) { return 0; }); lar_term t; mpq k; explanation expl; @@ -1423,14 +1268,14 @@ void test_gomory_cut_0() { row.push_back(std::make_pair(mpq(1), 1)); row.push_back(std::make_pair(mpq(2731, 1727), 2)); row.push_back(std::make_pair(mpq(-910, 1727), 3)); - g.mk_gomory_cut(t, k, expl, inf_col, row); + g.mk_gomory_cut(t, k, expl, inf_col, row); } void test_gomory_cut_1() { gomory_test g( - [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + [](unsigned j) { return "v" + T_to_string(j); } // name_function_p , - [](unsigned j) { //get_value_p + [](unsigned j) { // get_value_p if (j == 1) return mpq(-2); if (j == 2) @@ -1440,7 +1285,7 @@ void test_gomory_cut_1() { UNREACHABLE(); return zero_of_type(); }, - [](unsigned j) { // at_low_p + [](unsigned j) { // at_low_p if (j == 1) return false; if (j == 2) @@ -1450,7 +1295,7 @@ void test_gomory_cut_1() { UNREACHABLE(); return false; }, - [](unsigned j) { // at_upper + [](unsigned j) { // at_upper if (j == 1) return true; if (j == 2) @@ -1460,9 +1305,9 @@ void test_gomory_cut_1() { UNREACHABLE(); return false; }, - [](unsigned j) { // lower_bound + [](unsigned j) { // lower_bound if (j == 1) { - UNREACHABLE(); //unlimited from below + UNREACHABLE(); // unlimited from below return impq(0); } if (j == 2) @@ -1472,7 +1317,7 @@ void test_gomory_cut_1() { UNREACHABLE(); return impq(0); }, - [](unsigned j) { // upper + [](unsigned j) { // upper if (j == 1) { return impq(-2); } @@ -1483,9 +1328,7 @@ void test_gomory_cut_1() { UNREACHABLE(); return impq(0); }, - [] (unsigned) { return 0; }, - [] (unsigned) { return 0; } - ); + [](unsigned) { return 0; }, [](unsigned) { return 0; }); lar_term t; mpq k; explanation expl; @@ -1494,10 +1337,10 @@ void test_gomory_cut_1() { row.push_back(std::make_pair(mpq(1726667, 2730001), 1)); row.push_back(std::make_pair(mpq(-910000, 2730001), 3)); row.push_back(std::make_pair(mpq(1), 2)); - g.mk_gomory_cut(t, k, expl, inf_col, row); + g.mk_gomory_cut(t, k, expl, inf_col, row); } -void call_hnf(general_matrix & A); +void call_hnf(general_matrix &A); void test_hnf_m_less_than_n() { #ifdef Z3DEBUG @@ -1547,7 +1390,6 @@ void test_hnf_m_greater_than_n() { #endif } - void cutting_the_mix_example_1() { mpq sev(7); mpq nine(9); @@ -1560,9 +1402,9 @@ void cutting_the_mix_example_1() { hnf_calc::extended_gcd_minimal_uv(-nine, -nine, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; - hnf_calc::extended_gcd_minimal_uv(-sev*2, sev, d, u, vv); + hnf_calc::extended_gcd_minimal_uv(-sev * 2, sev, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; - + hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(-7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(-mpq(24), mpq(7), d, u, vv); @@ -1578,7 +1420,7 @@ void cutting_the_mix_example_1() { #ifdef Z3DEBUG -void fill_general_matrix(general_matrix & M) { +void fill_general_matrix(general_matrix &M) { unsigned m = M.row_count(); unsigned n = M.column_count(); for (unsigned i = 0; i < m; i++) @@ -1586,14 +1428,14 @@ void fill_general_matrix(general_matrix & M) { M[i][j] = mpq(static_cast(my_random() % 13) - 6); } -void call_hnf(general_matrix& A) { +void call_hnf(general_matrix &A) { svector r; - mpq d = hnf_calc::determinant_of_rectangular_matrix(A, r, mpq((int)1000000000)); + mpq d = + hnf_calc::determinant_of_rectangular_matrix(A, r, mpq((int)1000000000)); A.shrink_to_rank(r); hnf h(A, d); } - void test_hnf_for_dim(int m) { general_matrix M(m, m + my_random() % m); fill_general_matrix(M); @@ -1643,7 +1485,7 @@ void test_hnf_3_3() { v.push_back(mpq(-4)); v.push_back(mpq(-3)); A.push_row(v); - + call_hnf(A); std::cout << "test_hnf_3_3 passed" << std::endl; } @@ -1768,7 +1610,7 @@ void test_larger_generated_hnf() { void test_maximize_term() { std::cout << "test_maximize_term\n"; lar_solver solver; - int_solver i_solver(solver); // have to create it too + int_solver i_solver(solver); // have to create it too unsigned _x = 0; unsigned _y = 1; var_index x = solver.add_var(_x, false); @@ -1780,9 +1622,9 @@ void test_maximize_term() { term_ls.clear(); term_ls.push_back(std::pair(mpq(2), x)); term_ls.push_back(std::pair(mpq(2), y)); - + unsigned term_2x_pl_2y = solver.add_term(term_ls, -1); - solver.add_var_bound(term_x_min_y, LE, zero_of_type()); + solver.add_var_bound(term_x_min_y, LE, zero_of_type()); solver.add_var_bound(term_2x_pl_2y, LE, mpq(5)); solver.find_feasible_solution(); lp_assert(solver.get_status() == lp_status::OPTIMAL); @@ -1790,7 +1632,7 @@ void test_maximize_term() { std::unordered_map model; solver.get_model(model); for (auto p : model) { - std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + std::cout << "v[" << p.first << "] = " << p.second << std::endl; } std::cout << "calling int_solver\n"; explanation ex; @@ -1798,14 +1640,13 @@ void test_maximize_term() { VERIFY(lm == lia_move::sat); impq term_max; lp_status st = solver.maximize_term(term_2x_pl_2y, term_max); - + std::cout << "status = " << lp_status_to_string(st) << std::endl; std::cout << "term_max = " << term_max << std::endl; solver.get_model(model); for (auto p : model) { - std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + std::cout << "v[" << p.first << "] = " << p.second << std::endl; } - } #ifdef Z3DEBUG void test_hnf() { @@ -1816,7 +1657,7 @@ void test_hnf() { test_hnf_4_4(); test_hnf_5_5(); test_hnf_2_2(); - for (unsigned k=1000; k>0; k--) + for (unsigned k = 1000; k > 0; k--) for (int i = 1; i < 8; i++) test_hnf_for_dim(i); cutting_the_mix_example_1(); @@ -1829,13 +1670,9 @@ void test_gomory_cut() { test_gomory_cut_1(); } -void test_nla_order_lemma() { - nla::test_order_lemma(); -} +void test_nla_order_lemma() { nla::test_order_lemma(); } - -void test_lp_local(int argn, char**argv) { - +void test_lp_local(int argn, char **argv) { // initialize_util_module(); // initialize_numerics_module(); int ret; @@ -1854,7 +1691,10 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - + if (args_parser.option_is_used("--patching")) { + test_patching(); + return finalize(0); + } if (args_parser.option_is_used("-nla_cn")) { #ifdef Z3DEBUG nla::test_cn(); @@ -1874,7 +1714,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("-nla_order")) { #ifdef Z3DEBUG test_nla_order_lemma(); @@ -1882,7 +1721,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("-nla_monot")) { #ifdef Z3DEBUG nla::test_monotone_lemma(); @@ -1890,68 +1728,67 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("-nla_bsl")) { + if (args_parser.option_is_used("-nla_bsl")) { #ifdef Z3DEBUG nla::test_basic_sign_lemma(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_horner")) { + if (args_parser.option_is_used("-nla_horner")) { #ifdef Z3DEBUG nla::test_horner(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_tan")) { + if (args_parser.option_is_used("-nla_tan")) { #ifdef Z3DEBUG nla::test_tangent_lemma(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_blfmz_mf")) { + if (args_parser.option_is_used("-nla_blfmz_mf")) { #ifdef Z3DEBUG nla::test_basic_lemma_for_mon_zero_from_monomial_to_factors(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_blfmz_fm")) { + if (args_parser.option_is_used("-nla_blfmz_fm")) { #ifdef Z3DEBUG nla::test_basic_lemma_for_mon_zero_from_factors_to_monomial(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_blnt_mf")) { + if (args_parser.option_is_used("-nla_blnt_mf")) { #ifdef Z3DEBUG nla::test_basic_lemma_for_mon_neutral_from_monomial_to_factors(); #endif return finalize(0); } - if (args_parser.option_is_used("-nla_blnt_fm")) { + if (args_parser.option_is_used("-nla_blnt_fm")) { #ifdef Z3DEBUG nla::test_basic_lemma_for_mon_neutral_from_factors_to_monomial(); #endif return finalize(0); } - + if (args_parser.option_is_used("-hnf")) { #ifdef Z3DEBUG test_hnf(); #endif return finalize(0); } - + if (args_parser.option_is_used("-gomory")) { test_gomory_cut(); return finalize(0); } - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); return finalize(0); @@ -1960,10 +1797,120 @@ void test_lp_local(int argn, char**argv) { test_bound_propagation(); return finalize(0); } - - return finalize(0); // has_violations() ? 1 : 0); + + return finalize(0); // has_violations() ? 1 : 0); } -} -void tst_lp(char ** argv, int argc, int& i) { +} // namespace lp +void tst_lp(char **argv, int argc, int &i) { lp::test_lp_local(argc - 2, argv + 2); } +// clang-format on +bool coprime(int a, int b) { + return gcd(rational(a), rational(b)).is_one(); +} +bool coprime(rational &a, rational &b) { + return gcd(a, b).is_one(); +} +void asserts_on_patching(const rational &x, const rational &alpha) { + auto a1 = numerator(alpha); + auto a2 = denominator(alpha); + auto x1 = numerator(x); + auto x2 = denominator(x); + lp_assert(!a1.is_zero()); + lp_assert(abs(a1) < abs(a2)); + lp_assert(coprime(a1, a2)); + lp_assert(!x1.is_zero()); + lp_assert(abs(x1) < abs(x2)); + lp_assert(coprime(x1, x2)); + lp_assert((a2 / x2).is_int()); +} +bool get_patching_deltas(const rational &x, const rational &alpha, rational &delta_0, rational &delta_1) { + std::cout << "get_patching_deltas(" << x << ", " << alpha << ")" << std::endl; + auto a1 = numerator(alpha); + auto a2 = denominator(alpha); + auto x1 = numerator(x); + auto x2 = denominator(x); + // delta has to be integral. + // We need to find delta such that x1/x2 + (a1/a2)*delta is integral. + // Then a2*x1/x2 + a1*delta is integral, that means that t = a2/x2 is integral. + // We established that a2 = x2*t + // Then x1 + a1*delta*(x2/a2) = x1 + a1*(delta/t) is integral. Taking into account + // that t and a1 are coprime we have delta = t*k, where k is an integer. + rational t = a2 / x2; + std::cout << "t = " << t << std::endl; + // Now we have x1/x2 + (a1/x2)*k is integral, or (x1 + a1*k)/x2 is integral. + // It is equivalent to x1 + a1*k = x2*m, where m is an integer + // We know that a2 and a1 are coprime, and x2 divides a2, so x2 and a1 are coprime. + rational u, v; + auto g = gcd(a1, x2, u, v); + lp_assert(g.is_one() && u.is_int() && v.is_int() && g == u * a1 + v * x2); + std::cout << "u = " << u << ", v = " << v << std::endl; + std::cout << "x= " << (x1 / x2) << std::endl; + std::cout << "x + (a1 / a2) * (-u * t) * x1 = " << x + (a1 / a2) * (-u * t) * x1 << std::endl; + lp_assert((x + (a1 / a2) * (-u * t) * x1).is_int()); + // 1 = (u- l*x2 ) * a1 + (v + l*a1)*x2, for every integer l. + rational l_low, l_high; + auto sign = u.is_pos() ? 1 : -1; + auto p = sign * floor(abs(u / x2)); + auto p_ = p + sign; + lp_assert(p * x2 <= u && u <= p_ * x2 || p * x2 >= u && u >= p_ * x2); + std::cout << "u = " << u << ", v = " << v << std::endl; + std::cout << "p = " << p << ", p_ = " << p_ << std::endl; + std::cout << "u - p*x2 = " << u - p * x2 << ", u - p_*x2 = " << u - p_ * x2 << std::endl; + delta_0 = (u - p * x2) * t * x1; + delta_1 = (u - p_ * x2) * t * x1; + + std::cout << "delta_0 = " << delta_0 << std::endl; + std::cout << "delta_1 = " << delta_1 << std::endl; + + return true; +} +void test_patching_alpha(const rational &x, const rational &alpha) { + std::cout << "\nstart patching x = " << x << ", alpha = " << alpha << "\n"; + asserts_on_patching(x, alpha); + rational delta_0, delta_1; + bool r = get_patching_deltas(x, alpha, delta_0, delta_1); + if (r) { + lp_assert(delta_0 * delta_1 <= 0); + + lp_assert((x - alpha * delta_0).is_int()); + lp_assert((x - alpha * delta_1).is_int()); + + std::cout << "success\n"; + // std::cout << "delta_minus = " << delta_minus << ", delta_1 = " << delta_1 << "\n"; + // std::cout << "x + alpha*delta_minus = " << x + alpha * delta_minus << "\n"; + // std::cout << "x + alpha*delta_1 = " << x + alpha * delta_1 << "\n"; + } +} + +void find_a1_x1_x2_and_fix_a2(int &x1, int &x2, int &a1, int &a2) { + x2 = (rand() % a2) + (int)(a2 / 3); + auto g = gcd(rational(a2), rational(x2)); + a2 *= (x2 / numerator(g).get_uint64()); + lp_assert(rational(a2, x2).is_int()); + do { + x1 = rand() % (unsigned)x2 + 1; + } while (!coprime(x1, x2)); + + do { + a1 = rand() % (unsigned)a2 + 1; + } while (!coprime(a1, a2)); + x1 *= (rand() % 2 == 0 ? 1 : -1); + a1 *= (rand() % 2 == 0 ? 1 : -1); +} + +void test_patching() { + srand(1); + // repeat the test 100 times + + int range = 40; + for (int i = 0; i < 100; i++) { + int a1; + int a2 = std::max((int)rand() % range, (int)range / 3); + + int x1, x2; + find_a1_x1_x2_and_fix_a2(x1, x2, a1, a2); + + test_patching_alpha(rational(x1, x2), rational(a1, a2)); + } +} \ No newline at end of file From f5d9ffaca1f149a3673caf1bccfac8d8a548bfb6 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 30 Jun 2023 11:57:42 -0700 Subject: [PATCH 17/41] clean up and add clang-format off Signed-off-by: Lev Nachmanson --- src/math/lp/gomory.cpp | 1 + src/math/lp/gomory.h | 1 + src/math/lp/int_branch.cpp | 4 ++-- src/math/lp/int_solver.cpp | 3 --- src/smt/theory_arith_int.h | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2ecbc49ac..babb10d72 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -17,6 +17,7 @@ --*/ +// clang-format off #include "math/lp/gomory.h" #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" diff --git a/src/math/lp/gomory.h b/src/math/lp/gomory.h index 68e42feb9..acca900df 100644 --- a/src/math/lp/gomory.h +++ b/src/math/lp/gomory.h @@ -15,6 +15,7 @@ Author: Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lar_term.h" #include "math/lp/lia_move.h" diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index da34f77fd..47a9ddcbe 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -15,7 +15,7 @@ Revision History: --*/ - +// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" #include "math/lp/int_branch.h" @@ -63,7 +63,7 @@ int int_branch::find_inf_int_base_column() { 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 prev_usage = 0; // to quiet down the compiler unsigned k = 0; unsigned usage; unsigned j; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index af4488162..1c13ec25d 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -878,9 +878,6 @@ int int_solver::select_int_infeasible_var() { 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 (unsigned j : lra.r_basis()) { if (!column_is_int_inf(j)) continue; diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index c9bc9f31a..e89b81674 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -16,6 +16,7 @@ Author: Revision History: --*/ +// clang-format off #pragma once #include "util/numeral_buffer.h" From 61948fa1ffc04de735a2cf93815fde6bb5ae8404 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 1 Jul 2023 07:48:07 -0700 Subject: [PATCH 18/41] find minimal deltas in patching Signed-off-by: Lev Nachmanson --- src/math/lp/int_solver.cpp | 54 ++++++++++++------------------ src/test/lp/lp.cpp | 67 +++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 1c13ec25d..45c458ae6 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -70,9 +70,9 @@ namespace lp { // clang-format on /** * \brief find integral and minimal, in the absolute values, deltas such that x - alpha*delta is integral too. - */ + */ bool get_patching_deltas(const rational& x, const rational& alpha, - rational& delta_0, rational& delta_1) { + rational& delta_plus, rational& delta_minus) { auto a1 = numerator(alpha); auto a2 = denominator(alpha); auto x1 = numerator(x); @@ -102,36 +102,21 @@ namespace lp { // << x + (a1 / a2) * (-u * t) * x1 << std::endl; lp_assert((x + (a1 / a2) * (-u * t) * x1).is_int()); // 1 = (u- l*x2 ) * a1 + (v + l*a1)*x2, for every integer l. - rational l_low, l_high; - auto sign = u.is_pos() ? 1 : -1; - auto p = sign * floor(abs(u / x2)); - auto p_ = p + sign; - lp_assert(p * x2 <= u && u <= p_ * x2 || p * x2 >= u && u >= p_ * x2); - // std::cout << "u = " << u << ", v = " << v << std::endl; - // std::cout << "p = " << p << ", p_ = " << p_ << std::endl; - // std::cout << "u - p*x2 = " << u - p * x2 << ", u - p_*x2 = " << u - p_ * x2 - // << std::endl; - mpq d_0 = (u - p * x2) * t * x1; - mpq d_1 = (u - p_ * x2) * t * x1; - if (abs(d_0) < abs(d_1)) { - delta_0 = d_0; - delta_1 = d_1; - } else { - delta_0 = d_1; - delta_1 = d_0; - } + rational d = u * t * x1; + delta_plus = mod(d, a2); + lp_assert(delta_plus > 0); + delta_minus = delta_plus - a2; + lp_assert(delta_minus < 0); + return true; - // std::cout << "delta_0 = " << delta_0 << std::endl; - // std::cout << "delta_1 = " << delta_1 << std::endl; } - // clang-format off /** * \brief try to patch the basic column v */ bool int_solver::patcher::patch_basic_column_on_row_cell(unsigned v, row_cell const& c) { if (v == c.var()) return false; - if (!lra.column_is_int(c.var())) // could use real to patch integer + if (!lra.column_is_int(c.var())) // could use real to patch integer return false; if (c.coeff().is_int()) return false; @@ -139,19 +124,20 @@ namespace lp { mpq r = fractional_part(lra.get_value(v)); lp_assert(0 < r && r < 1); lp_assert(0 < a && a < 1); - mpq delta_0, delta_1; - if (!get_patching_deltas(r, a, delta_0, delta_1)) + mpq delta_plus, delta_minus; + if (!get_patching_deltas(r, a, delta_plus, delta_minus)) return false; - - if (try_patch_column(v, c.var(), delta_0)) - return true; - if (try_patch_column(v, c.var(), delta_1)) - return true; - - return false; + if (lia.random() % 2) { + return try_patch_column(v, c.var(), delta_plus) || + try_patch_column(v, c.var(), delta_minus); + } else { + return try_patch_column(v, c.var(), delta_minus) || + try_patch_column(v, c.var(), delta_plus); + } } - + // clang-format off + bool int_solver::patcher::try_patch_column(unsigned v, unsigned j, mpq const& delta) { const auto & A = lra.A_r(); if (delta < 0) { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 750eaefb1..a61b9339e 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1816,20 +1816,21 @@ void asserts_on_patching(const rational &x, const rational &alpha) { auto a2 = denominator(alpha); auto x1 = numerator(x); auto x2 = denominator(x); - lp_assert(!a1.is_zero()); + lp_assert(a1.is_pos()); lp_assert(abs(a1) < abs(a2)); lp_assert(coprime(a1, a2)); - lp_assert(!x1.is_zero()); - lp_assert(abs(x1) < abs(x2)); + lp_assert(x1.is_pos()); + lp_assert(x1 < x2); lp_assert(coprime(x1, x2)); lp_assert((a2 / x2).is_int()); } -bool get_patching_deltas(const rational &x, const rational &alpha, rational &delta_0, rational &delta_1) { +void get_patching_deltas(const rational &x, const rational &alpha, rational &delta_0, rational &delta_1) { std::cout << "get_patching_deltas(" << x << ", " << alpha << ")" << std::endl; auto a1 = numerator(alpha); auto a2 = denominator(alpha); auto x1 = numerator(x); auto x2 = denominator(x); + lp_assert(divides(x2, a2)); // delta has to be integral. // We need to find delta such that x1/x2 + (a1/a2)*delta is integral. // Then a2*x1/x2 + a1*delta is integral, that means that t = a2/x2 is integral. @@ -1849,44 +1850,52 @@ bool get_patching_deltas(const rational &x, const rational &alpha, rational &del std::cout << "x + (a1 / a2) * (-u * t) * x1 = " << x + (a1 / a2) * (-u * t) * x1 << std::endl; lp_assert((x + (a1 / a2) * (-u * t) * x1).is_int()); // 1 = (u- l*x2 ) * a1 + (v + l*a1)*x2, for every integer l. - rational l_low, l_high; - auto sign = u.is_pos() ? 1 : -1; - auto p = sign * floor(abs(u / x2)); - auto p_ = p + sign; - lp_assert(p * x2 <= u && u <= p_ * x2 || p * x2 >= u && u >= p_ * x2); - std::cout << "u = " << u << ", v = " << v << std::endl; - std::cout << "p = " << p << ", p_ = " << p_ << std::endl; - std::cout << "u - p*x2 = " << u - p * x2 << ", u - p_*x2 = " << u - p_ * x2 << std::endl; - delta_0 = (u - p * x2) * t * x1; - delta_1 = (u - p_ * x2) * t * x1; - + rational d = u * t * x1; + delta_0 = mod(d, a2); + lp_assert(delta_0 > 0); + delta_1 = delta_0 - a2; + lp_assert(delta_1 < 0); std::cout << "delta_0 = " << delta_0 << std::endl; std::cout << "delta_1 = " << delta_1 << std::endl; - - return true; } + +void try_find_smaller_delta(const rational &x, const rational &alpha, rational &delta_0, rational &delta_1) { + auto a1 = numerator(alpha); + auto a2 = denominator(alpha); + auto x1 = numerator(x); + auto x2 = denominator(x); + rational delta_minus, delta_plus; + auto del_min = delta_0 < delta_1 ? delta_0 : delta_1; + auto del_plus = delta_0 < delta_1 ? delta_1 : delta_0; + for (auto i = del_min + rational(1); i < del_plus; i += 1) { + if ((x - alpha * i).is_int()) { + std::cout << "found smaller delta = " << i << std::endl; + std::cout << "i - del_min = " << i - del_min << std::endl; + std::cout << "x - alpha*i = " << x - alpha * i << std::endl; + } + } +} + void test_patching_alpha(const rational &x, const rational &alpha) { std::cout << "\nstart patching x = " << x << ", alpha = " << alpha << "\n"; asserts_on_patching(x, alpha); rational delta_0, delta_1; - bool r = get_patching_deltas(x, alpha, delta_0, delta_1); - if (r) { - lp_assert(delta_0 * delta_1 <= 0); + get_patching_deltas(x, alpha, delta_0, delta_1); - lp_assert((x - alpha * delta_0).is_int()); - lp_assert((x - alpha * delta_1).is_int()); + lp_assert(delta_0 * delta_1 < 0); - std::cout << "success\n"; - // std::cout << "delta_minus = " << delta_minus << ", delta_1 = " << delta_1 << "\n"; - // std::cout << "x + alpha*delta_minus = " << x + alpha * delta_minus << "\n"; - // std::cout << "x + alpha*delta_1 = " << x + alpha * delta_1 << "\n"; - } + lp_assert((x - alpha * delta_0).is_int()); + lp_assert((x - alpha * delta_1).is_int()); + try_find_smaller_delta(x, alpha, delta_0, delta_1); + // std::cout << "delta_minus = " << delta_minus << ", delta_1 = " << delta_1 << "\n"; + // std::cout << "x + alpha*delta_minus = " << x + alpha * delta_minus << "\n"; + // std::cout << "x + alpha*delta_1 = " << x + alpha * delta_1 << "\n"; } void find_a1_x1_x2_and_fix_a2(int &x1, int &x2, int &a1, int &a2) { x2 = (rand() % a2) + (int)(a2 / 3); auto g = gcd(rational(a2), rational(x2)); - a2 *= (x2 / numerator(g).get_uint64()); + a2 *= (x2 / numerator(g).get_int32()); lp_assert(rational(a2, x2).is_int()); do { x1 = rand() % (unsigned)x2 + 1; @@ -1895,8 +1904,6 @@ void find_a1_x1_x2_and_fix_a2(int &x1, int &x2, int &a1, int &a2) { do { a1 = rand() % (unsigned)a2 + 1; } while (!coprime(a1, a2)); - x1 *= (rand() % 2 == 0 ? 1 : -1); - a1 *= (rand() % 2 == 0 ? 1 : -1); } void test_patching() { From d9e7b8c21f89c2dc536e401735efa8737c1853be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 3 Jul 2023 19:26:12 +0200 Subject: [PATCH 19/41] fixes to trim Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/proof_cmds.cpp | 28 ++++-- src/sat/sat_integrity_checker.cpp | 4 +- src/sat/sat_integrity_checker.h | 1 + src/sat/sat_proof_trim.cpp | 116 ++++++++++++++++++---- src/sat/sat_proof_trim.h | 5 +- src/sat/sat_solver.cpp | 3 +- 6 files changed, 123 insertions(+), 34 deletions(-) diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index 49a8da09c..0965439ae 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -63,6 +63,7 @@ class proof_trim { vector m_clauses; bool_vector m_is_infer; symbol m_rup; + bool m_empty = false; void mk_clause(expr_ref_vector const& clause) { trim.init_clause(); @@ -121,25 +122,32 @@ public: */ void infer(expr_ref_vector const& clause, app* hint) { + if (m_empty) + return; + if (hint && !is_rup(hint) && m_checker.check(hint)) { auto clause1 = m_checker.clause(hint); if (clause1.size() != clause.size()) { mk_clause(clause1); - trim.assume(m_clauses.size()); clause1.push_back(hint); + trim.assume(m_clauses.size()); m_clauses.push_back(clause1); - m_is_infer.push_back(true); - mk_clause(clause); - trim.infer(m_clauses.size()); - m_clauses.push_back(clause); - m_clauses.back().push_back(hint); - m_is_infer.push_back(true); - if (clause.empty()) + m_is_infer.push_back(false); + + if (clause.empty()) { + mk_clause(clause); + trim.infer(m_clauses.size()); + m_clauses.push_back(clause); + m_clauses.back().push_back(hint); + m_is_infer.push_back(true); + m_empty = true; do_trim(std::cout); + } return; } } + mk_clause(clause); if (is_rup(hint)) trim.infer(m_clauses.size()); @@ -149,8 +157,10 @@ public: if (hint) m_clauses.back().push_back(hint); m_is_infer.push_back(true); - if (clause.empty()) + if (clause.empty()) { + m_empty = true; do_trim(std::cout); + } } void updt_params(params_ref const& p) { diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index 031ce9202..a8de05e0f 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -28,7 +28,7 @@ namespace sat { } // for nary clauses - static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { + bool integrity_checker::contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) const { for (watched const& w : wlist) { if (w.is_clause()) { if (w.get_clause_offset() == cls_off) { @@ -38,6 +38,8 @@ namespace sat { } } } + TRACE("sat", tout << "clause " << c << " not found in watch-list\n"); + TRACE("sat", s.display_watches(tout)); UNREACHABLE(); return false; } diff --git a/src/sat/sat_integrity_checker.h b/src/sat/sat_integrity_checker.h index 22c8d0f5e..bc7fecf68 100644 --- a/src/sat/sat_integrity_checker.h +++ b/src/sat/sat_integrity_checker.h @@ -25,6 +25,7 @@ Revision History: namespace sat { class integrity_checker { solver const & s; + bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) const; public: integrity_checker(solver const & s); diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index df55aecd7..ee35efe09 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -32,21 +32,33 @@ namespace sat { unsigned_vector proof_trim::trim() { unsigned_vector result; m_core_literals.reset(); + m_propagated.resize(num_vars(), false); m_core_literals.insert(literal_vector()); - m_propagated.resize(num_vars(), false); - for (unsigned i = m_trail.size(); i-- > 0; ) { + m_core_literals.insert(m_conflict); + conflict_analysis_core(m_conflict, m_conflict_clause); + + IF_VERBOSE(10, s.display(verbose_stream() << "trim\n")); + + auto const& [id, cl, clp, is_add, is_initial] = m_trail.back(); + SASSERT(cl.empty()); + result.push_back(id); + m_trail.pop_back(); + + + for (unsigned i = m_trail.size(); i-- > 0; ) { auto const& [id, cl, clp, is_add, is_initial] = m_trail[i]; if (!is_add) { revive(cl, clp); continue; - } + } IF_VERBOSE(10, s.display(verbose_stream())); prune_trail(cl, clp); - IF_VERBOSE(10, verbose_stream() << cl << " " << in_core(cl, clp) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} "); IF_VERBOSE(10, s.display(verbose_stream() << "\n")); del(cl, clp); - if (!in_core(cl, clp)) + if (!in_core(cl, clp)) continue; + IF_VERBOSE(4, verbose_stream() << cl << " in-core " << in_core(cl, clp) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} "; verbose_stream() << "\n"); + result.push_back(id); if (is_initial) continue; @@ -57,6 +69,7 @@ namespace sat { } void proof_trim::del(literal_vector const& cl, clause* cp) { + CTRACE("sat", cp, tout << "del " << *cp << "\n"); if (cp) s.detach_clause(*cp); else @@ -90,6 +103,8 @@ namespace sat { void proof_trim::prune_trail(literal_vector const& cl, clause* cp) { m_in_clause.reset(); m_in_coi.reset(); + + // verbose_stream() << "prune trail " << cl << "\n"; if (cl.empty()) return; @@ -120,13 +135,23 @@ namespace sat { auto js = s.get_justification(l); bool in_coi = false; + //verbose_stream() << l << " " << js << "\n"; if (js.is_clause()) - for (literal lit : s.get_clause(j)) + for (literal lit : s.get_clause(js)) in_coi |= m_in_coi.contains(lit.index()); else if (js.is_binary_clause()) in_coi = m_in_coi.contains(js.get_literal().index()); - else + else if (js.is_none()) { + verbose_stream() << "none " << js << "\n"; + } + else if (js.is_ext_justification()) { + verbose_stream() << js << "\n"; UNREACHABLE(); // approach does not work for external justifications + } + else { + verbose_stream() << js << "\n"; + UNREACHABLE(); // approach does not work for external justifications + } if (in_coi) unassign_literal(l); @@ -134,6 +159,7 @@ namespace sat { s.m_trail[j++] = s.m_trail[i]; } s.m_trail.shrink(j); + // verbose_stream() << "trail after " << s.m_trail << "\n"; s.m_inconsistent = false; s.m_qhead = s.m_trail.size(); s.propagate(false); @@ -188,11 +214,14 @@ namespace sat { m_propagated[s.m_trail[i].var()] = true; } SASSERT(s.inconsistent()); - IF_VERBOSE(3, verbose_stream() << s.m_not_l << " " << s.m_conflict << "\n"); + IF_VERBOSE(3, s.display_justification(verbose_stream() << "conflict " << s.m_not_l << " ", s.m_conflict) << "\n"); + IF_VERBOSE(3, s.display(verbose_stream())); + sat::literal l = sat::null_literal; if (s.m_not_l != null_literal) { - add_core(~s.m_not_l, s.m_conflict); add_dependency(s.m_not_l); + l = ~s.m_not_l; } + add_core(l, s.m_conflict); add_dependency(s.m_conflict); for (unsigned i = s.m_trail.size(); i-- > trail_size0; ) { @@ -201,7 +230,7 @@ namespace sat { if (!s.is_marked(v)) continue; add_core(v); - s.reset_mark(v); + s.reset_mark(v); add_dependency(s.get_justification(v)); } if (!cl.empty()) @@ -210,8 +239,10 @@ namespace sat { void proof_trim::add_dependency(literal lit) { bool_var v = lit.var(); - if (m_propagated[v]) // literal was propagated after assuming ~C - s.mark(v); + if (m_propagated[v]) { // literal was propagated after assuming ~C + if (!s.is_marked(v)) + s.mark(v); + } else if (s.lvl(v) == 0) // literal depends on level 0, it is not assumed by ~C // inefficient for repeated insertions ? add_core(v); @@ -253,17 +284,18 @@ namespace sat { m_clause.push_back(j.get_literal()); break; case justification::CLAUSE: - s.get_clause(j).mark_used(); - IF_VERBOSE(3, verbose_stream() << "add core " << s.get_clause(j) << "\n"); - return; + for (auto lit : s.get_clause(j)) + m_clause.push_back(lit); + break; default: + verbose_stream() << j << "\n"; UNREACHABLE(); break; } std::sort(m_clause.begin(), m_clause.end()); IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n"); m_core_literals.insert(m_clause); - if (s.lvl(l) == 0) { + if (l != null_literal && s.lvl(l) == 0) { m_clause.reset(); m_clause.push_back(l); m_core_literals.insert(m_clause); @@ -271,10 +303,7 @@ namespace sat { } bool proof_trim::in_core(literal_vector const& cl, clause* cp) const { - if (cp) - return cp->was_used(); - else - return m_core_literals.contains(cl); + return m_core_literals.contains(cl); } void proof_trim::revive(literal_vector const& cl, clause* cp) { @@ -286,7 +315,7 @@ namespace sat { clause* proof_trim::del(literal_vector const& cl) { clause* cp = nullptr; - IF_VERBOSE(3, verbose_stream() << "del: " << cl << "\n"); + TRACE("sat", tout << "del: " << cl << "\n"); if (cl.size() == 2) { s.detach_bin_clause(cl[0], cl[1], true); return cp; @@ -297,7 +326,7 @@ namespace sat { auto& v = e->get_data().m_value; if (!v.empty()) { cp = v.back(); - IF_VERBOSE(3, verbose_stream() << "del: " << *cp << "\n"); + TRACE("sat", tout << "del: " << *cp << "\n"); s.detach_clause(*cp); v.pop_back(); } @@ -317,14 +346,57 @@ namespace sat { s.set_trim(); } + proof_trim::~proof_trim() { + } + + void proof_trim::assume(unsigned id, bool is_initial) { std::sort(m_clause.begin(), m_clause.end()); if (unit_or_binary_occurs()) return; + if (!m_conflict.empty() && m_clause.empty()) + m_trail.push_back({id , m_clause, nullptr, true, is_initial}); + if (!m_conflict.empty()) + return; + IF_VERBOSE(3, verbose_stream() << (is_initial?"assume ":"rup ") << m_clause << "\n"); auto* cl = s.mk_clause(m_clause, status::redundant()); + + auto is_unit2 = [&]() { + if (s.value(m_clause[0]) == l_false) { + std::swap(m_clause[0], m_clause[1]); + return true; + } + return s.value(m_clause[1]) == l_false; + }; + + auto is_unit = [&]() { + unsigned undef_idx = m_clause.size(); + for (unsigned i = 0; i < m_clause.size(); ++i) { + sat::literal lit = (*cl)[i]; + if (s.value(lit) != l_undef) + continue; + if (undef_idx < m_clause.size()) + return false; + undef_idx = i; + } + if (undef_idx < m_clause.size()) { + std::swap((*cl)[undef_idx], (*cl)[0]); + return true; + } + return false; + }; + m_trail.push_back({ id, m_clause, cl, true, is_initial }); + if (m_clause.size() == 2 && is_unit2()) + s.propagate_bin_clause(m_clause[0], m_clause[1]); + else if (m_clause.size() > 2 && is_unit()) + s.propagate_clause(*cl, true, 0, s.cls_allocator().get_offset(cl)); s.propagate(false); + // verbose_stream() << m_clause << " - " << s.inconsistent() << "\n"; + if (s.inconsistent() || all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) + set_conflict(m_clause, cl); + save(m_clause, cl); } diff --git a/src/sat/sat_proof_trim.h b/src/sat/sat_proof_trim.h index 6d996ad6e..6508f5a18 100644 --- a/src/sat/sat_proof_trim.h +++ b/src/sat/sat_proof_trim.h @@ -30,9 +30,10 @@ namespace sat { class proof_trim { solver s; - literal_vector m_clause; + literal_vector m_clause, m_conflict; uint_set m_in_clause; uint_set m_in_coi; + clause* m_conflict_clause = nullptr; vector> m_trail; @@ -70,10 +71,12 @@ namespace sat { uint_set m_units; bool unit_or_binary_occurs(); + void set_conflict(literal_vector const& c, clause* cp) { m_conflict.reset(); m_conflict.append(c); m_conflict_clause = cp;} public: proof_trim(params_ref const& p, reslimit& lim); + ~proof_trim(); bool_var mk_var() { return s.mk_var(true, true); } void init_clause() { m_clause.reset(); } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 5c1ed6dae..549783c61 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -90,7 +90,7 @@ namespace sat { solver::~solver() { m_ext = nullptr; - SASSERT(m_config.m_num_threads > 1 || check_invariant()); + SASSERT(m_config.m_num_threads > 1 || m_trim || check_invariant()); CTRACE("sat", !m_clauses.empty(), tout << "Delete clauses\n";); del_clauses(m_clauses); CTRACE("sat", !m_learned.empty(), tout << "Delete learned\n";); @@ -878,6 +878,7 @@ namespace sat { m_conflict = c; m_not_l = not_l; TRACE("sat", display(display_justification(tout << "conflict " << not_l << " ", c) << "\n")); + TRACE("sat", display_watches(tout)); } void solver::assign_core(literal l, justification j) { From 5ed2a828930d334f5275c21f3c6dfa699635d227 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Mon, 3 Jul 2023 17:35:16 -0700 Subject: [PATCH 20/41] set clang format off for lp files (#6795) * adding // clang-format off * set clang-format off at the beginning of lp files * set clang-format off * remove dead code --- src/math/lp/bound_analyzer_on_row.h | 1 + src/math/lp/column_info.h | 1 + src/math/lp/column_namer.h | 1 + src/math/lp/core_solver_pretty_printer.cpp | 1 + src/math/lp/core_solver_pretty_printer.h | 1 + src/math/lp/core_solver_pretty_printer_def.h | 1 + src/math/lp/cross_nested.h | 1 + src/math/lp/dense_matrix.cpp | 1 + src/math/lp/dense_matrix.h | 1 + src/math/lp/dense_matrix_def.h | 1 + src/math/lp/emonics.cpp | 1 + src/math/lp/emonics.h | 1 + src/math/lp/explanation.h | 1 + src/math/lp/factorization.h | 1 + src/math/lp/factorization_factory_imp.cpp | 1 + src/math/lp/factorization_factory_imp.h | 1 + src/math/lp/general_matrix.h | 1 + src/math/lp/hnf.h | 1 + src/math/lp/hnf_cutter.cpp | 1 + src/math/lp/hnf_cutter.h | 1 + src/math/lp/horner.cpp | 1 + src/math/lp/horner.h | 1 + src/math/lp/implied_bound.h | 1 + src/math/lp/incremental_vector.h | 1 + src/math/lp/indexed_value.h | 1 + src/math/lp/indexed_vector.cpp | 1 + src/math/lp/indexed_vector.h | 1 + src/math/lp/indexed_vector_def.h | 1 + src/math/lp/int_branch.h | 1 + src/math/lp/int_cube.cpp | 1 + src/math/lp/int_cube.h | 1 + src/math/lp/int_gcd_test.cpp | 1 + src/math/lp/int_gcd_test.h | 1 + src/math/lp/lar_constraints.h | 1 + src/math/lp/lar_core_solver.cpp | 1 + src/math/lp/lar_core_solver.h | 3 ++- src/math/lp/lar_core_solver_def.h | 1 + src/math/lp/lar_solver.h | 1 + src/math/lp/lar_term.h | 1 + src/math/lp/lia_move.h | 1 + src/math/lp/lp_api.h | 1 + src/math/lp/lp_core_solver_base.cpp | 1 + src/math/lp/lp_core_solver_base.h | 1 + src/math/lp/lp_core_solver_base_def.h | 1 + src/math/lp/lp_primal_core_solver.cpp | 1 + src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/lp_primal_core_solver_def.h | 1 + src/math/lp/lp_primal_core_solver_tableau_def.h | 1 + src/math/lp/lp_settings.cpp | 1 + src/math/lp/lp_settings.h | 1 + src/math/lp/lp_settings_def.h | 1 + src/math/lp/lp_types.h | 1 + src/math/lp/lp_utils.h | 1 + src/math/lp/matrix.cpp | 1 + src/math/lp/matrix.h | 1 + src/math/lp/matrix_def.h | 1 + src/math/lp/monomial_bounds.cpp | 1 + src/math/lp/monomial_bounds.h | 1 + src/math/lp/nex.h | 1 + src/math/lp/nex_creator.cpp | 1 + src/math/lp/nex_creator.h | 1 + src/math/lp/nla_basics_lemmas.cpp | 1 + src/math/lp/nla_basics_lemmas.h | 1 + src/math/lp/nla_common.cpp | 1 + src/math/lp/nla_common.h | 1 + src/math/lp/nla_core.cpp | 1 + src/math/lp/nla_core.h | 1 + src/math/lp/nla_defs.h | 1 + src/math/lp/nla_divisions.cpp | 1 + src/math/lp/nla_divisions.h | 1 + src/math/lp/nla_grobner.cpp | 1 + src/math/lp/nla_grobner.h | 1 + src/math/lp/nla_intervals.h | 1 + src/math/lp/nla_monotone_lemmas.cpp | 1 + src/math/lp/nla_monotone_lemmas.h | 1 + src/math/lp/nla_order_lemmas.cpp | 1 + src/math/lp/nla_order_lemmas.h | 1 + src/math/lp/nla_powers.cpp | 1 + src/math/lp/nla_powers.h | 1 + src/math/lp/nla_settings.h | 1 + src/math/lp/nla_solver.cpp | 1 + src/math/lp/nla_solver.h | 1 + src/math/lp/nla_tangent_lemmas.cpp | 1 + src/math/lp/nla_tangent_lemmas.h | 1 + src/math/lp/nla_types.h | 1 + src/math/lp/numeric_pair.h | 1 + src/math/lp/permutation_matrix.cpp | 1 + src/math/lp/permutation_matrix.h | 1 + src/math/lp/permutation_matrix_def.h | 1 + src/math/lp/random_updater.cpp | 1 + src/math/lp/random_updater.h | 1 + src/math/lp/random_updater_def.h | 1 + src/math/lp/stacked_vector.h | 1 + src/math/lp/static_matrix.cpp | 2 +- src/math/lp/static_matrix.h | 2 +- src/math/lp/static_matrix_def.h | 6 +----- src/math/lp/test_bound_analyzer.h | 1 + src/math/lp/u_set.h | 1 + src/math/lp/ul_pair.h | 1 + src/math/lp/var_eqs.h | 1 + src/math/lp/var_register.h | 1 + src/smt/theory_arith_core.h | 2 +- 102 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 0008a0ee9..bfae22f21 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -19,6 +19,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/column_info.h b/src/math/lp/column_info.h index 1dc0c60c7..c0b09e21f 100644 --- a/src/math/lp/column_info.h +++ b/src/math/lp/column_info.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/column_namer.h b/src/math/lp/column_namer.h index cef58ed21..ccd3cffad 100644 --- a/src/math/lp/column_namer.h +++ b/src/math/lp/column_namer.h @@ -18,6 +18,7 @@ Revision History: --*/ +// clang-format off #include #include "math/lp/static_matrix.h" namespace lp { diff --git a/src/math/lp/core_solver_pretty_printer.cpp b/src/math/lp/core_solver_pretty_printer.cpp index 18bef8303..e1ce8fb1b 100644 --- a/src/math/lp/core_solver_pretty_printer.cpp +++ b/src/math/lp/core_solver_pretty_printer.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #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); diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 5bf29d511..46e9ebf7e 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include #include diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index b8048b241..17f82af9c 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/cross_nested.h b/src/math/lp/cross_nested.h index a39ef5ef9..9633e2dab 100644 --- a/src/math/lp/cross_nested.h +++ b/src/math/lp/cross_nested.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include #include "math/lp/nex.h" diff --git a/src/math/lp/dense_matrix.cpp b/src/math/lp/dense_matrix.cpp index 25fc65a5d..cd8e019e6 100644 --- a/src/math/lp/dense_matrix.cpp +++ b/src/math/lp/dense_matrix.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include "math/lp/lp_settings.h" #include "math/lp/dense_matrix_def.h" #ifdef Z3DEBUG diff --git a/src/math/lp/dense_matrix.h b/src/math/lp/dense_matrix.h index fcc85cdd1..6b039a920 100644 --- a/src/math/lp/dense_matrix.h +++ b/src/math/lp/dense_matrix.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #ifdef Z3DEBUG #include "util/vector.h" diff --git a/src/math/lp/dense_matrix_def.h b/src/math/lp/dense_matrix_def.h index e850a9acd..986a2d20e 100644 --- a/src/math/lp/dense_matrix_def.h +++ b/src/math/lp/dense_matrix_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lp_settings.h" diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index bcdb81dd8..e5da7c702 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -18,6 +18,7 @@ replaced rooted_mons.h and rooted_mon, rooted_mon_tabled --*/ +// clang-format off #include "math/lp/emonics.h" #include "math/lp/nla_defs.h" diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index e4f4f4848..8f0f33175 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -18,6 +18,7 @@ to replace rooted_mons.h and rooted_mon, rooted_mon_tabled --*/ +// clang-format off #pragma once #include "math/lp/lp_utils.h" diff --git a/src/math/lp/explanation.h b/src/math/lp/explanation.h index b7f721a30..67df7bab9 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lp_utils.h" #include "util/map.h" diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index b233894ad..aad52c58e 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -18,6 +18,7 @@ --*/ +// clang-format off #pragma once #include "util/rational.h" #include "math/lp/monic.h" diff --git a/src/math/lp/factorization_factory_imp.cpp b/src/math/lp/factorization_factory_imp.cpp index ace85a050..1406d5864 100644 --- a/src/math/lp/factorization_factory_imp.cpp +++ b/src/math/lp/factorization_factory_imp.cpp @@ -17,6 +17,7 @@ --*/ +// clang-format off #include "math/lp/factorization_factory_imp.h" #include "math/lp/nla_core.h" namespace nla { diff --git a/src/math/lp/factorization_factory_imp.h b/src/math/lp/factorization_factory_imp.h index 599039094..7c76299dc 100644 --- a/src/math/lp/factorization_factory_imp.h +++ b/src/math/lp/factorization_factory_imp.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include "math/lp/factorization.h" namespace nla { diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index a4f6693a2..0fc9c404b 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include namespace lp { diff --git a/src/math/lp/hnf.h b/src/math/lp/hnf.h index 75a69393f..53b33b37a 100644 --- a/src/math/lp/hnf.h +++ b/src/math/lp/hnf.h @@ -26,6 +26,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/numeric_pair.h" #include "util/ext_gcd.h" diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index 3c4ea10ab..e2fd85866 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -9,6 +9,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" #include "math/lp/hnf_cutter.h" diff --git a/src/math/lp/hnf_cutter.h b/src/math/lp/hnf_cutter.h index b3530ea29..98f9770e8 100644 --- a/src/math/lp/hnf_cutter.h +++ b/src/math/lp/hnf_cutter.h @@ -14,6 +14,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "math/lp/lar_term.h" diff --git a/src/math/lp/horner.cpp b/src/math/lp/horner.cpp index 4d4ac4975..3b4cce827 100644 --- a/src/math/lp/horner.cpp +++ b/src/math/lp/horner.cpp @@ -17,6 +17,7 @@ --*/ +// clang-format off #include "math/lp/horner.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/horner.h b/src/math/lp/horner.h index 2b6fc3cd8..ce630b0c2 100644 --- a/src/math/lp/horner.h +++ b/src/math/lp/horner.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include "math/lp/nla_common.h" diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 9435edcdc..239103035 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lp_settings.h" #include "math/lp/lar_constraints.h" diff --git a/src/math/lp/incremental_vector.h b/src/math/lp/incremental_vector.h index 0bb2829eb..f124eb05e 100644 --- a/src/math/lp/incremental_vector.h +++ b/src/math/lp/incremental_vector.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index 92d8f2adf..aab13fc9c 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once namespace lp { diff --git a/src/math/lp/indexed_vector.cpp b/src/math/lp/indexed_vector.cpp index fe0892541..f5c34e1f9 100644 --- a/src/math/lp/indexed_vector.cpp +++ b/src/math/lp/indexed_vector.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include "util/vector.h" #include "math/lp/indexed_vector_def.h" namespace lp { diff --git a/src/math/lp/indexed_vector.h b/src/math/lp/indexed_vector.h index 9f3119e9a..b5f5e6cb8 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 034325088..c6f530d99 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/int_branch.h b/src/math/lp/int_branch.h index 9601cb65e..6943bede5 100644 --- a/src/math/lp/int_branch.h +++ b/src/math/lp/int_branch.h @@ -15,6 +15,7 @@ Author: Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lar_solver.h" diff --git a/src/math/lp/int_cube.cpp b/src/math/lp/int_cube.cpp index da724a543..787b82da7 100644 --- a/src/math/lp/int_cube.cpp +++ b/src/math/lp/int_cube.cpp @@ -15,6 +15,7 @@ Author: Revision History: --*/ +// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" diff --git a/src/math/lp/int_cube.h b/src/math/lp/int_cube.h index 4addc096b..9ac6773ab 100644 --- a/src/math/lp/int_cube.h +++ b/src/math/lp/int_cube.h @@ -19,6 +19,7 @@ Author: Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lia_move.h" diff --git a/src/math/lp/int_gcd_test.cpp b/src/math/lp/int_gcd_test.cpp index 4801cc436..e243783cc 100644 --- a/src/math/lp/int_gcd_test.cpp +++ b/src/math/lp/int_gcd_test.cpp @@ -45,6 +45,7 @@ Accumulative: --*/ +// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" diff --git a/src/math/lp/int_gcd_test.h b/src/math/lp/int_gcd_test.h index 28ac2f7b3..b77179409 100644 --- a/src/math/lp/int_gcd_test.h +++ b/src/math/lp/int_gcd_test.h @@ -24,6 +24,7 @@ Author: Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lia_move.h" diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index f8cffbe57..3bb6e7882 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/lar_core_solver.cpp b/src/math/lp/lar_core_solver.cpp index 09d01f4cc..bdb89f76b 100644 --- a/src/math/lp/lar_core_solver.cpp +++ b/src/math/lp/lar_core_solver.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include #include diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 10cd53141..a67eb4d48 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -5,6 +5,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "util/vector.h" #include @@ -16,7 +17,7 @@ Author: #include "math/lp/stacked_vector.h" #include "util/stacked_value.h" namespace lp { - +// clang-format off class lar_core_solver { vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 942c87b3b..3f7866717 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -9,6 +9,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 9c45fdb80..e9e26ffe6 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include #include diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index fc73f949f..65262d5dd 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include "math/lp/indexed_vector.h" #include "util/map.h" diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index ca61d7b7a..b1505dbc9 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once namespace lp { enum class lia_move { diff --git a/src/math/lp/lp_api.h b/src/math/lp/lp_api.h index 2a4e5058d..9d0bd2476 100644 --- a/src/math/lp/lp_api.h +++ b/src/math/lp/lp_api.h @@ -7,6 +7,7 @@ Author: Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "util/inf_rational.h" diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 28f92d8e2..1859630e4 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include #include diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index f38587573..06e67cbf7 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include #include "util/vector.h" diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 319966091..756ff9af3 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index efbfd27e1..0698c9962 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include #include diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 470405581..2a4a278a6 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -159,7 +159,7 @@ namespace lp { } return r; } - // clang-format on + // clang-format off int find_beneficial_entering_in_row_tableau_rows_bland_mode(int i, T &a_ent) { int j = -1; unsigned bj = this->m_basis[i]; diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index e18a5ef05..909dfd086 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include 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 dbcced4ab..905bbba3c 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once // this is a part of lp_primal_core_solver that deals with the tableau diff --git a/src/math/lp/lp_settings.cpp b/src/math/lp/lp_settings.cpp index b72b837fd..723a4ba2d 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include "util/vector.h" #include "smt/params/smt_params_helper.hpp" diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 727bc3531..4e03007bb 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index a19558949..12c53551f 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/lp_types.h b/src/math/lp/lp_types.h index e4ee535aa..6f1f2e3d4 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 3c1383cb3..3a796c42b 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include #include "math/lp/numeric_pair.h" diff --git a/src/math/lp/matrix.cpp b/src/math/lp/matrix.cpp index 1ea2da263..1176a6358 100644 --- a/src/math/lp/matrix.cpp +++ b/src/math/lp/matrix.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include "math/lp/lp_settings.h" #include "math/lp/matrix_def.h" #include "math/lp/static_matrix.h" diff --git a/src/math/lp/matrix.h b/src/math/lp/matrix.h index 88a405614..306f43a57 100644 --- a/src/math/lp/matrix.h +++ b/src/math/lp/matrix.h @@ -6,6 +6,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "math/lp/numeric_pair.h" #include "util/vector.h" diff --git a/src/math/lp/matrix_def.h b/src/math/lp/matrix_def.h index e3ac08f7e..b9d5efb48 100644 --- a/src/math/lp/matrix_def.h +++ b/src/math/lp/matrix_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 1ed0956dc..4f7052ee5 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -6,6 +6,7 @@ Lev Nachmanson (levnach) --*/ +// clang-format off #include "math/lp/monomial_bounds.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 236f29bc0..2d7079c50 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -6,6 +6,7 @@ Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "math/lp/nla_common.h" diff --git a/src/math/lp/nex.h b/src/math/lp/nex.h index e4087a407..e72af480b 100644 --- a/src/math/lp/nex.h +++ b/src/math/lp/nex.h @@ -4,6 +4,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/nex_creator.cpp b/src/math/lp/nex_creator.cpp index c43b723ef..ae73490f8 100644 --- a/src/math/lp/nex_creator.cpp +++ b/src/math/lp/nex_creator.cpp @@ -4,6 +4,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #include "util/lbool.h" #include "math/lp/nex_creator.h" #include diff --git a/src/math/lp/nex_creator.h b/src/math/lp/nex_creator.h index 9bce46395..4e147e373 100644 --- a/src/math/lp/nex_creator.h +++ b/src/math/lp/nex_creator.h @@ -5,6 +5,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include #include diff --git a/src/math/lp/nla_basics_lemmas.cpp b/src/math/lp/nla_basics_lemmas.cpp index 7124fd409..99638d9d1 100644 --- a/src/math/lp/nla_basics_lemmas.cpp +++ b/src/math/lp/nla_basics_lemmas.cpp @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "math/lp/nla_basics_lemmas.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/nla_basics_lemmas.h b/src/math/lp/nla_basics_lemmas.h index 58fb0e92f..c913cfcfc 100644 --- a/src/math/lp/nla_basics_lemmas.h +++ b/src/math/lp/nla_basics_lemmas.h @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "math/lp/monic.h" #include "math/lp/factorization.h" diff --git a/src/math/lp/nla_common.cpp b/src/math/lp/nla_common.cpp index 45898c613..d9f9b2bfc 100644 --- a/src/math/lp/nla_common.cpp +++ b/src/math/lp/nla_common.cpp @@ -6,6 +6,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "math/lp/nla_common.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/nla_common.h b/src/math/lp/nla_common.h index 1302c3909..dc393af95 100644 --- a/src/math/lp/nla_common.h +++ b/src/math/lp/nla_common.h @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "util/rational.h" #include "math/lp/nla_defs.h" diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 3c1f9da57..0931e005f 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -10,6 +10,7 @@ Author: Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "util/uint_set.h" #include "math/lp/nla_core.h" #include "math/lp/factorization_factory_imp.h" diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 938bcbe83..e178fbf75 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -10,6 +10,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "math/lp/factorization.h" #include "math/lp/lp_types.h" diff --git a/src/math/lp/nla_defs.h b/src/math/lp/nla_defs.h index df9158b42..23be7bb72 100644 --- a/src/math/lp/nla_defs.h +++ b/src/math/lp/nla_defs.h @@ -8,6 +8,7 @@ --*/ +// clang-format off #pragma once #include "math/lp/lp_types.h" #include "math/lp/column_info.h" diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index cbb30d9d9..fd83de32c 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -14,6 +14,7 @@ Description: Check divisions --*/ +// clang-format off #include "math/lp/nla_core.h" namespace nla { diff --git a/src/math/lp/nla_divisions.h b/src/math/lp/nla_divisions.h index 80bf5be4e..e07cf3aed 100644 --- a/src/math/lp/nla_divisions.h +++ b/src/math/lp/nla_divisions.h @@ -13,6 +13,7 @@ Description: Check division constraints. --*/ +// clang-format off #include "math/lp/nla_types.h" diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 974c48d14..9cdb19fbf 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -10,6 +10,7 @@ Author: Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "util/uint_set.h" #include "math/lp/nla_core.h" #include "math/lp/factorization_factory_imp.h" diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index 902ad3a46..12f8609d7 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -6,6 +6,7 @@ Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "math/lp/nla_common.h" diff --git a/src/math/lp/nla_intervals.h b/src/math/lp/nla_intervals.h index 0545e9933..1806051ae 100644 --- a/src/math/lp/nla_intervals.h +++ b/src/math/lp/nla_intervals.h @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "util/dependency.h" #include "util/region.h" diff --git a/src/math/lp/nla_monotone_lemmas.cpp b/src/math/lp/nla_monotone_lemmas.cpp index cc9241446..2694a7ef8 100644 --- a/src/math/lp/nla_monotone_lemmas.cpp +++ b/src/math/lp/nla_monotone_lemmas.cpp @@ -5,6 +5,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "math/lp/nla_basics_lemmas.h" #include "math/lp/nla_core.h" namespace nla { diff --git a/src/math/lp/nla_monotone_lemmas.h b/src/math/lp/nla_monotone_lemmas.h index d13f588e8..2828e601c 100644 --- a/src/math/lp/nla_monotone_lemmas.h +++ b/src/math/lp/nla_monotone_lemmas.h @@ -5,6 +5,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once namespace nla { class core; diff --git a/src/math/lp/nla_order_lemmas.cpp b/src/math/lp/nla_order_lemmas.cpp index 94ddc4d9b..9b4b0e187 100644 --- a/src/math/lp/nla_order_lemmas.cpp +++ b/src/math/lp/nla_order_lemmas.cpp @@ -5,6 +5,7 @@ Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) --*/ +// clang-format off #include "math/lp/nla_order_lemmas.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/nla_order_lemmas.h b/src/math/lp/nla_order_lemmas.h index c6961bc52..025e4b783 100644 --- a/src/math/lp/nla_order_lemmas.h +++ b/src/math/lp/nla_order_lemmas.h @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "math/lp/factorization.h" #include "math/lp/nla_common.h" diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index f389aad93..bbf50e9a7 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -74,6 +74,7 @@ am().set(yval, am_value(y)); am().set(rval, am_value(r)); --*/ +// clang-format off #include "math/lp/nla_core.h" namespace nla { diff --git a/src/math/lp/nla_powers.h b/src/math/lp/nla_powers.h index f74417ae3..f76460e1c 100644 --- a/src/math/lp/nla_powers.h +++ b/src/math/lp/nla_powers.h @@ -13,6 +13,7 @@ Description: Refines bounds on powers. --*/ +// clang-format off #include "math/lp/nla_types.h" diff --git a/src/math/lp/nla_settings.h b/src/math/lp/nla_settings.h index ec11ea5b2..6eb2ea1c3 100644 --- a/src/math/lp/nla_settings.h +++ b/src/math/lp/nla_settings.h @@ -6,6 +6,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once namespace nla { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index bd0f1953c..ada438edc 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -5,6 +5,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "math/lp/nla_solver.h" #include #include "math/lp/monic.h" diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index d04ff8e51..8da4815aa 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -6,6 +6,7 @@ Author: Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "util/vector.h" #include "math/lp/lp_settings.h" diff --git a/src/math/lp/nla_tangent_lemmas.cpp b/src/math/lp/nla_tangent_lemmas.cpp index 4ba9eeccc..c2081281e 100644 --- a/src/math/lp/nla_tangent_lemmas.cpp +++ b/src/math/lp/nla_tangent_lemmas.cpp @@ -6,6 +6,7 @@ Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #include "math/lp/nla_tangent_lemmas.h" #include "math/lp/nla_core.h" diff --git a/src/math/lp/nla_tangent_lemmas.h b/src/math/lp/nla_tangent_lemmas.h index 1895b3fcb..d3d71590d 100644 --- a/src/math/lp/nla_tangent_lemmas.h +++ b/src/math/lp/nla_tangent_lemmas.h @@ -5,6 +5,7 @@ Lev Nachmanson (levnach) Nikolaj Bjorner (nbjorner) --*/ +// clang-format off #pragma once #include "util/rational.h" #include "math/lp/factorization.h" diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index 8169266cc..1efc98860 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -13,6 +13,7 @@ Description: Types used for nla solver. --*/ +// clang-format off #pragma once diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 251274006..a59057288 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #define lp_for_z3 #include diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index be4b7335c..166330c6f 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include "util/vector.h" #include "math/lp/permutation_matrix_def.h" diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index a3fff4f7f..b31d3cdec 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" #include diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index c86fef4f4..e1bd8c4a8 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/random_updater.cpp b/src/math/lp/random_updater.cpp index a88df76e5..6535c840f 100644 --- a/src/math/lp/random_updater.cpp +++ b/src/math/lp/random_updater.cpp @@ -17,5 +17,6 @@ Revision History: --*/ +// clang-format off #include "math/lp/random_updater_def.h" diff --git a/src/math/lp/random_updater.h b/src/math/lp/random_updater.h index d5cd4928c..c90b1e6f2 100644 --- a/src/math/lp/random_updater.h +++ b/src/math/lp/random_updater.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include diff --git a/src/math/lp/random_updater_def.h b/src/math/lp/random_updater_def.h index 7d167a4a0..c31211e2c 100644 --- a/src/math/lp/random_updater_def.h +++ b/src/math/lp/random_updater_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/random_updater.h" diff --git a/src/math/lp/stacked_vector.h b/src/math/lp/stacked_vector.h index ecd61eb10..8b7dd4812 100644 --- a/src/math/lp/stacked_vector.h +++ b/src/math/lp/stacked_vector.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index efb6e07cf..c871ddc87 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #include #include "util/vector.h" #include @@ -29,7 +30,6 @@ namespace lp { template std::set> lp::static_matrix::get_domain(); template std::set> lp::static_matrix >::get_domain(); 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; template mpq static_matrix::get_balance() const; diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index f79ff36ac..2e25b5b9a 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -6,6 +6,7 @@ Author: Lev Nachmanson (levnach) --*/ +// clang-format off #pragma once #include "util/vector.h" @@ -126,7 +127,6 @@ public: unsigned lowest_row_in_column(unsigned col); - void add_columns_at_the_end(unsigned delta); void add_new_element(unsigned i, unsigned j, const T & v); void add_row() {m_rows.push_back(row_strip());} diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index 76c1dec54..c7fcc6d88 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" @@ -124,11 +125,6 @@ template unsigned static_matrix::lowest_row_in_co return ret; } -template void static_matrix::add_columns_at_the_end(unsigned delta) { - for (unsigned i = 0; i < delta; i++) - add_column(); -} - template void static_matrix::forget_last_columns(unsigned how_many_to_forget) { lp_assert(m_columns.size() >= how_many_to_forget); unsigned j = column_count() - 1; diff --git a/src/math/lp/test_bound_analyzer.h b/src/math/lp/test_bound_analyzer.h index 1ca99d637..438deffda 100644 --- a/src/math/lp/test_bound_analyzer.h +++ b/src/math/lp/test_bound_analyzer.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #if 0 #pragma once #include "util/vector.h" diff --git a/src/math/lp/u_set.h b/src/math/lp/u_set.h index ce59dccb7..54778669d 100644 --- a/src/math/lp/u_set.h +++ b/src/math/lp/u_set.h @@ -18,6 +18,7 @@ Revision History: TBD use indexed_uint_set from src/util/uint_set.h, --*/ +// clang-format off #pragma once #include "util/vector.h" #include diff --git a/src/math/lp/ul_pair.h b/src/math/lp/ul_pair.h index abfb4483b..95af719e0 100644 --- a/src/math/lp/ul_pair.h +++ b/src/math/lp/ul_pair.h @@ -17,6 +17,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/var_eqs.h b/src/math/lp/var_eqs.h index 998779dc6..88676715d 100644 --- a/src/math/lp/var_eqs.h +++ b/src/math/lp/var_eqs.h @@ -17,6 +17,7 @@ --*/ +// clang-format off #pragma once #include "util/union_find.h" diff --git a/src/math/lp/var_register.h b/src/math/lp/var_register.h index 49767274d..cea5ae2b6 100644 --- a/src/math/lp/var_register.h +++ b/src/math/lp/var_register.h @@ -16,6 +16,7 @@ Revision History: --*/ +// clang-format off #pragma once #include "math/lp/lp_types.h" namespace lp { diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 159758c80..0a4c1869b 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -17,7 +17,7 @@ Revision History: --*/ #pragma once - +// clang-format off #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" From ae29a5487672cd27b3795a8f4de5dbc90a051378 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Jul 2023 09:12:58 +0200 Subject: [PATCH 21/41] categorize theory axioms as inferences in output to capture justifications Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/proof_cmds.cpp | 2 +- src/sat/sat_proof_trim.cpp | 19 ------------------- src/sat/sat_proof_trim.h | 7 +------ 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index 0965439ae..5b3cb4da0 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -132,7 +132,7 @@ public: clause1.push_back(hint); trim.assume(m_clauses.size()); m_clauses.push_back(clause1); - m_is_infer.push_back(false); + m_is_infer.push_back(true); if (clause.empty()) { mk_clause(clause); diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index ee35efe09..01b078d18 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -75,20 +75,6 @@ namespace sat { else del(cl); } - - bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2) const { - return cl.size() == 2 && ((l1 == cl[0] && l2 == cl[1]) || (l1 == cl[1] && l2 == cl[0])); - } - - bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const { - return cl.size() == 3 && - ((l1 == cl[0] && l2 == cl[1] && l3 == cl[2]) || - (l1 == cl[0] && l2 == cl[2] && l3 == cl[1]) || - (l1 == cl[1] && l2 == cl[0] && l3 == cl[2]) || - (l1 == cl[1] && l2 == cl[2] && l3 == cl[0]) || - (l1 == cl[2] && l2 == cl[1] && l3 == cl[0]) || - (l1 == cl[2] && l2 == cl[0] && l3 == cl[1])); - } /** * cl is on the trail if there is some literal l that is implied by cl @@ -346,10 +332,6 @@ namespace sat { s.set_trim(); } - proof_trim::~proof_trim() { - } - - void proof_trim::assume(unsigned id, bool is_initial) { std::sort(m_clause.begin(), m_clause.end()); if (unit_or_binary_occurs()) @@ -393,7 +375,6 @@ namespace sat { else if (m_clause.size() > 2 && is_unit()) s.propagate_clause(*cl, true, 0, s.cls_allocator().get_offset(cl)); s.propagate(false); - // verbose_stream() << m_clause << " - " << s.inconsistent() << "\n"; if (s.inconsistent() || all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) set_conflict(m_clause, cl); diff --git a/src/sat/sat_proof_trim.h b/src/sat/sat_proof_trim.h index 6508f5a18..aa75589ac 100644 --- a/src/sat/sat_proof_trim.h +++ b/src/sat/sat_proof_trim.h @@ -35,8 +35,7 @@ namespace sat { uint_set m_in_coi; clause* m_conflict_clause = nullptr; vector> m_trail; - - + struct hash { unsigned operator()(literal_vector const& v) const { return string_hash((char const*)v.begin(), v.size()*sizeof(literal), 3); @@ -54,9 +53,6 @@ namespace sat { void del(literal_vector const& cl, clause* cp); - bool match_clause(literal_vector const& cl, literal l1, literal l2) const; - bool match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const; - void prune_trail(literal_vector const& cl, clause* cp); void conflict_analysis_core(literal_vector const& cl, clause* cp); @@ -76,7 +72,6 @@ namespace sat { public: proof_trim(params_ref const& p, reslimit& lim); - ~proof_trim(); bool_var mk_var() { return s.mk_var(true, true); } void init_clause() { m_clause.reset(); } From f0d3cbe39d38e958b5d6884a53c022f3ed5cf8f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Jul 2023 16:24:09 +0200 Subject: [PATCH 22/41] add dependency tracking to proof from trim Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/proof_cmds.cpp | 21 ++++++++++++++---- src/sat/sat_proof_trim.cpp | 26 ++++++++++++++--------- src/sat/sat_proof_trim.h | 8 ++++--- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index 5b3cb4da0..e2a19446d 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -43,6 +43,7 @@ Proof checker for clauses created during search. #include "util/small_object_allocator.h" #include "ast/ast_util.h" #include "ast/ast_ll_pp.h" +#include "ast/arith_decl_plugin.h" #include "smt/smt_solver.h" #include "sat/sat_solver.h" #include "sat/sat_drat.h" @@ -167,12 +168,22 @@ public: trim.updt_params(p); } + expr_ref mk_dep(unsigned id, unsigned_vector const& deps) { + arith_util a(m); + expr_ref_vector args(m); + args.push_back(a.mk_int(id)); + for (auto d : deps) + args.push_back(a.mk_int(d)); + return expr_ref(m.mk_app(symbol("deps"), args.size(), args.data(), m.mk_proof_sort()), m); + } + void do_trim(std::ostream& out) { ast_pp_util pp(m); auto ids = trim.trim(); - for (unsigned id : ids) { - auto const& clause = m_clauses[id]; + for (auto const& [id, deps] : ids) { + auto& clause = m_clauses[id]; bool is_infer = m_is_infer[id]; + clause.push_back(mk_dep(id, deps)); for (expr* e : clause) pp.collect(e); @@ -268,8 +279,10 @@ public: } void add_literal(expr* e) override { - if (m.is_proof(e)) - m_proof_hint = to_app(e); + if (m.is_proof(e)) { + if (!m_proof_hint) + m_proof_hint = to_app(e); + } else if (!m.is_bool(e)) throw default_exception("literal should be either a Proof or Bool"); else diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index 01b078d18..b4171f51d 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -29,19 +29,20 @@ namespace sat { Output: reduced trail - result */ - unsigned_vector proof_trim::trim() { - unsigned_vector result; + vector> proof_trim::trim() { + m_result.reset(); m_core_literals.reset(); m_propagated.resize(num_vars(), false); m_core_literals.insert(literal_vector()); m_core_literals.insert(m_conflict); - conflict_analysis_core(m_conflict, m_conflict_clause); + IF_VERBOSE(10, s.display(verbose_stream() << "trim\n")); auto const& [id, cl, clp, is_add, is_initial] = m_trail.back(); SASSERT(cl.empty()); - result.push_back(id); + m_result.push_back({id, unsigned_vector()}); + conflict_analysis_core(m_conflict, m_conflict_clause); m_trail.pop_back(); @@ -55,17 +56,17 @@ namespace sat { prune_trail(cl, clp); IF_VERBOSE(10, s.display(verbose_stream() << "\n")); del(cl, clp); - if (!in_core(cl, clp)) + if (!in_core(cl)) continue; - IF_VERBOSE(4, verbose_stream() << cl << " in-core " << in_core(cl, clp) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} "; verbose_stream() << "\n"); + IF_VERBOSE(4, verbose_stream() << cl << " in-core " << in_core(cl) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} "; verbose_stream() << "\n"); - result.push_back(id); + m_result.push_back({id, unsigned_vector()}); if (is_initial) continue; conflict_analysis_core(cl, clp); } - result.reverse(); - return result; + m_result.reverse(); + return m_result; } void proof_trim::del(literal_vector const& cl, clause* cp) { @@ -280,6 +281,9 @@ namespace sat { } std::sort(m_clause.begin(), m_clause.end()); IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n"); + unsigned id; + VERIFY(m_clause2id.find(m_clause, id)); + m_result.back().second.push_back(id); m_core_literals.insert(m_clause); if (l != null_literal && s.lvl(l) == 0) { m_clause.reset(); @@ -288,7 +292,7 @@ namespace sat { } } - bool proof_trim::in_core(literal_vector const& cl, clause* cp) const { + bool proof_trim::in_core(literal_vector const& cl) const { return m_core_literals.contains(cl); } @@ -342,6 +346,8 @@ namespace sat { return; IF_VERBOSE(3, verbose_stream() << (is_initial?"assume ":"rup ") << m_clause << "\n"); + std::sort(m_clause.begin(), m_clause.end()); + m_clause2id.insert(m_clause, id); auto* cl = s.mk_clause(m_clause, status::redundant()); auto is_unit2 = [&]() { diff --git a/src/sat/sat_proof_trim.h b/src/sat/sat_proof_trim.h index aa75589ac..9a43315f3 100644 --- a/src/sat/sat_proof_trim.h +++ b/src/sat/sat_proof_trim.h @@ -35,7 +35,8 @@ namespace sat { uint_set m_in_coi; clause* m_conflict_clause = nullptr; vector> m_trail; - + vector> m_result; + struct hash { unsigned operator()(literal_vector const& v) const { return string_hash((char const*)v.begin(), v.size()*sizeof(literal), 3); @@ -47,6 +48,7 @@ namespace sat { } }; map m_clauses; + map m_clause2id; hashtable m_core_literals; bool_vector m_propagated; @@ -60,7 +62,7 @@ namespace sat { void add_dependency(justification j); void add_core(bool_var v); void add_core(literal l, justification j); - bool in_core(literal_vector const& cl, clause* cp) const; + bool in_core(literal_vector const& cl) const; void revive(literal_vector const& cl, clause* cp); clause* del(literal_vector const& cl); void save(literal_vector const& lits, clause* cl); @@ -83,7 +85,7 @@ namespace sat { void infer(unsigned id); void updt_params(params_ref const& p) { s.updt_params(p); } - unsigned_vector trim(); + vector> trim(); }; } From 75897b7a2ece2ee567b81eb7667ac60590430f9e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 4 Jul 2023 11:38:10 -0700 Subject: [PATCH 23/41] a small change in trace feas --- src/math/lp/lp_core_solver_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 06e67cbf7..aac645631 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -56,7 +56,7 @@ private: public: bool current_x_is_feasible() const { TRACE("feas", - if (m_inf_heap.size()) { + if (!m_inf_heap.empty()) { tout << "column " << *m_inf_heap.begin() << " is infeasible" << std::endl; print_column_info(*m_inf_heap.begin(), tout); } else { From e360de6d71a2b96cbd7d260ccea48a1f69158ba4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 4 Jul 2023 13:23:56 -0700 Subject: [PATCH 24/41] improve tracing and a small fix in lp_core_solver_base::make_column_feasible --- src/math/lp/lar_solver.cpp | 249 +++++++++++++++--------------- src/math/lp/lar_solver.h | 3 +- src/math/lp/lp_core_solver_base.h | 30 ++-- 3 files changed, 139 insertions(+), 143 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 9fa9bc540..60184962f 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1810,7 +1810,12 @@ namespace lp { else update_column_type_and_bound_with_no_ub(j, kind, right_side, constr_index); } - + // clang-format on + void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { + m_columns_with_changed_bounds.insert(j); + TRACE("lar_solver", tout << "column " << j << (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + } + // clang-format off void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, lconstraint_kind kind, const mpq& right_side, @@ -1900,117 +1905,110 @@ namespace lp { } } - + // clang-format on void lar_solver::update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { lp_assert(column_has_lower_bound(j) && column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::boxed || - m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed); + m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed); mpq y_of_bound(0); switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - set_infeasible_column(j); - } - if (up >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) return; - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, ci); - insert_to_columns_with_changed_bounds(j); - } - break; - case GT: - y_of_bound = 1; - case GE: - { - auto low = numeric_pair(right_side, y_of_bound); - if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - set_infeasible_column(j); - } - if (low < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - return; - } - m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - insert_to_columns_with_changed_bounds(j); - set_lower_bound_witness(j, ci); - m_mpq_lar_core_solver.m_column_types[j] = (low == m_mpq_lar_core_solver.m_r_upper_bounds[j] ? column_type::fixed : column_type::boxed); - } - break; - case EQ: - { - auto v = numeric_pair(right_side, zero_of_type()); - if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j] || v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - set_infeasible_column(j); - } - set_upper_bound_witness(j, ci); - set_lower_bound_witness(j, ci); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; - break; - } + case LT: + y_of_bound = -1; + case LE: { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + set_infeasible_column(j); + } + if (up >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) return; + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + insert_to_columns_with_changed_bounds(j); + } break; + case GT: + y_of_bound = 1; + case GE: { + auto low = numeric_pair(right_side, y_of_bound); + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + set_infeasible_column(j); + } + if (low < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + return; + } + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = (low == m_mpq_lar_core_solver.m_r_upper_bounds[j] ? column_type::fixed : column_type::boxed); + insert_to_columns_with_changed_bounds(j); - default: - UNREACHABLE(); + } break; + case EQ: { + auto v = numeric_pair(right_side, zero_of_type()); + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j] || v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + set_infeasible_column(j); + } + set_upper_bound_witness(j, ci); + set_lower_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; + break; + } + + default: + 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; } } + // clang-format off void lar_solver::update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { lp_assert(column_has_lower_bound(j) && !column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::lower_bound); mpq y_of_bound(0); switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - set_infeasible_column(j); - } - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, ci); - insert_to_columns_with_changed_bounds(j); - m_mpq_lar_core_solver.m_column_types[j] = (up == m_mpq_lar_core_solver.m_r_lower_bounds[j] ? column_type::fixed : column_type::boxed); - } - break; - case GT: - y_of_bound = 1; - case GE: - { - auto low = numeric_pair(right_side, y_of_bound); - if (low < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - return; - } - m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - insert_to_columns_with_changed_bounds(j); - set_lower_bound_witness(j, ci); - } - break; - case EQ: - { - auto v = numeric_pair(right_side, zero_of_type()); - if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - set_infeasible_column(j); + case LT: + y_of_bound = -1; + case LE: { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + set_infeasible_column(j); + } + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = (up == m_mpq_lar_core_solver.m_r_lower_bounds[j] ? column_type::fixed : column_type::boxed); + insert_to_columns_with_changed_bounds(j); + + } break; + case GT: + y_of_bound = 1; + case GE: { + auto low = numeric_pair(right_side, y_of_bound); + if (low < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + return; + } + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_bound_witness(j, ci); + insert_to_columns_with_changed_bounds(j); + + } break; + case EQ: { + auto v = numeric_pair(right_side, zero_of_type()); + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + set_infeasible_column(j); + } + + set_upper_bound_witness(j, ci); + set_lower_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + break; } - set_upper_bound_witness(j, ci); - set_lower_bound_witness(j, ci); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; - m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - break; + default: + UNREACHABLE(); } - - default: - UNREACHABLE(); - } - } - + // clang-format off void lar_solver::update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { lp_assert(!column_has_lower_bound(j) && column_has_upper_bound(j)); lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::upper_bound); @@ -2036,9 +2034,10 @@ namespace lp { set_infeasible_column(j); } m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - insert_to_columns_with_changed_bounds(j); set_lower_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = (low == m_mpq_lar_core_solver.m_r_upper_bounds[j] ? column_type::fixed : column_type::boxed); + insert_to_columns_with_changed_bounds(j); + } break; case EQ: @@ -2059,48 +2058,44 @@ namespace lp { UNREACHABLE(); } } + // clang-format on void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { lp_assert(!column_has_lower_bound(j) && !column_has_upper_bound(j)); - insert_to_columns_with_changed_bounds(j); mpq y_of_bound(0); switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, ci); - m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; - } - break; - case GT: - y_of_bound = 1; - case GE: - { - auto low = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - insert_to_columns_with_changed_bounds(j); - set_lower_bound_witness(j, ci); - m_mpq_lar_core_solver.m_column_types[j] = column_type::lower_bound; - } - break; - case EQ: - { - auto v = numeric_pair(right_side, zero_of_type()); - set_upper_bound_witness(j, ci); - set_lower_bound_witness(j, ci); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; - m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - break; - } + case LT: + y_of_bound = -1; + case LE: { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; + } break; + case GT: + y_of_bound = 1; + case GE: { + auto low = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::lower_bound; - default: - UNREACHABLE(); + } break; + case EQ: { + auto v = numeric_pair(right_side, zero_of_type()); + set_upper_bound_witness(j, ci); + set_lower_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + break; + } + + default: + UNREACHABLE(); } + insert_to_columns_with_changed_bounds(j); } - + // clang-format off bool lar_solver::column_corresponds_to_term(unsigned j) const { return tv::is_term(m_var_register.local_to_external(j)); } @@ -2204,8 +2199,8 @@ namespace lp { } bool lar_solver::get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq& rs, constraint_index& ci, bool& upper_bound) const { - lp_assert(t.is_term()) - unsigned j; + lp_assert(t.is_term()); + unsigned j; bool is_int; if (!m_var_register.external_is_used(t.index(), j, is_int)) return false; // the term does not have a bound because it does not correspond to a column diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index e9e26ffe6..a257ae4e8 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -135,8 +135,7 @@ class lar_solver : public column_namer { inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.clear(); } inline void increase_by_one_columns_with_changed_bounds() { m_columns_with_changed_bounds.increase_size_by_one(); } - inline void insert_to_columns_with_changed_bounds(unsigned j) { m_columns_with_changed_bounds.insert(j); } - + void insert_to_columns_with_changed_bounds(unsigned j); void update_column_type_and_bound_check_on_equal(unsigned j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index, unsigned&); void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index constr_index); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index aac645631..d05d34753 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -310,8 +310,7 @@ public: if (x < m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; ret = true;; - } - if (x > m_upper_bounds[j]) { + } else if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; ret = true; } @@ -554,31 +553,34 @@ public: } void update_x(unsigned j, const X & v) { - TRACE("lar_solver", tout << "j = " << j << ", v = " << v << "\n";); m_x[j] = v; + TRACE("lar_solver", tout << "j = " << j << ", v = " << v << (column_is_feasible(j)? " feas":" non-feas") << "\n";); } - - void add_delta_to_x(unsigned j, const X & delta) { - TRACE("lar_solver", tout << "j = " << j << ", delta = " << delta << "\n";); - m_x[j] += delta; - } - + // clang-format on + void add_delta_to_x(unsigned j, const X& delta) { + m_x[j] += delta; + TRACE("lar_solver", tout << "j = " << j << " v = " << m_x[j] << " delta = " << delta << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + } + // clang-format off + void track_column_feasibility(unsigned j) { if (column_is_feasible(j)) remove_column_from_inf_heap(j); else insert_column_into_inf_heap(j); } - void insert_column_into_inf_heap(unsigned j) { - TRACE("lar_solver", tout << "j = " << j << "\n";); - if (!m_inf_heap.contains(j)) + void insert_column_into_inf_heap(unsigned j) { + if (!m_inf_heap.contains(j)) { m_inf_heap.insert(j); + TRACE("lar_solver", tout << "j = " << j << "\n";); + } lp_assert(!column_is_feasible(j)); } void remove_column_from_inf_heap(unsigned j) { - TRACE("lar_solver", tout << "j = " << j << "\n";); - if (m_inf_heap.contains(j)) + if (m_inf_heap.contains(j)) { + TRACE("lar_solver", tout << "j = " << j << "\n";); m_inf_heap.erase(j); + } lp_assert(column_is_feasible(j)); } From 1c907e8d0941fd5fe56959bc947de22bae48d38c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 5 Jul 2023 09:14:57 -0700 Subject: [PATCH 25/41] add a comment --- src/math/lp/int_solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 45c458ae6..16fc2b1a3 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -103,6 +103,8 @@ namespace lp { lp_assert((x + (a1 / a2) * (-u * t) * x1).is_int()); // 1 = (u- l*x2 ) * a1 + (v + l*a1)*x2, for every integer l. rational d = u * t * x1; + // We can prove that x+alpha*d is integral, + // and any other delta, satisfying x+alpha*delta, is equal to d modulo a2. delta_plus = mod(d, a2); lp_assert(delta_plus > 0); delta_minus = delta_plus - a2; From 4ad3324d2e54efec047244bd4067426b3f051766 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 5 Jul 2023 12:58:17 -0700 Subject: [PATCH 26/41] fixes to trim Signed-off-by: Nikolaj Bjorner --- src/sat/sat_proof_trim.cpp | 70 ++++++++++++++------------------------ src/sat/sat_proof_trim.h | 13 ++++--- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index b4171f51d..d47819921 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -7,15 +7,13 @@ Abstract: - proof replay and trim + The proof is trimmed by re-running the proof steps and collecting justified literals + at level 0. The proof is obtained by back-tracing the justificiations attached to literals. Author: Nikolaj Bjorner 2023-10-04 - Notes: - - --*/ #include "sat/sat_proof_trim.h" @@ -31,10 +29,7 @@ namespace sat { vector> proof_trim::trim() { m_result.reset(); - m_core_literals.reset(); - m_propagated.resize(num_vars(), false); - m_core_literals.insert(literal_vector()); - m_core_literals.insert(m_conflict); + m_propagated.resize(num_vars(), false); IF_VERBOSE(10, s.display(verbose_stream() << "trim\n")); @@ -58,7 +53,7 @@ namespace sat { del(cl, clp); if (!in_core(cl)) continue; - IF_VERBOSE(4, verbose_stream() << cl << " in-core " << in_core(cl) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} "; verbose_stream() << "\n"); + IF_VERBOSE(4, verbose_stream() << cl << " in-core " << in_core(cl) << ": "; for (auto const& [k,v] : m_clauses) verbose_stream() << "{" << v.m_clauses << "} "; verbose_stream() << "\n"); m_result.push_back({id, unsigned_vector()}); if (is_initial) @@ -122,7 +117,6 @@ namespace sat { auto js = s.get_justification(l); bool in_coi = false; - //verbose_stream() << l << " " << js << "\n"; if (js.is_clause()) for (literal lit : s.get_clause(js)) in_coi |= m_in_coi.contains(lit.index()); @@ -281,19 +275,18 @@ namespace sat { } std::sort(m_clause.begin(), m_clause.end()); IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n"); - unsigned id; - VERIFY(m_clause2id.find(m_clause, id)); + auto& [clauses, id, in_core] = m_clauses.find(m_clause); + in_core = true; m_result.back().second.push_back(id); - m_core_literals.insert(m_clause); if (l != null_literal && s.lvl(l) == 0) { m_clause.reset(); m_clause.push_back(l); - m_core_literals.insert(m_clause); + m_clauses.insert_if_not_there(m_clause, {{}, 0, true }).m_in_core = true; } } bool proof_trim::in_core(literal_vector const& cl) const { - return m_core_literals.contains(cl); + return m_clauses.find(cl).m_in_core; } void proof_trim::revive(literal_vector const& cl, clause* cp) { @@ -313,23 +306,15 @@ namespace sat { auto* e = m_clauses.find_core(cl); if (!e) return cp; - auto& v = e->get_data().m_value; - if (!v.empty()) { - cp = v.back(); + auto& [clauses, id, in_core] = e->get_data().m_value; + if (!clauses.empty()) { + cp = clauses.back(); TRACE("sat", tout << "del: " << *cp << "\n"); s.detach_clause(*cp); - v.pop_back(); + clauses.pop_back(); } return cp; - } - - void proof_trim::save(literal_vector const& lits, clause* cl) { - if (!cl) - return; - IF_VERBOSE(3, verbose_stream() << "add: " << *cl << "\n"); - auto& v = m_clauses.insert_if_not_there(lits, clause_vector()); - v.push_back(cl); - } + } proof_trim::proof_trim(params_ref const& p, reslimit& lim): s(p, lim) { @@ -337,24 +322,26 @@ namespace sat { } void proof_trim::assume(unsigned id, bool is_initial) { - std::sort(m_clause.begin(), m_clause.end()); + std::sort(m_clause.begin(), m_clause.end()); if (unit_or_binary_occurs()) - return; - if (!m_conflict.empty() && m_clause.empty()) - m_trail.push_back({id , m_clause, nullptr, true, is_initial}); + return; + if (!m_conflict.empty() && m_clause.empty()) { + m_clauses.insert(m_clause, { {}, id, m_clause.empty() }); + m_trail.push_back({ id , m_clause, nullptr, true, is_initial }); + } if (!m_conflict.empty()) return; IF_VERBOSE(3, verbose_stream() << (is_initial?"assume ":"rup ") << m_clause << "\n"); - std::sort(m_clause.begin(), m_clause.end()); - m_clause2id.insert(m_clause, id); auto* cl = s.mk_clause(m_clause, status::redundant()); + auto& [clauses, id2, in_core] = m_clauses.insert_if_not_there(m_clause, { {}, id, m_clause.empty() }); + if (cl) + clauses.push_back(cl); + m_trail.push_back({ id, m_clause, cl, true, is_initial }); auto is_unit2 = [&]() { - if (s.value(m_clause[0]) == l_false) { - std::swap(m_clause[0], m_clause[1]); - return true; - } + if (s.value(m_clause[0]) == l_false) + std::swap(m_clause[0], m_clause[1]); return s.value(m_clause[1]) == l_false; }; @@ -375,16 +362,13 @@ namespace sat { return false; }; - m_trail.push_back({ id, m_clause, cl, true, is_initial }); if (m_clause.size() == 2 && is_unit2()) s.propagate_bin_clause(m_clause[0], m_clause[1]); else if (m_clause.size() > 2 && is_unit()) s.propagate_clause(*cl, true, 0, s.cls_allocator().get_offset(cl)); s.propagate(false); if (s.inconsistent() || all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) - set_conflict(m_clause, cl); - - save(m_clause, cl); + set_conflict(m_clause, cl); } /** @@ -411,6 +395,4 @@ namespace sat { void proof_trim::infer(unsigned id) { assume(id, false); } - - } diff --git a/src/sat/sat_proof_trim.h b/src/sat/sat_proof_trim.h index 9a43315f3..24091e69c 100644 --- a/src/sat/sat_proof_trim.h +++ b/src/sat/sat_proof_trim.h @@ -47,10 +47,16 @@ namespace sat { return a == b; } }; - map m_clauses; - map m_clause2id; - hashtable m_core_literals; + + struct clause_info { + clause_vector m_clauses; + unsigned m_id = 0; + bool m_in_core = false; + }; + + + map m_clauses; bool_vector m_propagated; void del(literal_vector const& cl, clause* cp); @@ -65,7 +71,6 @@ namespace sat { bool in_core(literal_vector const& cl) const; void revive(literal_vector const& cl, clause* cp); clause* del(literal_vector const& cl); - void save(literal_vector const& lits, clause* cl); uint_set m_units; bool unit_or_binary_occurs(); From f4b87b37638cf48f91830f46861aeb53d2597d9f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 5 Jul 2023 13:04:49 -0700 Subject: [PATCH 27/41] fix memory smash in euf completion Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/euf_completion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/simplifiers/euf_completion.cpp b/src/ast/simplifiers/euf_completion.cpp index 3ede7024e..280b5e6bf 100644 --- a/src/ast/simplifiers/euf_completion.cpp +++ b/src/ast/simplifiers/euf_completion.cpp @@ -307,7 +307,7 @@ namespace euf { } }; SASSERT(e); - if (num_scopes() > 0) + if (num_scopes() > 0 && m_canonical.size() > n->get_id()) m_trail.push(vtrail(m_canonical, n->get_id())); m_canonical.setx(n->get_id(), e); m_epochs.setx(n->get_id(), m_epoch, 0); From 3782eb1be4a5bd228d144b1885db364f75818533 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 5 Jul 2023 19:50:07 -0700 Subject: [PATCH 28/41] fix #6785 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 32c7a5763..e6e52dd79 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11286,6 +11286,8 @@ def Plus(re): >>> print(simplify(InRe("", re))) False """ + if z3_debug(): + _z3_assert(is_expr(re), "expression expected") return ReRef(Z3_mk_re_plus(re.ctx_ref(), re.as_ast()), re.ctx) @@ -11299,6 +11301,8 @@ def Option(re): >>> print(simplify(InRe("aa", re))) False """ + if z3_debug(): + _z3_assert(is_expr(re), "expression expected") return ReRef(Z3_mk_re_option(re.ctx_ref(), re.as_ast()), re.ctx) @@ -11317,6 +11321,8 @@ def Star(re): >>> print(simplify(InRe("", re))) True """ + if z3_debug(): + _z3_assert(is_expr(re), "expression expected") return ReRef(Z3_mk_re_star(re.ctx_ref(), re.as_ast()), re.ctx) @@ -11330,6 +11336,8 @@ def Loop(re, lo, hi=0): >>> print(simplify(InRe("", re))) False """ + if z3_debug(): + _z3_assert(is_expr(re), "expression expected") return ReRef(Z3_mk_re_loop(re.ctx_ref(), re.as_ast(), lo, hi), re.ctx) @@ -11343,11 +11351,17 @@ def Range(lo, hi, ctx=None): """ lo = _coerce_seq(lo, ctx) hi = _coerce_seq(hi, ctx) + if z3_debug(): + _z3_assert(is_expr(lo), "expression expected") + _z3_assert(is_expr(hi), "expression expected") 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 expression """ + if z3_debug(): + _z3_assert(is_expr(a), "expression expected") + _z3_assert(is_expr(b), "expression expected") return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx) def AllChar(regex_sort, ctx=None): From 68663fd97a9bda48155de1bf087e303be256f1eb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 6 Jul 2023 09:02:58 -0700 Subject: [PATCH 29/41] fix indentation for python file Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index e6e52dd79..20871d36e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11351,7 +11351,7 @@ def Range(lo, hi, ctx=None): """ lo = _coerce_seq(lo, ctx) hi = _coerce_seq(hi, ctx) - if z3_debug(): + if z3_debug(): _z3_assert(is_expr(lo), "expression expected") _z3_assert(is_expr(hi), "expression expected") return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx) @@ -11359,7 +11359,7 @@ def Range(lo, hi, ctx=None): def Diff(a, b, ctx=None): """Create the difference regular expression """ - if z3_debug(): + if z3_debug(): _z3_assert(is_expr(a), "expression expected") _z3_assert(is_expr(b), "expression expected") return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx) From 4e327babda4b3c202170a05fd4bf27a86c094972 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 6 Jul 2023 15:07:26 -0700 Subject: [PATCH 30/41] remove dead code --- src/math/lp/lar_solver.cpp | 66 -------------------------------------- src/math/lp/lar_solver.h | 3 -- src/smt/theory_lra.cpp | 5 +-- 3 files changed, 1 insertion(+), 73 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 60184962f..b72b88f55 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1380,72 +1380,6 @@ 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 lar_solver::is_fixed_at_bound(column_index const& j, vector>& bounds) { - 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) { - push(); - k = column_is_int(j) ? LE : LT; - auto ci = mk_var_bound(j, k, column_is_int(j) ? val - 1 : val); - update_column_type_and_bound(j, k, column_is_int(j) ? val - 1 : val, ci); - auto st = find_feasible_solution(); - bool infeasible = st == lp_status::INFEASIBLE; - if (infeasible) { - explanation exp; - get_infeasibility_explanation(exp); - unsigned_vector cis; - exp.remove(ci); - verbose_stream() << "tight upper bound " << j << " " << val << "\n"; - bounds.push_back({exp, j, true, val}); - } - pop(1); - return infeasible; - } - if (column_has_lower_bound(j) && - get_lower_bound(j).x == val) { - push(); - k = column_is_int(j) ? GE : GT; - auto ci = mk_var_bound(j, k, column_is_int(j) ? val + 1 : val); - update_column_type_and_bound(j, k, column_is_int(j) ? val + 1 : val, ci); - auto st = find_feasible_solution(); - bool infeasible = st == lp_status::INFEASIBLE; - if (infeasible) { - explanation exp; - get_infeasibility_explanation(exp); - exp.remove(ci); - verbose_stream() << "tight lower bound " << j << " " << val << "\n"; - bounds.push_back({exp, j, false, val}); - } - pop(1); - return infeasible; - } - - return false; - } - - bool lar_solver::has_fixed_at_bound(vector>& bounds) { - verbose_stream() << "has-fixed-at-bound\n"; - for (unsigned j = 0; j < A_r().m_columns.size(); ++j) { - auto ci = column_index(j); - if (is_fixed_at_bound(ci, bounds)) - verbose_stream() << "fixed " << j << "\n"; - } - verbose_stream() << "num fixed " << bounds.size() << "\n"; - if (!bounds.empty()) - find_feasible_solution(); - return !bounds.empty(); - } - - // 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 a257ae4e8..96af2d4ce 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -364,9 +364,6 @@ class lar_solver : public column_namer { } } - bool is_fixed_at_bound(column_index const& j, vector>& bounds); - bool has_fixed_at_bound(vector>& bounds); - 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; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 5372a3b33..b21424e65 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1659,10 +1659,7 @@ public: 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. - + TRACE("arith", display(tout)); #if 0 m_dist.reset(); m_dist.push(0, 1); From ff875c936fdf1da3c863a7cda23ac2e38fddd37f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 6 Jul 2023 16:45:22 -0700 Subject: [PATCH 31/41] add TRACE stmts, more efficient remove from inf_heap Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.h | 4 ---- src/math/lp/lp_core_solver_base.h | 6 +++--- src/math/lp/lp_primal_core_solver.h | 6 +++++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 96af2d4ce..fd6fef8fd 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -393,10 +393,6 @@ class lar_solver : public column_namer { m_mpq_lar_core_solver.m_r_solver.inf_heap().clear(); } - inline void remove_column_from_inf_set(unsigned j) { - m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_heap(j); - } - void pivot(int entering, int leaving) { m_mpq_lar_core_solver.pivot(entering, leaving); } diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index d05d34753..00d079f77 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -55,7 +55,7 @@ private: lp_status m_status; public: bool current_x_is_feasible() const { - TRACE("feas", + TRACE("feas_bug", if (!m_inf_heap.empty()) { tout << "column " << *m_inf_heap.begin() << " is infeasible" << std::endl; print_column_info(*m_inf_heap.begin(), tout); @@ -572,13 +572,13 @@ public: void insert_column_into_inf_heap(unsigned j) { if (!m_inf_heap.contains(j)) { m_inf_heap.insert(j); - TRACE("lar_solver", tout << "j = " << j << "\n";); + TRACE("lar_solver_inf_heap", tout << "insert into heap j = " << j << "\n";); } lp_assert(!column_is_feasible(j)); } void remove_column_from_inf_heap(unsigned j) { if (m_inf_heap.contains(j)) { - TRACE("lar_solver", tout << "j = " << j << "\n";); + TRACE("lar_solver_inf_heap", tout << "insert into heap j = " << j << "\n";); m_inf_heap.erase(j); } lp_assert(column_is_feasible(j)); diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 2a4a278a6..79e0d7590 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -341,6 +341,7 @@ namespace lp { int find_smallest_inf_column() { if (this->inf_heap().empty()) return -1; + return this->inf_heap().min_value(); } @@ -393,7 +394,10 @@ namespace lp { 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_heap(leaving); + // this will remove the leaving from the heap + TRACE("lar_solver_inf_heap", tout << "leaving = " << leaving + << " removed from inf_heap()\n";); + this->inf_heap().erase_min(); advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); if (this->current_x_is_feasible()) this->set_status(lp_status::OPTIMAL); From 0ab102cbec2c6964f68ea9fab7a596bcaf7ca7c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Jul 2023 09:28:47 -0700 Subject: [PATCH 32/41] fix coefficient extraction and passing in Farkas lemmas, thanks to H. F. Bryant Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_diagnostics.cpp | 17 +++++++++++++++-- src/sat/smt/arith_solver.h | 1 + src/smt/qi_queue.cpp | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index a3e48256d..783f268bd 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -136,9 +136,22 @@ namespace arith { arith_proof_hint const* solver::explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { arith_proof_hint* hint = nullptr; if (ctx.use_drat()) { + m_coeffs.reset(); + for (auto const& e : m_explanation) { + if (inequality_source == m_constraint_sources[e.ci()]) + m_coeffs.push_back(e.coeff()); + } + m_arith_hint.set_type(ctx, hint_type::farkas_h); - for (auto lit : core) - m_arith_hint.add_lit(rational::one(), lit); + if (m_coeffs.size() == core.size()) { + unsigned i = 0; + for (auto lit : core) + m_arith_hint.add_lit(m_coeffs[i], lit), ++i; + } + else { + for (auto lit : core) + m_arith_hint.add_lit(rational::one(), lit); + } for (auto const& [a,b] : eqs) m_arith_hint.add_eq(a, b); hint = m_arith_hint.mk(ctx); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 522867a0d..2364bb16e 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -251,6 +251,7 @@ namespace arith { lp::explanation m_explanation; vector m_nla_lemma_vector; literal_vector m_core, m_core2; + vector m_coeffs; svector m_eqs; vector m_params; nla::lemma m_lemma; diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index 781ed7c49..ca83c68c8 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -398,6 +398,8 @@ namespace smt { bool qi_queue::final_check_eh() { TRACE("qi_queue", display_delayed_instances_stats(tout); tout << "lazy threshold: " << m_params.m_qi_lazy_threshold << ", scope_level: " << m_context.get_scope_level() << "\n";); + + verbose_stream() << "delayed entries " << m_delayed_entries.size() << "\n"; if (m_params.m_qi_conservative_final_check) { bool init = false; float min_cost = 0.0; From 8c7525c97fb5cd1d77cc0ce9b023eb51d8605f13 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Jul 2023 09:29:38 -0700 Subject: [PATCH 33/41] revert log addition Signed-off-by: Nikolaj Bjorner --- src/smt/qi_queue.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index ca83c68c8..52399abb6 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -399,7 +399,6 @@ namespace smt { TRACE("qi_queue", display_delayed_instances_stats(tout); tout << "lazy threshold: " << m_params.m_qi_lazy_threshold << ", scope_level: " << m_context.get_scope_level() << "\n";); - verbose_stream() << "delayed entries " << m_delayed_entries.size() << "\n"; if (m_params.m_qi_conservative_final_check) { bool init = false; float min_cost = 0.0; From f645bcf6057eba9303b89e80022f071ee4a3611b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Jul 2023 09:54:18 -0700 Subject: [PATCH 34/41] add direct detection for integer expressions Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 45 +++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index ed36562ca..00f2c04ae 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -1569,21 +1569,48 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) { - numeral a; - if (m_util.is_numeral(arg, a)) { - result = a.is_int() ? m.mk_true() : m.mk_false(); + numeral n; + + if (m_util.is_numeral(arg, n)) { + result = n.is_int() ? m.mk_true() : m.mk_false(); return BR_DONE; } - else if (m_util.is_to_real(arg)) { + + if (m_util.is_to_real(arg)) { result = m.mk_true(); return BR_DONE; } - else { - result = m.mk_eq(m.mk_app(get_fid(), OP_TO_REAL, - m.mk_app(get_fid(), OP_TO_INT, arg)), - arg); - return BR_REWRITE3; + + ptr_buffer todo; + todo.push_back(arg); + expr_fast_mark1 mark; + for (unsigned i = 0; i < todo.size(); ++i) { + expr* e = todo[i]; + if (mark.is_marked(e)) + continue; + mark.mark(e, true); + if (m_util.is_to_real(e)) + continue; + if (m_util.is_numeral(e, n)) { + if (n.is_int()) + continue; + goto bail; + } + if (m_util.is_mul(e) || m_util.is_add(e) || m_util.is_sub(e) || m_util.is_uminus(e)) { + for (expr* a : *to_app(e)) + todo.push_back(a); + continue; + } + goto bail; } + result = m.mk_true(); + return BR_DONE; + + bail: + result = m.mk_eq(m.mk_app(get_fid(), OP_TO_REAL, + m.mk_app(get_fid(), OP_TO_INT, arg)), + arg); + return BR_REWRITE3; } br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) { From f5c069f899740c7ca4b82ee9ea52a448d1fd3e54 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Fri, 7 Jul 2023 10:57:07 -0600 Subject: [PATCH 35/41] Fix regular expression strings with escapes (#6797) --- scripts/mk_genfile_common.py | 28 ++++++++++++++-------------- scripts/mk_util.py | 28 ++++++++++++++-------------- scripts/update_api.py | 4 ++-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index bb6d884e6..cd2c7c83b 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -139,7 +139,7 @@ def mk_z3consts_py_internal(api_files, output_dir): assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) + words = re.split('[^-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] @@ -227,7 +227,7 @@ def mk_z3consts_dotnet_internal(api_files, output_dir): assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) + words = re.split('[^-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] @@ -315,7 +315,7 @@ def mk_z3consts_java_internal(api_files, package_name, output_dir): assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) + words = re.split('[^-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] @@ -441,7 +441,7 @@ def mk_z3consts_ml_internal(api_files, output_dir): assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) + words = re.split('[^-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] @@ -574,7 +574,7 @@ def mk_def_file_internal(defname, dll_name, export_header_files): for line in api: m = pat1.match(line) if m: - words = re.split('\W+', line) + words = re.split(r'\W+', line) i = 0 for w in words: if w == 'Z3_API': @@ -618,9 +618,9 @@ def mk_gparams_register_modules_internal(h_files_full_path, path): fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include "util/gparams.h"\n') - reg_pat = re.compile('[ \t]*REG_PARAMS\(\'([^\']*)\'\)') - reg_mod_pat = re.compile('[ \t]*REG_MODULE_PARAMS\(\'([^\']*)\', *\'([^\']*)\'\)') - reg_mod_descr_pat = re.compile('[ \t]*REG_MODULE_DESCRIPTION\(\'([^\']*)\', *\'([^\']*)\'\)') + reg_pat = re.compile(r'[ \t]*REG_PARAMS\(\'([^\']*)\'\)') + reg_mod_pat = re.compile(r'[ \t]*REG_MODULE_PARAMS\(\'([^\']*)\', *\'([^\']*)\'\)') + reg_mod_descr_pat = re.compile(r'[ \t]*REG_MODULE_DESCRIPTION\(\'([^\']*)\', *\'([^\']*)\'\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False with io.open(h_file, encoding='utf-8', mode='r') as fin: @@ -698,9 +698,9 @@ def mk_install_tactic_cpp_internal(h_files_full_path, path): fout.write('#include "cmd_context/tactic_cmds.h"\n') fout.write('#include "cmd_context/simplifier_cmds.h"\n') fout.write('#include "cmd_context/cmd_context.h"\n') - tactic_pat = re.compile('[ \t]*ADD_TACTIC\(.*\)') - probe_pat = re.compile('[ \t]*ADD_PROBE\(.*\)') - simplifier_pat = re.compile('[ \t]*ADD_SIMPLIFIER\(.*\)') + tactic_pat = re.compile(r'[ \t]*ADD_TACTIC\(.*\)') + probe_pat = re.compile(r'[ \t]*ADD_PROBE\(.*\)') + simplifier_pat = re.compile(r'[ \t]*ADD_SIMPLIFIER\(.*\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False try: @@ -780,10 +780,10 @@ def mk_mem_initializer_cpp_internal(h_files_full_path, path): fullname = os.path.join(path, 'mem_initializer.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') - initializer_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\'\)') + initializer_pat = re.compile(r'[ \t]*ADD_INITIALIZER\(\'([^\']*)\'\)') # ADD_INITIALIZER with priority - initializer_prio_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\',[ \t]*(-?[0-9]*)\)') - finalizer_pat = re.compile('[ \t]*ADD_FINALIZER\(\'([^\']*)\'\)') + initializer_prio_pat = re.compile(r'[ \t]*ADD_INITIALIZER\(\'([^\']*)\',[ \t]*(-?[0-9]*)\)') + finalizer_pat = re.compile(r'[ \t]*ADD_FINALIZER\(\'([^\']*)\'\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False with io.open(h_file, encoding='utf-8', mode='r') as fin: diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 805aea19d..fbb4b3590 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -395,7 +395,7 @@ def check_java(): else: # Search for jni.h in the library directories... t = open('errout', 'r') - open_pat = re.compile("\[search path for class files: (.*)\]") + open_pat = re.compile(r"\[search path for class files: (.*)\]") cdirs = [] for line in t: m = open_pat.match(line) @@ -812,8 +812,8 @@ def parse_options(): def extract_c_includes(fname): result = {} # We look for well behaved #include directives - std_inc_pat = re.compile("[ \t]*#include[ \t]*\"(.*)\"[ \t]*") - system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*") + std_inc_pat = re.compile(r"[ \t]*#include[ \t]*\"(.*)\"[ \t]*") + system_inc_pat = re.compile(r"[ \t]*#include[ \t]*\<.*\>[ \t]*") # We should generate and error for any occurrence of #include that does not match the previous pattern. non_std_inc_pat = re.compile(".*#include.*") @@ -1720,7 +1720,7 @@ class DotNetDLLComponent(Component): print("Version output to csproj:", version) - core_csproj_str = """ + core_csproj_str = r""" netstandard1.4 @@ -2246,7 +2246,7 @@ class DotNetExampleComponent(ExampleComponent): else: platform = 'x86' - dotnet_proj_str = """ + dotnet_proj_str = r""" Exe netcoreapp2.0 @@ -3162,7 +3162,7 @@ def mk_vs_proj_property_groups(f, name, target_ext, type): f.write(' Win32Proj\n') f.write(' %s\n' % get_platform_toolset_str()) f.write(' \n') - f.write(' \n') + f.write(' \n') f.write(' \n') f.write(' %s\n' % type) f.write(' Unicode\n') @@ -3173,24 +3173,24 @@ def mk_vs_proj_property_groups(f, name, target_ext, type): f.write(' Unicode\n') f.write(' false\n') f.write(' \n') - f.write(' \n') + f.write(' \n') f.write(' \n') f.write(' \n') - f.write(' \n') + f.write(' \n') f.write(' \n') f.write(' \n') - f.write(' $(SolutionDir)\$(ProjectName)\$(Configuration)\\n') + f.write(' $(SolutionDir)\\$(ProjectName)\\$(Configuration)\\\n') f.write(' %s\n' % name) f.write(' .%s\n' % target_ext) - f.write(' $(SolutionDir)\$(ProjectName)\$(Configuration)\\n') + f.write(' $(SolutionDir)\\$(ProjectName)\\$(Configuration)\\\n') f.write(' %s\n' % name) f.write(' .%s\n' % target_ext) f.write(' \n') f.write(' \n') - f.write(' $(ProjectName)\$(Configuration)\\n') + f.write(' $(ProjectName)\\$(Configuration)\\\n') f.write(' \n') f.write(' \n') - f.write(' $(ProjectName)\$(Configuration)\\n') + f.write(' $(ProjectName)\\$(Configuration)\\\n') f.write(' \n') @@ -3267,7 +3267,7 @@ def mk_vs_proj(name, components): mk_vs_proj_link_exe(f, name, debug=False) f.write(' \n') mk_vs_proj_dep_groups(f, name, components) - f.write(' \n') + f.write(' \n') f.write(' \n') f.write(' \n') f.write('\n') @@ -3308,7 +3308,7 @@ def mk_vs_proj_dll(name, components): mk_vs_proj_link_dll(f, name, debug=False) f.write(' \n') mk_vs_proj_dep_groups(f, name, components) - f.write(' \n') + f.write(' \n') f.write(' \n') f.write(' \n') f.write('\n') diff --git a/scripts/update_api.py b/scripts/update_api.py index 89a895e3b..dc25f8df2 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -116,8 +116,8 @@ class APITypes: def def_Types(self, api_files): global Closures - pat1 = re.compile(" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*") - pat2 = re.compile("Z3_DECLARE_CLOSURE\((.*),(.*), \((.*)\)\)") + pat1 = re.compile(r" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*") + pat2 = re.compile(r"Z3_DECLARE_CLOSURE\((.*),(.*), \((.*)\)\)") for api_file in api_files: with open(api_file, 'r') as api: for line in api: From 4cb158a79b2f3c8afdf96e12e2873de815b66c74 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:58:41 +0200 Subject: [PATCH 36/41] User Propagator: Return if propagated lemma is redundant (#6791) * Give users ability to see if propagation failed * Skip propagations in the new core if they are already satisfied --- src/api/api_solver.cpp | 6 +++--- src/api/c++/z3++.h | 8 ++++---- src/api/dotnet/UserPropagator.cs | 16 ++++++++++++---- src/api/python/z3/z3.py | 2 +- src/api/z3_api.h | 16 ++++++++++------ src/sat/smt/user_solver.cpp | 14 +++++++++----- src/sat/smt/user_solver.h | 2 +- src/smt/theory_user_propagator.cpp | 11 ++++++----- src/smt/theory_user_propagator.h | 2 +- src/tactic/user_propagator_base.h | 2 +- 10 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 08f864226..ae77cb4ea 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -1092,15 +1092,15 @@ extern "C" { Z3_CATCH; } - void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned num_fixed, Z3_ast const* fixed_ids, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq) { + bool Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback s, unsigned num_fixed, Z3_ast const* fixed_ids, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq) { Z3_TRY; LOG_Z3_solver_propagate_consequence(c, s, num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, conseq); RESET_ERROR_CODE(); expr* const * _fixed_ids = (expr* const*) fixed_ids; expr* const * _eq_lhs = (expr*const*) eq_lhs; expr* const * _eq_rhs = (expr*const*) eq_rhs; - reinterpret_cast(s)->propagate_cb(num_fixed, _fixed_ids, num_eqs, _eq_lhs, _eq_rhs, to_expr(conseq)); - Z3_CATCH; + return reinterpret_cast(s)->propagate_cb(num_fixed, _fixed_ids, num_eqs, _eq_lhs, _eq_rhs, to_expr(conseq)); + Z3_CATCH_RETURN(false); } void Z3_API Z3_solver_propagate_created(Z3_context c, Z3_solver s, Z3_created_eh created_eh) { diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 88bbd2dcc..799644970 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -4496,14 +4496,14 @@ namespace z3 { Z3_solver_propagate_consequence(ctx(), cb, fixed.size(), _fixed.ptr(), lhs.size(), _lhs.ptr(), _rhs.ptr(), conseq); } - void propagate(expr_vector const& fixed, expr const& conseq) { + bool propagate(expr_vector const& fixed, expr const& conseq) { assert(cb); assert((Z3_context)conseq.ctx() == (Z3_context)ctx()); array _fixed(fixed); - Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), 0, nullptr, nullptr, conseq); + return Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), 0, nullptr, nullptr, conseq); } - void propagate(expr_vector const& fixed, + bool propagate(expr_vector const& fixed, expr_vector const& lhs, expr_vector const& rhs, expr const& conseq) { assert(cb); @@ -4513,7 +4513,7 @@ namespace z3 { array _lhs(lhs); array _rhs(rhs); - Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), lhs.size(), _lhs.ptr(), _rhs.ptr(), conseq); + return Z3_solver_propagate_consequence(ctx(), cb, _fixed.size(), _fixed.ptr(), lhs.size(), _lhs.ptr(), _rhs.ptr(), conseq); } }; diff --git a/src/api/dotnet/UserPropagator.cs b/src/api/dotnet/UserPropagator.cs index 68f2b0127..e591c3354 100644 --- a/src/api/dotnet/UserPropagator.cs +++ b/src/api/dotnet/UserPropagator.cs @@ -252,21 +252,29 @@ namespace Microsoft.Z3 /// /// Propagate consequence + /// + /// if the propagated expression is new for the solver; + /// if the propagation was ignored + /// /// - public void Propagate(IEnumerable terms, Expr conseq) + public bool Propagate(IEnumerable terms, Expr conseq) { - Propagate(terms, new EqualityPairs(), conseq); + return Propagate(terms, new EqualityPairs(), conseq); } /// /// Propagate consequence + /// + /// if the propagated expression is new for the solver; + /// if the propagation was ignored + /// /// - public void Propagate(IEnumerable terms, EqualityPairs equalities, Expr conseq) + public bool Propagate(IEnumerable terms, EqualityPairs equalities, Expr conseq) { var nTerms = Z3Object.ArrayToNative(terms.ToArray()); var nLHS = Z3Object.ArrayToNative(equalities.LHS.ToArray()); var nRHS = Z3Object.ArrayToNative(equalities.RHS.ToArray()); - Native.Z3_solver_propagate_consequence(ctx.nCtx, this.callback, (uint)nTerms.Length, nTerms, (uint)equalities.Count, nLHS, nRHS, conseq.NativeObject); + return Native.Z3_solver_propagate_consequence(ctx.nCtx, this.callback, (uint)nTerms.Length, nTerms, (uint)equalities.Count, nLHS, nRHS, conseq.NativeObject) != 0; } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 20871d36e..5c067f4d3 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11704,7 +11704,7 @@ class UserPropagateBase: num_eqs = len(eqs) _lhs, _num_lhs = _to_ast_array([x for x, y in eqs]) _rhs, _num_rhs = _to_ast_array([y for x, y in eqs]) - Z3_solver_propagate_consequence(e.ctx.ref(), ctypes.c_void_p( + return Z3_solver_propagate_consequence(e.ctx.ref(), ctypes.c_void_p( self.cb), num_fixed, _ids, num_eqs, _lhs, _rhs, e.ast) def conflict(self, deps = [], eqs = []): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 54974d57b..b73c71912 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7147,14 +7147,18 @@ extern "C" { /** \brief propagate a consequence based on fixed values. - This is a callback a client may invoke during the fixed_eh callback. + This is a callback a client may invoke during the fixed_eh callback. The callback adds a propagation consequence based on the fixed values of the - \c ids. - - def_API('Z3_solver_propagate_consequence', VOID, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST))) + \c ids. + The solver might discard the propagation in case it is true in the current state. + The function returns false in this case; otw. the function returns true. + At least one propagation in the final callback has to return true in order to + prevent the solver from finishing. + + def_API('Z3_solver_propagate_consequence', BOOL, (_in(CONTEXT), _in(SOLVER_CALLBACK), _in(UINT), _in_array(2, AST), _in(UINT), _in_array(4, AST), _in_array(4, AST), _in(AST))) */ - - void Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback cb, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); + + bool Z3_API Z3_solver_propagate_consequence(Z3_context c, Z3_solver_callback cb, unsigned num_fixed, Z3_ast const* fixed, unsigned num_eqs, Z3_ast const* eq_lhs, Z3_ast const* eq_rhs, Z3_ast conseq); /** \brief Check whether the assertions in a given solver are consistent or not. diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 1e8897b8c..2823c81f8 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -43,15 +43,19 @@ namespace user_solver { m_prop.push_back(prop_info(explain, v, r)); } - void solver::propagate_cb( - unsigned num_fixed, expr* const* fixed_ids, - unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, - expr* conseq) { + bool solver::propagate_cb( + unsigned num_fixed, expr* const* fixed_ids, + unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, + expr* conseq) { + auto* n = ctx.get_enode(conseq); + if (n && s().value(ctx.enode2literal(n)) == l_true) + return false; m_fixed_ids.reset(); for (unsigned i = 0; i < num_fixed; ++i) m_fixed_ids.push_back(get_th_var(fixed_ids[i])); m_prop.push_back(prop_info(num_fixed, m_fixed_ids.data(), num_eqs, eq_lhs, eq_rhs, expr_ref(conseq, m))); DEBUG_CODE(validate_propagation();); + return true; } void solver::register_cb(expr* e) { @@ -76,7 +80,7 @@ namespace user_solver { sat::check_result solver::check() { if (!(bool)m_final_eh) - return sat::check_result::CR_DONE; + return sat::check_result::CR_DONE; unsigned sz = m_prop.size(); m_final_eh(m_user_context, this); return sz == m_prop.size() ? sat::check_result::CR_DONE : sat::check_result::CR_CONTINUE; diff --git a/src/sat/smt/user_solver.h b/src/sat/smt/user_solver.h index bd1b703e0..cd94441ea 100644 --- a/src/sat/smt/user_solver.h +++ b/src/sat/smt/user_solver.h @@ -135,7 +135,7 @@ namespace user_solver { bool has_fixed() const { return (bool)m_fixed_eh; } - void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; + bool propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; void register_cb(expr* e) override; bool next_split_cb(expr* e, unsigned idx, lbool phase) override; diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 2d5b4917d..7c72419c1 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -83,7 +83,7 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { } -void theory_user_propagator::propagate_cb( +bool theory_user_propagator::propagate_cb( unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr* conseq) { @@ -95,9 +95,10 @@ void theory_user_propagator::propagate_cb( 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)); + if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) + return false; + m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, _conseq)); + return true; } void theory_user_propagator::register_cb(expr* e) { @@ -386,7 +387,7 @@ bool theory_user_propagator::internalize_atom(app* atom, bool gate_ctx) { return internalize_term(atom); } -bool theory_user_propagator::internalize_term(app* term) { +bool theory_user_propagator::internalize_term(app* term) { for (auto arg : *term) ensure_enode(arg); if (term->get_family_id() == get_id() && !ctx.e_internalized(term)) diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index 5a6eafc0a..2f045b0ba 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -130,7 +130,7 @@ namespace smt { bool has_fixed() const { return (bool)m_fixed_eh; } - void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; + bool propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override; void register_cb(expr* e) override; bool next_split_cb(expr* e, unsigned idx, lbool phase) override; diff --git a/src/tactic/user_propagator_base.h b/src/tactic/user_propagator_base.h index d4dae5166..40c0fa8fc 100644 --- a/src/tactic/user_propagator_base.h +++ b/src/tactic/user_propagator_base.h @@ -9,7 +9,7 @@ namespace user_propagator { class callback { public: virtual ~callback() = default; - virtual void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr* conseq) = 0; + virtual bool propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* eq_lhs, expr* const* eq_rhs, expr* conseq) = 0; virtual void register_cb(expr* e) = 0; virtual bool next_split_cb(expr* e, unsigned idx, lbool phase) = 0; }; From 0fceb80e0fe6234275264f0b7882ec04d77858c5 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 7 Jul 2023 11:48:21 -0700 Subject: [PATCH 37/41] edit tracing, add lar_solver::column_is_feasible() --- src/math/lp/lar_solver.cpp | 10 ++++++---- src/math/lp/lar_solver.h | 1 + src/math/lp/lp_core_solver_base.h | 25 ++++++++++++------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index b72b88f55..0ad3420b9 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -213,7 +213,7 @@ namespace lp { void lar_solver::fill_explanation_from_crossed_bounds_column(explanation& evidence) const { lp_assert(static_cast(get_column_type(m_crossed_bounds_column)) >= static_cast(column_type::boxed)); - lp_assert(!m_mpq_lar_core_solver.m_r_solver.column_is_feasible(m_crossed_bounds_column)); + lp_assert(!column_is_feasible(m_crossed_bounds_column)); // this is the case when the lower bound is in conflict with the upper one const ul_pair& ul = m_columns_to_ul_pairs[m_crossed_bounds_column]; @@ -673,7 +673,7 @@ namespace lp { 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;); + (column_is_feasible(bj) ? "feas" : "inf") << std::endl;); } } @@ -1327,7 +1327,7 @@ namespace lp { became_feas.clear(); for (unsigned j : m_mpq_lar_core_solver.m_r_solver.inf_heap()) { lp_assert(m_mpq_lar_core_solver.m_r_heading[j] >= 0); - if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) + if (column_is_feasible(j)) became_feas.push_back(j); } for (unsigned j : became_feas) @@ -1738,16 +1738,18 @@ namespace lp { lconstraint_kind kind, const mpq& right_side, constraint_index constr_index) { + TRACE("lar_solver_feas", tout << "j = " << j << " was " << (this->column_is_feasible(j)?"feas":"non-feas") << std::endl;); m_constraints.activate(constr_index); if (column_has_upper_bound(j)) update_column_type_and_bound_with_ub(j, kind, right_side, constr_index); else update_column_type_and_bound_with_no_ub(j, kind, right_side, constr_index); + TRACE("lar_solver_feas", tout << "j = " << j << " became " << (this->column_is_feasible(j)?"feas":"non-feas") << ", and " << (this->column_is_bounded(j)? "bounded":"non-bounded") << std::endl;); } // clang-format on void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { m_columns_with_changed_bounds.insert(j); - TRACE("lar_solver", tout << "column " << j << (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); } // clang-format off void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index fd6fef8fd..b130c198e 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -481,6 +481,7 @@ class lar_solver : public column_namer { unsigned map_term_index_to_column_index(unsigned j) const; bool column_is_fixed(unsigned j) const; bool column_is_free(unsigned j) const; + bool column_is_feasible(unsigned j) const { return m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j);} unsigned column_to_reported_index(unsigned j) const; lp_settings& settings(); lp_settings const& settings() const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 00d079f77..e65e839a6 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -540,29 +540,28 @@ public: void update_x_with_feasibility_tracking(unsigned j, const X & v) { - TRACE("lar_solver", tout << "j = " << j << ", v = " << v << "\n";); + TRACE("lar_solver_feas_bug", tout << "j = " << j << ", v = " << v << "\n";); m_x[j] = v; track_column_feasibility(j); } void add_delta_to_x_and_track_feasibility(unsigned j, const X & del) { - TRACE("lar_solver", tout << "del = " << del << ", was x[" << j << "] = " << m_x[j] << "\n";); + TRACE("lar_solver_feas_bug", tout << "del = " << del << ", was x[" << j << "] = " << m_x[j] << "\n";); m_x[j] += del; - TRACE("lar_solver", tout << "became x[" << j << "] = " << m_x[j] << "\n";); + TRACE("lar_solver_feas_bug", tout << "became x[" << j << "] = " << m_x[j] << "\n";); track_column_feasibility(j); } void update_x(unsigned j, const X & v) { m_x[j] = v; - TRACE("lar_solver", tout << "j = " << j << ", v = " << v << (column_is_feasible(j)? " feas":" non-feas") << "\n";); + TRACE("lar_solver_feas", tout << "not tracking feas j = " << j << ", v = " << v << (column_is_feasible(j)? " feas":" non-feas") << "\n";); } - // clang-format on - void add_delta_to_x(unsigned j, const X& delta) { - m_x[j] += delta; - TRACE("lar_solver", tout << "j = " << j << " v = " << m_x[j] << " delta = " << delta << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); - } - // clang-format off - + + void add_delta_to_x(unsigned j, const X& delta) { + m_x[j] += delta; + TRACE("lar_solver_feas", tout << "not tracking feas j = " << j << " v = " << m_x[j] << " delta = " << delta << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + } + void track_column_feasibility(unsigned j) { if (column_is_feasible(j)) remove_column_from_inf_heap(j); @@ -572,7 +571,7 @@ public: void insert_column_into_inf_heap(unsigned j) { if (!m_inf_heap.contains(j)) { m_inf_heap.insert(j); - TRACE("lar_solver_inf_heap", tout << "insert into heap j = " << j << "\n";); + TRACE("lar_solver_inf_heap", tout << "insert into inf_heap j = " << j << "\n";); } lp_assert(!column_is_feasible(j)); } @@ -585,7 +584,7 @@ public: } void clear_inf_heap() { - TRACE("lar_solver",); + TRACE("lar_solver_feas",); m_inf_heap.clear(); } From 56b5492752088b23393ebaa00b1e251cad1ded97 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 7 Jul 2023 15:05:17 -0700 Subject: [PATCH 38/41] remove dead code --- src/math/lp/lp_core_solver_base.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index e65e839a6..47e09a4c8 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -538,13 +538,6 @@ public: return m_basis_heading[j] >= 0; } - - void update_x_with_feasibility_tracking(unsigned j, const X & v) { - TRACE("lar_solver_feas_bug", tout << "j = " << j << ", v = " << v << "\n";); - m_x[j] = v; - track_column_feasibility(j); - } - void add_delta_to_x_and_track_feasibility(unsigned j, const X & del) { TRACE("lar_solver_feas_bug", tout << "del = " << del << ", was x[" << j << "] = " << m_x[j] << "\n";); m_x[j] += del; From 5806869ae43656fc92411ceeb38311a795167435 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Jul 2023 17:22:56 -0700 Subject: [PATCH 39/41] fix #6792, add scaffolding for type variables Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 6 ++++++ src/ast/ast.h | 4 ++++ src/ast/rewriter/bool_rewriter.cpp | 11 +++++++++-- src/cmd_context/pdecl.cpp | 24 ++++++++++++++++++++++++ src/cmd_context/pdecl.h | 19 ++++++++++++++++--- src/math/lp/lar_solver.cpp | 15 ++++++++------- src/math/lp/lp_core_solver_base.h | 5 +++-- src/parsers/smt2/smt2parser.cpp | 26 ++++++++++++++++++++++++++ 8 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 7f9542fe4..354a945e3 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1378,6 +1378,7 @@ void ast_manager::init() { ENSURE(model_value_family_id == mk_family_id("model-value")); ENSURE(user_sort_family_id == mk_family_id("user-sort")); ENSURE(arith_family_id == mk_family_id("arith")); + ENSURE(poly_family_id == mk_family_id("polymorphic")); basic_decl_plugin * plugin = alloc(basic_decl_plugin); register_plugin(basic_family_id, plugin); m_bool_sort = plugin->mk_bool_sort(); @@ -2019,6 +2020,11 @@ sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_para return plugin->mk_sort(kind, num_parameters, parameters); } +sort * ast_manager::mk_type_var(symbol const& name) { + sort_info si(poly_family_id, 0); + return mk_sort(name, &si); +} + func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm, bool inj) { func_decl_info info(null_family_id, null_decl_kind); diff --git a/src/ast/ast.h b/src/ast/ast.h index e0ae7b92f..bdb76b2dc 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -85,6 +85,7 @@ const family_id user_sort_family_id = 4; const family_id last_builtin_family_id = 4; const family_id arith_family_id = 5; +const family_id poly_family_id = 6; // ----------------------------------- // @@ -622,6 +623,7 @@ public: sort_size const & get_num_elements() const { return get_info()->get_num_elements(); } void set_num_elements(sort_size const& s) { get_info()->set_num_elements(s); } unsigned get_size() const { return get_obj_size(); } + bool is_type_var() const { return get_family_id() == poly_family_id; } }; // ----------------------------------- @@ -1709,6 +1711,8 @@ public: sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); } + sort * mk_type_var(symbol const& name); + sort * mk_sort(symbol const & name, sort_info const & info) { if (info.get_family_id() == null_family_id) { return mk_uninterpreted_sort(name); diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9806424a3..9354b53f6 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -665,12 +665,19 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) SASSERT(m().is_value(val)); if (m().are_distinct(val, e)) { - mk_eq(t, val, result); + if (get_depth(t) < 500) + mk_eq(t, val, result); + else + result = m().mk_eq(t, val); + result = m().mk_and(result, cond); return BR_REWRITE2; } if (m().are_distinct(val, t)) { - mk_eq(e, val, result); + if (get_depth(e) < 500) + mk_eq(e, val, result); + else + result = m().mk_eq(e, val); result = m().mk_and(result, m().mk_not(cond)); return BR_REWRITE2; } diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 776a91a28..a64d0ede9 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -348,6 +348,26 @@ std::ostream& psort_user_decl::display(std::ostream & out) const { return out << ")"; } +// ------------------- +// psort_type_var_decl + +psort_type_var_decl::psort_type_var_decl(unsigned id, pdecl_manager & m, symbol const & n): + psort_decl(id, 0, m, n) { + m_psort_kind = PSORT_TV; +} + +void psort_type_var_decl::finalize(pdecl_manager & m) { + psort_decl::finalize(m); +} + +sort * psort_type_var_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { + return m.m().mk_type_var(m_name); +} + +std::ostream& psort_type_var_decl::display(std::ostream & out) const { + return out << "(declare-type-var " << m_name << ")"; +} + // ------------------- // psort_dt_decl @@ -969,6 +989,10 @@ psort_decl * pdecl_manager::mk_psort_dt_decl(unsigned num_params, symbol const & return new (a().allocate(sizeof(psort_dt_decl))) psort_dt_decl(m_id_gen.mk(), num_params, *this, n); } +psort_decl * pdecl_manager::mk_psort_type_var_decl(symbol const & n) { + return new (a().allocate(sizeof(psort_type_var_decl))) psort_type_var_decl(m_id_gen.mk(), *this, n); +} + psort_decl * pdecl_manager::mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k) { return new (a().allocate(sizeof(psort_builtin_decl))) psort_builtin_decl(m_id_gen.mk(), *this, n, fid, k); diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 818c97eda..a3005f182 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -62,7 +62,7 @@ class psort_inst_cache; */ class psort : public pdecl { protected: - psort_inst_cache * m_inst_cache; + psort_inst_cache* m_inst_cache; friend class pdecl_manager; psort(unsigned id, unsigned num_params):pdecl(id, num_params), m_inst_cache(nullptr) {} bool is_psort() const override { return true; } @@ -86,7 +86,7 @@ typedef ptr_hashtable psort_table; #define PSORT_DECL_VAR_PARAMS UINT_MAX -typedef enum { PSORT_BASE = 0, PSORT_USER, PSORT_BUILTIN, PSORT_DT } psort_decl_kind; +typedef enum { PSORT_BASE = 0, PSORT_USER, PSORT_BUILTIN, PSORT_DT, PSORT_TV } psort_decl_kind; class psort_decl : public pdecl { protected: @@ -123,7 +123,19 @@ public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; std::ostream& display(std::ostream & out) const override; }; - + +class psort_type_var_decl : public psort_decl { +protected: + friend class pdecl_manager; + psort * m_def; + psort_type_var_decl(unsigned id, pdecl_manager & m, symbol const & n); + size_t obj_size() const override { return sizeof(psort_type_var_decl); } + void finalize(pdecl_manager & m) override; +public: + sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; + std::ostream& display(std::ostream & out) const override; +}; + class psort_builtin_decl : public psort_decl { protected: friend class pdecl_manager; @@ -304,6 +316,7 @@ public: psort_decl * mk_psort_dt_decl(unsigned num_params, symbol const & n); psort_decl * mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def); psort_decl * mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k); + psort_decl * mk_psort_type_var_decl(symbol const& n); paccessor_decl * mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p); pconstructor_decl * mk_pconstructor_decl(unsigned num_params, symbol const & s, symbol const & r, unsigned num, paccessor_decl * const * as); pdatatype_decl * mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs); diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index b72b88f55..b6e9b63fe 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1858,7 +1858,8 @@ namespace lp { m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; set_upper_bound_witness(j, ci); insert_to_columns_with_changed_bounds(j); - } break; + break; + } case GT: y_of_bound = 1; case GE: { @@ -1873,8 +1874,8 @@ namespace lp { set_lower_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = (low == m_mpq_lar_core_solver.m_r_upper_bounds[j] ? column_type::fixed : column_type::boxed); insert_to_columns_with_changed_bounds(j); - - } break; + break; + } case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j] || v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { @@ -1911,8 +1912,8 @@ namespace lp { set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = (up == m_mpq_lar_core_solver.m_r_lower_bounds[j] ? column_type::fixed : column_type::boxed); insert_to_columns_with_changed_bounds(j); - - } break; + break; + } case GT: y_of_bound = 1; case GE: { @@ -1923,8 +1924,8 @@ namespace lp { m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; set_lower_bound_witness(j, ci); insert_to_columns_with_changed_bounds(j); - - } break; + break; + } case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 00d079f77..e058100ab 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -309,8 +309,9 @@ public: case column_type::boxed: if (x < m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; - ret = true;; - } else if (x > m_upper_bounds[j]) { + ret = true; + } + else if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; ret = true; } diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 081a8c838..5e22d9d29 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -102,6 +102,7 @@ namespace smt2 { symbol m_declare_const; symbol m_define_sort; symbol m_declare_sort; + symbol m_declare_type_var; symbol m_declare_datatypes; symbol m_declare_datatype; symbol m_par; @@ -856,6 +857,26 @@ namespace smt2 { if (ct_decls.empty()) throw parser_exception("invalid datatype declaration, datatype does not have any constructors"); } + + void parse_declare_type_var() { + SASSERT(curr_is_identifier()); + SASSERT(curr_id() == m_declare_type_var); + next(); + + check_nonreserved_identifier("invalid sort declaration, symbol expected"); + symbol id = curr_id(); + if (m_ctx.find_psort_decl(id) != nullptr) + throw parser_exception("invalid sort declaration, sort already declared/defined"); + next(); + check_rparen("invalid sort declaration, ')' expected"); + + psort_decl * decl = pm().mk_psort_type_var_decl(id); + m_ctx.insert(decl); + + m_ctx.print_success(); + next(); + + } void parse_declare_datatypes() { SASSERT(curr_is_identifier()); @@ -2975,6 +2996,10 @@ namespace smt2 { parse_declare_sort(); return; } + if (s == m_declare_type_var) { + parse_declare_type_var(); + return; + } if (s == m_declare_datatypes) { parse_declare_datatypes(); return; @@ -3048,6 +3073,7 @@ namespace smt2 { m_declare_const("declare-const"), m_define_sort("define-sort"), m_declare_sort("declare-sort"), + m_declare_type_var("declare-type-var"), m_declare_datatypes("declare-datatypes"), m_declare_datatype("declare-datatype"), m_par("par"), From dc0887db5ab909f149c5b4894433f24ee53b3dec Mon Sep 17 00:00:00 2001 From: THE Spellchecker <113312245+THE-Spellchecker@users.noreply.github.com> Date: Sun, 9 Jul 2023 14:56:10 -0400 Subject: [PATCH 40/41] Typo Fixes (#6803) --- CMakeLists.txt | 4 +- README-CMake.md | 6 +- RELEASE_NOTES.md | 12 +- scripts/mk_genfile_common.py | 2 +- src/ackermannization/ackermannize_bv_tactic.h | 2 +- src/ackermannization/ackr_info.h | 2 +- src/api/api_context.h | 2 +- src/api/api_fpa.cpp | 2 +- src/api/c++/z3++.h | 2 +- src/api/dotnet/Context.cs | 2 +- src/api/dotnet/Symbol.cs | 2 +- src/api/dotnet/UserPropagator.cs | 2 +- src/api/java/Context.java | 2 +- src/api/java/IDecRefQueue.java | 2 +- src/api/java/Quantifier.java | 2 +- src/api/js/README.md | 2 +- src/api/js/scripts/parse-api.ts | 2 +- src/api/ml/README.md | 2 +- src/api/python/setup.py | 2 +- src/api/python/z3/z3.py | 2 +- src/api/python/z3/z3util.py | 4 +- src/api/z3_api.h | 14 +- src/ast/bv_decl_plugin.cpp | 2 +- src/ast/bv_decl_plugin.h | 2 +- src/ast/char_decl_plugin.cpp | 2 +- src/ast/char_decl_plugin.h | 2 +- src/ast/converters/expr_inverter.cpp | 4 +- src/ast/datatype_decl_plugin.cpp | 2 +- src/ast/datatype_decl_plugin.h | 2 +- src/ast/euf/euf_egraph.h | 2 +- src/ast/euf/euf_justification.h | 2 +- src/ast/macros/macro_finder.cpp | 2 +- src/ast/macros/macro_manager.h | 2 +- src/ast/normal_forms/defined_names.cpp | 2 +- src/ast/proofs/proof_utils.cpp | 4 +- src/ast/rewriter/arith_rewriter.cpp | 8 +- src/math/simplex/model_based_opt.cpp | 3496 ++++++++--------- src/sat/sat_aig_finder.cpp | 2 +- src/sat/sat_binspr.cpp | 4 +- src/sat/sat_solver.cpp | 2 +- src/sat/sat_solver/sat_smt_solver.cpp | 2 +- src/solver/check_logic.cpp | 2 +- src/solver/check_sat_result.cpp | 2 +- 43 files changed, 1811 insertions(+), 1811 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9164e8e28..d7173e946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,7 +81,7 @@ if (EXISTS "${GIT_DIR}") # This mimics the behaviour of the old build system. set(Z3_FULL_VERSION_STR "${Z3_FULL_VERSION_STR} ${Z3_GIT_DESCRIPTION}") else() - message(STATUS "Not including git descrption in version") + message(STATUS "Not including git description in version") endif() else() message(WARNING "Failed to add git dependency.") @@ -462,7 +462,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") # generate files used for Z3's build. Changes to these files will trigger # a rebuild of all the generated files. ################################################################################ -# Note: ``update_api.py`` is deliberately not here because it not used +# Note: ``update_api.py`` is deliberately not here because it is not used # to generate every generated file. The targets that need it list it explicitly. set(Z3_GENERATED_FILE_EXTRA_DEPENDENCIES "${PROJECT_SOURCE_DIR}/scripts/mk_genfile_common.py" diff --git a/README-CMake.md b/README-CMake.md index 5845a52c3..b50167b48 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -125,7 +125,7 @@ Note that this is `libz3` not `z3` (`libz3` refers to the library target from `s [Ninja](https://ninja-build.org/) is a simple build system that is built for speed. It can be significantly faster than "UNIX Makefile"s because it is not a recursive -build system and thus doesn't create a new process everytime it traverses into a directory. +build system and thus doesn't create a new process every time it traverses into a directory. Ninja is particularly appropriate if you want fast incremental building. Basic usage is as follows: @@ -236,7 +236,7 @@ more interactive and allow you to change various options. In both these tools the basic steps to follow are: 1. Configure. -2. Change any options you wish. Everytime you change a set of options +2. Change any options you wish. Every time you change a set of options You should configure again. This may cause new options to appear 3. Generate. @@ -348,7 +348,7 @@ These notes are help developers and packagers of Z3. ### Install/Uninstall Install and uninstall targets are supported. Use ``CMAKE_INSTALL_PREFIX`` to -set the install prefix. If you also need need to control which directories are +set the install prefix. If you also need to control which directories are used for install set the documented ``CMAKE_INSTALL_*`` options. To install run diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9e007d1a1..245d53ca7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -62,7 +62,7 @@ Version 4.12.0 Clauses that are deduced by theories are marked by default by 'smt', and when more detailed information is available with proof hints or proof objects. - Instantations are considered useful to track so they + Instantiations are considered useful to track so they are logged using terms of the form (inst (not (forall (x) body)) body[t/x] (bind t)), where @@ -88,7 +88,7 @@ Version 4.12.0 checker cannot check. It is mainly a limitation of the arithmetic solver not pulling relevant information. Ensuring a tight coupling with proof hints and the validator - capabilites is open ended future work and good material for theses. + capabilities is open ended future work and good material for theses. - bit-vector inferences - are treated as trusted (there is no validation, it always blindly succeeds) - arrays, datatypes - there is no custom validation for @@ -158,13 +158,13 @@ Version 4.11.2 with SMT format that is extensible. The resulting format is a mild extension of SMTLIB with three extra commands assume, learn, del. They track input clauses, generated clauses and deleted clauses. They are optionally augmented by proof hints. Two proof hints are used in the current version: "rup" and "farkas". - "rup" is used whent the generated clause can be justified by reverse unit propagation. "farkas" is used when + "rup" is used when the generated clause can be justified by reverse unit propagation. "farkas" is used when the clause can be justified by a combination of Farkas cutting planes. There is a built-in proof checker for the format. Quantifier instantiations are also tracked as proof hints. - Other proof hints are to be added as the feature set is tested and developed. The fallback, buit-in, + Other proof hints are to be added as the feature set is tested and developed. The fallback, built-in, self-checker uses z3 to check that the generated clause is a consequence. Note that this is generally insufficient as generated clauses are in principle required to only be satisfiability preserving. - Proof checking and tranformation operations is overall open ended. + Proof checking and transformation operations is overall open ended. The log for the first commit introducing this change contains further information on the format. - fix to re-entrancy bug in user propagator (thanks to Clemens Eisenhofer). - handle _toExpr for quantified formulas in JS bindings @@ -638,7 +638,7 @@ xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissa (assert F) (check-sat a) (check-sat) - If 'F' is unstatisfiable independently of the assumption 'a', and + If 'F' is unsatisfiable independently of the assumption 'a', and the inconsistency can be detected by just performing propagation, Then, version <= 4.3.1 may return unsat diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index cd2c7c83b..3be314a53 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -952,7 +952,7 @@ def mk_hpp_from_pyg(pyg_file, output_dir): 'UINT_MAX' : UINT_MAX, 'max_memory_param' : max_memory_param, 'max_steps_param' : max_steps_param, - # Note that once this function is enterred that function + # Note that once this function is entered that function # executes with respect to the globals of this module and # not the globals defined here 'def_module_params' : def_module_params, diff --git a/src/ackermannization/ackermannize_bv_tactic.h b/src/ackermannization/ackermannize_bv_tactic.h index b99ae00e4..1afac4498 100644 --- a/src/ackermannization/ackermannize_bv_tactic.h +++ b/src/ackermannization/ackermannize_bv_tactic.h @@ -22,7 +22,7 @@ A tactic for performing Ackermann reduction for bit-vector formulas ### Long Description The Ackermann reduction replaces uninterpreted functions $f(t_1), f(t_2)$ -by fresh variables $f_1, f_2$ and addes axioms $t_1 \simeq t_2 \implies f_1 \simeq f_2$. +by fresh variables $f_1, f_2$ and adds axioms $t_1 \simeq t_2 \implies f_1 \simeq f_2$. The reduction has the effect of eliminating uninterpreted functions. When the reduction produces a pure bit-vector benchmark, it allows Z3 to use a specialized SAT solver. diff --git a/src/ackermannization/ackr_info.h b/src/ackermannization/ackr_info.h index fd2361064..67c41bda2 100644 --- a/src/ackermannization/ackr_info.h +++ b/src/ackermannization/ackr_info.h @@ -23,7 +23,7 @@ Revision History: /** \brief Information about how a formula is being converted into - a formula without uninterpreted function symbols via ackermannization. + a formula without uninterpreted function symbols via ackermannization. The intended use is that new terms are added via set_abstr. Once all terms are abstracted, call seal. diff --git a/src/api/api_context.h b/src/api/api_context.h index a3f027dd5..8b049ce16 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -231,7 +231,7 @@ namespace api { void handle_exception(z3_exception & ex); char const * get_exception_msg() const { return m_exception_msg.c_str(); } - // Interrupt the current interruptable object + // Interrupt the current interruptible object void interrupt(); void invoke_error_handler(Z3_error_code c); diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 2dda84af4..aeb075e45 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -742,7 +742,7 @@ extern "C" { fpa_util & fu = ctx->fpautil(); if (!ctx->bvutil().is_bv(to_expr(bv)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG, "bv sort the flaot sort expected"); + SET_ERROR_CODE(Z3_INVALID_ARG, "bv sort the float sort expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(bv)); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 799644970..4e51e83dd 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -320,7 +320,7 @@ namespace z3 { /** \brief Create a recursive datatype over a single sort. \c name is the name of the recursive datatype - \c n - the numer of constructors of the datatype + \c n - the number of constructors of the datatype \c cs - the \c n constructors used to define the datatype References to the datatype can be created using \ref datatype_sort. diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 6365852a6..629e96484 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3770,7 +3770,7 @@ namespace Microsoft.Z3 } /// - /// Create a simplifie that applies and + /// Create a simplifier that applies and /// then . /// public Simplifier AndThen(Simplifier t1, Simplifier t2, params Simplifier[] ts) diff --git a/src/api/dotnet/Symbol.cs b/src/api/dotnet/Symbol.cs index c0e1e3e73..f6756d5f4 100644 --- a/src/api/dotnet/Symbol.cs +++ b/src/api/dotnet/Symbol.cs @@ -97,7 +97,7 @@ namespace Microsoft.Z3 } /// - /// The Symbols's hash code. + /// The Symbol's hash code. /// /// A hash code public override int GetHashCode() diff --git a/src/api/dotnet/UserPropagator.cs b/src/api/dotnet/UserPropagator.cs index e591c3354..b1a2d3df5 100644 --- a/src/api/dotnet/UserPropagator.cs +++ b/src/api/dotnet/UserPropagator.cs @@ -58,7 +58,7 @@ namespace Microsoft.Z3 public delegate void CreatedEh(Expr term); /// - /// Delegate type for callback into solver's branching. The values can be overriden by calling . + /// Delegate type for callback into solver's branching. The values can be overridden by calling . /// /// A bit-vector or Boolean used for branching /// If the term is a bit-vector, then an index into the bit-vector being branched on diff --git a/src/api/java/Context.java b/src/api/java/Context.java index b5b22405c..ad690b7e9 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2309,7 +2309,7 @@ public class Context implements AutoCloseable { /** * Create the empty regular expression. - * Coresponds to re.none + * Corresponds to re.none */ public final ReExpr mkEmptyRe(ReSort s) { diff --git a/src/api/java/IDecRefQueue.java b/src/api/java/IDecRefQueue.java index 4b515a3b6..b9ece3172 100644 --- a/src/api/java/IDecRefQueue.java +++ b/src/api/java/IDecRefQueue.java @@ -28,7 +28,7 @@ import java.util.Map; * *

Mechanics: once an object is created, a metadata is stored for it in * {@code referenceMap}, and a {@link PhantomReference} is created with a - * reference to {@code referenceQueue}. + * reference to {@code referenceQueue}. * Once the object becomes strongly unreachable, the phantom reference gets * added by JVM to the {@code referenceQueue}. * After each object creation, we iterate through the available objects in diff --git a/src/api/java/Quantifier.java b/src/api/java/Quantifier.java index efeac9bb5..c44534196 100644 --- a/src/api/java/Quantifier.java +++ b/src/api/java/Quantifier.java @@ -166,7 +166,7 @@ public class Quantifier extends BoolExpr * @param sorts Sorts of bound variables. * @param names Names of bound variables * @param body Body of quantifier - * @param weight Weight used to indicate priority for qunatifier instantiation + * @param weight Weight used to indicate priority for quantifier instantiation * @param patterns Nullable patterns * @param noPatterns Nullable noPatterns * @param quantifierID Nullable quantifierID diff --git a/src/api/js/README.md b/src/api/js/README.md index 8c446b910..f53428bbd 100644 --- a/src/api/js/README.md +++ b/src/api/js/README.md @@ -13,7 +13,7 @@ Then run `npm i` to install dependencies, `npm run build:ts` to build the TypeSc ### Build on your own -Consult the file [build-wasm.ts](https://github.com/Z3Prover/z3/blob/master/src/api/js/scripts/build-wasm.ts) for configurations used for building wasm. +Consult the file [build-wasm.ts](https://github.com/Z3Prover/z3/blob/master/src/api/js/scripts/build-wasm.ts) for configurations used for building wasm. ## Tests diff --git a/src/api/js/scripts/parse-api.ts b/src/api/js/scripts/parse-api.ts index a3aa81acd..151e2f7bd 100644 --- a/src/api/js/scripts/parse-api.ts +++ b/src/api/js/scripts/parse-api.ts @@ -350,7 +350,7 @@ for (let fn of functions) { param.sizeIndex = defParams[idx].sizeIndex; if (!param.isArray && param.isPtr) { // not clear why some things are written as `int * x` and others `int x[]` - // but we can jsut cast + // but we can just cast param.isArray = true; param.isPtr = false; } diff --git a/src/api/ml/README.md b/src/api/ml/README.md index 7b9d5b476..041f37eb4 100644 --- a/src/api/ml/README.md +++ b/src/api/ml/README.md @@ -223,7 +223,7 @@ correctly found by gcc. I specifically left the cygwin part of the code intact as I have no idea what the original author meant by this, neither do I use or -tested this patch in the cygwin or mingw environemt. I think that this +tested this patch in the cygwin or mingw environment. I think that this code is rather outdated and shouldn't really work. E.g., in the --staticlib mode adding z3linkdep (which is libz3-static.a) as an argument to `ocamlmklib` will yield the following broken archive diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 81c472232..1837b5bec 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -292,7 +292,7 @@ if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: distos = RELEASE_METADATA[2] if distos in ('debian', 'ubuntu'): raise Exception( - "Linux binary distributions must be built on centos to conform to PEP 513 or alpine if targetting musl" + "Linux binary distributions must be built on centos to conform to PEP 513 or alpine if targeting musl" ) elif distos == 'glibc': if arch == 'x64': diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 5c067f4d3..ee47e7dd7 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -5385,7 +5385,7 @@ def EnumSort(name, values, ctx=None): """ if z3_debug(): _z3_assert(isinstance(name, str), "Name must be a string") - _z3_assert(all([isinstance(v, str) for v in values]), "Eumeration sort values must be strings") + _z3_assert(all([isinstance(v, str) for v in values]), "Enumeration sort values must be strings") _z3_assert(len(values) > 0, "At least one value expected") ctx = _get_ctx(ctx) num = len(values) diff --git a/src/api/python/z3/z3util.py b/src/api/python/z3/z3util.py index 071e2b60e..b60038f2f 100644 --- a/src/api/python/z3/z3util.py +++ b/src/api/python/z3/z3util.py @@ -275,7 +275,7 @@ def prove(claim, assume=None, verbose=0): def get_models(f, k): """ - Returns the first k models satisfiying f. + Returns the first k models satisfying f. If f is not satisfiable, returns False. If f cannot be solved, returns None If f is satisfiable, returns the first k models @@ -485,7 +485,7 @@ def model_str(m, as_str=True): x = 10, y = 3 EXAMPLES: - see doctest exampels from function prove() + see doctest examples from function prove() """ if z3_debug(): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index b73c71912..29eed7e26 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -2172,7 +2172,7 @@ extern "C" { \brief Query constructor for declared functions. \param c logical context. - \param constr constructor container. The container must have been passed in to a #Z3_mk_datatype call. + \param constr constructor container. The container must have been passed into a #Z3_mk_datatype call. \param num_fields number of accessor fields in the constructor. \param constructor constructor function declaration, allocated by user. \param tester constructor test function declaration, allocated by user. @@ -2317,7 +2317,7 @@ extern "C" { \param args constants that are used as arguments to the recursive function in the definition. \param body body of the recursive function - After declaring a recursive function or a collection of mutually recursive functions, use + After declaring a recursive function or a collection of mutually recursive functions, use this function to provide the definition for the recursive function. \sa Z3_mk_rec_func_decl @@ -3614,7 +3614,7 @@ extern "C" { /** \brief Retrieve the string constant stored in \c s. - Characters outside the basic printiable ASCII range are escaped. + Characters outside the basic printable ASCII range are escaped. \pre Z3_is_string(c, s) @@ -4897,7 +4897,7 @@ extern "C" { /** \brief Return a hash code for the given AST. The hash code is structural but two different AST objects can map to the same hash. - The result of \c Z3_get_ast_id returns an indentifier that is unique over the + The result of \c Z3_get_ast_id returns an identifier that is unique over the set of live AST objects. def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST))) @@ -5346,7 +5346,7 @@ extern "C" { Z3_ast const to[]); /** - \brief Substitute funcions in \c from with new expressions in \c to. + \brief Substitute functions in \c from with new expressions in \c to. The expressions in \c to can have free variables. The free variable in \c to at index 0 refers to the first argument of \c from, the free variable at index 1 corresponds to the second argument. @@ -7026,13 +7026,13 @@ extern "C" { Z3_on_clause_eh on_clause_eh); /** - \brief register a user-properator with the solver. + \brief register a user-propagator with the solver. \param c - context. \param s - solver object. \param user_context - a context used to maintain state for callbacks. \param push_eh - a callback invoked when scopes are pushed - \param pop_eh - a callback invoked when scopes are poped + \param pop_eh - a callback invoked when scopes are popped \param fresh_eh - a solver may spawn new solvers internally. This callback is used to produce a fresh user_context to be associated with fresh solvers. def_API('Z3_solver_propagate_init', VOID, (_in(CONTEXT), _in(SOLVER), _in(VOID_PTR), _fnptr(Z3_push_eh), _fnptr(Z3_pop_eh), _fnptr(Z3_fresh_eh))) diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 4d38327a5..f725fefc5 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -651,7 +651,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p for (unsigned i = 0; i < num_args; ++i) { if (args[i]->get_sort() != r->get_domain(i)) { std::ostringstream buffer; - buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " has sort " << mk_pp(args[i]->get_sort(), m) << " it does does not match declaration " << mk_pp(r, m); + buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " has sort " << mk_pp(args[i]->get_sort(), m) << " it does not match declaration " << mk_pp(r, m); m.raise_exception(buffer.str()); return nullptr; } diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 51faca7ed..9126b97b7 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -96,7 +96,7 @@ enum bv_op_kind { 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_BSDIV_OVFL, // signed division overflow predicate OP_BNEG_OVFL, // negation overflow predicate diff --git a/src/ast/char_decl_plugin.cpp b/src/ast/char_decl_plugin.cpp index 029312ba3..6a94db121 100644 --- a/src/ast/char_decl_plugin.cpp +++ b/src/ast/char_decl_plugin.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - char_plugin for unicode suppport + char_plugin for unicode support Author: diff --git a/src/ast/char_decl_plugin.h b/src/ast/char_decl_plugin.h index 3d934ffe4..686b7105f 100644 --- a/src/ast/char_decl_plugin.h +++ b/src/ast/char_decl_plugin.h @@ -7,7 +7,7 @@ Module Name: Abstract: - char_plugin for unicode suppport + char_plugin for unicode support Author: diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index 0ee3e130d..bdc32e97c 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -78,7 +78,7 @@ public: * * x = t -> fresh * x := if(fresh, t, diff(t)) - * where diff is a diagnonalization function available in domains of size > 1. + * where diff is a diagonalization function available in domains of size > 1. * */ @@ -807,7 +807,7 @@ bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const { /** \brief Create a fresh variable for abstracting (f args[0] ... args[num-1]) - Return true if it a new variable was created, and false if the variable already existed for this + Return true if a new variable was created, and false if the variable already existed for this application. Store the variable in v */ void iexpr_inverter::mk_fresh_uncnstr_var_for(sort * s, expr_ref & v) { diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index d0214d44f..a4ddbdaed 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -275,7 +275,7 @@ namespace datatype { } parameter const & name = parameters[0]; if (!name.is_symbol()) { - TRACE("datatype", tout << "expected symol parameter at position " << 0 << " got: " << name << "\n";); + TRACE("datatype", tout << "expected symbol parameter at position " << 0 << " got: " << name << "\n";); throw invalid_datatype(); } for (unsigned i = 1; i < num_parameters; ++i) { diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 7229636cb..341f3669b 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -52,7 +52,7 @@ namespace datatype { class accessor { symbol m_name; sort_ref m_range; - unsigned m_index; // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed. + unsigned m_index; // reference to recursive data-type may only get resolved after all mutually recursive data-types are processed. constructor* m_constructor{ nullptr }; public: accessor(ast_manager& m, symbol const& n, sort* range): diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index c1b9b7849..1193d4df0 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -19,7 +19,7 @@ Notes: - data structures form the (legacy) SMT solver. - it still uses eager path compression. - NB. The worklist is in reality inheritied from the legacy SMT solver. + NB. The worklist is in reality inherited from the legacy SMT solver. It is claimed to have the same effect as delayed congruence table reconstruction from egg. Similar to the legacy solver, parents are partially deduplicated. diff --git a/src/ast/euf/euf_justification.h b/src/ast/euf/euf_justification.h index 57b532e3b..c98002396 100644 --- a/src/ast/euf/euf_justification.h +++ b/src/ast/euf/euf_justification.h @@ -16,7 +16,7 @@ Author: Notes: - congruence closure justifications are given a timestamp so it is easy to sort them. - See the longer descriptoin in euf_proof_checker.cpp + See the longer description in euf_proof_checker.cpp --*/ diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp index e7452ee9c..bc63aae8e 100644 --- a/src/ast/macros/macro_finder.cpp +++ b/src/ast/macros/macro_finder.cpp @@ -65,7 +65,7 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, bool deps_valid, expr_de // functions introduced within macros are Skolem functions // To avoid unsound expansion of these as macros (because they // appear in model conversions and are therefore not fully - // replacable) we prevent these from being treated as macro functions. + // replaceable) we prevent these from being treated as macro functions. if (m_macro_manager.contains(f) || f->is_skolem()) return false; diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h index a3c1a8d97..758e3c1a7 100644 --- a/src/ast/macros/macro_manager.h +++ b/src/ast/macros/macro_manager.h @@ -32,7 +32,7 @@ Revision History: where T[X] does not contain f. This class is responsible for storing macros and expanding them. - It has support for backtracking and tagging declarations in an expression as forbidded for being macros. + It has support for backtracking and tagging declarations in an expression as forbidden for being macros. */ class macro_manager { ast_manager & m; diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index ad5f83486..c931c6fad 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -207,7 +207,7 @@ void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var // the instantiation rules for store(a, i, v) are: // store(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } // The first pattern is not included. - // TBD use a model-based scheme for exracting instantiations instead of + // TBD use a model-based scheme for extracting instantiations instead of // using multi-patterns. // diff --git a/src/ast/proofs/proof_utils.cpp b/src/ast/proofs/proof_utils.cpp index bcc1aed27..a3508969b 100644 --- a/src/ast/proofs/proof_utils.cpp +++ b/src/ast/proofs/proof_utils.cpp @@ -260,7 +260,7 @@ class reduce_hypotheses { { cls.push_back(cls_fact->get_arg(i)); } } else { cls.push_back(cls_fact); } - // construct new resovent + // construct new resolvent ptr_buffer new_fact_cls; bool found; // XXX quadratic @@ -604,7 +604,7 @@ public: // -- otherwise, the fact has not changed. nothing to simplify SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); parents.push_back(tmp); - // remember that we have this derivation while we have not poped the trail + // remember that we have this derivation while we have not popped the trail // but only if the proof is closed (i.e., a real unit) if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) { m_units.insert(m.get_fact(tmp), tmp); diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 00f2c04ae..dee5b7f44 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -1121,7 +1121,7 @@ bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { if (m_util.is_numeral(arg, num_r)) num_e = arg; } for (expr* arg : args2) { - // dont remove divisor on (div (* -1 x) (* -1 y)) because rewriting would diverge. + // don't remove divisor on (div (* -1 x) (* -1 y)) because rewriting would diverge. if (mark.is_marked(arg) && (!m_util.is_numeral(arg, num_r) || !num_r.is_minus_one())) { result = remove_divisor(arg, num, den); return true; @@ -1619,7 +1619,7 @@ br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) { } -// Return true if t is of the form c*Pi where c is a numeral. +// Return true if t is of the form c*Pi where c is a numeral. // Store c into k bool arith_rewriter::is_pi_multiple(expr * t, rational & k) { if (m_util.is_pi(t)) { @@ -1630,7 +1630,7 @@ bool arith_rewriter::is_pi_multiple(expr * t, rational & k) { return m_util.is_mul(t, a, b) && m_util.is_pi(b) && m_util.is_numeral(a, k); } -// Return true if t is of the form (+ s c*Pi) where c is a numeral. +// Return true if t is of the form (+ s c*Pi) where c is a numeral. // Store c into k, and c*Pi into m. bool arith_rewriter::is_pi_offset(expr * t, rational & k, expr * & m) { if (m_util.is_add(t)) { @@ -1943,7 +1943,7 @@ br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { br_status arith_rewriter::mk_asin_core(expr * arg, expr_ref & result) { // Remark: we assume that ForAll x : asin(-x) == asin(x). // Mathematica uses this as an axiom. Although asin is an underspecified function for x < -1 or x > 1. - // Actually, in Mathematica, asin(x) is a total function that returns a complex number fo x < -1 or x > 1. + // Actually, in Mathematica, asin(x) is a total function that returns a complex number for x < -1 or x > 1. rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 3c38cfb0e..e66cab310 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -1,1748 +1,1748 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Module Name: - - model_based_opt.cpp - -Abstract: - - Model-based optimization and projection for linear real, integer arithmetic. - -Author: - - Nikolaj Bjorner (nbjorner) 2016-27-4 - -Revision History: - - ---*/ - -#include "math/simplex/model_based_opt.h" -#include "util/uint_set.h" -#include "util/z3_exception.h" - -std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { - switch (ie) { - case opt::t_eq: return out << " = "; - case opt::t_lt: return out << " < "; - case opt::t_le: return out << " <= "; - case opt::t_divides: return out << " divides "; - case opt::t_mod: return out << " mod "; - case opt::t_div: return out << " div "; - } - return out; -} - - -namespace opt { - - /** - * Convert a row ax + coeffs + coeff = value into a definition for x - * x = (value - coeffs - coeff)/a - * as backdrop we have existing assignments to x and other variables that - * satisfy the equality with value, and such that value satisfies - * the row constraint ( = , <= , < , mod) - */ - model_based_opt::def::def(row const& r, unsigned x) { - for (var const & v : r.m_vars) { - if (v.m_id != x) { - m_vars.push_back(v); - } - else { - m_div = -v.m_coeff; - } - } - m_coeff = r.m_coeff; - switch (r.m_type) { - case opt::t_lt: - m_coeff += m_div; - break; - case opt::t_le: - // for: ax >= t, then x := (t + a - 1) div a - if (m_div.is_pos()) { - m_coeff += m_div; - m_coeff -= rational::one(); - } - break; - default: - break; - } - normalize(); - SASSERT(m_div.is_pos()); - } - - model_based_opt::def model_based_opt::def::operator+(def const& other) const { - def result; - vector const& vs1 = m_vars; - vector const& vs2 = other.m_vars; - vector & vs = result.m_vars; - rational c1(1), c2(1); - if (m_div != other.m_div) { - c1 = other.m_div; - c2 = m_div; - } - unsigned i = 0, j = 0; - while (i < vs1.size() || j < vs2.size()) { - unsigned v1 = UINT_MAX, v2 = UINT_MAX; - if (i < vs1.size()) v1 = vs1[i].m_id; - if (j < vs2.size()) v2 = vs2[j].m_id; - if (v1 == v2) { - vs.push_back(vs1[i]); - vs.back().m_coeff *= c1; - vs.back().m_coeff += c2 * vs2[j].m_coeff; - ++i; ++j; - if (vs.back().m_coeff.is_zero()) { - vs.pop_back(); - } - } - else if (v1 < v2) { - vs.push_back(vs1[i]); - vs.back().m_coeff *= c1; - ++i; - } - else { - vs.push_back(vs2[j]); - vs.back().m_coeff *= c2; - ++j; - } - } - result.m_div = c1*m_div; - result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2); - result.normalize(); - return result; - } - - /** - a1*x1 + a2*x2 + a3*x3 + coeff1 / c1 - x2 |-> b1*x1 + b4*x4 + ceoff2 / c2 - ------------------------------------------------------------------------ - (a1*x1 + a2*((b1*x1 + b4*x4 + coeff2) / c2) + a3*x3 + coeff1) / c1 - ------------------------------------------------------------------------ - (c2*a1*x1 + a2*b1*x1 + a2*b4*x4 + c2*a3*x3 + c2*coeff1 + coeff2) / c1*c2 - */ - void model_based_opt::def::substitute(unsigned v, def const& other) { - vector const& vs1 = m_vars; - rational coeff(0); - for (auto const& [id, c] : vs1) { - if (id == v) { - coeff = c; - break; - } - } - if (coeff == 0) - return; - - rational c1 = m_div; - rational c2 = other.m_div; - - vector const& vs2 = other.m_vars; - vector vs; - unsigned i = 0, j = 0; - while (i < vs1.size() || j < vs2.size()) { - unsigned v1 = UINT_MAX, v2 = UINT_MAX; - if (i < vs1.size()) v1 = vs1[i].m_id; - if (j < vs2.size()) v2 = vs2[j].m_id; - if (v1 == v) - ++i; - else if (v1 == v2) { - vs.push_back(vs1[i]); - vs.back().m_coeff *= c2; - vs.back().m_coeff += coeff * vs2[j].m_coeff; - ++i; ++j; - if (vs.back().m_coeff.is_zero()) - vs.pop_back(); - } - else if (v1 < v2) { - vs.push_back(vs1[i]); - vs.back().m_coeff *= c2; - ++i; - } - else { - vs.push_back(vs2[j]); - vs.back().m_coeff *= coeff; - ++j; - } - } - m_div *= other.m_div; - m_coeff *= c2; - m_coeff += coeff*other.m_coeff; - m_vars.reset(); - m_vars.append(vs); - normalize(); - } - - model_based_opt::def model_based_opt::def::operator/(rational const& r) const { - def result(*this); - result.m_div *= r; - result.normalize(); - return result; - } - - model_based_opt::def model_based_opt::def::operator*(rational const& n) const { - def result(*this); - for (var& v : result.m_vars) { - v.m_coeff *= n; - } - result.m_coeff *= n; - result.normalize(); - return result; - } - - model_based_opt::def model_based_opt::def::operator+(rational const& n) const { - def result(*this); - result.m_coeff += n * result.m_div; - result.normalize(); - return result; - } - - void model_based_opt::def::normalize() { - if (!m_div.is_int()) { - rational den = denominator(m_div); - SASSERT(den > 1); - for (var& v : m_vars) - v.m_coeff *= den; - m_coeff *= den; - m_div *= den; - - } - if (m_div.is_neg()) { - for (var& v : m_vars) - v.m_coeff.neg(); - m_coeff.neg(); - m_div.neg(); - } - if (m_div.is_one()) - return; - rational g(m_div); - if (!m_coeff.is_int()) - return; - g = gcd(g, m_coeff); - for (var const& v : m_vars) { - if (!v.m_coeff.is_int()) - return; - g = gcd(g, abs(v.m_coeff)); - if (g.is_one()) - break; - } - if (!g.is_one()) { - for (var& v : m_vars) - v.m_coeff /= g; - m_coeff /= g; - m_div /= g; - } - } - - model_based_opt::model_based_opt() { - m_rows.push_back(row()); - } - - bool model_based_opt::invariant() { - for (unsigned i = 0; i < m_rows.size(); ++i) { - if (!invariant(i, m_rows[i])) { - return false; - } - } - return true; - } - -#define PASSERT(_e_) { CTRACE("qe", !(_e_), display(tout, r); display(tout);); SASSERT(_e_); } - - bool model_based_opt::invariant(unsigned index, row const& r) { - vector const& vars = r.m_vars; - for (unsigned i = 0; i < vars.size(); ++i) { - // variables in each row are sorted and have non-zero coefficients - PASSERT(i + 1 == vars.size() || vars[i].m_id < vars[i+1].m_id); - PASSERT(!vars[i].m_coeff.is_zero()); - PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index)); - } - - PASSERT(r.m_value == eval(r)); - PASSERT(r.m_type != t_eq || r.m_value.is_zero()); - // values satisfy constraints - PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg()); - PASSERT(index == 0 || r.m_type != t_le || !r.m_value.is_pos()); - PASSERT(index == 0 || r.m_type != t_divides || (mod(r.m_value, r.m_mod).is_zero())); - PASSERT(index == 0 || r.m_type != t_mod || r.m_id < m_var2value.size()); - PASSERT(index == 0 || r.m_type != t_div || r.m_id < m_var2value.size()); - return true; - } - - // a1*x + obj - // a2*x + t2 <= 0 - // a3*x + t3 <= 0 - // a4*x + t4 <= 0 - // a1 > 0, a2 > 0, a3 > 0, a4 < 0 - // x <= -t2/a2 - // x <= -t2/a3 - // determine lub among these. - // then resolve lub with others - // e.g., -t2/a2 <= -t3/a3, then - // replace inequality a3*x + t3 <= 0 by -t2/a2 + t3/a3 <= 0 - // mark a4 as invalid. - // - - // a1 < 0, a2 < 0, a3 < 0, a4 > 0 - // x >= t2/a2 - // x >= t3/a3 - // determine glb among these - // the resolve glb with others. - // e.g. t2/a2 >= t3/a3 - // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 - // - inf_eps model_based_opt::maximize() { - SASSERT(invariant()); - unsigned_vector bound_trail, bound_vars; - TRACE("opt", display(tout << "tableau\n");); - while (!objective().m_vars.empty()) { - var v = objective().m_vars.back(); - unsigned x = v.m_id; - rational const& coeff = v.m_coeff; - unsigned bound_row_index; - rational bound_coeff; - if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { - SASSERT(!bound_coeff.is_zero()); - TRACE("opt", display(tout << "update: " << v << " ", objective()); - for (unsigned above : m_above) { - display(tout << "resolve: ", m_rows[above]); - }); - for (unsigned above : m_above) { - resolve(bound_row_index, bound_coeff, above, x); - } - for (unsigned below : m_below) { - resolve(bound_row_index, bound_coeff, below, x); - } - // coeff*x + objective <= ub - // a2*x + t2 <= 0 - // => coeff*x <= -t2*coeff/a2 - // objective + t2*coeff/a2 <= ub - - mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); - retire_row(bound_row_index); - bound_trail.push_back(bound_row_index); - bound_vars.push_back(x); - } - else { - TRACE("opt", display(tout << "unbound: " << v << " ", objective());); - update_values(bound_vars, bound_trail); - return inf_eps::infinity(); - } - } - - // - // update the evaluation of variables to satisfy the bound. - // - - update_values(bound_vars, bound_trail); - - rational value = objective().m_value; - if (objective().m_type == t_lt) { - return inf_eps(inf_rational(value, rational(-1))); - } - else { - return inf_eps(inf_rational(value)); - } - } - - - void model_based_opt::update_value(unsigned x, rational const& val) { - rational old_val = m_var2value[x]; - m_var2value[x] = val; - SASSERT(val.is_int() || !is_int(x)); - unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned row_id : row_ids) { - rational coeff = get_coefficient(row_id, x); - if (coeff.is_zero()) { - continue; - } - row & r = m_rows[row_id]; - rational delta = coeff * (val - old_val); - r.m_value += delta; - SASSERT(invariant(row_id, r)); - } - } - - - void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { - for (unsigned i = bound_trail.size(); i-- > 0; ) { - unsigned x = bound_vars[i]; - row& r = m_rows[bound_trail[i]]; - rational val = r.m_coeff; - rational old_x_val = m_var2value[x]; - rational new_x_val; - rational x_coeff, eps(0); - vector const& vars = r.m_vars; - for (var const& v : vars) { - if (x == v.m_id) { - x_coeff = v.m_coeff; - } - else { - val += m_var2value[v.m_id]*v.m_coeff; - } - } - SASSERT(!x_coeff.is_zero()); - new_x_val = -val/x_coeff; - - if (r.m_type == t_lt) { - eps = abs(old_x_val - new_x_val)/rational(2); - eps = std::min(rational::one(), eps); - SASSERT(!eps.is_zero()); - - // - // ax + t < 0 - // <=> x < -t/a - // <=> x := -t/a - epsilon - // - if (x_coeff.is_pos()) { - new_x_val -= eps; - } - // - // -ax + t < 0 - // <=> -ax < -t - // <=> -x < -t/a - // <=> x > t/a - // <=> x := t/a + epsilon - // - else { - new_x_val += eps; - } - } - TRACE("opt", display(tout << "v" << x - << " coeff_x: " << x_coeff - << " old_x_val: " << old_x_val - << " new_x_val: " << new_x_val - << " eps: " << eps << " ", r); ); - m_var2value[x] = new_x_val; - - r.m_value = eval(r); - SASSERT(invariant(bound_trail[i], r)); - } - - // update and check bounds for all other affected rows. - for (unsigned i = bound_trail.size(); i-- > 0; ) { - unsigned x = bound_vars[i]; - unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned row_id : row_ids) { - row & r = m_rows[row_id]; - r.m_value = eval(r); - SASSERT(invariant(row_id, r)); - } - } - SASSERT(invariant()); - } - - bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, bool is_pos) { - bound_row_index = UINT_MAX; - rational lub_val; - rational const& x_val = m_var2value[x]; - unsigned_vector const& row_ids = m_var2row_ids[x]; - uint_set visited; - m_above.reset(); - m_below.reset(); - for (unsigned row_id : row_ids) { - SASSERT(row_id != m_objective_id); - if (visited.contains(row_id)) - continue; - visited.insert(row_id); - row& r = m_rows[row_id]; - if (!r.m_alive) - continue; - rational a = get_coefficient(row_id, x); - if (a.is_zero()) { - // skip - } - else if (a.is_pos() == is_pos || r.m_type == t_eq) { - rational value = x_val - (r.m_value/a); - if (bound_row_index == UINT_MAX) { - lub_val = value; - bound_row_index = row_id; - bound_coeff = a; - } - else if ((value == lub_val && r.m_type == opt::t_lt) || - (is_pos && value < lub_val) || - - (!is_pos && value > lub_val)) { - m_above.push_back(bound_row_index); - lub_val = value; - bound_row_index = row_id; - bound_coeff = a; - } - else - m_above.push_back(row_id); - } - else - m_below.push_back(row_id); - } - return bound_row_index != UINT_MAX; - } - - void model_based_opt::retire_row(unsigned row_id) { - SASSERT(!m_retired_rows.contains(row_id)); - m_rows[row_id].m_alive = false; - m_retired_rows.push_back(row_id); - } - - rational model_based_opt::eval(unsigned x) const { - return m_var2value[x]; - } - - rational model_based_opt::eval(def const& d) const { - vector const& vars = d.m_vars; - rational val = d.m_coeff; - for (var const& v : vars) { - val += v.m_coeff * eval(v.m_id); - } - val /= d.m_div; - return val; - } - - rational model_based_opt::eval(row const& r) const { - vector const& vars = r.m_vars; - rational val = r.m_coeff; - for (var const& v : vars) { - val += v.m_coeff * eval(v.m_id); - } - return val; - } - - rational model_based_opt::eval(vector const& coeffs) const { - rational val(0); - for (var const& v : coeffs) - val += v.m_coeff * eval(v.m_id); - return val; - } - - rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { - return m_rows[row_id].get_coefficient(var_id); - } - - rational model_based_opt::row::get_coefficient(unsigned var_id) const { - if (m_vars.empty()) - return rational::zero(); - unsigned lo = 0, hi = m_vars.size(); - while (lo < hi) { - unsigned mid = lo + (hi - lo)/2; - SASSERT(mid < hi); - unsigned id = m_vars[mid].m_id; - if (id == var_id) { - lo = mid; - break; - } - if (id < var_id) - lo = mid + 1; - else - hi = mid; - } - if (lo == m_vars.size()) - return rational::zero(); - unsigned id = m_vars[lo].m_id; - if (id == var_id) - return m_vars[lo].m_coeff; - else - return rational::zero(); - } - - model_based_opt::row& model_based_opt::row::normalize() { -#if 0 - if (m_type == t_divides || m_type == t_mod || m_type == t_div) - return *this; - rational D(denominator(abs(m_coeff))); - if (D == 0) - D = 1; - for (auto const& [id, coeff] : m_vars) - if (coeff != 0) - D = lcm(D, denominator(abs(coeff))); - if (D == 1) - return *this; - SASSERT(D > 0); - for (auto & [id, coeff] : m_vars) - coeff *= D; - m_coeff *= D; -#endif - return *this; - } - - // - // Let - // row1: t1 + a1*x <= 0 - // row2: t2 + a2*x <= 0 - // - // assume a1, a2 have the same signs: - // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 - // <=> t2*a1/a2 - t1 <= 0 - // <=> t2 - t1*a2/a1 <= 0 - // - // assume a1 > 0, -a2 < 0: - // t1 + a1*x <= 0, t2 - a2*x <= 0 - // t2/a2 <= -t1/a1 - // t2 + t1*a2/a1 <= 0 - // assume -a1 < 0, a2 > 0: - // t1 - a1*x <= 0, t2 + a2*x <= 0 - // t1/a1 <= -t2/a2 - // t2 + t1*a2/a1 <= 0 - // - // the resolvent is the same in all cases (simpler proof should exist) - // - // assume a1 < 0, -a1 = a2: - // t1 <= a2*div(t2, a2) - // - - void model_based_opt::resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { - - SASSERT(a1 == get_coefficient(row_src, x)); - SASSERT(!a1.is_zero()); - SASSERT(row_src != row_dst); - - if (m_rows[row_dst].m_alive) { - rational a2 = get_coefficient(row_dst, x); - if (is_int(x)) { - TRACE("opt", - tout << x << ": " << a1 << " " << a2 << ": "; - display(tout, m_rows[row_dst]); - display(tout, m_rows[row_src]);); - if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) { - mul_add(x, a1, row_src, a2, row_dst); - } - else { - mul(row_dst, abs(a1)); - mul_add(false, row_dst, -abs(a2), row_src); - } - TRACE("opt", display(tout << "result ", m_rows[row_dst]);); - normalize(row_dst); - } - else { - mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); - } - } - } - - /** - * a1 > 0 - * a1*x + r1 = value - * a2*x + r2 <= 0 - * ------------------ - * a1*r2 - a2*r1 <= value - */ - void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { - SASSERT(a1 == get_coefficient(row_src, x)); - SASSERT(a1.is_pos()); - SASSERT(row_src != row_dst); - if (!m_rows[row_dst].m_alive) return; - rational a2 = get_coefficient(row_dst, x); - mul(row_dst, a1); - mul_add(false, row_dst, -a2, row_src); - normalize(row_dst); - SASSERT(get_coefficient(row_dst, x).is_zero()); - } - - // resolution for integer rows. - void model_based_opt::mul_add( - unsigned x, rational src_c, unsigned row_src, rational dst_c, unsigned row_dst) { - row& dst = m_rows[row_dst]; - row const& src = m_rows[row_src]; - SASSERT(is_int(x)); - SASSERT(t_le == dst.m_type && t_le == src.m_type); - SASSERT(src_c.is_int()); - SASSERT(dst_c.is_int()); - SASSERT(m_var2value[x].is_int()); - - rational abs_src_c = abs(src_c); - rational abs_dst_c = abs(dst_c); - rational x_val = m_var2value[x]; - rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one()); - rational dst_val = dst.m_value - x_val*dst_c; - rational src_val = src.m_value - x_val*src_c; - rational distance = abs_src_c * dst_val + abs_dst_c * src_val + slack; - bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); - bool use_case2 = false && abs_src_c == abs_dst_c && src_c.is_pos() != dst_c.is_pos() && !abs_src_c.is_one() && t_le == dst.m_type && t_le == src.m_type; - bool use_case3 = false && src_c.is_pos() != dst_c.is_pos() && t_le == dst.m_type && t_le == src.m_type; - - - if (use_case1) { - TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); - // dst <- abs_src_c*dst + abs_dst_c*src + slack - mul(row_dst, abs_src_c); - add(row_dst, slack); - mul_add(false, row_dst, abs_dst_c, row_src); - return; - } - - if (use_case2 || use_case3) { - // case2: - // x*src_c + s <= 0 - // -x*src_c + t <= 0 - // - // -src_c*div(-s, src_c) + t <= 0 - // - // Example: - // t <= 100*x <= s - // Then t <= 100*div(s, 100) - // - // case3: - // x*src_c + s <= 0 - // -x*dst_c + t <= 0 - // t <= x*dst_c, x*src_c <= -s -> - // t <= dst_c*div(-s, src_c) -> - // -dst_c*div(-s,src_c) + t <= 0 - // - - bool swapped = false; - if (src_c < 0) { - std::swap(row_src, row_dst); - std::swap(src_c, dst_c); - std::swap(abs_src_c, abs_dst_c); - swapped = true; - } - vector src_coeffs, dst_coeffs; - rational src_coeff = m_rows[row_src].m_coeff; - rational dst_coeff = m_rows[row_dst].m_coeff; - for (auto const& v : m_rows[row_src].m_vars) - if (v.m_id != x) - src_coeffs.push_back(var(v.m_id, -v.m_coeff)); - for (auto const& v : m_rows[row_dst].m_vars) - if (v.m_id != x) - dst_coeffs.push_back(v); - unsigned v = UINT_MAX; - if (src_coeffs.empty()) - dst_coeff -= abs_dst_c*div(-src_coeff, abs_src_c); - else - v = add_div(src_coeffs, -src_coeff, abs_src_c); - if (v != UINT_MAX) dst_coeffs.push_back(var(v, -abs_dst_c)); - if (swapped) - std::swap(row_src, row_dst); - retire_row(row_dst); - add_constraint(dst_coeffs, dst_coeff, t_le); - return; - } - - // - // create finite disjunction for |b|. - // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 - // <=> - // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 - // <=> - // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 - // - - TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";); - vector coeffs; - if (abs_dst_c <= abs_src_c) { - rational z = mod(dst_val, abs_dst_c); - if (!z.is_zero()) z = abs_dst_c - z; - mk_coeffs_without(coeffs, dst.m_vars, x); - add_divides(coeffs, dst.m_coeff + z, abs_dst_c); - add(row_dst, z); - mul(row_dst, src_c * n_sign(dst_c)); - mul_add(false, row_dst, abs_dst_c, row_src); - } - else { - // z := b - (s + bx) mod b - // := b - s mod b - // b | s + z <=> b | s + b - s mod b <=> b | s - s mod b - rational z = mod(src_val, abs_src_c); - if (!z.is_zero()) z = abs_src_c - z; - mk_coeffs_without(coeffs, src.m_vars, x); - add_divides(coeffs, src.m_coeff + z, abs_src_c); - mul(row_dst, abs_src_c); - add(row_dst, z * dst_c * n_sign(src_c)); - mul_add(false, row_dst, dst_c * n_sign(src_c), row_src); - } - } - - void model_based_opt::mk_coeffs_without(vector& dst, vector const& src, unsigned x) { - for (var const & v : src) { - if (v.m_id != x) dst.push_back(v); - } - } - - rational model_based_opt::n_sign(rational const& b) const { - return rational(b.is_pos()?-1:1); - } - - void model_based_opt::mul(unsigned dst, rational const& c) { - if (c.is_one()) - return; - row& r = m_rows[dst]; - for (auto & v : r.m_vars) - v.m_coeff *= c; - r.m_mod *= c; - r.m_coeff *= c; - if (r.m_type != t_div && r.m_type != t_mod) - r.m_value *= c; - } - - void model_based_opt::add(unsigned dst, rational const& c) { - row& r = m_rows[dst]; - r.m_coeff += c; - r.m_value += c; - } - - void model_based_opt::sub(unsigned dst, rational const& c) { - row& r = m_rows[dst]; - r.m_coeff -= c; - r.m_value -= c; - } - - void model_based_opt::normalize(unsigned row_id) { - row& r = m_rows[row_id]; - if (!r.m_alive) - return; - if (r.m_vars.empty()) { - retire_row(row_id); - return; - } - if (r.m_type == t_divides) - return; - if (r.m_type == t_mod) - return; - if (r.m_type == t_div) - return; - rational g(abs(r.m_vars[0].m_coeff)); - bool all_int = g.is_int(); - for (unsigned i = 1; all_int && !g.is_one() && i < r.m_vars.size(); ++i) { - rational const& coeff = r.m_vars[i].m_coeff; - if (coeff.is_int()) { - g = gcd(g, abs(coeff)); - } - else { - all_int = false; - } - } - if (all_int && !r.m_coeff.is_zero()) { - if (r.m_coeff.is_int()) { - g = gcd(g, abs(r.m_coeff)); - } - else { - all_int = false; - } - } - if (all_int && !g.is_one()) { - SASSERT(!g.is_zero()); - mul(row_id, rational::one()/g); - } - } - - // - // set row1 <- row1 + c*row2 - // - void model_based_opt::mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2) { - if (c.is_zero()) - return; - - - m_new_vars.reset(); - row& r1 = m_rows[row_id1]; - row const& r2 = m_rows[row_id2]; - unsigned i = 0, j = 0; - while (i < r1.m_vars.size() || j < r2.m_vars.size()) { - if (j == r2.m_vars.size()) { - m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.data() + i); - break; - } - if (i == r1.m_vars.size()) { - for (; j < r2.m_vars.size(); ++j) { - m_new_vars.push_back(r2.m_vars[j]); - m_new_vars.back().m_coeff *= c; - if (row_id1 != m_objective_id) - m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); - } - break; - } - - unsigned v1 = r1.m_vars[i].m_id; - unsigned v2 = r2.m_vars[j].m_id; - if (v1 == v2) { - m_new_vars.push_back(r1.m_vars[i]); - m_new_vars.back().m_coeff += c*r2.m_vars[j].m_coeff; - ++i; - ++j; - if (m_new_vars.back().m_coeff.is_zero()) - m_new_vars.pop_back(); - } - else if (v1 < v2) { - m_new_vars.push_back(r1.m_vars[i]); - ++i; - } - else { - m_new_vars.push_back(r2.m_vars[j]); - m_new_vars.back().m_coeff *= c; - if (row_id1 != m_objective_id) - m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); - ++j; - } - } - r1.m_coeff += c*r2.m_coeff; - r1.m_vars.swap(m_new_vars); - r1.m_value += c*r2.m_value; - - if (!same_sign && r2.m_type == t_lt) - r1.m_type = t_lt; - else if (same_sign && r1.m_type == t_lt && r2.m_type == t_lt) - r1.m_type = t_le; - SASSERT(invariant(row_id1, r1)); - } - - void model_based_opt::display(std::ostream& out) const { - for (auto const& r : m_rows) - display(out, r); - for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { - unsigned_vector const& rows = m_var2row_ids[i]; - out << i << ": "; - for (auto const& r : rows) - out << r << " "; - out << "\n"; - } - } - - void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { - unsigned i = 0; - for (var const& v : vars) { - if (i > 0 && v.m_coeff.is_pos()) - out << "+ "; - ++i; - if (v.m_coeff.is_one()) - out << "v" << v.m_id << " "; - else - out << v.m_coeff << "*v" << v.m_id << " "; - } - if (coeff.is_pos()) - out << " + " << coeff << " "; - else if (coeff.is_neg()) - out << coeff << " "; - } - - std::ostream& model_based_opt::display(std::ostream& out, row const& r) { - out << (r.m_alive?"a":"d") << " "; - display(out, r.m_vars, r.m_coeff); - switch (r.m_type) { - case opt::t_divides: - out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n"; - break; - case opt::t_mod: - out << r.m_type << " " << r.m_mod << " = v" << r.m_id << " ; mod: " << mod(r.m_value, r.m_mod) << "\n"; - break; - case opt::t_div: - out << r.m_type << " " << r.m_mod << " = v" << r.m_id << " ; div: " << div(r.m_value, r.m_mod) << "\n"; - break; - default: - out << r.m_type << " 0; value: " << r.m_value << "\n"; - break; - } - return out; - } - - std::ostream& model_based_opt::display(std::ostream& out, def const& r) { - display(out, r.m_vars, r.m_coeff); - if (!r.m_div.is_one()) { - out << " / " << r.m_div; - } - return out; - } - - unsigned model_based_opt::add_var(rational const& value, bool is_int) { - unsigned v = m_var2value.size(); - m_var2value.push_back(value); - m_var2is_int.push_back(is_int); - SASSERT(value.is_int() || !is_int); - m_var2row_ids.push_back(unsigned_vector()); - return v; - } - - rational model_based_opt::get_value(unsigned var) { - return m_var2value[var]; - } - - void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { - row& r = m_rows[row_id]; - rational val(c); - SASSERT(r.m_vars.empty()); - r.m_vars.append(coeffs.size(), coeffs.data()); - bool is_int_row = !coeffs.empty(); - std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); - for (auto const& c : coeffs) { - val += m_var2value[c.m_id] * c.m_coeff; - SASSERT(!is_int(c.m_id) || c.m_coeff.is_int()); - is_int_row &= is_int(c.m_id); - } - r.m_alive = true; - r.m_coeff = c; - r.m_value = val; - r.m_type = rel; - r.m_mod = m; - if (is_int_row && rel == t_lt) { - r.m_type = t_le; - r.m_coeff += rational::one(); - r.m_value += rational::one(); - } - } - - unsigned model_based_opt::new_row() { - unsigned row_id = 0; - if (m_retired_rows.empty()) { - row_id = m_rows.size(); - m_rows.push_back(row()); - } - else { - row_id = m_retired_rows.back(); - m_retired_rows.pop_back(); - SASSERT(!m_rows[row_id].m_alive); - m_rows[row_id].reset(); - m_rows[row_id].m_alive = true; - } - return row_id; - } - - unsigned model_based_opt::copy_row(unsigned src, unsigned excl) { - unsigned dst = new_row(); - row const& r = m_rows[src]; - set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type); - for (auto const& v : r.m_vars) { - if (v.m_id != excl) - m_var2row_ids[v.m_id].push_back(dst); - } - SASSERT(invariant(dst, m_rows[dst])); - return dst; - } - - // -x + lo <= 0 - void model_based_opt::add_lower_bound(unsigned x, rational const& lo) { - vector coeffs; - coeffs.push_back(var(x, rational::minus_one())); - add_constraint(coeffs, lo, t_le); - } - - // x - hi <= 0 - void model_based_opt::add_upper_bound(unsigned x, rational const& hi) { - vector coeffs; - coeffs.push_back(var(x, rational::one())); - add_constraint(coeffs, -hi, t_le); - } - - void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { - add_constraint(coeffs, c, rational::zero(), rel, 0); - } - - void model_based_opt::add_divides(vector const& coeffs, rational const& c, rational const& m) { - rational g(c); - for (auto const& [v, coeff] : coeffs) - g = gcd(coeff, g); - if ((g/m).is_int()) - return; - add_constraint(coeffs, c, m, t_divides, 0); - } - - unsigned model_based_opt::add_mod(vector const& coeffs, rational const& c, rational const& m) { - rational value = c; - for (auto const& var : coeffs) - value += var.m_coeff * m_var2value[var.m_id]; - unsigned v = add_var(mod(value, m), true); - add_constraint(coeffs, c, m, t_mod, v); - return v; - } - - unsigned model_based_opt::add_div(vector const& coeffs, rational const& c, rational const& m) { - rational value = c; - for (auto const& var : coeffs) - value += var.m_coeff * m_var2value[var.m_id]; - unsigned v = add_var(div(value, m), true); - add_constraint(coeffs, c, m, t_div, v); - return v; - } - - unsigned model_based_opt::add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type rel, unsigned id) { - auto const& r = m_rows.back(); - if (r.m_vars == coeffs && r.m_coeff == c && r.m_mod == m && r.m_type == rel && r.m_id == id && r.m_alive) - return m_rows.size() - 1; - unsigned row_id = new_row(); - set_row(row_id, coeffs, c, m, rel); - m_rows[row_id].m_id = id; - for (var const& coeff : coeffs) - m_var2row_ids[coeff.m_id].push_back(row_id); - SASSERT(invariant(row_id, m_rows[row_id])); - normalize(row_id); - return row_id; - } - - void model_based_opt::set_objective(vector const& coeffs, rational const& c) { - set_row(m_objective_id, coeffs, c, rational::zero(), t_le); - } - - void model_based_opt::get_live_rows(vector& rows) { - for (row & r : m_rows) - if (r.m_alive) - rows.push_back(r.normalize()); - } - - // - // pick glb and lub representative. - // The representative is picked such that it - // represents the fewest inequalities. - // The constraints that enforce a glb or lub are not forced. - // The constraints that separate the glb from ub or the lub from lb - // are not forced. - // In other words, suppose there are - // . N inequalities of the form t <= x - // . M inequalities of the form s >= x - // . t0 is glb among N under valuation. - // . s0 is lub among M under valuation. - // If N < M - // create the inequalities: - // t <= t0 for each t other than t0 (N-1 inequalities). - // t0 <= s for each s (M inequalities). - // If N >= M the construction is symmetric. - // - model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) { - unsigned_vector& lub_rows = m_lub; - unsigned_vector& glb_rows = m_glb; - unsigned_vector& divide_rows = m_divides; - unsigned_vector& mod_rows = m_mod; - unsigned_vector& div_rows = m_div; - unsigned lub_index = UINT_MAX, glb_index = UINT_MAX; - bool lub_strict = false, glb_strict = false; - rational lub_val, glb_val; - rational const& x_val = m_var2value[x]; - unsigned_vector const& row_ids = m_var2row_ids[x]; - uint_set visited; - lub_rows.reset(); - glb_rows.reset(); - divide_rows.reset(); - mod_rows.reset(); - div_rows.reset(); - bool lub_is_unit = true, glb_is_unit = true; - unsigned eq_row = UINT_MAX; - // select the lub and glb. - for (unsigned row_id : row_ids) { - if (visited.contains(row_id)) - continue; - visited.insert(row_id); - row& r = m_rows[row_id]; - if (!r.m_alive) - continue; - rational a = get_coefficient(row_id, x); - if (a.is_zero()) - continue; - if (r.m_type == t_eq) - eq_row = row_id; - else if (r.m_type == t_mod) - mod_rows.push_back(row_id); - else if (r.m_type == t_div) - div_rows.push_back(row_id); - else if (r.m_type == t_divides) - divide_rows.push_back(row_id); - else if (a.is_pos()) { - rational lub_value = x_val - (r.m_value/a); - if (lub_rows.empty() || - lub_value < lub_val || - (lub_value == lub_val && r.m_type == t_lt && !lub_strict)) { - lub_val = lub_value; - lub_index = row_id; - lub_strict = r.m_type == t_lt; - } - lub_rows.push_back(row_id); - lub_is_unit &= a.is_one(); - } - else { - SASSERT(a.is_neg()); - rational glb_value = x_val - (r.m_value/a); - if (glb_rows.empty() || - glb_value > glb_val || - (glb_value == glb_val && r.m_type == t_lt && !glb_strict)) { - glb_val = glb_value; - glb_index = row_id; - glb_strict = r.m_type == t_lt; - } - glb_rows.push_back(row_id); - glb_is_unit &= a.is_minus_one(); - } - } - - if (!divide_rows.empty()) - return solve_divides(x, divide_rows, compute_def); - - if (!div_rows.empty() || !mod_rows.empty()) - return solve_mod_div(x, mod_rows, div_rows, compute_def); - - if (eq_row != UINT_MAX) - return solve_for(eq_row, x, compute_def); - - def result; - unsigned lub_size = lub_rows.size(); - unsigned glb_size = glb_rows.size(); - unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; - - // There are only upper or only lower bounds. - if (row_index == UINT_MAX) { - if (compute_def) { - if (lub_index != UINT_MAX) - result = solve_for(lub_index, x, true); - else if (glb_index != UINT_MAX) - result = solve_for(glb_index, x, true); - else - result = def() + m_var2value[x]; - SASSERT(eval(result) == eval(x)); - } - else { - for (unsigned row_id : lub_rows) retire_row(row_id); - for (unsigned row_id : glb_rows) retire_row(row_id); - } - return result; - } - - SASSERT(lub_index != UINT_MAX); - SASSERT(glb_index != UINT_MAX); - if (compute_def) { - if (lub_size <= glb_size) - result = def(m_rows[lub_index], x); - else - result = def(m_rows[glb_index], x); - } - - // The number of matching lower and upper bounds is small. - if ((lub_size <= 2 || glb_size <= 2) && - (lub_size <= 3 && glb_size <= 3) && - (!is_int(x) || lub_is_unit || glb_is_unit)) { - for (unsigned i = 0; i < lub_size; ++i) { - unsigned row_id1 = lub_rows[i]; - bool last = i + 1 == lub_size; - rational coeff = get_coefficient(row_id1, x); - for (unsigned row_id2 : glb_rows) { - if (last) { - resolve(row_id1, coeff, row_id2, x); - } - else { - unsigned row_id3 = copy_row(row_id2); - resolve(row_id1, coeff, row_id3, x); - } - } - } - for (unsigned row_id : lub_rows) - retire_row(row_id); - - return result; - } - - // General case. - rational coeff = get_coefficient(row_index, x); - - for (unsigned row_id : lub_rows) - if (row_id != row_index) - resolve(row_index, coeff, row_id, x); - - for (unsigned row_id : glb_rows) - if (row_id != row_index) - resolve(row_index, coeff, row_id, x); - retire_row(row_index); - return result; - } - - - // - // Given v = a*x + b mod K - // - // - remove v = a*x + b mod K - // - // case a = 1: - // - add w = b mod K - // - x |-> K*y + z, 0 <= z < K - // - if z.value + w.value < K: - // add z + w - v = 0 - // - if z.value + w.value >= K: - // add z + w - v - K = 0 - // - // case a != 1, gcd(a, K) = 1 - // - x |-> x*y + a^-1*z, 0 <= z < K - // - add w = b mod K - // if z.value + w.value < K - // add z + w - v = 0 - // if z.value + w.value >= K - // add z + w - v - K = 0 - // - // case a != 1, gcd(a,K) = g != 1 - // - x |-> x*y + a^-1*z, 0 <= z < K - // a*x + b mod K = v is now - // g*z + b mod K = v - // - add w = b mod K - // - 0 <= g*z.value + w.value < K*(g+1) - // - add g*z + w - v - k*K = 0 for suitable k from 0 .. g based on model - // - // - // - // Given v = a*x + b div K - // Replace x |-> K*y + z - // - w = b div K - // - v = ((a*K*y + a*z) + b) div K - // = a*y + (a*z + b) div K - // = a*y + b div K + (b mod K + a*z) div K - // = a*y + b div K + k - // where k := (b.value mod K + a*z.value) div K - // k is between 0 and a - // - // - k*K <= b mod K + a*z < (k+1)*K - // - // A better version using a^-1 - // - v = (a*K*y + a^-1*a*z + b) div K - // = a*y + ((K*A + g)*z + b) div K where we write a*a^-1 = K*A + g - // = a*y + A + (g*z + b) div K - // - k*K <= b Kod m + gz < (k+1)*K - // where k is between 0 and g - // when gcd(a, K) = 1, then there are only two cases. - // - model_based_opt::def model_based_opt::solve_mod_div(unsigned x, unsigned_vector const& _mod_rows, unsigned_vector const& _div_rows, bool compute_def) { - def result; - unsigned_vector div_rows(_div_rows), mod_rows(_mod_rows); - SASSERT(!div_rows.empty() || !mod_rows.empty()); - TRACE("opt", display(tout << "solve_div v" << x << "\n")); - - rational K(1); - for (unsigned ri : div_rows) - K = lcm(K, m_rows[ri].m_mod); - for (unsigned ri : mod_rows) - K = lcm(K, m_rows[ri].m_mod); - - rational x_value = m_var2value[x]; - rational z_value = mod(x_value, K); - rational y_value = div(x_value, K); - SASSERT(x_value == K * y_value + z_value); - SASSERT(0 <= z_value && z_value < K); - // add new variables - unsigned z = add_var(z_value, true); - unsigned y = add_var(y_value, true); - - uint_set visited; - unsigned j = 0; - for (unsigned ri : div_rows) { - if (visited.contains(ri)) - continue; - row& r = m_rows[ri]; - mul(ri, K / r.m_mod); - r.m_alive = false; - visited.insert(ri); - div_rows[j++] = ri; - } - div_rows.shrink(j); - - j = 0; - for (unsigned ri : mod_rows) { - if (visited.contains(ri)) - continue; - m_rows[ri].m_alive = false; - visited.insert(ri); - mod_rows[j++] = ri; - } - mod_rows.shrink(j); - - - // replace x by K*y + z in other rows. - for (unsigned ri : m_var2row_ids[x]) { - if (visited.contains(ri)) - continue; - replace_var(ri, x, K, y, rational::one(), z); - visited.insert(ri); - normalize(ri); - } - - // add bounds for z - add_lower_bound(z, rational::zero()); - add_upper_bound(z, K - 1); - - - // solve for x_value = K*y_value + z_value, 0 <= z_value < K. - - unsigned_vector vs; - - for (unsigned ri : div_rows) { - - rational a = get_coefficient(ri, x); - replace_var(ri, x, rational::zero()); - - // add w = b div m - vector coeffs = m_rows[ri].m_vars; - rational coeff = m_rows[ri].m_coeff; - unsigned w = UINT_MAX; - rational offset(0); - if (K == 1) - offset = coeff; - else if (coeffs.empty()) - offset = div(coeff, K); - else - w = add_div(coeffs, coeff, K); - - // - // w = b div K - // v = a*y + w + k - // k = (a*z_value + (b_value mod K)) div K - // k*K <= a*z + b mod K < (k+1)*K - // - /** - * It is based on the following claim (tested for select values of a, K) - * (define-const K Int 13) - * (declare-const b Int) - * (define-const a Int -11) - * (declare-const y Int) - * (declare-const z Int) - * (define-const w Int (div b K)) - * (define-const k1 Int (+ (* a z) (mod b K))) - * (define-const k Int (div k1 K)) - * (define-const x Int (+ (* K y) z)) - * (define-const u Int (+ (* a x) b)) - * (define-const v Int (+ (* a y) w k)) - * (assert (<= 0 z)) - * (assert (< z K)) - * (assert (<= (* K k) k1)) - * (assert (< k1 (* K (+ k 1)))) - * (assert (not (= (div u K) v))) - * (check-sat) - */ - unsigned v = m_rows[ri].m_id; - rational b_value = eval(coeffs) + coeff; - rational k = div(a * z_value + mod(b_value, K), K); - vector div_coeffs; - div_coeffs.push_back(var(v, rational::minus_one())); - div_coeffs.push_back(var(y, a)); - if (w != UINT_MAX) - div_coeffs.push_back(var(w, rational::one())); - else if (K == 1) - div_coeffs.append(coeffs); - add_constraint(div_coeffs, k + offset, t_eq); - - unsigned u = UINT_MAX; - offset = 0; - if (K == 1) - offset = 0; - else if (coeffs.empty()) - offset = mod(coeff, K); - else - u = add_mod(coeffs, coeff, K); - - - // add a*z + (b mod K) < (k + 1)*K - vector bound_coeffs; - bound_coeffs.push_back(var(z, a)); - if (u != UINT_MAX) - bound_coeffs.push_back(var(u, rational::one())); - add_constraint(bound_coeffs, 1 - K * (k + 1) + offset, t_le); - - // add k*K <= az + (b mod K) - for (auto& c : bound_coeffs) - c.m_coeff.neg(); - add_constraint(bound_coeffs, k * K - offset, t_le); - // allow to recycle row. - retire_row(ri); - vs.push_back(v); - } - - for (unsigned ri : mod_rows) { - rational a = get_coefficient(ri, x); - replace_var(ri, x, rational::zero()); - rational rMod = m_rows[ri].m_mod; - - // add w = b mod rMod - vector coeffs = m_rows[ri].m_vars; - rational coeff = m_rows[ri].m_coeff; - unsigned v = m_rows[ri].m_id; - rational v_value = m_var2value[v]; - - unsigned w = UINT_MAX; - rational offset(0); - if (coeffs.empty() || rMod == 1) - offset = mod(coeff, rMod); - else - w = add_mod(coeffs, coeff, rMod); - - - rational w_value = w == UINT_MAX ? offset : m_var2value[w]; - -#if 0 - // V := (a * z_value + w_value) div rMod - // V*rMod <= a*z + w < (V+1)*rMod - // v = a*z + w - V*rMod - SASSERT(a > 0); - SASSERT(z_value >= 0); - SASSERT(w_value >= 0); - SASSERT(a * z_value + w_value >= 0); - rational V = div(a * z_value + w_value, rMod); - vector mod_coeffs; - SASSERT(V >= 0); - SASSERT(a * z_value + w_value >= V*rMod); - SASSERT((V+1)*rMod > a*z_value + w_value); - // -a*z - w + V*rMod <= 0 - mod_coeffs.push_back(var(z, -a)); - if (w != UINT_MAX) mod_coeffs.push_back(var(w, -rational::one())); - add_constraint(mod_coeffs, V*rMod - offset, t_le); - mod_coeffs.reset(); - // a*z + w - (V+1)*rMod + 1 <= 0 - mod_coeffs.push_back(var(z, a)); - if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); - add_constraint(mod_coeffs, -(V+1)*rMod + offset + 1, t_le); - mod_coeffs.reset(); - // -v + a*z + w - V*rMod = 0 - mod_coeffs.push_back(var(v, rational::minus_one())); - mod_coeffs.push_back(var(z, a)); - if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); - add_constraint(mod_coeffs, offset - V*rMod, t_eq); - -#else - // add v = a*z + w - V, for V = v_value - a * z_value - w_value - // claim: (= (mod x rMod) (- x (* rMod (div x rMod)))))) is a theorem for every x, rMod != 0 - rational V = v_value - a * z_value - w_value; - vector mod_coeffs; - mod_coeffs.push_back(var(v, rational::minus_one())); - mod_coeffs.push_back(var(z, a)); - if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); - add_constraint(mod_coeffs, V + offset, t_eq); - add_lower_bound(v, rational::zero()); - add_upper_bound(v, rMod - 1); -#endif - - retire_row(ri); - vs.push_back(v); - } - - - for (unsigned v : vs) { - def v_def = project(v, compute_def); - if (compute_def) - eliminate(v, v_def); - } - - // project internal variables. - def z_def = project(z, compute_def); - def y_def = project(y, compute_def); // may depend on z - - if (compute_def) { - z_def.substitute(y, y_def); - eliminate(y, y_def); - eliminate(z, z_def); - - result = (y_def * K) + z_def; - m_var2value[x] = eval(result); - TRACE("opt", tout << y << " := " << y_def << "\n"; - tout << z << " := " << z_def << "\n"; - tout << x << " := " << result << "\n"); - } - TRACE("opt", display(tout << "solve_div done v" << x << "\n")); - return result; - } - - // - // compute D and u. - // - // D = lcm(d1, d2) - // u = eval(x) mod D - // - // d1 | (a1x + t1) & d2 | (a2x + t2) - // = - // d1 | (a1(D*x' + u) + t1) & d2 | (a2(D*x' + u) + t2) - // = - // d1 | (a1*u + t1) & d2 | (a2*u + t2) - // - // x := D*x' + u - // - - model_based_opt::def model_based_opt::solve_divides(unsigned x, unsigned_vector const& divide_rows, bool compute_def) { - SASSERT(!divide_rows.empty()); - rational D(1); - for (unsigned idx : divide_rows) { - D = lcm(D, m_rows[idx].m_mod); - } - if (D.is_zero()) { - throw default_exception("modulo 0 is not defined"); - } - if (D.is_neg()) D = abs(D); - TRACE("opt1", display(tout << "lcm: " << D << " x: v" << x << " tableau\n");); - rational val_x = m_var2value[x]; - rational u = mod(val_x, D); - SASSERT(u.is_nonneg() && u < D); - for (unsigned idx : divide_rows) { - replace_var(idx, x, u); - SASSERT(invariant(idx, m_rows[idx])); - normalize(idx); - } - TRACE("opt1", display(tout << "tableau after replace x under mod\n");); - // - // update inequalities such that u is added to t and - // D is multiplied to coefficient of x. - // the interpretation of the new version of x is (x-u)/D - // - // a*x + t <= 0 - // a*(D*x' + u) + t <= 0 - // a*D*x' + a*u + t <= 0 - // - rational new_val = (val_x - u) / D; - SASSERT(new_val.is_int()); - unsigned y = add_var(new_val, true); - unsigned_vector const& row_ids = m_var2row_ids[x]; - uint_set visited; - for (unsigned row_id : row_ids) { - if (visited.contains(row_id)) - continue; - // x |-> D*y + u - replace_var(row_id, x, D, y, u); - visited.insert(row_id); - normalize(row_id); - } - TRACE("opt1", display(tout << "tableau after replace x by y := v" << y << "\n");); - def result = project(y, compute_def); - if (compute_def) { - result = (result * D) + u; - m_var2value[x] = eval(result); - } - TRACE("opt1", display(tout << "tableau after project y" << y << "\n");); - - return result; - } - - // update row with: x |-> C - void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& C) { - row& r = m_rows[row_id]; - SASSERT(!get_coefficient(row_id, x).is_zero()); - unsigned sz = r.m_vars.size(); - unsigned i = 0, j = 0; - rational coeff(0); - for (; i < sz; ++i) { - if (r.m_vars[i].m_id == x) { - coeff = r.m_vars[i].m_coeff; - } - else { - if (i != j) { - r.m_vars[j] = r.m_vars[i]; - } - ++j; - } - } - if (j != sz) { - r.m_vars.shrink(j); - } - r.m_coeff += coeff*C; - r.m_value += coeff*(C - m_var2value[x]); - } - - // update row with: x |-> A*y + B - void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B) { - row& r = m_rows[row_id]; - rational coeff = get_coefficient(row_id, x); - if (coeff.is_zero()) return; - if (!r.m_alive) return; - replace_var(row_id, x, B); - r.m_vars.push_back(var(y, coeff*A)); - r.m_value += coeff*A*m_var2value[y]; - if (!r.m_vars.empty() && r.m_vars.back().m_id > y) - std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); - m_var2row_ids[y].push_back(row_id); - SASSERT(invariant(row_id, r)); - } - - // update row with: x |-> A*y + B*z - void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B, unsigned z) { - row& r = m_rows[row_id]; - rational coeff = get_coefficient(row_id, x); - if (coeff.is_zero() || !r.m_alive) - return; - replace_var(row_id, x, rational::zero()); - if (A != 0) r.m_vars.push_back(var(y, coeff*A)); - if (B != 0) r.m_vars.push_back(var(z, coeff*B)); - r.m_value += coeff*A*m_var2value[y]; - r.m_value += coeff*B*m_var2value[z]; - std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); - if (A != 0) m_var2row_ids[y].push_back(row_id); - if (B != 0) m_var2row_ids[z].push_back(row_id); - SASSERT(invariant(row_id, r)); - } - - // 3x + t = 0 & 7 | (c*x + s) & ax <= u - // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u - - model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) { - TRACE("opt", tout << "v" << x << " := " << eval(x) << "\n" << m_rows[row_id1] << "\n"; - display(tout)); - rational a = get_coefficient(row_id1, x), b; - row& r1 = m_rows[row_id1]; - ineq_type ty = r1.m_type; - SASSERT(!a.is_zero()); - SASSERT(r1.m_alive); - if (a.is_neg()) { - a.neg(); - r1.neg(); - } - SASSERT(a.is_pos()); - if (ty == t_lt) { - SASSERT(compute_def); - r1.m_coeff -= r1.m_value; - r1.m_type = t_le; - r1.m_value = 0; - } - - if (m_var2is_int[x] && !a.is_one()) { - r1.m_coeff -= r1.m_value; - r1.m_value = 0; - vector coeffs; - mk_coeffs_without(coeffs, r1.m_vars, x); - rational c = mod(-eval(coeffs), a); - add_divides(coeffs, c, a); - } - unsigned_vector const& row_ids = m_var2row_ids[x]; - uint_set visited; - visited.insert(row_id1); - for (unsigned row_id2 : row_ids) { - if (visited.contains(row_id2)) - continue; - visited.insert(row_id2); - row& r = m_rows[row_id2]; - if (!r.m_alive) - continue; - b = get_coefficient(row_id2, x); - if (b.is_zero()) - continue; - row& dst = m_rows[row_id2]; - switch (dst.m_type) { - case t_eq: - case t_lt: - case t_le: - solve(row_id1, a, row_id2, x); - break; - case t_divides: - case t_mod: - case t_div: - // mod reduction already done. - UNREACHABLE(); - break; - } - } - def result; - if (compute_def) { - result = def(m_rows[row_id1], x); - m_var2value[x] = eval(result); - TRACE("opt1", tout << "updated eval " << x << " := " << eval(x) << "\n";); - } - retire_row(row_id1); - TRACE("opt", display(tout << "solved v" << x << "\n")); - return result; - } - - void model_based_opt::eliminate(unsigned v, def const& new_def) { - for (auto & d : m_result) - d.substitute(v, new_def); - } - - vector model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) { - m_result.reset(); - for (unsigned i = 0; i < num_vars; ++i) { - m_result.push_back(project(vars[i], compute_def)); - eliminate(vars[i], m_result.back()); - TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); - } - return m_result; - } - -} - +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + model_based_opt.cpp + +Abstract: + + Model-based optimization and projection for linear real, integer arithmetic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-27-4 + +Revision History: + + +--*/ + +#include "math/simplex/model_based_opt.h" +#include "util/uint_set.h" +#include "util/z3_exception.h" + +std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { + switch (ie) { + case opt::t_eq: return out << " = "; + case opt::t_lt: return out << " < "; + case opt::t_le: return out << " <= "; + case opt::t_divides: return out << " divides "; + case opt::t_mod: return out << " mod "; + case opt::t_div: return out << " div "; + } + return out; +} + + +namespace opt { + + /** + * Convert a row ax + coeffs + coeff = value into a definition for x + * x = (value - coeffs - coeff)/a + * as backdrop we have existing assignments to x and other variables that + * satisfy the equality with value, and such that value satisfies + * the row constraint ( = , <= , < , mod) + */ + model_based_opt::def::def(row const& r, unsigned x) { + for (var const & v : r.m_vars) { + if (v.m_id != x) { + m_vars.push_back(v); + } + else { + m_div = -v.m_coeff; + } + } + m_coeff = r.m_coeff; + switch (r.m_type) { + case opt::t_lt: + m_coeff += m_div; + break; + case opt::t_le: + // for: ax >= t, then x := (t + a - 1) div a + if (m_div.is_pos()) { + m_coeff += m_div; + m_coeff -= rational::one(); + } + break; + default: + break; + } + normalize(); + SASSERT(m_div.is_pos()); + } + + model_based_opt::def model_based_opt::def::operator+(def const& other) const { + def result; + vector const& vs1 = m_vars; + vector const& vs2 = other.m_vars; + vector & vs = result.m_vars; + rational c1(1), c2(1); + if (m_div != other.m_div) { + c1 = other.m_div; + c2 = m_div; + } + unsigned i = 0, j = 0; + while (i < vs1.size() || j < vs2.size()) { + unsigned v1 = UINT_MAX, v2 = UINT_MAX; + if (i < vs1.size()) v1 = vs1[i].m_id; + if (j < vs2.size()) v2 = vs2[j].m_id; + if (v1 == v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + vs.back().m_coeff += c2 * vs2[j].m_coeff; + ++i; ++j; + if (vs.back().m_coeff.is_zero()) { + vs.pop_back(); + } + } + else if (v1 < v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + ++i; + } + else { + vs.push_back(vs2[j]); + vs.back().m_coeff *= c2; + ++j; + } + } + result.m_div = c1*m_div; + result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2); + result.normalize(); + return result; + } + + /** + a1*x1 + a2*x2 + a3*x3 + coeff1 / c1 + x2 |-> b1*x1 + b4*x4 + ceoff2 / c2 + ------------------------------------------------------------------------ + (a1*x1 + a2*((b1*x1 + b4*x4 + coeff2) / c2) + a3*x3 + coeff1) / c1 + ------------------------------------------------------------------------ + (c2*a1*x1 + a2*b1*x1 + a2*b4*x4 + c2*a3*x3 + c2*coeff1 + coeff2) / c1*c2 + */ + void model_based_opt::def::substitute(unsigned v, def const& other) { + vector const& vs1 = m_vars; + rational coeff(0); + for (auto const& [id, c] : vs1) { + if (id == v) { + coeff = c; + break; + } + } + if (coeff == 0) + return; + + rational c1 = m_div; + rational c2 = other.m_div; + + vector const& vs2 = other.m_vars; + vector vs; + unsigned i = 0, j = 0; + while (i < vs1.size() || j < vs2.size()) { + unsigned v1 = UINT_MAX, v2 = UINT_MAX; + if (i < vs1.size()) v1 = vs1[i].m_id; + if (j < vs2.size()) v2 = vs2[j].m_id; + if (v1 == v) + ++i; + else if (v1 == v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c2; + vs.back().m_coeff += coeff * vs2[j].m_coeff; + ++i; ++j; + if (vs.back().m_coeff.is_zero()) + vs.pop_back(); + } + else if (v1 < v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c2; + ++i; + } + else { + vs.push_back(vs2[j]); + vs.back().m_coeff *= coeff; + ++j; + } + } + m_div *= other.m_div; + m_coeff *= c2; + m_coeff += coeff*other.m_coeff; + m_vars.reset(); + m_vars.append(vs); + normalize(); + } + + model_based_opt::def model_based_opt::def::operator/(rational const& r) const { + def result(*this); + result.m_div *= r; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator*(rational const& n) const { + def result(*this); + for (var& v : result.m_vars) { + v.m_coeff *= n; + } + result.m_coeff *= n; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator+(rational const& n) const { + def result(*this); + result.m_coeff += n * result.m_div; + result.normalize(); + return result; + } + + void model_based_opt::def::normalize() { + if (!m_div.is_int()) { + rational den = denominator(m_div); + SASSERT(den > 1); + for (var& v : m_vars) + v.m_coeff *= den; + m_coeff *= den; + m_div *= den; + + } + if (m_div.is_neg()) { + for (var& v : m_vars) + v.m_coeff.neg(); + m_coeff.neg(); + m_div.neg(); + } + if (m_div.is_one()) + return; + rational g(m_div); + if (!m_coeff.is_int()) + return; + g = gcd(g, m_coeff); + for (var const& v : m_vars) { + if (!v.m_coeff.is_int()) + return; + g = gcd(g, abs(v.m_coeff)); + if (g.is_one()) + break; + } + if (!g.is_one()) { + for (var& v : m_vars) + v.m_coeff /= g; + m_coeff /= g; + m_div /= g; + } + } + + model_based_opt::model_based_opt() { + m_rows.push_back(row()); + } + + bool model_based_opt::invariant() { + for (unsigned i = 0; i < m_rows.size(); ++i) { + if (!invariant(i, m_rows[i])) { + return false; + } + } + return true; + } + +#define PASSERT(_e_) { CTRACE("qe", !(_e_), display(tout, r); display(tout);); SASSERT(_e_); } + + bool model_based_opt::invariant(unsigned index, row const& r) { + vector const& vars = r.m_vars; + for (unsigned i = 0; i < vars.size(); ++i) { + // variables in each row are sorted and have non-zero coefficients + PASSERT(i + 1 == vars.size() || vars[i].m_id < vars[i+1].m_id); + PASSERT(!vars[i].m_coeff.is_zero()); + PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index)); + } + + PASSERT(r.m_value == eval(r)); + PASSERT(r.m_type != t_eq || r.m_value.is_zero()); + // values satisfy constraints + PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg()); + PASSERT(index == 0 || r.m_type != t_le || !r.m_value.is_pos()); + PASSERT(index == 0 || r.m_type != t_divides || (mod(r.m_value, r.m_mod).is_zero())); + PASSERT(index == 0 || r.m_type != t_mod || r.m_id < m_var2value.size()); + PASSERT(index == 0 || r.m_type != t_div || r.m_id < m_var2value.size()); + return true; + } + + // a1*x + obj + // a2*x + t2 <= 0 + // a3*x + t3 <= 0 + // a4*x + t4 <= 0 + // a1 > 0, a2 > 0, a3 > 0, a4 < 0 + // x <= -t2/a2 + // x <= -t2/a3 + // determine lub among these. + // then resolve lub with others + // e.g., -t2/a2 <= -t3/a3, then + // replace inequality a3*x + t3 <= 0 by -t2/a2 + t3/a3 <= 0 + // mark a4 as invalid. + // + + // a1 < 0, a2 < 0, a3 < 0, a4 > 0 + // x >= t2/a2 + // x >= t3/a3 + // determine glb among these + // the resolve glb with others. + // e.g. t2/a2 >= t3/a3 + // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 + // + inf_eps model_based_opt::maximize() { + SASSERT(invariant()); + unsigned_vector bound_trail, bound_vars; + TRACE("opt", display(tout << "tableau\n");); + while (!objective().m_vars.empty()) { + var v = objective().m_vars.back(); + unsigned x = v.m_id; + rational const& coeff = v.m_coeff; + unsigned bound_row_index; + rational bound_coeff; + if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { + SASSERT(!bound_coeff.is_zero()); + TRACE("opt", display(tout << "update: " << v << " ", objective()); + for (unsigned above : m_above) { + display(tout << "resolve: ", m_rows[above]); + }); + for (unsigned above : m_above) { + resolve(bound_row_index, bound_coeff, above, x); + } + for (unsigned below : m_below) { + resolve(bound_row_index, bound_coeff, below, x); + } + // coeff*x + objective <= ub + // a2*x + t2 <= 0 + // => coeff*x <= -t2*coeff/a2 + // objective + t2*coeff/a2 <= ub + + mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); + retire_row(bound_row_index); + bound_trail.push_back(bound_row_index); + bound_vars.push_back(x); + } + else { + TRACE("opt", display(tout << "unbound: " << v << " ", objective());); + update_values(bound_vars, bound_trail); + return inf_eps::infinity(); + } + } + + // + // update the evaluation of variables to satisfy the bound. + // + + update_values(bound_vars, bound_trail); + + rational value = objective().m_value; + if (objective().m_type == t_lt) { + return inf_eps(inf_rational(value, rational(-1))); + } + else { + return inf_eps(inf_rational(value)); + } + } + + + void model_based_opt::update_value(unsigned x, rational const& val) { + rational old_val = m_var2value[x]; + m_var2value[x] = val; + SASSERT(val.is_int() || !is_int(x)); + unsigned_vector const& row_ids = m_var2row_ids[x]; + for (unsigned row_id : row_ids) { + rational coeff = get_coefficient(row_id, x); + if (coeff.is_zero()) { + continue; + } + row & r = m_rows[row_id]; + rational delta = coeff * (val - old_val); + r.m_value += delta; + SASSERT(invariant(row_id, r)); + } + } + + + void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { + for (unsigned i = bound_trail.size(); i-- > 0; ) { + unsigned x = bound_vars[i]; + row& r = m_rows[bound_trail[i]]; + rational val = r.m_coeff; + rational old_x_val = m_var2value[x]; + rational new_x_val; + rational x_coeff, eps(0); + vector const& vars = r.m_vars; + for (var const& v : vars) { + if (x == v.m_id) { + x_coeff = v.m_coeff; + } + else { + val += m_var2value[v.m_id]*v.m_coeff; + } + } + SASSERT(!x_coeff.is_zero()); + new_x_val = -val/x_coeff; + + if (r.m_type == t_lt) { + eps = abs(old_x_val - new_x_val)/rational(2); + eps = std::min(rational::one(), eps); + SASSERT(!eps.is_zero()); + + // + // ax + t < 0 + // <=> x < -t/a + // <=> x := -t/a - epsilon + // + if (x_coeff.is_pos()) { + new_x_val -= eps; + } + // + // -ax + t < 0 + // <=> -ax < -t + // <=> -x < -t/a + // <=> x > t/a + // <=> x := t/a + epsilon + // + else { + new_x_val += eps; + } + } + TRACE("opt", display(tout << "v" << x + << " coeff_x: " << x_coeff + << " old_x_val: " << old_x_val + << " new_x_val: " << new_x_val + << " eps: " << eps << " ", r); ); + m_var2value[x] = new_x_val; + + r.m_value = eval(r); + SASSERT(invariant(bound_trail[i], r)); + } + + // update and check bounds for all other affected rows. + for (unsigned i = bound_trail.size(); i-- > 0; ) { + unsigned x = bound_vars[i]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + for (unsigned row_id : row_ids) { + row & r = m_rows[row_id]; + r.m_value = eval(r); + SASSERT(invariant(row_id, r)); + } + } + SASSERT(invariant()); + } + + bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, bool is_pos) { + bound_row_index = UINT_MAX; + rational lub_val; + rational const& x_val = m_var2value[x]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + uint_set visited; + m_above.reset(); + m_below.reset(); + for (unsigned row_id : row_ids) { + SASSERT(row_id != m_objective_id); + if (visited.contains(row_id)) + continue; + visited.insert(row_id); + row& r = m_rows[row_id]; + if (!r.m_alive) + continue; + rational a = get_coefficient(row_id, x); + if (a.is_zero()) { + // skip + } + else if (a.is_pos() == is_pos || r.m_type == t_eq) { + rational value = x_val - (r.m_value/a); + if (bound_row_index == UINT_MAX) { + lub_val = value; + bound_row_index = row_id; + bound_coeff = a; + } + else if ((value == lub_val && r.m_type == opt::t_lt) || + (is_pos && value < lub_val) || + + (!is_pos && value > lub_val)) { + m_above.push_back(bound_row_index); + lub_val = value; + bound_row_index = row_id; + bound_coeff = a; + } + else + m_above.push_back(row_id); + } + else + m_below.push_back(row_id); + } + return bound_row_index != UINT_MAX; + } + + void model_based_opt::retire_row(unsigned row_id) { + SASSERT(!m_retired_rows.contains(row_id)); + m_rows[row_id].m_alive = false; + m_retired_rows.push_back(row_id); + } + + rational model_based_opt::eval(unsigned x) const { + return m_var2value[x]; + } + + rational model_based_opt::eval(def const& d) const { + vector const& vars = d.m_vars; + rational val = d.m_coeff; + for (var const& v : vars) { + val += v.m_coeff * eval(v.m_id); + } + val /= d.m_div; + return val; + } + + rational model_based_opt::eval(row const& r) const { + vector const& vars = r.m_vars; + rational val = r.m_coeff; + for (var const& v : vars) { + val += v.m_coeff * eval(v.m_id); + } + return val; + } + + rational model_based_opt::eval(vector const& coeffs) const { + rational val(0); + for (var const& v : coeffs) + val += v.m_coeff * eval(v.m_id); + return val; + } + + rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { + return m_rows[row_id].get_coefficient(var_id); + } + + rational model_based_opt::row::get_coefficient(unsigned var_id) const { + if (m_vars.empty()) + return rational::zero(); + unsigned lo = 0, hi = m_vars.size(); + while (lo < hi) { + unsigned mid = lo + (hi - lo)/2; + SASSERT(mid < hi); + unsigned id = m_vars[mid].m_id; + if (id == var_id) { + lo = mid; + break; + } + if (id < var_id) + lo = mid + 1; + else + hi = mid; + } + if (lo == m_vars.size()) + return rational::zero(); + unsigned id = m_vars[lo].m_id; + if (id == var_id) + return m_vars[lo].m_coeff; + else + return rational::zero(); + } + + model_based_opt::row& model_based_opt::row::normalize() { +#if 0 + if (m_type == t_divides || m_type == t_mod || m_type == t_div) + return *this; + rational D(denominator(abs(m_coeff))); + if (D == 0) + D = 1; + for (auto const& [id, coeff] : m_vars) + if (coeff != 0) + D = lcm(D, denominator(abs(coeff))); + if (D == 1) + return *this; + SASSERT(D > 0); + for (auto & [id, coeff] : m_vars) + coeff *= D; + m_coeff *= D; +#endif + return *this; + } + + // + // Let + // row1: t1 + a1*x <= 0 + // row2: t2 + a2*x <= 0 + // + // assume a1, a2 have the same signs: + // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 + // <=> t2*a1/a2 - t1 <= 0 + // <=> t2 - t1*a2/a1 <= 0 + // + // assume a1 > 0, -a2 < 0: + // t1 + a1*x <= 0, t2 - a2*x <= 0 + // t2/a2 <= -t1/a1 + // t2 + t1*a2/a1 <= 0 + // assume -a1 < 0, a2 > 0: + // t1 - a1*x <= 0, t2 + a2*x <= 0 + // t1/a1 <= -t2/a2 + // t2 + t1*a2/a1 <= 0 + // + // the resolvent is the same in all cases (simpler proof should exist) + // + // assume a1 < 0, -a1 = a2: + // t1 <= a2*div(t2, a2) + // + + void model_based_opt::resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { + + SASSERT(a1 == get_coefficient(row_src, x)); + SASSERT(!a1.is_zero()); + SASSERT(row_src != row_dst); + + if (m_rows[row_dst].m_alive) { + rational a2 = get_coefficient(row_dst, x); + if (is_int(x)) { + TRACE("opt", + tout << x << ": " << a1 << " " << a2 << ": "; + display(tout, m_rows[row_dst]); + display(tout, m_rows[row_src]);); + if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) { + mul_add(x, a1, row_src, a2, row_dst); + } + else { + mul(row_dst, abs(a1)); + mul_add(false, row_dst, -abs(a2), row_src); + } + TRACE("opt", display(tout << "result ", m_rows[row_dst]);); + normalize(row_dst); + } + else { + mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); + } + } + } + + /** + * a1 > 0 + * a1*x + r1 = value + * a2*x + r2 <= 0 + * ------------------ + * a1*r2 - a2*r1 <= value + */ + void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { + SASSERT(a1 == get_coefficient(row_src, x)); + SASSERT(a1.is_pos()); + SASSERT(row_src != row_dst); + if (!m_rows[row_dst].m_alive) return; + rational a2 = get_coefficient(row_dst, x); + mul(row_dst, a1); + mul_add(false, row_dst, -a2, row_src); + normalize(row_dst); + SASSERT(get_coefficient(row_dst, x).is_zero()); + } + + // resolution for integer rows. + void model_based_opt::mul_add( + unsigned x, rational src_c, unsigned row_src, rational dst_c, unsigned row_dst) { + row& dst = m_rows[row_dst]; + row const& src = m_rows[row_src]; + SASSERT(is_int(x)); + SASSERT(t_le == dst.m_type && t_le == src.m_type); + SASSERT(src_c.is_int()); + SASSERT(dst_c.is_int()); + SASSERT(m_var2value[x].is_int()); + + rational abs_src_c = abs(src_c); + rational abs_dst_c = abs(dst_c); + rational x_val = m_var2value[x]; + rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one()); + rational dst_val = dst.m_value - x_val*dst_c; + rational src_val = src.m_value - x_val*src_c; + rational distance = abs_src_c * dst_val + abs_dst_c * src_val + slack; + bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); + bool use_case2 = false && abs_src_c == abs_dst_c && src_c.is_pos() != dst_c.is_pos() && !abs_src_c.is_one() && t_le == dst.m_type && t_le == src.m_type; + bool use_case3 = false && src_c.is_pos() != dst_c.is_pos() && t_le == dst.m_type && t_le == src.m_type; + + + if (use_case1) { + TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); + // dst <- abs_src_c*dst + abs_dst_c*src + slack + mul(row_dst, abs_src_c); + add(row_dst, slack); + mul_add(false, row_dst, abs_dst_c, row_src); + return; + } + + if (use_case2 || use_case3) { + // case2: + // x*src_c + s <= 0 + // -x*src_c + t <= 0 + // + // -src_c*div(-s, src_c) + t <= 0 + // + // Example: + // t <= 100*x <= s + // Then t <= 100*div(s, 100) + // + // case3: + // x*src_c + s <= 0 + // -x*dst_c + t <= 0 + // t <= x*dst_c, x*src_c <= -s -> + // t <= dst_c*div(-s, src_c) -> + // -dst_c*div(-s,src_c) + t <= 0 + // + + bool swapped = false; + if (src_c < 0) { + std::swap(row_src, row_dst); + std::swap(src_c, dst_c); + std::swap(abs_src_c, abs_dst_c); + swapped = true; + } + vector src_coeffs, dst_coeffs; + rational src_coeff = m_rows[row_src].m_coeff; + rational dst_coeff = m_rows[row_dst].m_coeff; + for (auto const& v : m_rows[row_src].m_vars) + if (v.m_id != x) + src_coeffs.push_back(var(v.m_id, -v.m_coeff)); + for (auto const& v : m_rows[row_dst].m_vars) + if (v.m_id != x) + dst_coeffs.push_back(v); + unsigned v = UINT_MAX; + if (src_coeffs.empty()) + dst_coeff -= abs_dst_c*div(-src_coeff, abs_src_c); + else + v = add_div(src_coeffs, -src_coeff, abs_src_c); + if (v != UINT_MAX) dst_coeffs.push_back(var(v, -abs_dst_c)); + if (swapped) + std::swap(row_src, row_dst); + retire_row(row_dst); + add_constraint(dst_coeffs, dst_coeff, t_le); + return; + } + + // + // create finite disjunction for |b|. + // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 + // <=> + // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 + // <=> + // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 + // + + TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";); + vector coeffs; + if (abs_dst_c <= abs_src_c) { + rational z = mod(dst_val, abs_dst_c); + if (!z.is_zero()) z = abs_dst_c - z; + mk_coeffs_without(coeffs, dst.m_vars, x); + add_divides(coeffs, dst.m_coeff + z, abs_dst_c); + add(row_dst, z); + mul(row_dst, src_c * n_sign(dst_c)); + mul_add(false, row_dst, abs_dst_c, row_src); + } + else { + // z := b - (s + bx) mod b + // := b - s mod b + // b | s + z <=> b | s + b - s mod b <=> b | s - s mod b + rational z = mod(src_val, abs_src_c); + if (!z.is_zero()) z = abs_src_c - z; + mk_coeffs_without(coeffs, src.m_vars, x); + add_divides(coeffs, src.m_coeff + z, abs_src_c); + mul(row_dst, abs_src_c); + add(row_dst, z * dst_c * n_sign(src_c)); + mul_add(false, row_dst, dst_c * n_sign(src_c), row_src); + } + } + + void model_based_opt::mk_coeffs_without(vector& dst, vector const& src, unsigned x) { + for (var const & v : src) { + if (v.m_id != x) dst.push_back(v); + } + } + + rational model_based_opt::n_sign(rational const& b) const { + return rational(b.is_pos()?-1:1); + } + + void model_based_opt::mul(unsigned dst, rational const& c) { + if (c.is_one()) + return; + row& r = m_rows[dst]; + for (auto & v : r.m_vars) + v.m_coeff *= c; + r.m_mod *= c; + r.m_coeff *= c; + if (r.m_type != t_div && r.m_type != t_mod) + r.m_value *= c; + } + + void model_based_opt::add(unsigned dst, rational const& c) { + row& r = m_rows[dst]; + r.m_coeff += c; + r.m_value += c; + } + + void model_based_opt::sub(unsigned dst, rational const& c) { + row& r = m_rows[dst]; + r.m_coeff -= c; + r.m_value -= c; + } + + void model_based_opt::normalize(unsigned row_id) { + row& r = m_rows[row_id]; + if (!r.m_alive) + return; + if (r.m_vars.empty()) { + retire_row(row_id); + return; + } + if (r.m_type == t_divides) + return; + if (r.m_type == t_mod) + return; + if (r.m_type == t_div) + return; + rational g(abs(r.m_vars[0].m_coeff)); + bool all_int = g.is_int(); + for (unsigned i = 1; all_int && !g.is_one() && i < r.m_vars.size(); ++i) { + rational const& coeff = r.m_vars[i].m_coeff; + if (coeff.is_int()) { + g = gcd(g, abs(coeff)); + } + else { + all_int = false; + } + } + if (all_int && !r.m_coeff.is_zero()) { + if (r.m_coeff.is_int()) { + g = gcd(g, abs(r.m_coeff)); + } + else { + all_int = false; + } + } + if (all_int && !g.is_one()) { + SASSERT(!g.is_zero()); + mul(row_id, rational::one()/g); + } + } + + // + // set row1 <- row1 + c*row2 + // + void model_based_opt::mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2) { + if (c.is_zero()) + return; + + + m_new_vars.reset(); + row& r1 = m_rows[row_id1]; + row const& r2 = m_rows[row_id2]; + unsigned i = 0, j = 0; + while (i < r1.m_vars.size() || j < r2.m_vars.size()) { + if (j == r2.m_vars.size()) { + m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.data() + i); + break; + } + if (i == r1.m_vars.size()) { + for (; j < r2.m_vars.size(); ++j) { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; + if (row_id1 != m_objective_id) + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } + break; + } + + unsigned v1 = r1.m_vars[i].m_id; + unsigned v2 = r2.m_vars[j].m_id; + if (v1 == v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_new_vars.back().m_coeff += c*r2.m_vars[j].m_coeff; + ++i; + ++j; + if (m_new_vars.back().m_coeff.is_zero()) + m_new_vars.pop_back(); + } + else if (v1 < v2) { + m_new_vars.push_back(r1.m_vars[i]); + ++i; + } + else { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; + if (row_id1 != m_objective_id) + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + ++j; + } + } + r1.m_coeff += c*r2.m_coeff; + r1.m_vars.swap(m_new_vars); + r1.m_value += c*r2.m_value; + + if (!same_sign && r2.m_type == t_lt) + r1.m_type = t_lt; + else if (same_sign && r1.m_type == t_lt && r2.m_type == t_lt) + r1.m_type = t_le; + SASSERT(invariant(row_id1, r1)); + } + + void model_based_opt::display(std::ostream& out) const { + for (auto const& r : m_rows) + display(out, r); + for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { + unsigned_vector const& rows = m_var2row_ids[i]; + out << i << ": "; + for (auto const& r : rows) + out << r << " "; + out << "\n"; + } + } + + void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { + unsigned i = 0; + for (var const& v : vars) { + if (i > 0 && v.m_coeff.is_pos()) + out << "+ "; + ++i; + if (v.m_coeff.is_one()) + out << "v" << v.m_id << " "; + else + out << v.m_coeff << "*v" << v.m_id << " "; + } + if (coeff.is_pos()) + out << " + " << coeff << " "; + else if (coeff.is_neg()) + out << coeff << " "; + } + + std::ostream& model_based_opt::display(std::ostream& out, row const& r) { + out << (r.m_alive?"a":"d") << " "; + display(out, r.m_vars, r.m_coeff); + switch (r.m_type) { + case opt::t_divides: + out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n"; + break; + case opt::t_mod: + out << r.m_type << " " << r.m_mod << " = v" << r.m_id << " ; mod: " << mod(r.m_value, r.m_mod) << "\n"; + break; + case opt::t_div: + out << r.m_type << " " << r.m_mod << " = v" << r.m_id << " ; div: " << div(r.m_value, r.m_mod) << "\n"; + break; + default: + out << r.m_type << " 0; value: " << r.m_value << "\n"; + break; + } + return out; + } + + std::ostream& model_based_opt::display(std::ostream& out, def const& r) { + display(out, r.m_vars, r.m_coeff); + if (!r.m_div.is_one()) { + out << " / " << r.m_div; + } + return out; + } + + unsigned model_based_opt::add_var(rational const& value, bool is_int) { + unsigned v = m_var2value.size(); + m_var2value.push_back(value); + m_var2is_int.push_back(is_int); + SASSERT(value.is_int() || !is_int); + m_var2row_ids.push_back(unsigned_vector()); + return v; + } + + rational model_based_opt::get_value(unsigned var) { + return m_var2value[var]; + } + + void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { + row& r = m_rows[row_id]; + rational val(c); + SASSERT(r.m_vars.empty()); + r.m_vars.append(coeffs.size(), coeffs.data()); + bool is_int_row = !coeffs.empty(); + std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); + for (auto const& c : coeffs) { + val += m_var2value[c.m_id] * c.m_coeff; + SASSERT(!is_int(c.m_id) || c.m_coeff.is_int()); + is_int_row &= is_int(c.m_id); + } + r.m_alive = true; + r.m_coeff = c; + r.m_value = val; + r.m_type = rel; + r.m_mod = m; + if (is_int_row && rel == t_lt) { + r.m_type = t_le; + r.m_coeff += rational::one(); + r.m_value += rational::one(); + } + } + + unsigned model_based_opt::new_row() { + unsigned row_id = 0; + if (m_retired_rows.empty()) { + row_id = m_rows.size(); + m_rows.push_back(row()); + } + else { + row_id = m_retired_rows.back(); + m_retired_rows.pop_back(); + SASSERT(!m_rows[row_id].m_alive); + m_rows[row_id].reset(); + m_rows[row_id].m_alive = true; + } + return row_id; + } + + unsigned model_based_opt::copy_row(unsigned src, unsigned excl) { + unsigned dst = new_row(); + row const& r = m_rows[src]; + set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type); + for (auto const& v : r.m_vars) { + if (v.m_id != excl) + m_var2row_ids[v.m_id].push_back(dst); + } + SASSERT(invariant(dst, m_rows[dst])); + return dst; + } + + // -x + lo <= 0 + void model_based_opt::add_lower_bound(unsigned x, rational const& lo) { + vector coeffs; + coeffs.push_back(var(x, rational::minus_one())); + add_constraint(coeffs, lo, t_le); + } + + // x - hi <= 0 + void model_based_opt::add_upper_bound(unsigned x, rational const& hi) { + vector coeffs; + coeffs.push_back(var(x, rational::one())); + add_constraint(coeffs, -hi, t_le); + } + + void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { + add_constraint(coeffs, c, rational::zero(), rel, 0); + } + + void model_based_opt::add_divides(vector const& coeffs, rational const& c, rational const& m) { + rational g(c); + for (auto const& [v, coeff] : coeffs) + g = gcd(coeff, g); + if ((g/m).is_int()) + return; + add_constraint(coeffs, c, m, t_divides, 0); + } + + unsigned model_based_opt::add_mod(vector const& coeffs, rational const& c, rational const& m) { + rational value = c; + for (auto const& var : coeffs) + value += var.m_coeff * m_var2value[var.m_id]; + unsigned v = add_var(mod(value, m), true); + add_constraint(coeffs, c, m, t_mod, v); + return v; + } + + unsigned model_based_opt::add_div(vector const& coeffs, rational const& c, rational const& m) { + rational value = c; + for (auto const& var : coeffs) + value += var.m_coeff * m_var2value[var.m_id]; + unsigned v = add_var(div(value, m), true); + add_constraint(coeffs, c, m, t_div, v); + return v; + } + + unsigned model_based_opt::add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type rel, unsigned id) { + auto const& r = m_rows.back(); + if (r.m_vars == coeffs && r.m_coeff == c && r.m_mod == m && r.m_type == rel && r.m_id == id && r.m_alive) + return m_rows.size() - 1; + unsigned row_id = new_row(); + set_row(row_id, coeffs, c, m, rel); + m_rows[row_id].m_id = id; + for (var const& coeff : coeffs) + m_var2row_ids[coeff.m_id].push_back(row_id); + SASSERT(invariant(row_id, m_rows[row_id])); + normalize(row_id); + return row_id; + } + + void model_based_opt::set_objective(vector const& coeffs, rational const& c) { + set_row(m_objective_id, coeffs, c, rational::zero(), t_le); + } + + void model_based_opt::get_live_rows(vector& rows) { + for (row & r : m_rows) + if (r.m_alive) + rows.push_back(r.normalize()); + } + + // + // pick glb and lub representative. + // The representative is picked such that it + // represents the fewest inequalities. + // The constraints that enforce a glb or lub are not forced. + // The constraints that separate the glb from ub or the lub from lb + // are not forced. + // In other words, suppose there are + // . N inequalities of the form t <= x + // . M inequalities of the form s >= x + // . t0 is glb among N under valuation. + // . s0 is lub among M under valuation. + // If N < M + // create the inequalities: + // t <= t0 for each t other than t0 (N-1 inequalities). + // t0 <= s for each s (M inequalities). + // If N >= M the construction is symmetric. + // + model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) { + unsigned_vector& lub_rows = m_lub; + unsigned_vector& glb_rows = m_glb; + unsigned_vector& divide_rows = m_divides; + unsigned_vector& mod_rows = m_mod; + unsigned_vector& div_rows = m_div; + unsigned lub_index = UINT_MAX, glb_index = UINT_MAX; + bool lub_strict = false, glb_strict = false; + rational lub_val, glb_val; + rational const& x_val = m_var2value[x]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + uint_set visited; + lub_rows.reset(); + glb_rows.reset(); + divide_rows.reset(); + mod_rows.reset(); + div_rows.reset(); + bool lub_is_unit = true, glb_is_unit = true; + unsigned eq_row = UINT_MAX; + // select the lub and glb. + for (unsigned row_id : row_ids) { + if (visited.contains(row_id)) + continue; + visited.insert(row_id); + row& r = m_rows[row_id]; + if (!r.m_alive) + continue; + rational a = get_coefficient(row_id, x); + if (a.is_zero()) + continue; + if (r.m_type == t_eq) + eq_row = row_id; + else if (r.m_type == t_mod) + mod_rows.push_back(row_id); + else if (r.m_type == t_div) + div_rows.push_back(row_id); + else if (r.m_type == t_divides) + divide_rows.push_back(row_id); + else if (a.is_pos()) { + rational lub_value = x_val - (r.m_value/a); + if (lub_rows.empty() || + lub_value < lub_val || + (lub_value == lub_val && r.m_type == t_lt && !lub_strict)) { + lub_val = lub_value; + lub_index = row_id; + lub_strict = r.m_type == t_lt; + } + lub_rows.push_back(row_id); + lub_is_unit &= a.is_one(); + } + else { + SASSERT(a.is_neg()); + rational glb_value = x_val - (r.m_value/a); + if (glb_rows.empty() || + glb_value > glb_val || + (glb_value == glb_val && r.m_type == t_lt && !glb_strict)) { + glb_val = glb_value; + glb_index = row_id; + glb_strict = r.m_type == t_lt; + } + glb_rows.push_back(row_id); + glb_is_unit &= a.is_minus_one(); + } + } + + if (!divide_rows.empty()) + return solve_divides(x, divide_rows, compute_def); + + if (!div_rows.empty() || !mod_rows.empty()) + return solve_mod_div(x, mod_rows, div_rows, compute_def); + + if (eq_row != UINT_MAX) + return solve_for(eq_row, x, compute_def); + + def result; + unsigned lub_size = lub_rows.size(); + unsigned glb_size = glb_rows.size(); + unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; + + // There are only upper or only lower bounds. + if (row_index == UINT_MAX) { + if (compute_def) { + if (lub_index != UINT_MAX) + result = solve_for(lub_index, x, true); + else if (glb_index != UINT_MAX) + result = solve_for(glb_index, x, true); + else + result = def() + m_var2value[x]; + SASSERT(eval(result) == eval(x)); + } + else { + for (unsigned row_id : lub_rows) retire_row(row_id); + for (unsigned row_id : glb_rows) retire_row(row_id); + } + return result; + } + + SASSERT(lub_index != UINT_MAX); + SASSERT(glb_index != UINT_MAX); + if (compute_def) { + if (lub_size <= glb_size) + result = def(m_rows[lub_index], x); + else + result = def(m_rows[glb_index], x); + } + + // The number of matching lower and upper bounds is small. + if ((lub_size <= 2 || glb_size <= 2) && + (lub_size <= 3 && glb_size <= 3) && + (!is_int(x) || lub_is_unit || glb_is_unit)) { + for (unsigned i = 0; i < lub_size; ++i) { + unsigned row_id1 = lub_rows[i]; + bool last = i + 1 == lub_size; + rational coeff = get_coefficient(row_id1, x); + for (unsigned row_id2 : glb_rows) { + if (last) { + resolve(row_id1, coeff, row_id2, x); + } + else { + unsigned row_id3 = copy_row(row_id2); + resolve(row_id1, coeff, row_id3, x); + } + } + } + for (unsigned row_id : lub_rows) + retire_row(row_id); + + return result; + } + + // General case. + rational coeff = get_coefficient(row_index, x); + + for (unsigned row_id : lub_rows) + if (row_id != row_index) + resolve(row_index, coeff, row_id, x); + + for (unsigned row_id : glb_rows) + if (row_id != row_index) + resolve(row_index, coeff, row_id, x); + retire_row(row_index); + return result; + } + + + // + // Given v = a*x + b mod K + // + // - remove v = a*x + b mod K + // + // case a = 1: + // - add w = b mod K + // - x |-> K*y + z, 0 <= z < K + // - if z.value + w.value < K: + // add z + w - v = 0 + // - if z.value + w.value >= K: + // add z + w - v - K = 0 + // + // case a != 1, gcd(a, K) = 1 + // - x |-> x*y + a^-1*z, 0 <= z < K + // - add w = b mod K + // if z.value + w.value < K + // add z + w - v = 0 + // if z.value + w.value >= K + // add z + w - v - K = 0 + // + // case a != 1, gcd(a,K) = g != 1 + // - x |-> x*y + a^-1*z, 0 <= z < K + // a*x + b mod K = v is now + // g*z + b mod K = v + // - add w = b mod K + // - 0 <= g*z.value + w.value < K*(g+1) + // - add g*z + w - v - k*K = 0 for suitable k from 0 .. g based on model + // + // + // + // Given v = a*x + b div K + // Replace x |-> K*y + z + // - w = b div K + // - v = ((a*K*y + a*z) + b) div K + // = a*y + (a*z + b) div K + // = a*y + b div K + (b mod K + a*z) div K + // = a*y + b div K + k + // where k := (b.value mod K + a*z.value) div K + // k is between 0 and a + // + // - k*K <= b mod K + a*z < (k+1)*K + // + // A better version using a^-1 + // - v = (a*K*y + a^-1*a*z + b) div K + // = a*y + ((K*A + g)*z + b) div K where we write a*a^-1 = K*A + g + // = a*y + A + (g*z + b) div K + // - k*K <= b Kod m + gz < (k+1)*K + // where k is between 0 and g + // when gcd(a, K) = 1, then there are only two cases. + // + model_based_opt::def model_based_opt::solve_mod_div(unsigned x, unsigned_vector const& _mod_rows, unsigned_vector const& _div_rows, bool compute_def) { + def result; + unsigned_vector div_rows(_div_rows), mod_rows(_mod_rows); + SASSERT(!div_rows.empty() || !mod_rows.empty()); + TRACE("opt", display(tout << "solve_div v" << x << "\n")); + + rational K(1); + for (unsigned ri : div_rows) + K = lcm(K, m_rows[ri].m_mod); + for (unsigned ri : mod_rows) + K = lcm(K, m_rows[ri].m_mod); + + rational x_value = m_var2value[x]; + rational z_value = mod(x_value, K); + rational y_value = div(x_value, K); + SASSERT(x_value == K * y_value + z_value); + SASSERT(0 <= z_value && z_value < K); + // add new variables + unsigned z = add_var(z_value, true); + unsigned y = add_var(y_value, true); + + uint_set visited; + unsigned j = 0; + for (unsigned ri : div_rows) { + if (visited.contains(ri)) + continue; + row& r = m_rows[ri]; + mul(ri, K / r.m_mod); + r.m_alive = false; + visited.insert(ri); + div_rows[j++] = ri; + } + div_rows.shrink(j); + + j = 0; + for (unsigned ri : mod_rows) { + if (visited.contains(ri)) + continue; + m_rows[ri].m_alive = false; + visited.insert(ri); + mod_rows[j++] = ri; + } + mod_rows.shrink(j); + + + // replace x by K*y + z in other rows. + for (unsigned ri : m_var2row_ids[x]) { + if (visited.contains(ri)) + continue; + replace_var(ri, x, K, y, rational::one(), z); + visited.insert(ri); + normalize(ri); + } + + // add bounds for z + add_lower_bound(z, rational::zero()); + add_upper_bound(z, K - 1); + + + // solve for x_value = K*y_value + z_value, 0 <= z_value < K. + + unsigned_vector vs; + + for (unsigned ri : div_rows) { + + rational a = get_coefficient(ri, x); + replace_var(ri, x, rational::zero()); + + // add w = b div m + vector coeffs = m_rows[ri].m_vars; + rational coeff = m_rows[ri].m_coeff; + unsigned w = UINT_MAX; + rational offset(0); + if (K == 1) + offset = coeff; + else if (coeffs.empty()) + offset = div(coeff, K); + else + w = add_div(coeffs, coeff, K); + + // + // w = b div K + // v = a*y + w + k + // k = (a*z_value + (b_value mod K)) div K + // k*K <= a*z + b mod K < (k+1)*K + // + /** + * It is based on the following claim (tested for select values of a, K) + * (define-const K Int 13) + * (declare-const b Int) + * (define-const a Int -11) + * (declare-const y Int) + * (declare-const z Int) + * (define-const w Int (div b K)) + * (define-const k1 Int (+ (* a z) (mod b K))) + * (define-const k Int (div k1 K)) + * (define-const x Int (+ (* K y) z)) + * (define-const u Int (+ (* a x) b)) + * (define-const v Int (+ (* a y) w k)) + * (assert (<= 0 z)) + * (assert (< z K)) + * (assert (<= (* K k) k1)) + * (assert (< k1 (* K (+ k 1)))) + * (assert (not (= (div u K) v))) + * (check-sat) + */ + unsigned v = m_rows[ri].m_id; + rational b_value = eval(coeffs) + coeff; + rational k = div(a * z_value + mod(b_value, K), K); + vector div_coeffs; + div_coeffs.push_back(var(v, rational::minus_one())); + div_coeffs.push_back(var(y, a)); + if (w != UINT_MAX) + div_coeffs.push_back(var(w, rational::one())); + else if (K == 1) + div_coeffs.append(coeffs); + add_constraint(div_coeffs, k + offset, t_eq); + + unsigned u = UINT_MAX; + offset = 0; + if (K == 1) + offset = 0; + else if (coeffs.empty()) + offset = mod(coeff, K); + else + u = add_mod(coeffs, coeff, K); + + + // add a*z + (b mod K) < (k + 1)*K + vector bound_coeffs; + bound_coeffs.push_back(var(z, a)); + if (u != UINT_MAX) + bound_coeffs.push_back(var(u, rational::one())); + add_constraint(bound_coeffs, 1 - K * (k + 1) + offset, t_le); + + // add k*K <= az + (b mod K) + for (auto& c : bound_coeffs) + c.m_coeff.neg(); + add_constraint(bound_coeffs, k * K - offset, t_le); + // allow to recycle row. + retire_row(ri); + vs.push_back(v); + } + + for (unsigned ri : mod_rows) { + rational a = get_coefficient(ri, x); + replace_var(ri, x, rational::zero()); + rational rMod = m_rows[ri].m_mod; + + // add w = b mod rMod + vector coeffs = m_rows[ri].m_vars; + rational coeff = m_rows[ri].m_coeff; + unsigned v = m_rows[ri].m_id; + rational v_value = m_var2value[v]; + + unsigned w = UINT_MAX; + rational offset(0); + if (coeffs.empty() || rMod == 1) + offset = mod(coeff, rMod); + else + w = add_mod(coeffs, coeff, rMod); + + + rational w_value = w == UINT_MAX ? offset : m_var2value[w]; + +#if 0 + // V := (a * z_value + w_value) div rMod + // V*rMod <= a*z + w < (V+1)*rMod + // v = a*z + w - V*rMod + SASSERT(a > 0); + SASSERT(z_value >= 0); + SASSERT(w_value >= 0); + SASSERT(a * z_value + w_value >= 0); + rational V = div(a * z_value + w_value, rMod); + vector mod_coeffs; + SASSERT(V >= 0); + SASSERT(a * z_value + w_value >= V*rMod); + SASSERT((V+1)*rMod > a*z_value + w_value); + // -a*z - w + V*rMod <= 0 + mod_coeffs.push_back(var(z, -a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, -rational::one())); + add_constraint(mod_coeffs, V*rMod - offset, t_le); + mod_coeffs.reset(); + // a*z + w - (V+1)*rMod + 1 <= 0 + mod_coeffs.push_back(var(z, a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); + add_constraint(mod_coeffs, -(V+1)*rMod + offset + 1, t_le); + mod_coeffs.reset(); + // -v + a*z + w - V*rMod = 0 + mod_coeffs.push_back(var(v, rational::minus_one())); + mod_coeffs.push_back(var(z, a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); + add_constraint(mod_coeffs, offset - V*rMod, t_eq); + +#else + // add v = a*z + w - V, for V = v_value - a * z_value - w_value + // claim: (= (mod x rMod) (- x (* rMod (div x rMod)))))) is a theorem for every x, rMod != 0 + rational V = v_value - a * z_value - w_value; + vector mod_coeffs; + mod_coeffs.push_back(var(v, rational::minus_one())); + mod_coeffs.push_back(var(z, a)); + if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one())); + add_constraint(mod_coeffs, V + offset, t_eq); + add_lower_bound(v, rational::zero()); + add_upper_bound(v, rMod - 1); +#endif + + retire_row(ri); + vs.push_back(v); + } + + + for (unsigned v : vs) { + def v_def = project(v, compute_def); + if (compute_def) + eliminate(v, v_def); + } + + // project internal variables. + def z_def = project(z, compute_def); + def y_def = project(y, compute_def); // may depend on z + + if (compute_def) { + z_def.substitute(y, y_def); + eliminate(y, y_def); + eliminate(z, z_def); + + result = (y_def * K) + z_def; + m_var2value[x] = eval(result); + TRACE("opt", tout << y << " := " << y_def << "\n"; + tout << z << " := " << z_def << "\n"; + tout << x << " := " << result << "\n"); + } + TRACE("opt", display(tout << "solve_div done v" << x << "\n")); + return result; + } + + // + // compute D and u. + // + // D = lcm(d1, d2) + // u = eval(x) mod D + // + // d1 | (a1x + t1) & d2 | (a2x + t2) + // = + // d1 | (a1(D*x' + u) + t1) & d2 | (a2(D*x' + u) + t2) + // = + // d1 | (a1*u + t1) & d2 | (a2*u + t2) + // + // x := D*x' + u + // + + model_based_opt::def model_based_opt::solve_divides(unsigned x, unsigned_vector const& divide_rows, bool compute_def) { + SASSERT(!divide_rows.empty()); + rational D(1); + for (unsigned idx : divide_rows) { + D = lcm(D, m_rows[idx].m_mod); + } + if (D.is_zero()) { + throw default_exception("modulo 0 is not defined"); + } + if (D.is_neg()) D = abs(D); + TRACE("opt1", display(tout << "lcm: " << D << " x: v" << x << " tableau\n");); + rational val_x = m_var2value[x]; + rational u = mod(val_x, D); + SASSERT(u.is_nonneg() && u < D); + for (unsigned idx : divide_rows) { + replace_var(idx, x, u); + SASSERT(invariant(idx, m_rows[idx])); + normalize(idx); + } + TRACE("opt1", display(tout << "tableau after replace x under mod\n");); + // + // update inequalities such that u is added to t and + // D is multiplied to coefficient of x. + // the interpretation of the new version of x is (x-u)/D + // + // a*x + t <= 0 + // a*(D*x' + u) + t <= 0 + // a*D*x' + a*u + t <= 0 + // + rational new_val = (val_x - u) / D; + SASSERT(new_val.is_int()); + unsigned y = add_var(new_val, true); + unsigned_vector const& row_ids = m_var2row_ids[x]; + uint_set visited; + for (unsigned row_id : row_ids) { + if (visited.contains(row_id)) + continue; + // x |-> D*y + u + replace_var(row_id, x, D, y, u); + visited.insert(row_id); + normalize(row_id); + } + TRACE("opt1", display(tout << "tableau after replace x by y := v" << y << "\n");); + def result = project(y, compute_def); + if (compute_def) { + result = (result * D) + u; + m_var2value[x] = eval(result); + } + TRACE("opt1", display(tout << "tableau after project y" << y << "\n");); + + return result; + } + + // update row with: x |-> C + void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& C) { + row& r = m_rows[row_id]; + SASSERT(!get_coefficient(row_id, x).is_zero()); + unsigned sz = r.m_vars.size(); + unsigned i = 0, j = 0; + rational coeff(0); + for (; i < sz; ++i) { + if (r.m_vars[i].m_id == x) { + coeff = r.m_vars[i].m_coeff; + } + else { + if (i != j) { + r.m_vars[j] = r.m_vars[i]; + } + ++j; + } + } + if (j != sz) { + r.m_vars.shrink(j); + } + r.m_coeff += coeff*C; + r.m_value += coeff*(C - m_var2value[x]); + } + + // update row with: x |-> A*y + B + void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B) { + row& r = m_rows[row_id]; + rational coeff = get_coefficient(row_id, x); + if (coeff.is_zero()) return; + if (!r.m_alive) return; + replace_var(row_id, x, B); + r.m_vars.push_back(var(y, coeff*A)); + r.m_value += coeff*A*m_var2value[y]; + if (!r.m_vars.empty() && r.m_vars.back().m_id > y) + std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); + m_var2row_ids[y].push_back(row_id); + SASSERT(invariant(row_id, r)); + } + + // update row with: x |-> A*y + B*z + void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B, unsigned z) { + row& r = m_rows[row_id]; + rational coeff = get_coefficient(row_id, x); + if (coeff.is_zero() || !r.m_alive) + return; + replace_var(row_id, x, rational::zero()); + if (A != 0) r.m_vars.push_back(var(y, coeff*A)); + if (B != 0) r.m_vars.push_back(var(z, coeff*B)); + r.m_value += coeff*A*m_var2value[y]; + r.m_value += coeff*B*m_var2value[z]; + std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); + if (A != 0) m_var2row_ids[y].push_back(row_id); + if (B != 0) m_var2row_ids[z].push_back(row_id); + SASSERT(invariant(row_id, r)); + } + + // 3x + t = 0 & 7 | (c*x + s) & ax <= u + // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u + + model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) { + TRACE("opt", tout << "v" << x << " := " << eval(x) << "\n" << m_rows[row_id1] << "\n"; + display(tout)); + rational a = get_coefficient(row_id1, x), b; + row& r1 = m_rows[row_id1]; + ineq_type ty = r1.m_type; + SASSERT(!a.is_zero()); + SASSERT(r1.m_alive); + if (a.is_neg()) { + a.neg(); + r1.neg(); + } + SASSERT(a.is_pos()); + if (ty == t_lt) { + SASSERT(compute_def); + r1.m_coeff -= r1.m_value; + r1.m_type = t_le; + r1.m_value = 0; + } + + if (m_var2is_int[x] && !a.is_one()) { + r1.m_coeff -= r1.m_value; + r1.m_value = 0; + vector coeffs; + mk_coeffs_without(coeffs, r1.m_vars, x); + rational c = mod(-eval(coeffs), a); + add_divides(coeffs, c, a); + } + unsigned_vector const& row_ids = m_var2row_ids[x]; + uint_set visited; + visited.insert(row_id1); + for (unsigned row_id2 : row_ids) { + if (visited.contains(row_id2)) + continue; + visited.insert(row_id2); + row& r = m_rows[row_id2]; + if (!r.m_alive) + continue; + b = get_coefficient(row_id2, x); + if (b.is_zero()) + continue; + row& dst = m_rows[row_id2]; + switch (dst.m_type) { + case t_eq: + case t_lt: + case t_le: + solve(row_id1, a, row_id2, x); + break; + case t_divides: + case t_mod: + case t_div: + // mod reduction already done. + UNREACHABLE(); + break; + } + } + def result; + if (compute_def) { + result = def(m_rows[row_id1], x); + m_var2value[x] = eval(result); + TRACE("opt1", tout << "updated eval " << x << " := " << eval(x) << "\n";); + } + retire_row(row_id1); + TRACE("opt", display(tout << "solved v" << x << "\n")); + return result; + } + + void model_based_opt::eliminate(unsigned v, def const& new_def) { + for (auto & d : m_result) + d.substitute(v, new_def); + } + + vector model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) { + m_result.reset(); + for (unsigned i = 0; i < num_vars; ++i) { + m_result.push_back(project(vars[i], compute_def)); + eliminate(vars[i], m_result.back()); + TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); + } + return m_result; + } + +} + diff --git a/src/sat/sat_aig_finder.cpp b/src/sat/sat_aig_finder.cpp index eba4366ae..a1013108f 100644 --- a/src/sat/sat_aig_finder.cpp +++ b/src/sat/sat_aig_finder.cpp @@ -94,7 +94,7 @@ namespace sat { // from clause x, y, z // then ~x, ~y -> z // look for ~y, z -> ~x - contains ternary(y, ~z, ~x) - // look for ~x, y -> u - u is used in a ternary claues (~y, x) + // look for ~x, y -> u - u is used in a ternary clause (~y, x) // look for y, u -> ~x - contains ternary(~u, ~x, ~y) // then ~x = if ~y then z else u diff --git a/src/sat/sat_binspr.cpp b/src/sat/sat_binspr.cpp index 460b6a4c0..acfd12980 100644 --- a/src/sat/sat_binspr.cpp +++ b/src/sat/sat_binspr.cpp @@ -47,7 +47,7 @@ Marijn's version: if inconsistent(): learn C (subsumes C or p) else: - candidates' := C union ~(consequencs of propagate(~C)) + candidates' := C union ~(consequences of propagate(~C)) candidates := candidates' intersect candidates pop(1) for q in candidates: @@ -77,7 +77,7 @@ Marijn's version: if inconsistent(): learn C (subsumes C or p) else: - candidates := candicates union C union ~(consequencs of propagate(~C)) + candidates := candidates union C union ~(consequences of propagate(~C)) pop(1) for q in candidates: push(1) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 549783c61..e636ab110 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3462,7 +3462,7 @@ namespace sat { } } - // can't eliminat FUIP + // can't eliminate FUIP SASSERT(is_marked_lit(m_lemma[0])); unsigned j = 0; diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index a5dd9b415..36fc8e42f 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -47,7 +47,7 @@ class sat_smt_solver : public solver { ast_manager& m; trail_stack& m_trail; expr_ref_vector m_refs; - obj_map m_dep2orig; // map original dependency to uninterpeted literal + obj_map m_dep2orig; // map original dependency to uninterpreted literal u_map m_lit2dep; // map from literal assumption to original expression obj_map m_dep2lit; // map uninterpreted literal to sat literal diff --git a/src/solver/check_logic.cpp b/src/solver/check_logic.cpp index 231c21a80..36c08c4d5 100644 --- a/src/solver/check_logic.cpp +++ b/src/solver/check_logic.cpp @@ -38,7 +38,7 @@ struct check_logic::imp { datatype_util m_dt_util; pb_util m_pb_util; bool m_uf; // true if the logic supports uninterpreted functions - bool m_dt; // true if the lgoic supports dattypes + bool m_dt; // true if the logic supports dattypes bool m_arrays; // true if the logic supports arbitrary arrays bool m_bv_arrays; // true if the logic supports only bv arrays bool m_reals; // true if the logic supports reals diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index c0f3979aa..fd7939d95 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -22,7 +22,7 @@ void check_sat_result::set_reason_unknown(event_handler& eh) { switch (eh.caller_id()) { case UNSET_EH_CALLER: if (reason_unknown() == "") - set_reason_unknown("unclassifed exception"); + set_reason_unknown("unclassified exception"); break; case CTRL_C_EH_CALLER: set_reason_unknown("interrupted from keyboard"); From 241e845da8427b9b0f09f4dbaf5593bc0f8dded4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 9 Jul 2023 12:07:43 -0700 Subject: [PATCH 41/41] fix #6802 Signed-off-by: Nikolaj Bjorner --- src/ast/arith_decl_plugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 4778caf89..a1d067a32 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -370,7 +370,7 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { if (is_real) { return m_manager->mk_func_decl(symbol("^0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_POWER0)); } - return m_manager->mk_func_decl(symbol("^0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_POWER0)); + return m_manager->mk_func_decl(symbol("^0"), m_int_decl, m_int_decl, m_real_decl, func_decl_info(m_family_id, OP_POWER0)); case OP_TO_REAL: return m_to_real_decl; case OP_TO_INT: return m_to_int_decl; case OP_IS_INT: return m_is_int_decl; @@ -834,7 +834,7 @@ bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* con func_decl* arith_util::mk_ipower0() { sort* s = mk_int(); sort* rs[2] = { s, s }; - return m_manager.mk_func_decl(arith_family_id, OP_POWER0, 0, nullptr, 2, rs, s); + return m_manager.mk_func_decl(arith_family_id, OP_POWER0, 0, nullptr, 2, rs, mk_real()); } func_decl* arith_util::mk_rpower0() {