From e091a2e775b38ea76d2ba3b653b3ae74120ead48 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 10 Jul 2023 12:05:59 -0700 Subject: [PATCH 001/428] remove the line with clang-format off --- 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_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/gomory.cpp | 1 - src/math/lp/gomory.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.cpp | 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/int_solver.cpp | 4 +--- src/math/lp/int_solver.h | 1 - src/math/lp/lar_constraints.h | 1 - src/math/lp/lar_core_solver.cpp | 1 - src/math/lp/lar_core_solver.h | 2 -- src/math/lp/lar_core_solver_def.h | 1 - src/math/lp/lar_solver.cpp | 14 ++++---------- 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_bound_propagator.h | 1 - src/math/lp/lp_core_solver_base.cpp | 1 - src/math/lp/lp_core_solver_base.h | 7 +++---- src/math/lp/lp_core_solver_base_def.h | 1 - src/math/lp/lp_primal_core_solver.cpp | 1 - src/math/lp/lp_primal_core_solver.h | 7 ++----- 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 | 2 -- 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 | 1 - src/math/lp/static_matrix.h | 1 - src/math/lp/static_matrix_def.h | 1 - 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_lra.cpp | 1 + 107 files changed, 11 insertions(+), 126 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index bfae22f21..0008a0ee9 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -19,7 +19,6 @@ 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 c0b09e21f..1dc0c60c7 100644 --- a/src/math/lp/column_info.h +++ b/src/math/lp/column_info.h @@ -17,7 +17,6 @@ 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 ccd3cffad..cef58ed21 100644 --- a/src/math/lp/column_namer.h +++ b/src/math/lp/column_namer.h @@ -18,7 +18,6 @@ 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 e1ce8fb1b..18bef8303 100644 --- a/src/math/lp/core_solver_pretty_printer.cpp +++ b/src/math/lp/core_solver_pretty_printer.cpp @@ -17,7 +17,6 @@ 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 46e9ebf7e..5bf29d511 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -17,7 +17,6 @@ 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 17f82af9c..b8048b241 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -17,7 +17,6 @@ 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 9633e2dab..a39ef5ef9 100644 --- a/src/math/lp/cross_nested.h +++ b/src/math/lp/cross_nested.h @@ -17,7 +17,6 @@ --*/ -// clang-format off #pragma once #include #include "math/lp/nex.h" diff --git a/src/math/lp/dense_matrix_def.h b/src/math/lp/dense_matrix_def.h index 986a2d20e..e850a9acd 100644 --- a/src/math/lp/dense_matrix_def.h +++ b/src/math/lp/dense_matrix_def.h @@ -17,7 +17,6 @@ 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 e5da7c702..bcdb81dd8 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -18,7 +18,6 @@ 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 8f0f33175..e4f4f4848 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -18,7 +18,6 @@ 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 67df7bab9..b7f721a30 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -17,7 +17,6 @@ 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 aad52c58e..b233894ad 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -18,7 +18,6 @@ --*/ -// 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 1406d5864..ace85a050 100644 --- a/src/math/lp/factorization_factory_imp.cpp +++ b/src/math/lp/factorization_factory_imp.cpp @@ -17,7 +17,6 @@ --*/ -// 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 7c76299dc..599039094 100644 --- a/src/math/lp/factorization_factory_imp.h +++ b/src/math/lp/factorization_factory_imp.h @@ -17,7 +17,6 @@ --*/ -// 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 0fc9c404b..a4f6693a2 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include namespace lp { diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index babb10d72..2ecbc49ac 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -17,7 +17,6 @@ --*/ -// 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 acca900df..68e42feb9 100644 --- a/src/math/lp/gomory.h +++ b/src/math/lp/gomory.h @@ -15,7 +15,6 @@ 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/hnf.h b/src/math/lp/hnf.h index 53b33b37a..75a69393f 100644 --- a/src/math/lp/hnf.h +++ b/src/math/lp/hnf.h @@ -26,7 +26,6 @@ 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 e2fd85866..3c4ea10ab 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -9,7 +9,6 @@ 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 98f9770e8..b3530ea29 100644 --- a/src/math/lp/hnf_cutter.h +++ b/src/math/lp/hnf_cutter.h @@ -14,7 +14,6 @@ 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 3b4cce827..4d4ac4975 100644 --- a/src/math/lp/horner.cpp +++ b/src/math/lp/horner.cpp @@ -17,7 +17,6 @@ --*/ -// 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 ce630b0c2..2b6fc3cd8 100644 --- a/src/math/lp/horner.h +++ b/src/math/lp/horner.h @@ -17,7 +17,6 @@ --*/ -// 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 239103035..9435edcdc 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -17,7 +17,6 @@ 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 f124eb05e..0bb2829eb 100644 --- a/src/math/lp/incremental_vector.h +++ b/src/math/lp/incremental_vector.h @@ -17,7 +17,6 @@ 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 aab13fc9c..92d8f2adf 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -17,7 +17,6 @@ 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 f5c34e1f9..fe0892541 100644 --- a/src/math/lp/indexed_vector.cpp +++ b/src/math/lp/indexed_vector.cpp @@ -17,7 +17,6 @@ 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 b5f5e6cb8..9f3119e9a 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -17,7 +17,6 @@ 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 c6f530d99..034325088 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index 47a9ddcbe..4bfe2b827 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -15,7 +15,6 @@ Revision History: --*/ -// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" #include "math/lp/int_branch.h" diff --git a/src/math/lp/int_branch.h b/src/math/lp/int_branch.h index 6943bede5..9601cb65e 100644 --- a/src/math/lp/int_branch.h +++ b/src/math/lp/int_branch.h @@ -15,7 +15,6 @@ 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 787b82da7..da724a543 100644 --- a/src/math/lp/int_cube.cpp +++ b/src/math/lp/int_cube.cpp @@ -15,7 +15,6 @@ 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 9ac6773ab..4addc096b 100644 --- a/src/math/lp/int_cube.h +++ b/src/math/lp/int_cube.h @@ -19,7 +19,6 @@ 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 e243783cc..4801cc436 100644 --- a/src/math/lp/int_gcd_test.cpp +++ b/src/math/lp/int_gcd_test.cpp @@ -45,7 +45,6 @@ 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 b77179409..28ac2f7b3 100644 --- a/src/math/lp/int_gcd_test.h +++ b/src/math/lp/int_gcd_test.h @@ -24,7 +24,6 @@ Author: Revision History: --*/ -// clang-format off #pragma once #include "math/lp/lia_move.h" diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 16fc2b1a3..88a31e65d 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -2,7 +2,6 @@ Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson */ -// clang-format off #include "math/lp/int_solver.h" #include "math/lp/lar_solver.h" #include "math/lp/lp_utils.h" @@ -138,8 +137,7 @@ namespace lp { 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/math/lp/int_solver.h b/src/math/lp/int_solver.h index fad040965..4c9f43ffe 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include "math/lp/lp_settings.h" #include "math/lp/static_matrix.h" diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 3bb6e7882..f8cffbe57 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -17,7 +17,6 @@ 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 bdb89f76b..09d01f4cc 100644 --- a/src/math/lp/lar_core_solver.cpp +++ b/src/math/lp/lar_core_solver.cpp @@ -17,7 +17,6 @@ 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 a67eb4d48..5d445105f 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -5,7 +5,6 @@ Author: Lev Nachmanson (levnach) --*/ -// clang-format off #pragma once #include "util/vector.h" #include @@ -17,7 +16,6 @@ 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 3f7866717..942c87b3b 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -9,7 +9,6 @@ Revision History: --*/ -// clang-format off #pragma once #include diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 0ad3420b9..d1a9ece86 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2,7 +2,6 @@ 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" @@ -696,7 +695,6 @@ namespace lp { } } - void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); else @@ -1753,8 +1751,7 @@ namespace lp { } // clang-format off void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, - lconstraint_kind kind, - const mpq& right_side, + const mpq& right_side, constraint_index constr_index, unsigned& equal_to_j) { update_column_type_and_bound(j, kind, right_side, constr_index); @@ -1897,8 +1894,7 @@ namespace lp { } // 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); + lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::lower_bound); mpq y_of_bound(0); switch (kind) { @@ -1946,8 +1942,7 @@ namespace lp { } // 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); + lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::upper_bound); mpq y_of_bound(0); switch (kind) { case LT: @@ -2033,8 +2028,7 @@ namespace lp { } // clang-format off bool lar_solver::column_corresponds_to_term(unsigned j) const { - return tv::is_term(m_var_register.local_to_external(j)); - } + } var_index lar_solver::to_column(unsigned ext_j) const { return m_var_register.external_to_local(ext_j); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index b130c198e..75fb4338c 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -17,7 +17,6 @@ --*/ -// clang-format off #pragma once #include #include diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 65262d5dd..fc73f949f 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -17,7 +17,6 @@ --*/ -// 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 b1505dbc9..ca61d7b7a 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -17,7 +17,6 @@ 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 9d0bd2476..2a4e5058d 100644 --- a/src/math/lp/lp_api.h +++ b/src/math/lp/lp_api.h @@ -7,7 +7,6 @@ Author: Nikolaj Bjorner (nbjorner) --*/ -// clang-format off #pragma once #include "util/inf_rational.h" diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index a909e8472..da2e4488d 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -4,7 +4,6 @@ Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) */ -//clang-format off #pragma once #include diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 1859630e4..28f92d8e2 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -17,7 +17,6 @@ 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 47e09a4c8..15449f92f 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include #include "util/vector.h" @@ -291,8 +290,8 @@ public: X bound_span(unsigned j) const { return m_upper_bounds[j] - m_lower_bounds[j]; } - - std::string column_name(unsigned column) const; + // clang-format on + std::string column_name(unsigned column) const; bool make_column_feasible(unsigned j, numeric_pair & delta) { bool ret = false; @@ -303,7 +302,7 @@ public: lp_assert(m_lower_bounds[j] == m_upper_bounds[j]); if (x != m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; - ret = true;; + ret = true; } break; case column_type::boxed: diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 756ff9af3..319966091 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -17,7 +17,6 @@ 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 0698c9962..efbfd27e1 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -17,7 +17,6 @@ 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 79e0d7590..d4130faae 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" @@ -159,8 +158,7 @@ namespace lp { } return r; } - // clang-format off - int find_beneficial_entering_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); @@ -183,8 +181,7 @@ namespace lp { m_inf_row_index_for_tableau = i; return j; } - //clang-format off - int find_beneficial_entering_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_entering_in_row_tableau_rows_bland_mode(i, a_ent); // a short row produces short infeasibility explanation and benefits at diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 909dfd086..e18a5ef05 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -17,7 +17,6 @@ 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 905bbba3c..dbcced4ab 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -17,7 +17,6 @@ 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 723a4ba2d..b72b837fd 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -17,7 +17,6 @@ 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 4e03007bb..727bc3531 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -17,7 +17,6 @@ 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 12c53551f..a19558949 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -17,7 +17,6 @@ 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 6f1f2e3d4..ac046e1a1 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -17,11 +17,9 @@ Revision History: --*/ -// clang-format off #pragma once #include -// clang-format off #include #include "util/debug.h" namespace nla { diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 3a796c42b..3c1383cb3 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -17,7 +17,6 @@ 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 1176a6358..1ea2da263 100644 --- a/src/math/lp/matrix.cpp +++ b/src/math/lp/matrix.cpp @@ -17,7 +17,6 @@ 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 306f43a57..88a405614 100644 --- a/src/math/lp/matrix.h +++ b/src/math/lp/matrix.h @@ -6,7 +6,6 @@ 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 b9d5efb48..e3ac08f7e 100644 --- a/src/math/lp/matrix_def.h +++ b/src/math/lp/matrix_def.h @@ -17,7 +17,6 @@ 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 4f7052ee5..1ed0956dc 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -6,7 +6,6 @@ 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 2d7079c50..236f29bc0 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -6,7 +6,6 @@ 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 e72af480b..e4087a407 100644 --- a/src/math/lp/nex.h +++ b/src/math/lp/nex.h @@ -4,7 +4,6 @@ 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 ae73490f8..c43b723ef 100644 --- a/src/math/lp/nex_creator.cpp +++ b/src/math/lp/nex_creator.cpp @@ -4,7 +4,6 @@ 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 4e147e373..9bce46395 100644 --- a/src/math/lp/nex_creator.h +++ b/src/math/lp/nex_creator.h @@ -5,7 +5,6 @@ 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 99638d9d1..7124fd409 100644 --- a/src/math/lp/nla_basics_lemmas.cpp +++ b/src/math/lp/nla_basics_lemmas.cpp @@ -6,7 +6,6 @@ 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 c913cfcfc..58fb0e92f 100644 --- a/src/math/lp/nla_basics_lemmas.h +++ b/src/math/lp/nla_basics_lemmas.h @@ -6,7 +6,6 @@ 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 d9f9b2bfc..45898c613 100644 --- a/src/math/lp/nla_common.cpp +++ b/src/math/lp/nla_common.cpp @@ -6,7 +6,6 @@ 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 dc393af95..1302c3909 100644 --- a/src/math/lp/nla_common.h +++ b/src/math/lp/nla_common.h @@ -6,7 +6,6 @@ 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 0931e005f..3c1f9da57 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -10,7 +10,6 @@ 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 e178fbf75..938bcbe83 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -10,7 +10,6 @@ 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 23be7bb72..df9158b42 100644 --- a/src/math/lp/nla_defs.h +++ b/src/math/lp/nla_defs.h @@ -8,7 +8,6 @@ --*/ -// 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 fd83de32c..cbb30d9d9 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -14,7 +14,6 @@ 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 e07cf3aed..80bf5be4e 100644 --- a/src/math/lp/nla_divisions.h +++ b/src/math/lp/nla_divisions.h @@ -13,7 +13,6 @@ 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 9cdb19fbf..974c48d14 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -10,7 +10,6 @@ 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 12f8609d7..902ad3a46 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -6,7 +6,6 @@ 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 1806051ae..0545e9933 100644 --- a/src/math/lp/nla_intervals.h +++ b/src/math/lp/nla_intervals.h @@ -6,7 +6,6 @@ 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 2694a7ef8..cc9241446 100644 --- a/src/math/lp/nla_monotone_lemmas.cpp +++ b/src/math/lp/nla_monotone_lemmas.cpp @@ -5,7 +5,6 @@ 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 2828e601c..d13f588e8 100644 --- a/src/math/lp/nla_monotone_lemmas.h +++ b/src/math/lp/nla_monotone_lemmas.h @@ -5,7 +5,6 @@ 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 9b4b0e187..94ddc4d9b 100644 --- a/src/math/lp/nla_order_lemmas.cpp +++ b/src/math/lp/nla_order_lemmas.cpp @@ -5,7 +5,6 @@ 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 025e4b783..c6961bc52 100644 --- a/src/math/lp/nla_order_lemmas.h +++ b/src/math/lp/nla_order_lemmas.h @@ -6,7 +6,6 @@ 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 bbf50e9a7..f389aad93 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -74,7 +74,6 @@ 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 f76460e1c..f74417ae3 100644 --- a/src/math/lp/nla_powers.h +++ b/src/math/lp/nla_powers.h @@ -13,7 +13,6 @@ 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 6eb2ea1c3..ec11ea5b2 100644 --- a/src/math/lp/nla_settings.h +++ b/src/math/lp/nla_settings.h @@ -6,7 +6,6 @@ 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 ada438edc..bd0f1953c 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -5,7 +5,6 @@ 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 8da4815aa..d04ff8e51 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -6,7 +6,6 @@ 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 c2081281e..4ba9eeccc 100644 --- a/src/math/lp/nla_tangent_lemmas.cpp +++ b/src/math/lp/nla_tangent_lemmas.cpp @@ -6,7 +6,6 @@ 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 d3d71590d..1895b3fcb 100644 --- a/src/math/lp/nla_tangent_lemmas.h +++ b/src/math/lp/nla_tangent_lemmas.h @@ -5,7 +5,6 @@ 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 1efc98860..8169266cc 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -13,7 +13,6 @@ 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 a59057288..251274006 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -17,7 +17,6 @@ --*/ -// 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 166330c6f..be4b7335c 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -17,7 +17,6 @@ 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 b31d3cdec..a3fff4f7f 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -17,7 +17,6 @@ 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 e1bd8c4a8..c86fef4f4 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -17,7 +17,6 @@ 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 6535c840f..a88df76e5 100644 --- a/src/math/lp/random_updater.cpp +++ b/src/math/lp/random_updater.cpp @@ -17,6 +17,5 @@ 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 c90b1e6f2..d5cd4928c 100644 --- a/src/math/lp/random_updater.h +++ b/src/math/lp/random_updater.h @@ -17,7 +17,6 @@ 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 c31211e2c..7d167a4a0 100644 --- a/src/math/lp/random_updater_def.h +++ b/src/math/lp/random_updater_def.h @@ -17,7 +17,6 @@ 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 8b7dd4812..ecd61eb10 100644 --- a/src/math/lp/stacked_vector.h +++ b/src/math/lp/stacked_vector.h @@ -17,7 +17,6 @@ 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 c871ddc87..a46b5abc0 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #include #include "util/vector.h" #include diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index 2e25b5b9a..ffbd48021 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -6,7 +6,6 @@ Author: Lev Nachmanson (levnach) --*/ -// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index c7fcc6d88..0370ee899 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -17,7 +17,6 @@ Revision History: --*/ -// clang-format off #pragma once #include "util/vector.h" diff --git a/src/math/lp/test_bound_analyzer.h b/src/math/lp/test_bound_analyzer.h index 438deffda..1ca99d637 100644 --- a/src/math/lp/test_bound_analyzer.h +++ b/src/math/lp/test_bound_analyzer.h @@ -17,7 +17,6 @@ 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 54778669d..ce59dccb7 100644 --- a/src/math/lp/u_set.h +++ b/src/math/lp/u_set.h @@ -18,7 +18,6 @@ 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 95af719e0..abfb4483b 100644 --- a/src/math/lp/ul_pair.h +++ b/src/math/lp/ul_pair.h @@ -17,7 +17,6 @@ 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 88676715d..998779dc6 100644 --- a/src/math/lp/var_eqs.h +++ b/src/math/lp/var_eqs.h @@ -17,7 +17,6 @@ --*/ -// 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 cea5ae2b6..49767274d 100644 --- a/src/math/lp/var_register.h +++ b/src/math/lp/var_register.h @@ -16,7 +16,6 @@ Revision History: --*/ -// clang-format off #pragma once #include "math/lp/lp_types.h" namespace lp { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index b21424e65..0c182dce5 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3220,6 +3220,7 @@ public: lbool make_feasible() { TRACE("pcs", tout << lp().constraints();); + TRACE("arith_verbose", tout << "before calling lp().find_feasible_solution()\n"; display(tout);); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); if (lp().is_feasible()) From 9ae6c88e3fbdf69ca0c9eaa93c60dd3086894360 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 10 Jul 2023 12:19:32 -0700 Subject: [PATCH 002/428] fix the build --- src/math/lp/lar_solver.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 99b05beaf..735f835e0 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -695,6 +695,7 @@ namespace lp { } } + void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); else @@ -1751,7 +1752,8 @@ namespace lp { } // clang-format off void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, - const mpq& right_side, + lconstraint_kind kind, + const mpq& right_side, constraint_index constr_index, unsigned& equal_to_j) { update_column_type_and_bound(j, kind, right_side, constr_index); @@ -1895,7 +1897,8 @@ namespace lp { } // 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(m_mpq_lar_core_solver.m_column_types[j] == column_type::lower_bound); + 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) { @@ -1943,7 +1946,8 @@ namespace lp { } // 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(m_mpq_lar_core_solver.m_column_types[j] == column_type::upper_bound); + 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); mpq y_of_bound(0); switch (kind) { case LT: @@ -2029,7 +2033,8 @@ namespace lp { } // clang-format off bool lar_solver::column_corresponds_to_term(unsigned j) const { - } + return tv::is_term(m_var_register.local_to_external(j)); + } var_index lar_solver::to_column(unsigned ext_j) const { return m_var_register.external_to_local(ext_j); From d6f2c23627c9c2fef1adc672e2c6a844f13b1302 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Jul 2023 09:40:09 -0700 Subject: [PATCH 003/428] #6805 --- src/ast/ast.cpp | 100 ++++++++++++++++++++++++++++++++++----------- src/ast/ast.h | 27 ++++++++++-- src/ast/occurs.cpp | 23 ++++++++++- src/ast/occurs.h | 5 +++ 4 files changed, 128 insertions(+), 27 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 354a945e3..aeccc7612 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -255,7 +255,8 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa m_injective(false), m_idempotent(false), m_skolem(false), - m_lambda(false) { + m_lambda(false), + m_polymorphic(false) { } bool func_decl_info::operator==(func_decl_info const & info) const { @@ -283,6 +284,7 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { if (info.is_idempotent()) out << " :idempotent "; if (info.is_skolem()) out << " :skolem "; if (info.is_lambda()) out << " :lambda "; + if (info.is_polymorphic()) out << " :polymorphic "; return out; } @@ -1309,10 +1311,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), - m_proof_mode(m), - m_trace_stream(nullptr), - m_trace_stream_owner(false), - m_lambda_def(":lambda-def") { + m_proof_mode(m) { if (trace_file) { m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out); @@ -1333,9 +1332,7 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_ m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), - m_trace_stream(trace_stream), - m_trace_stream_owner(false), - m_lambda_def(":lambda-def") { + m_trace_stream(trace_stream) { if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); @@ -1350,9 +1347,7 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode), - m_trace_stream(src.m_trace_stream), - m_trace_stream_owner(false), - m_lambda_def(":lambda-def") { + m_trace_stream(src.m_trace_stream) { SASSERT(!src.is_format_manager()); m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); init(); @@ -1880,6 +1875,8 @@ void ast_manager::delete_node(ast * n) { break; case AST_FUNC_DECL: { func_decl* f = to_func_decl(n); + if (f->is_polymorphic()) + m_poly_roots.erase(f); if (f->m_info != nullptr) { func_decl_info * info = f->get_info(); if (info->is_lambda()) { @@ -2020,10 +2017,6 @@ 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) { @@ -2035,13 +2028,30 @@ func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort } func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info) { - SASSERT(arity == 1 || info == 0 || !info->is_injective()); - SASSERT(arity == 2 || info == 0 || !info->is_associative()); - SASSERT(arity == 2 || info == 0 || !info->is_commutative()); + SASSERT(arity == 1 || !info || !info->is_injective()); + SASSERT(arity == 2 || !info || !info->is_associative()); + SASSERT(arity == 2 || !info || !info->is_commutative()); unsigned sz = func_decl::get_obj_size(arity); void * mem = allocate_node(sz); - func_decl * new_node = new (mem) func_decl(name, arity, domain, range, info); - return register_node(new_node); + + // determine if function is a polymorphic root object. + // instances of polymorphic functions are automatically tagged as polymorphic and + // inserted into the m_poly_roots table. + bool is_polymorphic_root = false; + func_decl_info info0; + if (has_type_var(arity, domain, range)) { + if (!info) + info = &info0; + if (!info->is_polymorphic()) { + info->set_polymorphic(true); + is_polymorphic_root = true; + } + } + func_decl* new_node = new (mem) func_decl(name, arity, domain, range, info); + new_node = register_node(new_node); + if (is_polymorphic_root) + m_poly_roots.insert(new_node, new_node); + return new_node; } void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const { @@ -2306,9 +2316,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const func_decl_info info(null_family_id, null_decl_kind); info.m_skolem = skolem; SASSERT(skolem == info.is_skolem()); + func_decl_info* infop = skolem ? &info : nullptr; func_decl * d; if (prefix == symbol::null && suffix == symbol::null) { - d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, &info); + d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, infop); } else { string_buffer<64> buffer; @@ -2320,10 +2331,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const if (suffix != symbol::null) buffer << suffix << "!"; buffer << m_fresh_id; - d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, &info); + d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, infop); } m_fresh_id++; - SASSERT(d->get_info()); + SASSERT(!skolem || d->get_info()); SASSERT(skolem == d->is_skolem()); return d; } @@ -2725,6 +2736,49 @@ bool ast_manager::is_fully_interp(sort * s) const { return false; } +// ----------------------------------------- +// Polymorphism +// ----------------------------------------- +sort * ast_manager::mk_type_var(symbol const& name) { + m_has_type_vars = true; + sort_info si(poly_family_id, 0); + return mk_sort(name, &si); +} + +bool ast_manager::has_type_var(sort* s) const { + if (is_type_var(s)) + return true; + for (parameter const& p : s->parameters()) + if (p.is_ast() && is_sort(p.get_ast()) && has_type_var(to_sort(p.get_ast()))) + return true; + return false; +} + +bool ast_manager::has_type_var(func_decl* f) const { + return has_type_var(f->get_arity(), f->get_domain(), f->get_range()); +} + +bool ast_manager::has_type_var(unsigned n, sort* const* domain, sort* range) const { + if (!has_type_vars()) + return false; + for (unsigned i = n; i-- > 0; ) + if (has_type_var(domain[i])) + return true; + return has_type_var(range); +} + +/** + * \brief create an instantiation of polymorphic function f. + */ + +func_decl* ast_manager::instantiate_polymorphic(func_decl* f, unsigned arity, sort * const* domain, sort * range) { + SASSERT(f->is_polymorphic()); + func_decl* g = mk_func_decl(f->get_name(), arity, domain, range, f->get_info()); + m_poly_roots.insert(f, g); + SASSERT(g->is_polymorphic()); + return g; +} + // ----------------------------------- // // Proof generation diff --git a/src/ast/ast.h b/src/ast/ast.h index bdb76b2dc..d7a715cd1 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -400,6 +400,7 @@ struct func_decl_info : public decl_info { bool m_idempotent:1; bool m_skolem:1; bool m_lambda:1; + bool m_polymorphic:1; func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr); @@ -414,6 +415,7 @@ struct func_decl_info : public decl_info { bool is_idempotent() const { return m_idempotent; } bool is_skolem() const { return m_skolem; } bool is_lambda() const { return m_lambda; } + bool is_polymorphic() const { return m_polymorphic; } void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; } void set_left_associative(bool flag = true) { m_left_assoc = flag; } @@ -426,6 +428,7 @@ struct func_decl_info : public decl_info { void set_idempotent(bool flag = true) { m_idempotent = flag; } void set_skolem(bool flag = true) { m_skolem = flag; } void set_lambda(bool flag = true) { m_lambda = flag; } + void set_polymorphic(bool flag = true) { m_polymorphic = flag; } bool operator==(func_decl_info const & info) const; @@ -655,6 +658,7 @@ public: bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); } bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); } bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); } + bool is_polymorphic() const { return get_info() != nullptr && get_info()->is_polymorphic(); } unsigned get_arity() const { return m_arity; } sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; } sort * const * get_domain() const { return m_domain; } @@ -1511,13 +1515,15 @@ protected: unsigned m_fresh_id; bool m_debug_ref_count; u_map m_debug_free_indices; - std::fstream* m_trace_stream; - bool m_trace_stream_owner; + std::fstream* m_trace_stream = nullptr; + bool m_trace_stream_owner = false; + bool m_has_type_vars = false; #ifdef Z3DEBUG bool slow_not_contains(ast const * n); #endif ast_manager * m_format_manager; // hack for isolating format objects in a different manager. - symbol m_lambda_def; + symbol m_lambda_def = symbol(":lambda-def"); + obj_map m_poly_roots; void init(); @@ -1734,12 +1740,27 @@ public: bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == user_sort_family_id; } + bool is_type_var(sort const* s) const { return s->get_family_id() == poly_family_id; } + + bool has_type_vars() const { return m_has_type_vars; } + + func_decl* poly_root(func_decl* f) const { SASSERT(f->is_polymorphic()); return m_poly_roots[f]; } + + + func_decl* instantiate_polymorphic(func_decl* f, unsigned arity, sort * const* domain, sort * range); + /** \brief A sort is "fully" interpreted if it is interpreted, and doesn't depend on other uninterpreted sorts. */ bool is_fully_interp(sort * s) const; + bool has_type_var(sort* s) const; + + bool has_type_var(func_decl* f) const; + + bool has_type_var(unsigned n, sort* const* domain, sort* range) const; + func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range = nullptr); diff --git a/src/ast/occurs.cpp b/src/ast/occurs.cpp index 2bcd98396..a619dfdbd 100644 --- a/src/ast/occurs.cpp +++ b/src/ast/occurs.cpp @@ -19,6 +19,7 @@ Revision History: #include "ast/occurs.h" #include "ast/for_each_expr.h" +#include "ast/for_each_ast.h" // ----------------------------------- // @@ -49,6 +50,15 @@ namespace { void operator()(quantifier const * n) { } }; + + struct sort_proc { + sort* m_s; + sort_proc(sort* s) :m_s(s) {} + void operator()(sort const* s2) { if (m_s == s2) throw found(); } + void operator()(ast*) {} + }; + + } // Return true if n1 occurs in n2 @@ -74,6 +84,17 @@ bool occurs(func_decl * d, expr * n) { return false; } +bool occurs(sort* s1, sort* s2) { + sort_proc p(s1); + try { + for_each_ast(p, s2); + } + catch (const found&) { + return true; + } + return false; +} + void mark_occurs(ptr_vector& to_check, expr* v, expr_mark& occ) { expr_fast_mark2 visited; occ.mark(v, true); @@ -116,4 +137,4 @@ void mark_occurs(ptr_vector& to_check, expr* v, expr_mark& occ) { to_check.pop_back(); } } -} \ No newline at end of file +} diff --git a/src/ast/occurs.h b/src/ast/occurs.h index 7475a292c..f2f42aaee 100644 --- a/src/ast/occurs.h +++ b/src/ast/occurs.h @@ -31,6 +31,11 @@ bool occurs(expr * n1, expr * n2); */ bool occurs(func_decl * d, expr * n); +/** +* \brief Return true if s1 occurs in s2 +*/ +bool occurs(sort* s1, sort* s2); + /** * \brief Mark sub-expressions of to_check by whether v occurs in these. */ From 939bf1c725212d3c036947c1ba6f78227ad5dc7e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 12 Jul 2023 18:09:02 -0700 Subject: [PATCH 004/428] wip - alpha support for polymorphism An initial update to support polymorphism from SMTLIB3 and the API (so far C, Python). The WIP SMTLIB3 format is assumed to be supporting the following declaration ``` (declare-type-var A) ``` Whenever A is used in a type signature of a function/constant or bound quantified variable, it is taken to mean that all instantiations of A are included in the signature and assertions. For example, if the function f is declared with signature A -> A, then there is a version of f for all instances of A. The semantics of polymorphism appears to follow previous proposals: the instances are effectively different functions. This may clash with some other notions, such as the type signature forall 'a . 'a -> 'a would be inhabited by a unique function (the identity), while this is not enforced in this version (and hopefully never because it is more busy work). The C API has the function 'Z3_mk_type_variable' to create a type variable and applying functions modulo polymorphic type signatures is possible. The kind Z3_TYPE_VAR is added to sort discriminators. This version is considered as early alpha. It passes a first rudimentary unit test involving quantified axioms, declare-fun, define-fun, and define-fun-rec. --- src/api/api_ast.cpp | 29 ++- src/api/python/z3/z3.py | 22 ++ src/api/z3_api.h | 12 ++ src/ast/CMakeLists.txt | 2 + src/ast/ast.cpp | 8 +- src/ast/ast_translation.cpp | 11 + src/ast/occurs.cpp | 2 +- src/ast/polymorphism_inst.cpp | 138 +++++++++++++ src/ast/polymorphism_inst.h | 91 +++++++++ src/ast/polymorphism_util.cpp | 349 ++++++++++++++++++++++++++++++++ src/ast/polymorphism_util.h | 112 ++++++++++ src/cmd_context/cmd_context.cpp | 122 ++++++++--- src/cmd_context/cmd_context.h | 17 +- src/smt/smt_setup.cpp | 8 + src/smt/smt_setup.h | 1 + src/smt/theory_polymorphism.h | 105 ++++++++++ 16 files changed, 987 insertions(+), 42 deletions(-) create mode 100644 src/ast/polymorphism_inst.cpp create mode 100644 src/ast/polymorphism_inst.h create mode 100644 src/ast/polymorphism_util.cpp create mode 100644 src/ast/polymorphism_util.h create mode 100644 src/smt/theory_polymorphism.h diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index bc29826ff..a28b3f20b 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -29,6 +29,7 @@ Revision History: #include "ast/ast_ll_pp.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" +#include "ast/polymorphism_util.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/expr_safe_replace.h" @@ -88,6 +89,16 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_sort Z3_API Z3_mk_type_variable(Z3_context c, Z3_symbol name) { + Z3_TRY; + LOG_Z3_mk_type_variable(c, name); + RESET_ERROR_CODE(); + sort* ty = mk_c(c)->m().mk_type_var(to_symbol(name)); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(nullptr); + } + bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast s1, Z3_ast s2) { RESET_ERROR_CODE(); return s1 == s2; @@ -180,7 +191,20 @@ extern "C" { arg_list.push_back(to_expr(args[i])); } func_decl* _d = reinterpret_cast(d); - app* a = mk_c(c)->m().mk_app(_d, num_args, arg_list.data()); + ast_manager& m = mk_c(c)->m(); + if (_d->is_polymorphic()) { + polymorphism::util u(m); + polymorphism::substitution sub(m); + ptr_buffer domain; + for (unsigned i = 0; i < num_args; ++i) { + if (!sub.match(_d->get_domain(i), arg_list[i]->get_sort())) + SET_ERROR_CODE(Z3_INVALID_ARG, "failed to match argument of polymorphic function"); + domain.push_back(arg_list[i]->get_sort()); + } + sort_ref range = sub(_d->get_range()); + _d = m.instantiate_polymorphic(_d, num_args, domain.data(), range); + } + app* a = m.mk_app(_d, num_args, arg_list.data()); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); @@ -728,6 +752,9 @@ extern "C" { else if (fid == mk_c(c)->get_char_fid() && k == CHAR_SORT) { return Z3_CHAR_SORT; } + else if (fid == poly_family_id) { + return Z3_TYPE_VAR; + } else { return Z3_UNKNOWN_SORT; } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index ee47e7dd7..d1a856174 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -683,6 +683,8 @@ def _to_sort_ref(s, ctx): return SeqSortRef(s, ctx) elif k == Z3_CHAR_SORT: return CharSortRef(s, ctx) + elif k == Z3_TYPE_VAR: + return TypeVarRef(s, ctx) return SortRef(s, ctx) @@ -708,6 +710,26 @@ def DeclareSort(name, ctx=None): ctx = _get_ctx(ctx) return SortRef(Z3_mk_uninterpreted_sort(ctx.ref(), to_symbol(name, ctx)), ctx) +class TypeVarRef(SortRef): + """Type variable reference""" + + def subsort(self, other): + return True + + def cast(self, val): + return val + + +def DeclareTypeVar(name, ctx=None): + """Create a new type variable named `name`. + + If `ctx=None`, then the new sort is declared in the global Z3Py context. + + """ + ctx = _get_ctx(ctx) + return TypeVarRef(Z3_mk_type_variable(ctx.ref(), to_symbol(name, ctx)), ctx) + + ######################################### # # Function Declarations diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 29eed7e26..a931bc523 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -151,6 +151,7 @@ typedef enum Z3_SEQ_SORT, Z3_RE_SORT, Z3_CHAR_SORT, + Z3_TYPE_VAR, Z3_UNKNOWN_SORT = 1000 } Z3_sort_kind; @@ -1883,6 +1884,17 @@ extern "C" { */ Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol s); + /** + \brief Create a type variable. + + Functions using type variables can be applied to instantiations that match the signature + of the function. Assertions using type variables correspond to assertions over all possible + instantiations. + + def_API('Z3_mk_type_variable', SORT, (_in(CONTEXT), _in(SYMBOL))) + */ + Z3_sort Z3_API Z3_mk_type_variable(Z3_context c, Z3_symbol s); + /** \brief Create the Boolean type. diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index 8dd870964..9df3ff001 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -37,6 +37,8 @@ z3_add_component(ast num_occurs.cpp occurs.cpp pb_decl_plugin.cpp + polymorphism_inst.cpp + polymorphism_util.cpp pp.cpp quantifier_stat.cpp recfun_decl_plugin.cpp diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index aeccc7612..3bb435ed8 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2049,8 +2049,8 @@ func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort } func_decl* new_node = new (mem) func_decl(name, arity, domain, range, info); new_node = register_node(new_node); - if (is_polymorphic_root) - m_poly_roots.insert(new_node, new_node); + if (is_polymorphic_root) + m_poly_roots.insert(new_node, new_node); return new_node; } @@ -2774,8 +2774,8 @@ bool ast_manager::has_type_var(unsigned n, sort* const* domain, sort* range) con func_decl* ast_manager::instantiate_polymorphic(func_decl* f, unsigned arity, sort * const* domain, sort * range) { SASSERT(f->is_polymorphic()); func_decl* g = mk_func_decl(f->get_name(), arity, domain, range, f->get_info()); - m_poly_roots.insert(f, g); - SASSERT(g->is_polymorphic()); + m_poly_roots.insert(g, f); + // SASSERT(g->is_polymorphic()); return g; } diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index 781593b38..e2369a35a 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -65,6 +65,13 @@ void ast_translation::collect_decl_extra_children(decl * d) { } void ast_translation::push_frame(ast * n) { + // ensure poly roots are pushed first. + if (m_from_manager.has_type_vars() && n->get_kind() == AST_FUNC_DECL && to_func_decl(n)->is_polymorphic()) { + func_decl* g = m_from_manager.poly_root(to_func_decl(n)); + if (n != g && m_cache.contains(g)) { + m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size())); + } + } m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size())); switch (n->get_kind()) { case AST_SORT: @@ -153,6 +160,10 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { new_domain, new_range); } + else if (f->is_polymorphic() && m_from_manager.poly_root(f) != f) { + func_decl* fr = to_func_decl(m_cache[m_from_manager.poly_root(f)]); + new_f = m_to_manager.instantiate_polymorphic(fr, f->get_arity(), new_domain, new_range); + } else { buffer ps; copy_params(f, fr.m_rpos, ps); diff --git a/src/ast/occurs.cpp b/src/ast/occurs.cpp index a619dfdbd..4e0008373 100644 --- a/src/ast/occurs.cpp +++ b/src/ast/occurs.cpp @@ -87,7 +87,7 @@ bool occurs(func_decl * d, expr * n) { bool occurs(sort* s1, sort* s2) { sort_proc p(s1); try { - for_each_ast(p, s2); + for_each_ast(p, s2, true); } catch (const found&) { return true; diff --git a/src/ast/polymorphism_inst.cpp b/src/ast/polymorphism_inst.cpp new file mode 100644 index 000000000..42dd516f9 --- /dev/null +++ b/src/ast/polymorphism_inst.cpp @@ -0,0 +1,138 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + polymorphism_inst.cpp + +Abstract: + + Utilities for instantiating polymorphic assertions. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-7-8 + + +--*/ +#include "ast/polymorphism_inst.h" +#include "ast/ast_pp.h" + +namespace polymorphism { + + void inst::add(expr* e) { + if (!m.has_type_vars()) + return; + + if (m_from_instantiation.contains(e)) + return; + + instances inst; + u.collect_poly_instances(e, inst.m_poly_fns); + if (inst.m_poly_fns.empty()) + return; + if (m_instances.contains(e)) + return; + + add_instantiations(e, inst.m_poly_fns); + + if (!u.has_type_vars(e)) + return; + // insert e into the occurs list for polymorphic roots + ast_mark seen; + for (auto* f : inst.m_poly_fns) { + f = m.poly_root(f); + if (seen.is_marked(f)) + continue; + seen.mark(f, true); + if (!m_occurs.contains(f)) { + m_occurs.insert(f, ptr_vector()); + t.push(insert_map(m_occurs, f)); + } + auto& es = m_occurs.find(f); + es.push_back(e); + t.push(remove_back(m_occurs, f)); + } + m_assertions.push_back(e); + t.push(push_back_vector(m_assertions)); + u.collect_type_vars(e, inst.m_tvs); + inst.m_subst = alloc(substitutions); + inst.m_subst->insert(alloc(substitution, m)); + m_instances.insert(e, inst); + t.push(new_obj_trail(inst.m_subst)); + t.push(insert_map(m_instances, e)); + } + + void inst::collect_instantiations(expr* e) { + ptr_vector instances; + u.collect_poly_instances(e, instances); + add_instantiations(e, instances); + } + + void inst::add_instantiations(expr* e, ptr_vector const& instances) { + for (auto* f : instances) { + if (m_in_decl_queue.is_marked(f)) + continue; + m_in_decl_queue.mark(f, true); + m_decl_queue.push_back(f); + t.push(add_decl_queue(*this)); + } + } + + void inst::instantiate(vector& instances) { + unsigned num_decls = m_decl_queue.size(); + if (m_assertions_qhead < m_assertions.size()) { + t.push(value_trail(m_assertions_qhead)); + for (; m_assertions_qhead < m_assertions.size(); ++m_assertions_qhead) { + expr* e = m_assertions.get(m_assertions_qhead); + for (unsigned i = 0; i < num_decls; ++i) + instantiate(m_decl_queue.get(i), e, instances); + } + } + if (m_decl_qhead < num_decls) { + t.push(value_trail(m_decl_qhead)); + for (; m_decl_qhead < num_decls; ++m_decl_qhead) { + func_decl* p = m_decl_queue.get(m_decl_qhead); + for (expr* e : m_occurs[m.poly_root(p)]) + instantiate(p, e, instances); + } + } + } + + void inst::instantiate(func_decl* f1, expr* e, vector& instances) { + auto const& [tv, fns, substs] = m_instances[e]; + + for (auto* f2 : fns) { + substitution sub1(m), new_sub(m); + if (!u.unify(f1, f2, sub1)) + continue; + if (substs->contains(&sub1)) + continue; + substitutions new_substs; + for (auto* sub2 : *substs) { + if (!u.unify(sub1, *sub2, new_sub)) + continue; + if (substs->contains(&new_sub)) + continue; + if (new_substs.contains(&new_sub)) + continue; + expr_ref e_inst = new_sub(e); + if (!m_from_instantiation.contains(e_inst)) { + collect_instantiations(e_inst); + auto* new_sub1 = alloc(substitution, new_sub); + instances.push_back(instantiation(e, e_inst, new_sub1)); + new_substs.insert(new_sub1); + m_from_instantiation.insert(e_inst); + m.inc_ref(e_inst); + t.push(insert_ref_map(m, m_from_instantiation, e_inst)); + } + } + for (auto* sub2 : new_substs) { + SASSERT(!substs->contains(sub2)); + substs->insert(sub2); + t.push(new_obj_trail(sub2)); + t.push(insert_map(*substs, sub2)); + } + } + } +} diff --git a/src/ast/polymorphism_inst.h b/src/ast/polymorphism_inst.h new file mode 100644 index 000000000..1d171b314 --- /dev/null +++ b/src/ast/polymorphism_inst.h @@ -0,0 +1,91 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + polymorphism_inst.h + +Abstract: + + Utilities for instantiating polymorphic assertions. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-7-8 + + +--*/ +#pragma once + +#include "util/trail.h" +#include "ast/ast.h" +#include "ast/polymorphism_util.h" + +namespace polymorphism { + + struct instantiation { + expr* orig; + expr_ref inst; + substitution* sub; + instantiation(expr* orig, expr_ref& inst, substitution* s): + orig(orig), inst(inst), sub(s) {} + }; + + class inst { + ast_manager& m; + trail_stack& t; + util u; + + struct instances { + ptr_vector m_tvs; + ptr_vector m_poly_fns; + substitutions* m_subst = nullptr; + }; + + func_decl_ref_vector m_poly_roots; + obj_map> m_occurs; + obj_map m_instances; + func_decl_ref_vector m_decl_queue; + unsigned m_decl_qhead = 0; + ast_mark m_in_decl_queue; + expr_ref_vector m_assertions; + unsigned m_assertions_qhead = 0; + obj_hashtable m_from_instantiation; + + struct add_decl_queue : public trail { + inst& i; + add_decl_queue(inst& i): i(i) {} + void undo() override { + i.m_in_decl_queue.mark(i.m_decl_queue.back(), false); + i.m_decl_queue.pop_back(); + }; + }; + + struct remove_back : public trail { + obj_map>& occ; + func_decl* f; + remove_back(obj_map>& occ, func_decl* f): + occ(occ), f(f) {} + void undo() override { + occ.find(f).pop_back(); + } + }; + + void instantiate(func_decl* p, expr* e, vector& instances); + + void collect_instantiations(expr* e); + + void add_instantiations(expr* e, ptr_vector const& insts); + + public: + inst(ast_manager& m, trail_stack& t): + m(m), t(t), u(m), m_poly_roots(m), m_decl_queue(m), m_assertions(m) {} + + void add(expr* e); + + void instantiate(vector& instances); + + bool pending() const { return m_decl_qhead < m_decl_queue.size() || m_assertions_qhead < m_assertions.size(); } + + }; +} diff --git a/src/ast/polymorphism_util.cpp b/src/ast/polymorphism_util.cpp new file mode 100644 index 000000000..2fe271fc5 --- /dev/null +++ b/src/ast/polymorphism_util.cpp @@ -0,0 +1,349 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + polymorphism_util.cpp + +Abstract: + + Utilities for supporting polymorphic type signatures. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-7-8 + +--*/ + +#include "ast/polymorphism_util.h" +#include "ast/for_each_ast.h" +#include "ast/occurs.h" +#include "ast/ast_pp.h" + +namespace polymorphism { + + sort_ref_vector substitution::operator()(sort_ref_vector const& s) { + sort_ref_vector r(m); + for (auto* srt : s) + r.push_back((*this)(srt)); + return r; + } + + sort_ref substitution::operator()(sort* s) { + if (!m.has_type_var(s)) + return sort_ref(s, m); + if (s->is_type_var()) { + if (m_sub.find(s, s)) + return (*this)(s); + return sort_ref(s, m); + } + unsigned n = s->get_num_parameters(); + vector ps; + for (unsigned i = 0; i < n; ++i) { + auto p = s->get_parameter(i); + if (p.is_ast() && is_sort(p.get_ast())) + ps.push_back(parameter((*this)(to_sort(p.get_ast())))); + else + ps.push_back(p); + } + sort_info si(s->get_family_id(), s->get_decl_kind(), n, ps.data(), s->private_parameters()); + return sort_ref(m.mk_sort(s->get_name(), si), m); + } + + expr_ref substitution::operator()(expr* e) { + ptr_vector todo; + expr_ref_vector result(m); + todo.push_back(e); + auto in_cache = [&](expr* a) { + return result.size() > a->get_id() && result.get(a->get_id()); + }; + ptr_buffer args; + sort_ref_buffer domain(m); + while (!todo.empty()) { + expr* a = todo.back(); + if (in_cache(a)) { + todo.pop_back(); + continue; + } + if (is_var(a)) { + if (m.has_type_var(a->get_sort())) + result.setx(a->get_id(), m.mk_var(to_var(a)->get_idx(), (*this)(a->get_sort()))); + else + result.setx(a->get_id(), a); + todo.pop_back(); + } + else if (is_quantifier(a)) { + quantifier* q = to_quantifier(a); + bool pending = false; + if (!in_cache(q->get_expr())) { + todo.push_back(q->get_expr()); + pending = true; + } + ptr_buffer patterns, no_patterns; + unsigned np = q->get_num_patterns(); + for (unsigned i = 0; i < np; ++i) { + if (!in_cache(q->get_pattern(i))) { + todo.push_back(q->get_pattern(i)); + pending = true; + } + else + patterns.push_back(result.get(q->get_pattern(i)->get_id())); + } + np = q->get_num_no_patterns(); + for (unsigned i = 0; i < np; ++i) { + if (!in_cache(q->get_no_pattern(i))) { + todo.push_back(q->get_no_pattern(i)); + pending = true; + } + else + no_patterns.push_back(result.get(q->get_no_pattern(i)->get_id())); + } + if (pending) + continue; + todo.pop_back(); + ptr_buffer sorts; + for (unsigned i = 0; i < q->get_num_decls(); ++i) + sorts.push_back((*this)(q->get_decl_sort(i))); + quantifier* q2 = + m.mk_quantifier(q->get_kind(), q->get_num_decls(), sorts.data(), q->get_decl_names(), result.get(q->get_expr()->get_id()), + q->get_weight(), + q->get_qid(), q->get_skid(), + q->get_num_patterns(), patterns.data(), q->get_num_no_patterns(), no_patterns.data() + ); + result.setx(q->get_id(), q2); + } + else if (is_app(a)) { + args.reset(); + unsigned n = todo.size(); + for (expr* arg : *to_app(a)) { + if (!in_cache(arg)) + todo.push_back(arg); + else + args.push_back(result.get(arg->get_id())); + } + if (n < todo.size()) + continue; + func_decl* f = to_app(a)->get_decl(); + if (f->is_polymorphic()) { + domain.reset(); + for (unsigned i = 0; i < f->get_arity(); ++i) + domain.push_back((*this)(f->get_domain(i))); + sort_ref range = (*this)(f->get_range()); + f = m.instantiate_polymorphic(f, f->get_arity(), domain.data(), range); + } + result.setx(a->get_id(), m.mk_app(f, args)); + todo.pop_back(); + } + } + return expr_ref(result.get(e->get_id()), m); + } + + bool substitution::unify(sort* s1, sort* s2) { + if (s1 == s2) + return true; + if (s1->is_type_var() && m_sub.find(s1, s1)) + return unify(s1, s2); + if (s2->is_type_var() && m_sub.find(s2, s2)) + return unify(s1, s2); + if (s2->is_type_var() && !s1->is_type_var()) + std::swap(s1, s2); + if (s1->is_type_var()) { + auto s22 = (*this)(s2); + if (occurs(s1, s22)) + return false; + m_trail.push_back(s22); + m_trail.push_back(s1); + m_sub.insert(s1, s22); + return true; + } + if (s1->get_family_id() != s2->get_family_id()) + return false; + if (s1->get_decl_kind() != s2->get_decl_kind()) + return false; + if (s1->get_name() != s2->get_name()) + return false; + if (s1->get_num_parameters() != s2->get_num_parameters()) + return false; + for (unsigned i = s1->get_num_parameters(); i-- > 0;) { + auto p1 = s1->get_parameter(i); + auto p2 = s2->get_parameter(i); + if (p1.is_ast() && is_sort(p1.get_ast())) { + if (!p2.is_ast()) + return false; + if (!is_sort(p2.get_ast())) + return false; + if (!unify(to_sort(p1.get_ast()), to_sort(p2.get_ast()))) + return false; + continue; + } + if (p1 != p2) + return false; + } + return true; + } + + bool substitution::match(sort* s1, sort* s2) { + if (s1 == s2) + return true; + if (s1->is_type_var() && m_sub.find(s1, s1)) + return match(s1, s2); + if (s1->is_type_var()) { + m_trail.push_back(s2); + m_trail.push_back(s1); + m_sub.insert(s1, s2); + return true; + } + if (s1->get_family_id() != s2->get_family_id()) + return false; + if (s1->get_decl_kind() != s2->get_decl_kind()) + return false; + if (s1->get_name() != s2->get_name()) + return false; + if (s1->get_num_parameters() != s2->get_num_parameters()) + return false; + for (unsigned i = s1->get_num_parameters(); i-- > 0;) { + auto p1 = s1->get_parameter(i); + auto p2 = s2->get_parameter(i); + if (p1.is_ast() && is_sort(p1.get_ast())) { + if (!p2.is_ast()) + return false; + if (!is_sort(p2.get_ast())) + return false; + if (!match(to_sort(p1.get_ast()), to_sort(p2.get_ast()))) + return false; + continue; + } + if (p1 != p2) + return false; + } + return true; + } + + // util + bool util::unify(sort* s1, sort* s2, substitution& sub) { + return sub.unify(s1, s2); + } + + bool util::unify(func_decl* f1, func_decl* f2, substitution& sub) { + if (f1 == f2) + return true; + if (!f1->is_polymorphic() || !f2->is_polymorphic()) + return false; + if (m.poly_root(f1) != m.poly_root(f2)) + return false; + for (unsigned i = f1->get_arity(); i-- > 0; ) + if (!sub.unify(fresh(f1->get_domain(i)), f2->get_domain(i))) + return false; + return sub.unify(fresh(f1->get_range()), f2->get_range()); + } + + bool util::unify(substitution const& s1, substitution const& s2, + substitution& sub) { + sort* v2; + for (auto const& [k, v] : s1) + sub.insert(k, v); + for (auto const& [k, v] : s2) { + if (sub.find(k, v2)) { + if (!sub.unify(sub(v), v2)) + return false; + } + else + sub.insert(k, sub(v)); + } + return true; + } + + bool util::match(substitution& sub, sort* s1, sort* s_ground) { + return sub.match(s1, s_ground); + } + + /** + * Create fresh variables, but with caching. + * So "fresh" variables are not truly fresh globally. + * This can block some unifications and therefore block some instantiations of + * polymorphic assertions. A different caching scheme could be created to + * ensure that fresh variables are introduced at the right time, or use other + * tricks such as creating variable/offset pairs to distinguish name spaces without + * incurring costs. + */ + sort_ref util::fresh(sort* s) { + sort* s1; + if (m_fresh.find(s, s1)) + return sort_ref(s1, m); + + if (m.is_type_var(s)) { + s1 = m.mk_type_var(symbol("fresh!" + std::to_string(m_counter))); + m_trail.push_back(s1); + m_trail.push_back(s); + m_fresh.insert(s, s1); + return sort_ref(s1, m); + } + vector params; + for (unsigned i = 0; i < s->get_num_parameters(); ++i) { + parameter p = s->get_parameter(i); + if (p.is_ast() && is_sort(p.get_ast())) + params.push_back(parameter(fresh(to_sort(p.get_ast())))); + else + params.push_back(p); + } + sort_info info(s->get_family_id(), s->get_decl_kind(), params.size(), params.data(), s->private_parameters()); + s1 = m.mk_sort(s->get_name(), info); + m_trail.push_back(s1); + m_trail.push_back(s); + m_fresh.insert(s, s1); + return sort_ref(s1, m); + } + + sort_ref_vector util::fresh(unsigned n, sort* const* s) { + sort_ref_vector r(m); + for (unsigned i = 0; i < n; ++i) + r.push_back(fresh(s[i])); + return r; + } + + void util::collect_poly_instances(expr* e, ptr_vector& instances) { + struct proc { + ast_manager& m; + ptr_vector& instances; + proc(ast_manager& m, ptr_vector& instances) : m(m), instances(instances) {} + void operator()(func_decl* f) { + if (f->is_polymorphic() && !m.is_eq(f) && !is_decl_of(f, pattern_family_id, OP_PATTERN)) + instances.push_back(f); + } + void operator()(ast* a) {} + }; + proc proc(m, instances); + for_each_ast(proc, e, false); + } + + bool util::has_type_vars(expr* e) { + struct proc { + ast_manager& m; + bool found = false; + proc(ast_manager& m) : m(m) {} + void operator()(sort* f) { + if (m.has_type_var(f)) + found = true; + } + void operator()(ast* a) {} + }; + proc proc(m); + for_each_ast(proc, e, false); + return proc.found; + } + + void util::collect_type_vars(expr* e, ptr_vector& tvs) { + struct proc { + ast_manager& m; + ptr_vector& tvs; + proc(ast_manager& m, ptr_vector& tvs) : m(m), tvs(tvs) {} + void operator()(sort* s) { + if (m.is_type_var(s)) + tvs.push_back(s); + } + void operator()(ast* a) {} + }; + proc proc(m, tvs); + for_each_ast(proc, e, true); + } +} diff --git a/src/ast/polymorphism_util.h b/src/ast/polymorphism_util.h new file mode 100644 index 000000000..3023d0338 --- /dev/null +++ b/src/ast/polymorphism_util.h @@ -0,0 +1,112 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + polymorphism_util.h + +Abstract: + + Utilities for supporting polymorphic type signatures. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-7-8 + +--*/ +#pragma once + +#include "ast/ast.h" +#include "util/hashtable.h" + +namespace polymorphism { + + class substitution { + ast_manager& m; + obj_map m_sub; + sort_ref_vector m_trail; + public: + substitution(ast_manager& m): m(m), m_trail(m) {} + + sort_ref_vector operator()(sort_ref_vector const& s); + + sort_ref operator()(sort* s); + + expr_ref operator()(expr* e); + + bool unify(sort* s1, sort* s2); + + bool match(sort* s1, sort* s_ground); + + obj_map::iterator begin() const { return m_sub.begin(); } + obj_map::iterator end() const { return m_sub.end(); } + + void insert(sort* v, sort* t) { m_trail.push_back(v).push_back(t); m_sub.insert(v, t); } + + bool find(sort* v, sort*& t) const { return m_sub.find(v, t); } + + unsigned size() const { return m_sub.size(); } + + /** + * weak equality: strong equality considers applying substitutions recursively in range + * because substitutions may be in triangular form. + */ + struct eq { + bool operator()(substitution const* s1, substitution const* s2) const { + if (s1->size() != s2->size()) + return false; + sort* v2; + for (auto const& [k, v] : *s1) { + if (!s2->find(k, v2)) + return false; + if (v != v2) + return false; + } + return true; + } + }; + + struct hash { + unsigned operator()(substitution const* s) const { + unsigned hash = 0xfabc1234 + s->size(); + for (auto const& [k, v] : *s) + hash ^= k->hash() + 2 * v->hash(); + return hash; + } + }; + }; + + typedef hashtable substitutions; + + class util { + ast_manager& m; + sort_ref_vector m_trail; + obj_map m_fresh; + unsigned m_counter = 0; + + sort_ref fresh(sort* s); + + sort_ref_vector fresh(unsigned n, sort* const* s); + + public: + util(ast_manager& m): m(m), m_trail(m) {} + + bool unify(sort* s1, sort* s2, substitution& sub); + + bool unify(func_decl* f1, func_decl* f2, substitution& sub); + + bool unify(substitution const& s1, substitution const& s2, + substitution& sub); + + bool match(substitution& sub, sort* s1, sort* s_ground); + + // collect instantiations of polymorphic functions + void collect_poly_instances(expr* e, ptr_vector& instances); + + // test if expression contains polymorphic variable. + bool has_type_vars(expr* e); + + void collect_type_vars(expr* e, ptr_vector& tvs); + + }; +} diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 6b42d9ee1..6f04799f6 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -42,6 +42,7 @@ Notes: #include "ast/for_each_expr.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/recfun_replace.h" +#include "ast/polymorphism_util.h" #include "model/model_evaluator.h" #include "model/model_smt2_pp.h" #include "model/model_v2_pp.h" @@ -223,12 +224,48 @@ bool func_decls::check_signature(ast_manager& m, func_decl* f, unsigned arity, s return true; } -func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) const { +bool func_decls::check_poly_signature(ast_manager& m, func_decl* f, unsigned arity, sort* const* domain, sort* range, func_decl*& g) { + polymorphism::substitution sub(m); + arith_util au(m); + sort_ref range_ref(range, m); + if (range != nullptr && !sub.match(f->get_range(), range)) + return false; + if (f->get_arity() != arity) + return false; + for (unsigned i = 0; i < arity; i++) + if (!sub.match(f->get_domain(i), domain[i])) + return false; + if (!range) + range_ref = sub(f->get_range()); + + recfun::util u(m); + auto& p = u.get_plugin(); + if (!u.has_def(f)) { + g = m.instantiate_polymorphic(f, arity, domain, range_ref); + return true; + } + // this is an instantiation of a recursive polymorphic function. + // create a self-contained polymorphic definition for the instantiation. + auto def = u.get_def(f); + auto promise_def = p.mk_def(f->get_name(), arity, domain, range_ref, false); + recfun_replace replace(m); + expr_ref tt = sub(def.get_rhs()); + p.set_definition(replace, promise_def, def.is_macro(), def.get_vars().size(), def.get_vars().data(), tt); + g = promise_def.get_def()->get_decl(); + insert(m, g); + return true; +} + + +func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) { bool coerced = false; + func_decl* g = nullptr; if (!more_than_one()) { func_decl* f = first(); if (check_signature(m, f, arity, domain, range, coerced)) - return f; + return f; + if (check_poly_signature(m, f, arity, domain, range, g)) + return g; return nullptr; } func_decl_set * fs = UNTAG(func_decl_set *, m_decls); @@ -241,10 +278,15 @@ func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * doma return f; } } - return best_f; + if (best_f != nullptr) + return best_f; + for (func_decl* f : *fs) + if (check_poly_signature(m, f, arity, domain, range, g)) + return g; + return nullptr; } -func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const { +func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) { if (!more_than_one()) first(); ptr_buffer sorts; @@ -376,12 +418,13 @@ void cmd_context::erase_macro(symbol const& s) { decls.erase_last(m()); } -bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr*& t) const { +bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr_ref& t) { macro_decls decls; if (!m_macros.find(s, decls)) return false; for (macro_decl const& d : decls) { - if (d.m_domain.size() != n) continue; + if (d.m_domain.size() != n) + continue; bool eq = true; coerced_args.reset(); for (unsigned i = 0; eq && i < n; ++i) { @@ -406,6 +449,26 @@ bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, exp return true; } } + for (macro_decl const& d : decls) { + if (d.m_domain.size() != n) + continue; + polymorphism::substitution sub(m()); + bool eq = true; + for (unsigned i = 0; eq && i < n; ++i) { + if (!sub.match(d.m_domain[i], args[i]->get_sort())) + eq = false; + } + if (eq) { + t = d.m_body; + t = sub(t); + verbose_stream() << "macro " << t << "\n"; + ptr_buffer domain; + for (unsigned i = 0; i < n; ++i) + domain.push_back(args[i]->get_sort()); + insert_macro(s, n, domain.data(), t); + return true; + } + } return false; } @@ -939,18 +1002,16 @@ void cmd_context::insert(cmd * c) { void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) { sm().inc_ref(d); sexpr * old_d; - if (m_user_tactic_decls.find(s, old_d)) { - sm().dec_ref(old_d); - } + if (m_user_tactic_decls.find(s, old_d)) + sm().dec_ref(old_d); m_user_tactic_decls.insert(s, d); } void cmd_context::insert(symbol const & s, object_ref * r) { r->inc_ref(*this); object_ref * old_r = nullptr; - if (m_object_refs.find(s, old_r)) { - old_r->dec_ref(*this); - } + if (m_object_refs.find(s, old_r)) + old_r->dec_ref(*this); m_object_refs.insert(s, r); } @@ -1054,16 +1115,17 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family } func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, - unsigned arity, sort * const * domain, sort * range) const { + unsigned arity, sort * const * domain, sort * range) { if (domain && contains_macro(s, arity, domain)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); func_decl * f = nullptr; - func_decls fs; - if (num_indices == 0 && m_func_decls.find(s, fs)) + if (num_indices == 0 && m_func_decls.contains(s)) { + auto& fs = m_func_decls.find(s); f = fs.find(m(), arity, domain, range); - if (f) + } + if (f) return f; builtin_decl d; if ((arity == 0 || domain) && m_builtin_decls.find(s, d)) { @@ -1089,11 +1151,12 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } - if (num_indices > 0 && m_func_decls.find(s, fs)) + if (num_indices > 0 && m_func_decls.contains(s)) { + auto& fs = m_func_decls.find(s); f = fs.find(m(), arity, domain, range); - if (f) + } + if (f) return f; - throw cmd_exception("invalid function declaration reference, unknown indexed function ", s); } @@ -1125,7 +1188,7 @@ object_ref * cmd_context::find_object_ref(symbol const & s) const { #define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T) -void cmd_context::mk_const(symbol const & s, expr_ref & result) const { +void cmd_context::mk_const(symbol const & s, expr_ref & result) { mk_app(s, 0, nullptr, 0, nullptr, nullptr, result); } @@ -1153,9 +1216,10 @@ bool cmd_context::try_mk_builtin_app(symbol const & s, unsigned num_args, expr * bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - func_decls& fs, expr_ref & result) const { - if (!m_func_decls.find(s, fs)) + expr_ref & result) { + if (!m_func_decls.contains(s)) return false; + func_decls& fs = m_func_decls.find(s); if (num_args == 0 && !range) { if (fs.more_than_one()) @@ -1180,8 +1244,8 @@ bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr bool cmd_context::try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - expr_ref & result) const { - expr* _t; + expr_ref & result) { + expr_ref _t(m()); expr_ref_vector coerced_args(m()); if (macros_find(s, num_args, args, coerced_args, _t)) { TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n"; @@ -1256,19 +1320,21 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - expr_ref & result) const { + expr_ref & result) { - func_decls fs; + if (try_mk_macro_app(s, num_args, args, num_indices, indices, range, result)) return; - if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, fs, result)) - return; + if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, result)) + return; if (try_mk_builtin_app(s, num_args, args, num_indices, indices, range, result)) return; if (!range && try_mk_pdecl_app(s, num_args, args, num_indices, indices, result)) return; + func_decls fs; + m_func_decls.find(s, fs); std::ostringstream buffer; buffer << "unknown constant " << s; if (num_args > 0) { diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index a4eb53237..c07d888c7 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -58,11 +58,12 @@ public: bool clash(func_decl * f) const; bool empty() const { return m_decls == nullptr; } func_decl * first() const; - func_decl * find(ast_manager & m, unsigned arity, sort * const * domain, sort * range) const; - func_decl * find(ast_manager & m, unsigned arity, expr * const * args, sort * range) const; + func_decl * find(ast_manager & m, unsigned arity, sort * const * domain, sort * range); + func_decl * find(ast_manager & m, unsigned arity, expr * const * args, sort * range); unsigned get_num_entries() const; func_decl * get_entry(unsigned inx); bool check_signature(ast_manager& m, func_decl* f, unsigned arityh, sort * const* domain, sort * range, bool& coerced) const; + bool check_poly_signature(ast_manager& m, func_decl* f, unsigned arity, sort* const* domain, sort* range, func_decl*& g); }; struct macro_decl { @@ -355,7 +356,7 @@ protected: bool contains_macro(symbol const& s, unsigned arity, sort *const* domain) const; void insert_macro(symbol const& s, unsigned arity, sort*const* domain, expr* t); void erase_macro(symbol const& s); - bool macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr*& t) const; + bool macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr_ref& t); recfun::decl::plugin& get_recfun_plugin(); @@ -449,22 +450,22 @@ public: void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, - unsigned arity, sort * const * domain, sort * range) const; + unsigned arity, sort * const * domain, sort * range); recfun::promise_def decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range); psort_decl * find_psort_decl(symbol const & s) const; cmd * find_cmd(symbol const & s) const; sexpr * find_user_tactic(symbol const & s) const; object_ref * find_object_ref(symbol const & s) const; - void mk_const(symbol const & s, expr_ref & result) const; + void mk_const(symbol const & s, expr_ref & result); void mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - expr_ref & r) const; + expr_ref & r); bool try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - expr_ref & r) const; + expr_ref & r); bool try_mk_builtin_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & r) const; bool try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, - func_decls& fs, expr_ref & result) const; + expr_ref & result); bool try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, expr_ref & r) const; void erase_cmd(symbol const & s); void erase_func_decl(symbol const & s); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 7699fc3a5..caeca9659 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -38,6 +38,7 @@ Revision History: #include "smt/theory_pb.h" #include "smt/theory_fpa.h" #include "smt/theory_str.h" +#include "smt/theory_polymorphism.h" namespace smt { @@ -788,6 +789,11 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); } + void setup::setup_polymorphism() { + if (m_manager.has_type_vars()) + m_context.register_plugin(alloc(theory_polymorphism, m_context)); + } + void setup::setup_unknown() { static_features st(m_manager); ptr_vector fmls; @@ -803,6 +809,7 @@ namespace smt { setup_seq_str(st); setup_fpa(); setup_special_relations(); + setup_polymorphism(); } void setup::setup_unknown(static_features & st) { @@ -819,6 +826,7 @@ namespace smt { setup_fpa(); setup_recfuns(); setup_special_relations(); + setup_polymorphism(); return; } diff --git a/src/smt/smt_setup.h b/src/smt/smt_setup.h index 2daa67085..bb4a81671 100644 --- a/src/smt/smt_setup.h +++ b/src/smt/smt_setup.h @@ -82,6 +82,7 @@ namespace smt { void setup_LRA(); void setup_CSP(); void setup_special_relations(); + void setup_polymorphism(); void setup_AUFLIA(bool simple_array = true); void setup_AUFLIA(static_features const & st); void setup_AUFLIRA(bool simple_array = true); diff --git a/src/smt/theory_polymorphism.h b/src/smt/theory_polymorphism.h new file mode 100644 index 000000000..4c64a0a9c --- /dev/null +++ b/src/smt/theory_polymorphism.h @@ -0,0 +1,105 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + theory_polymorphism.h + +Abstract: + + Plugin for handling polymorphism + The plugin instantiates polymorphic axioms based on occurrences of polymorphic functions in other axioms. + It uses blocking literals to restart search when there are new axioms that can be instantiated. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-07-11 + +--*/ +#pragma once + +#include "ast/polymorphism_inst.h" +#include "smt/smt_theory.h" + +namespace smt { + + class theory_polymorphism : public theory { + trail_stack m_trail; + polymorphism::inst m_inst; + expr_ref m_assumption; + unsigned m_qhead = 0; + bool m_pending = true; + + bool internalize_atom(app*, bool) override { return false; } + bool internalize_term(app*) override { return false; } + void new_eq_eh(theory_var, theory_var) override { } + void new_diseq_eh(theory_var, theory_var) override {} + theory* mk_fresh(context* new_ctx) override { return alloc(theory_polymorphism, *new_ctx); } + char const * get_name() const override { return "polymorphism"; } + void display(std::ostream& out) const override {} + + void push_scope_eh() override { + m_trail.push_scope(); + } + + void pop_scope_eh(unsigned n) override { + m_trail.pop_scope(n); + } + + bool can_propagate() override { + return m_pending; + } + + /** + * Assert instances of polymorphic axioms + */ + void propagate() override { + if (!m_pending) + return; + m_pending = false; + vector instances; + m_inst.instantiate(instances); + if (instances.empty()) + return; + for (auto const& [orig, inst, sub] : instances) + ctx.add_asserted(inst); + ctx.internalize_assertions(); + } + + final_check_status final_check_eh() override { + if (m_inst.pending()) + ctx.assign(~mk_literal(m_assumption), nullptr); + return FC_DONE; + } + + void add_theory_assumptions(expr_ref_vector & assumptions) override { + if (m_qhead == ctx.get_num_asserted_formulas()) + return; + m_assumption = m.mk_fresh_const("poly", m.mk_bool_sort()); + assumptions.push_back(m_assumption); + ctx.push_trail(value_trail(m_qhead)); + for (; m_qhead < ctx.get_num_asserted_formulas(); ++m_qhead) + m_inst.add(ctx.get_asserted_formula(m_qhead)); + m_pending = true; + } + + bool should_research(expr_ref_vector & assumptions) override { + for (auto * a : assumptions) + if (a == m_assumption) + return true; + return false; + } + + + public: + theory_polymorphism(context& ctx): + theory(ctx, poly_family_id), + m_inst(ctx.get_manager(), m_trail), + m_assumption(ctx.get_manager()) {} + + void init_model(model_generator & mg) override { } + }; + +}; + + From b909b87accc592f8bbe8bc3ac441d00d2fc8c344 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 09:13:41 -0700 Subject: [PATCH 005/428] build fixes Signed-off-by: Nikolaj Bjorner --- src/ast/polymorphism_inst.cpp | 45 ++++++++++++++++++----------------- src/ast/polymorphism_util.cpp | 12 ++++++---- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/ast/polymorphism_inst.cpp b/src/ast/polymorphism_inst.cpp index 42dd516f9..4da83ee10 100644 --- a/src/ast/polymorphism_inst.cpp +++ b/src/ast/polymorphism_inst.cpp @@ -38,29 +38,30 @@ namespace polymorphism { if (!u.has_type_vars(e)) return; - // insert e into the occurs list for polymorphic roots - ast_mark seen; - for (auto* f : inst.m_poly_fns) { - f = m.poly_root(f); - if (seen.is_marked(f)) - continue; - seen.mark(f, true); - if (!m_occurs.contains(f)) { - m_occurs.insert(f, ptr_vector()); - t.push(insert_map(m_occurs, f)); - } - auto& es = m_occurs.find(f); - es.push_back(e); - t.push(remove_back(m_occurs, f)); + + // insert e into the occurs list for polymorphic roots + ast_mark seen; + for (auto* f : inst.m_poly_fns) { + f = m.poly_root(f); + if (seen.is_marked(f)) + continue; + seen.mark(f, true); + if (!m_occurs.contains(f)) { + m_occurs.insert(f, ptr_vector()); + t.push(insert_map(m_occurs, f)); } - m_assertions.push_back(e); - t.push(push_back_vector(m_assertions)); - u.collect_type_vars(e, inst.m_tvs); - inst.m_subst = alloc(substitutions); - inst.m_subst->insert(alloc(substitution, m)); - m_instances.insert(e, inst); - t.push(new_obj_trail(inst.m_subst)); - t.push(insert_map(m_instances, e)); + auto& es = m_occurs.find(f); + es.push_back(e); + t.push(remove_back(m_occurs, f)); + } + m_assertions.push_back(e); + t.push(push_back_vector(m_assertions)); + u.collect_type_vars(e, inst.m_tvs); + inst.m_subst = alloc(substitutions); + inst.m_subst->insert(alloc(substitution, m)); + m_instances.insert(e, inst); + t.push(new_obj_trail(inst.m_subst)); + t.push(insert_map(m_instances, e)); } void inst::collect_instantiations(expr* e) { diff --git a/src/ast/polymorphism_util.cpp b/src/ast/polymorphism_util.cpp index 2fe271fc5..60048fe41 100644 --- a/src/ast/polymorphism_util.cpp +++ b/src/ast/polymorphism_util.cpp @@ -41,8 +41,10 @@ namespace polymorphism { vector ps; for (unsigned i = 0; i < n; ++i) { auto p = s->get_parameter(i); - if (p.is_ast() && is_sort(p.get_ast())) - ps.push_back(parameter((*this)(to_sort(p.get_ast())))); + if (p.is_ast() && is_sort(p.get_ast())) { + sort_ref s = (*this)(to_sort(p.get_ast())); + ps.push_back(parameter(s.get())); + } else ps.push_back(p); } @@ -101,11 +103,11 @@ namespace polymorphism { if (pending) continue; todo.pop_back(); - ptr_buffer sorts; + domain.reset(); for (unsigned i = 0; i < q->get_num_decls(); ++i) - sorts.push_back((*this)(q->get_decl_sort(i))); + domain.push_back((*this)(q->get_decl_sort(i))); quantifier* q2 = - m.mk_quantifier(q->get_kind(), q->get_num_decls(), sorts.data(), q->get_decl_names(), result.get(q->get_expr()->get_id()), + m.mk_quantifier(q->get_kind(), q->get_num_decls(), domain.data(), q->get_decl_names(), result.get(q->get_expr()->get_id()), q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), patterns.data(), q->get_num_no_patterns(), no_patterns.data() From 3e58f0cff1253256b02cd3dd2d8c0ec623ae314a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 09:25:20 -0700 Subject: [PATCH 006/428] build fixes --- src/ast/polymorphism_util.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ast/polymorphism_util.cpp b/src/ast/polymorphism_util.cpp index 60048fe41..1c961d21f 100644 --- a/src/ast/polymorphism_util.cpp +++ b/src/ast/polymorphism_util.cpp @@ -283,8 +283,10 @@ namespace polymorphism { vector params; for (unsigned i = 0; i < s->get_num_parameters(); ++i) { parameter p = s->get_parameter(i); - if (p.is_ast() && is_sort(p.get_ast())) - params.push_back(parameter(fresh(to_sort(p.get_ast())))); + if (p.is_ast() && is_sort(p.get_ast())) { + sort_ref fs = fresh(to_sort(p.get_ast())); + params.push_back(parameter(fs.get())); + } else params.push_back(p); } From d0d434e4f1fc847abc38940f4a9e6d97bf1ec44a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 10:23:28 -0700 Subject: [PATCH 007/428] fix #6807 --- src/ackermannization/ackr_helper.h | 30 +++++++++++++----------------- src/ackermannization/lackr.cpp | 15 ++++++++------- src/ast/array_decl_plugin.cpp | 6 ++++++ src/ast/array_decl_plugin.h | 2 ++ 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/ackermannization/ackr_helper.h b/src/ackermannization/ackr_helper.h index 9abe151e9..5499e7d3a 100644 --- a/src/ackermannization/ackr_helper.h +++ b/src/ackermannization/ackr_helper.h @@ -18,6 +18,7 @@ #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" +#include "ast/ast_ll_pp.h" class ackr_helper { public: @@ -40,10 +41,8 @@ public: inline bool is_uninterp_fn(app const * a) const { if (is_uninterp(a)) return true; - else { - decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); - return p->is_considered_uninterpreted(a->get_decl()); - } + decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); + return p->is_considered_uninterpreted(a->get_decl()); } /** @@ -64,9 +63,8 @@ public: } } else { - for (expr* arg : *a) { + for (expr* arg : *a) non_select.mark(arg, true); - } } } @@ -112,7 +110,8 @@ public: } void insert(fun2terms_map& f2t, sel2terms_map& s2t, app* a) { - if (a->get_num_args() == 0) return; + if (a->get_num_args() == 0) + return; ast_manager& m = m_bvutil.get_manager(); app_set* ts = nullptr; bool is_const_args = true; @@ -129,21 +128,18 @@ public: ts = alloc(app_set); f2t.insert(fd, ts); } - is_const_args = m.is_value(a->get_arg(0)); + is_const_args = m.is_unique_value(a->get_arg(0)); } - else { + else return; - } - for (unsigned i = 1; is_const_args && i < a->get_num_args(); ++i) { - is_const_args &= m.is_value(a->get_arg(i)); - } - if (is_const_args) { + for (unsigned i = 1; is_const_args && i < a->get_num_args(); ++i) + is_const_args &= m.is_unique_value(a->get_arg(i)); + + if (is_const_args) ts->const_args.insert(a); - } - else { + else ts->var_args.insert(a); - } } private: diff --git a/src/ackermannization/lackr.cpp b/src/ackermannization/lackr.cpp index 887f1d519..b02ef8c44 100644 --- a/src/ackermannization/lackr.cpp +++ b/src/ackermannization/lackr.cpp @@ -22,6 +22,8 @@ #include "ackermannization/ackr_info.h" #include "ast/for_each_expr.h" #include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" #include "model/model_smt2_pp.h" lackr::lackr(ast_manager& m, const params_ref& p, lackr_stats& st, @@ -142,10 +144,10 @@ bool lackr::ackr(app * const t1, app * const t2) { // Introduce the ackermann lemma for each pair of terms. // void lackr::eager_enc() { - TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << "#sels: " << m_sel2terms.size() << std::endl;); - for (auto const& kv : m_fun2terms) { + TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << " #sels: " << m_sel2terms.size() << std::endl;); + for (auto const& [k,v] : m_fun2terms) { checkpoint(); - ackr(kv.get_value()); + ackr(v); } for (auto const& kv : m_sel2terms) { checkpoint(); @@ -172,14 +174,13 @@ void lackr::ackr(app_set const* ts) { } void lackr::abstract_fun(fun2terms_map const& apps) { - for (auto const& kv : apps) { - func_decl* fd = kv.m_key; - for (app * t : kv.m_value->var_args) { + for (auto const& [fd, v] : apps) { + for (app * t : v->var_args) { app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort()); SASSERT(t->get_decl() == fd); m_info->set_abstr(t, fc); } - for (app * t : kv.m_value->const_args) { + for (app * t : v->const_args) { app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort()); SASSERT(t->get_decl() == fd); m_info->set_abstr(t, fc); diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 6778bec7c..f38894d06 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -633,6 +633,12 @@ bool array_decl_plugin::is_value(app * _e) const { } } +bool array_decl_plugin::is_unique_value(app* _e) const { + array_util u(*m_manager); + expr* e = _e; + return u.is_const(e, e) && m_manager->is_unique_value(e); +} + func_decl * array_recognizers::get_as_array_func_decl(expr * n) const { SASSERT(is_as_array(n)); diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 79c6e682e..2804b4169 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -137,6 +137,8 @@ class array_decl_plugin : public decl_plugin { bool is_value(app * e) const override; + bool is_unique_value(app* e) const override; + }; class array_recognizers { From 08599177d016812ec1f621ce4562cdd2910d1ec6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 10:47:55 -0700 Subject: [PATCH 008/428] fix #6808 remove bv_eq_axioms as an external option to toggle. Diseqalities have to be enforced for extensionality. There are no internal code paths where the option is set to false. --- src/sat/smt/bv_solver.cpp | 8 +---- src/smt/params/smt_params_helper.pyg | 1 - src/smt/params/theory_bv_params.cpp | 2 -- src/smt/params/theory_bv_params.h | 1 - src/smt/theory_bv.cpp | 51 ++++++++++++---------------- 5 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/sat/smt/bv_solver.cpp b/src/sat/smt/bv_solver.cpp index f76e5ab70..a3aa5f7b3 100644 --- a/src/sat/smt/bv_solver.cpp +++ b/src/sat/smt/bv_solver.cpp @@ -96,8 +96,6 @@ namespace bv { } void solver::add_fixed_eq(theory_var v1, theory_var v2) { - if (!get_config().m_bv_eq_axioms) - return; m_ackerman.used_eq_eh(v1, v2); } @@ -149,8 +147,6 @@ namespace bv { *\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. */ void solver::find_new_diseq_axioms(atom& a, theory_var v, unsigned idx) { - if (!get_config().m_bv_eq_axioms) - return; literal l = m_bits[v][idx]; l.neg(); for (auto vp : a) { @@ -271,7 +267,7 @@ namespace bv { ++num_undef; undef_idx = -static_cast(i + 1); } - if (num_undef > 1 && get_config().m_bv_eq_axioms) + if (num_undef > 1) return; } if (num_undef == 0) @@ -293,8 +289,6 @@ namespace bv { ++m_stats.m_num_ne2bit; s().assign(consequent, mk_ne2bit_justification(undef_idx, v1, v2, consequent, antecedent)); } - else if (!get_config().m_bv_eq_axioms) - ; else if (s().at_search_lvl()) { force_push(); assert_ackerman(v1, v2); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 4b4a453c8..7bc9bc3fa 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -53,7 +53,6 @@ def_module_params(module_name='smt', ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('bv.watch_diseq', BOOL, False, 'use watch lists instead of eager axioms for bit-vectors'), ('bv.delay', BOOL, False, 'delay internalize expensive bit-vector operations'), - ('bv.eq_axioms', BOOL, True, 'enable redundant equality axioms for bit-vectors'), ('bv.size_reduce', BOOL, False, 'pre-processing; turn assertions that set the upper bits of a bit-vector to constants into a substitution that replaces the bit-vector with constant bits. Useful for minimizing circuits as many input bits to circuits are constant'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 6, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index 09fa4513f..734a983fb 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -27,7 +27,6 @@ void theory_bv_params::updt_params(params_ref const & _p) { m_bv_reflect = p.bv_reflect(); m_bv_enable_int2bv2int = p.bv_enable_int2bv(); m_bv_delay = p.bv_delay(); - m_bv_eq_axioms = p.bv_eq_axioms(); m_bv_size_reduce = p.bv_size_reduce(); } @@ -38,7 +37,6 @@ void theory_bv_params::display(std::ostream & out) const { DISPLAY_PARAM(m_hi_div0); DISPLAY_PARAM(m_bv_reflect); DISPLAY_PARAM(m_bv_lazy_le); - DISPLAY_PARAM(m_bv_eq_axioms); DISPLAY_PARAM(m_bv_cc); DISPLAY_PARAM(m_bv_blast_max_size); DISPLAY_PARAM(m_bv_enable_int2bv2int); diff --git a/src/smt/params/theory_bv_params.h b/src/smt/params/theory_bv_params.h index e83b0b5db..523459f09 100644 --- a/src/smt/params/theory_bv_params.h +++ b/src/smt/params/theory_bv_params.h @@ -31,7 +31,6 @@ struct theory_bv_params { bool m_bv_reflect = true; bool m_bv_lazy_le = false; bool m_bv_cc = false; - bool m_bv_eq_axioms = true; unsigned m_bv_blast_max_size = INT_MAX; bool m_bv_enable_int2bv2int = true; bool m_bv_watch_diseq = false; diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index a44e1a1aa..55d3a1d62 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -429,12 +429,9 @@ namespace smt { }; void theory_bv::add_fixed_eq(theory_var v1, theory_var v2) { - if (!params().m_bv_eq_axioms) - return; - if (v1 > v2) { + if (v1 > v2) std::swap(v1, v2); - } unsigned act = m_eq_activity[hash_u_u(v1, v2) & 0xFF]++; if ((act & 0xFF) != 0xFF) { @@ -1163,8 +1160,6 @@ namespace smt { } void theory_bv::expand_diseq(theory_var v1, theory_var v2) { - if (!params().m_bv_eq_axioms) - return; SASSERT(get_bv_size(v1) == get_bv_size(v2)); if (v1 > v2) { @@ -1331,29 +1326,27 @@ namespace smt { } else { ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent)); - if (params().m_bv_eq_axioms) { - literal_vector lits; - lits.push_back(~consequent); - lits.push_back(antecedent); - literal eq = mk_eq(get_expr(v1), get_expr(v2), false); - lits.push_back(~eq); - // - // Issue #3035: - // merge_eh invokes assign_bit, which updates the propagation queue and includes the - // theory axiom for the propagated equality. When relevancy is non-zero, propagation may get - // lost on backtracking because the propagation queue is reset on conflicts. - // An alternative approach is to ensure the propagation queue is chronological with - // backtracking scopes (ie., it doesn't get reset, but shrunk to a previous level, and similar - // with a qhead indicator. - // - ctx.mark_as_relevant(lits[0]); - ctx.mark_as_relevant(lits[1]); - ctx.mark_as_relevant(lits[2]); - { - scoped_trace_stream _sts(*this, lits); - ctx.mk_th_axiom(get_id(), lits.size(), lits.data()); - } + literal_vector lits; + lits.push_back(~consequent); + lits.push_back(antecedent); + literal eq = mk_eq(get_expr(v1), get_expr(v2), false); + lits.push_back(~eq); + // + // Issue #3035: + // merge_eh invokes assign_bit, which updates the propagation queue and includes the + // theory axiom for the propagated equality. When relevancy is non-zero, propagation may get + // lost on backtracking because the propagation queue is reset on conflicts. + // An alternative approach is to ensure the propagation queue is chronological with + // backtracking scopes (ie., it doesn't get reset, but shrunk to a previous level, and similar + // with a qhead indicator. + // + ctx.mark_as_relevant(lits[0]); + ctx.mark_as_relevant(lits[1]); + ctx.mark_as_relevant(lits[2]); + { + scoped_trace_stream _sts(*this, lits); + ctx.mk_th_axiom(get_id(), lits.size(), lits.data()); } if (m_wpos[v2] == idx) @@ -1382,7 +1375,7 @@ namespace smt { } } } - + void theory_bv::relevant_eh(app * n) { TRACE("arith", tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_bounded_pp(n, m) << "\n";); TRACE("bv", tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_pp(n, m) << "\n";); From d1482287d4acb8c31efae00f011c0cf74c56588b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 14:03:40 -0700 Subject: [PATCH 009/428] fix #6793, disable unbound_compressor when used in context of a moel converter Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_coi_filter.cpp | 9 +++---- .../transforms/dl_mk_unbound_compressor.cpp | 25 +++++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 73541b0cd..d59aa66dc 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -102,14 +102,11 @@ namespace datalog { // set to false each unreached predicate if (res && m_context.get_model_converter()) { generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); - for (auto const& kv : engine) { - if (!kv.m_value.is_reachable()) { + for (auto const& kv : engine) + if (!kv.m_value.is_reachable()) unreachable.insert(kv.m_key); - } - } - for (func_decl* f : unreachable) { + for (func_decl* f : unreachable) mc0->add(f, m.mk_false()); - } m_context.add_model_converter(mc0); TRACE("dl", m_context.get_model_converter()->display(tout);); } diff --git a/src/muz/transforms/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp index e93209f95..167da3ae8 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -294,13 +294,11 @@ namespace datalog { replace_original_rule = true; replace_by_decompression_rule(source, rule_index, tail_index, arg_index); // NB. arg_indices becomes stale after original rule is replaced. - if (is_negated_predicate && !can_remove_orig_rule) { + if (is_negated_predicate && !can_remove_orig_rule) break; - } } - else { + else add_decompression_rule(source, r, tail_index, arg_index); - } } return replace_original_rule; } @@ -343,20 +341,19 @@ namespace datalog { } rule_set * mk_unbound_compressor::operator()(rule_set const & source) { - // TODO mc - if (!m_context.compress_unbound()) { + if (!m_context.compress_unbound() || m_context.get_model_converter()) return nullptr; - } m_modified = false; SASSERT(m_rules.empty()); rel_context_base* rel = m_context.get_rel_context(); - if (rel) { + if (rel) rel->collect_non_empty_predicates(m_non_empty_rels); - } + + unsigned init_rule_cnt = source.get_num_rules(); for (unsigned i = 0; i < init_rule_cnt; i++) { rule * r = source.get_rule(i); @@ -390,13 +387,15 @@ namespace datalog { scoped_ptr result; if (m_modified) { result = alloc(rule_set, m_context); - unsigned fin_rule_cnt = m_rules.size(); - for (unsigned i=0; iadd_rule(m_rules.get(i)); - } + for (auto* r : m_rules) + result->add_rule(r); result->inherit_predicates(source); } + if (result && m_context.get_model_converter()) { + // TODO mc + } reset(); + return result.detach(); } From 4a9c4ca2ce7d85d8fe0023ba48d8b38107334731 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 14:12:29 -0700 Subject: [PATCH 010/428] initialize poly solver in incremental mode Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index e55208575..7902a3440 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -36,6 +36,7 @@ Revision History: #include "smt/smt_quick_checker.h" #include "smt/uses_theory.h" #include "smt/theory_special_relations.h" +#include "smt/theory_polymorphism.h" #include "smt/smt_for_each_relevant_expr.h" #include "smt/smt_model_generator.h" #include "smt/smt_model_checker.h" @@ -3703,6 +3704,8 @@ namespace smt { m_phase_default = false; m_case_split_queue ->init_search_eh(); m_next_progress_sample = 0; + if (m.has_type_vars() && !m_theories.get_plugin(poly_family_id)) + register_plugin(alloc(theory_polymorphism, *this)); TRACE("literal_occ", display_literal_num_occs(tout);); } From 3727f7036390072e0146b26616233e5f853cea0b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 19:22:31 -0700 Subject: [PATCH 011/428] fix #6742 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bool_rewriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9354b53f6..cd6e2e353 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -229,7 +229,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args pos_lits.mark(arg); } buffer.push_back(arg); - s |= prev && lt(arg, prev); + s |= prev && arg->get_id() < prev->get_id(); prev = arg; } @@ -327,7 +327,7 @@ br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, } else { flat_args.push_back(arg); - ordered &= (!prev || !lt(arg, prev)); + ordered &= (!prev || arg->get_id() >= prev->get_id()); prev = arg; } } From b78c7887f691c676f62ed6a1d65cd102fc814b46 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 19:31:35 -0700 Subject: [PATCH 012/428] updating release-readme Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 245d53ca7..9e92195d4 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,7 +12,16 @@ Version 4.next Version 4.12.3 ============== - +- Alpha support for polymorphism. + - SMTLIB3-ish, C, Python + It adds the new command `(declare-type-var A)` that declares a symbol (in this case `A`) globally as a polymorphic type variable. + The C API contains a new function `Z3_mk_type_variable` and a new enumeration case `Z3_TYPE_VAR` as a kind associated with sorts. + All occurrences of `A` are treated as type variables. A function declaration whose signature uses `A` is treated as a shorthand + for declarations of all functions that use instances of `A`. + Assertions that use type variables are shorthands for assertions covering all instantiations. +- Various (ongoing) performance fixes and improvements to smt.arith.solver=6 +- A working version of solver.proof.trim=true option. Proofs logs created when using sat.smt=true may be trimmed by running z3 + on the generated proof log using the option solver.proof.trim=true. Version 4.12.2 ============== From dda9242616b46bbb03aa8cca30db916a09ea1f94 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 21:39:04 -0700 Subject: [PATCH 013/428] revert lt change Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bool_rewriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index cd6e2e353..9354b53f6 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -229,7 +229,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args pos_lits.mark(arg); } buffer.push_back(arg); - s |= prev && arg->get_id() < prev->get_id(); + s |= prev && lt(arg, prev); prev = arg; } @@ -327,7 +327,7 @@ br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, } else { flat_args.push_back(arg); - ordered &= (!prev || arg->get_id() >= prev->get_id()); + ordered &= (!prev || !lt(arg, prev)); prev = arg; } } From a8da0a685184ffbe89292ac1f325c8ae32c68303 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Jul 2023 21:48:46 -0700 Subject: [PATCH 014/428] #6696 Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index b04445d16..a35956454 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -149,7 +149,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); + p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); else p = m.mk_skolemization(q, r); } From 3849f665d620f823b405e5cfcfad0f04e5ca95fa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 14 Jul 2023 10:17:19 -0700 Subject: [PATCH 015/428] #6523 --- src/sat/smt/pb_internalize.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index abbd79c44..42b06872a 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -171,6 +171,15 @@ namespace pb { wl.second.neg(); k += rational(wl.first); } + if (k < 0) { + bool_var v = s().add_var(false); + literal l(v, false); + s().assign_unit(~l); + si.cache(t, l); + if (sign) l.neg(); + return l; + } + check_unsigned(k); add_pb_ge(v2, false, wlits, k.get_unsigned()); if (base_assert) { From 401ec04ec301d4b61aa5990cebd2beb31be33874 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:19:13 -0700 Subject: [PATCH 016/428] code cleaning around m_touched_rows of lar_solver (#6814) --- src/math/lp/int_solver.cpp | 8 ++-- src/math/lp/lar_solver.cpp | 67 +++++++++++---------------- src/math/lp/lar_solver.h | 20 ++++---- src/math/lp/lp_core_solver_base.h | 3 +- src/math/lp/lp_core_solver_base_def.h | 6 +-- src/sat/smt/arith_solver.cpp | 2 +- src/smt/theory_arith.h | 2 +- src/smt/theory_arith_core.h | 6 +-- src/smt/theory_lra.cpp | 2 +- 9 files changed, 51 insertions(+), 65 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 88a31e65d..fc1c8d3dc 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -358,14 +358,14 @@ int_solver::int_solver(lar_solver& lar_slv) : // this will allow to enable and disable tracking of the pivot rows struct check_return_helper { lar_solver& lra; - bool m_track_pivoted_rows; + bool m_track_touched_rows; check_return_helper(lar_solver& ls) : lra(ls), - m_track_pivoted_rows(lra.get_track_pivoted_rows()) { - lra.set_track_pivoted_rows(false); + m_track_touched_rows(lra.touched_rows_are_tracked()) { + lra.track_touched_rows(false); } ~check_return_helper() { - lra.set_track_pivoted_rows(m_track_pivoted_rows); + lra.track_touched_rows(m_track_touched_rows); } }; diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 735f835e0..84f1b70c7 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -16,7 +16,7 @@ namespace lp { void lar_solver::updt_params(params_ref const& _p) { smt_params_helper p(_p); - set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); + track_touched_rows(p.arith_bprop_on_pivoted_rows()); set_cut_strategy(p.arith_branch_cut_ratio()); m_settings.updt_params(_p); } @@ -28,12 +28,14 @@ namespace lp { m_term_register(true), m_constraints(*this) {} - void lar_solver::set_track_pivoted_rows(bool v) { - m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v ? (&m_rows_with_changed_bounds) : nullptr; + // start or ends tracking the rows that were changed by solve() + void lar_solver::track_touched_rows(bool v) { + m_mpq_lar_core_solver.m_r_solver.m_touched_rows = v ? (&m_touched_rows) : nullptr; } - - bool lar_solver::get_track_pivoted_rows() const { - return m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows != nullptr; + + // returns true iff the solver tracks the rows that were changed by solve() + bool lar_solver::touched_rows_are_tracked() const { + return m_mpq_lar_core_solver.m_r_solver.m_touched_rows != nullptr; } lar_solver::~lar_solver() { @@ -201,12 +203,6 @@ namespace lp { return m_status; } solve_with_core_solver(); - if (m_status != lp_status::INFEASIBLE) { - if (m_settings.bound_propagation()) - detect_rows_with_changed_bounds(); - } - - clear_columns_with_changed_bounds(); return m_status; } @@ -271,11 +267,11 @@ namespace lp { clean_popped_elements(n, m_incorrect_columns); for (auto rid : m_row_bounds_to_replay) - insert_row_with_changed_bounds(rid); + add_touched_row(rid); m_row_bounds_to_replay.reset(); unsigned m = A_r().row_count(); - clean_popped_elements(m, m_rows_with_changed_bounds); + clean_popped_elements(m, m_touched_rows); clean_inf_heap_of_r_solver_after_pop(); lp_assert( m_settings.simplex_strategy() == simplex_strategy_enum::undecided || @@ -616,17 +612,11 @@ namespace lp { left_side.push_back(std::make_pair(c, v)); } - void lar_solver::insert_row_with_changed_bounds(unsigned rid) { - m_rows_with_changed_bounds.insert(rid); - } - - - - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { - for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) - insert_row_with_changed_bounds(rc.var()); + void lar_solver::add_touched_row(unsigned rid) { + m_touched_rows.insert(rid); } + bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; @@ -697,9 +687,9 @@ namespace lp { void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) - insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); + add_touched_row(m_mpq_lar_core_solver.m_r_heading[j]); else - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + add_column_rows_to_touched_rows(j); } void lar_solver::detect_rows_with_changed_bounds() { @@ -710,6 +700,8 @@ namespace lp { void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); + // whoever consumes this should clear it + m_columns_with_changed_bounds.clear(); } @@ -1170,10 +1162,10 @@ namespace lp { ru.update(); } - void lar_solver::mark_rows_for_bound_prop(lpvar j) { - auto& column = A_r().m_columns[j]; + void lar_solver::add_column_rows_to_touched_rows(lpvar j) { + const auto& column = A_r().m_columns[j]; for (auto const& r : column) - insert_row_with_changed_bounds(r.var()); + add_touched_row(r.var()); } void lar_solver::pop() { @@ -1457,8 +1449,7 @@ namespace lp { A_r().add_row(); m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); m_mpq_lar_core_solver.m_r_basis.push_back(j); - if (m_settings.bound_propagation()) - insert_row_with_changed_bounds(A_r().row_count() - 1); + add_touched_row(A_r().row_count() - 1); } else { m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); @@ -1545,8 +1536,7 @@ namespace lp { var_index ret = tv::mask_term(adjusted_term_index); if (!coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); - if (m_settings.bound_propagation()) - insert_row_with_changed_bounds(A_r().row_count() - 1); + add_touched_row(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); if (m_need_register_terms) @@ -1594,7 +1584,7 @@ namespace lp { m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); m_incorrect_columns.increase_size_by_one(); - m_rows_with_changed_bounds.increase_size_by_one(); + m_touched_rows.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); } @@ -1745,23 +1735,20 @@ namespace lp { 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 << (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, - constraint_index constr_index, - unsigned& equal_to_j) { + lconstraint_kind kind, + const mpq& right_side, + constraint_index constr_index, + unsigned& equal_to_j) { update_column_type_and_bound(j, kind, right_side, constr_index); equal_to_j = null_lpvar; if (column_is_fixed(j)) { register_in_fixed_var_table(j, equal_to_j); } - } constraint_index lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq& right_side) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 75fb4338c..e73e6818c 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -86,7 +86,7 @@ class lar_solver : public column_namer { 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; + u_set m_touched_rows; unsigned_vector m_row_bounds_to_replay; u_set m_basic_columns_with_changed_cost; @@ -190,14 +190,12 @@ class lar_solver : public column_namer { 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 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); + void add_touched_row(unsigned rid); void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); @@ -328,11 +326,11 @@ class lar_solver : public column_namer { void activate_check_on_equal(constraint_index, var_index&); void activate(constraint_index); void random_update(unsigned sz, var_index const* vars); - void mark_rows_for_bound_prop(lpvar j); + void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { unsigned num_prop = 0; - for (unsigned i : m_rows_with_changed_bounds) { + for (unsigned i : m_touched_rows) { num_prop += calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) return; @@ -342,7 +340,7 @@ class lar_solver : public column_namer { // and add fixed columns this way if (settings().propagate_eqs()) { bp.clear_for_eq(); - for (unsigned i : m_rows_with_changed_bounds) { + for (unsigned i : m_touched_rows) { unsigned offset_eqs = stats().m_offset_eqs; bp.cheap_eq_tree(i); if (settings().get_cancel_flag()) @@ -351,13 +349,13 @@ class lar_solver : public column_namer { m_row_bounds_to_replay.push_back(i); } } - m_rows_with_changed_bounds.clear(); + m_touched_rows.clear(); } template void check_missed_propagations(lp_bound_propagator& bp) { for (unsigned i = 0; i < A_r().row_count(); i++) - if (!m_rows_with_changed_bounds.contains(i)) + if (!m_touched_rows.contains(i)) if (0 < calculate_implied_bounds_for_row(i, bp)) { verbose_stream() << i << ": " << get_row(i) << "\n"; } @@ -612,8 +610,8 @@ class lar_solver : public column_namer { 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; + void track_touched_rows(bool v); + bool touched_rows_are_tracked() 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; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 66154368e..6db4776db 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -91,7 +91,8 @@ public: unsigned m_basis_sort_counter; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; - u_set* m_pivoted_rows; + // these rows are changed by adding to them a multiple of the pivot row + u_set* m_touched_rows; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 319966091..1687786a8 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -58,7 +58,7 @@ lp_core_solver_base(static_matrix & A, m_upper_bounds(upper_bound_values), m_basis_sort_counter(0), m_tracing_basis_changes(false), - m_pivoted_rows(nullptr), + m_touched_rows(nullptr), m_look_for_feasible_solution_only(false) { lp_assert(bounds_for_boxed_are_set_correctly()); init(); @@ -324,8 +324,8 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) { if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) { return false; } - if (m_pivoted_rows!= nullptr) - m_pivoted_rows->insert(c.var()); + if (m_touched_rows!= nullptr) + m_touched_rows->insert(c.var()); } if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index bd5dd315f..ccbe2591b 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -396,7 +396,7 @@ namespace arith { propagate_eqs(b.tv(), ci, k, b, value.get_rational()); #if 0 if (propagation_mode() != BP_NONE) - lp().mark_rows_for_bound_prop(b.tv().id()); + lp().add_column_rows_to_touched_rows(b.tv().id()); #endif } diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 92aa4baec..8cbf844b9 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -788,7 +788,7 @@ namespace smt { // // ----------------------------------- void mark_row_for_bound_prop(unsigned r1); - void mark_rows_for_bound_prop(theory_var v); + void add_column_rows_to_touched_rows(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; unsigned imply_bound_for_monomial(row const & r, int idx, bool lower); unsigned imply_bound_for_all_monomials(row const & r, bool lower); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 0a4c1869b..9fb99c0b4 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -2486,7 +2486,7 @@ namespace smt { set_bound(b, false); if (propagation_mode() != bound_prop_mode::BP_NONE) - mark_rows_for_bound_prop(v); + add_column_rows_to_touched_rows(v); return true; } @@ -2534,7 +2534,7 @@ namespace smt { set_bound(b, true); if (propagation_mode() != bound_prop_mode::BP_NONE) - mark_rows_for_bound_prop(v); + add_column_rows_to_touched_rows(v); return true; } @@ -2603,7 +2603,7 @@ namespace smt { \brief Mark all rows that contain v for bound propagation. */ template - void theory_arith::mark_rows_for_bound_prop(theory_var v) { + void theory_arith::add_column_rows_to_touched_rows(theory_var v) { for (col_entry const& ce : m_columns[v]) { if (!ce.is_dead()) mark_row_for_bound_prop(ce.m_row_id); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0c182dce5..6ffefbdc6 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2989,7 +2989,7 @@ public: } #if 0 if (should_propagate()) - lp().mark_rows_for_bound_prop(b.tv().id()); + lp().add_column_rows_to_touched_rows(b.tv().id()); #endif } From 144c9a7b827f235f6331287d75ee8b3c6109c3ce Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 15 Jul 2023 09:23:37 -0700 Subject: [PATCH 017/428] restore the change_rows population in lar_solver --- src/math/lp/lar_solver.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 84f1b70c7..f4067d9bb 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -203,6 +203,12 @@ namespace lp { return m_status; } solve_with_core_solver(); + if (m_status != lp_status::INFEASIBLE) { + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); + } + + clear_columns_with_changed_bounds(); return m_status; } @@ -700,8 +706,6 @@ namespace lp { void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); - // whoever consumes this should clear it - m_columns_with_changed_bounds.clear(); } From 8a913981f6bce740ed10de969e97df336e9df37d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Jul 2023 17:03:04 -0700 Subject: [PATCH 018/428] fix #6813 - proofs terms are fragile with respect to simplificiation of not(not(e)). It would be better if proof terms didn't have to track this level of detail, but the legacy proof format assumes strictly checkable proofs. A patch is to fixup terms within the mk_transitivity constructor Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 41 +++++++++++++++++++++++------------- src/ast/euf/euf_etable.cpp | 7 ++++++ src/ast/normal_forms/nnf.cpp | 4 +++- src/sat/smt/pb_card.cpp | 14 ++++++------ 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 3bb435ed8..f1db92a57 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2901,29 +2901,40 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { SASSERT(has_fact(p2)); SASSERT(is_app(get_fact(p1))); SASSERT(is_app(get_fact(p2))); - SASSERT(to_app(get_fact(p1))->get_num_args() == 2); - SASSERT(to_app(get_fact(p2))->get_num_args() == 2); - CTRACE("mk_transitivity", to_app(get_fact(p1))->get_decl() != to_app(get_fact(p2))->get_decl(), - tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; - tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; - tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); - SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || - ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && - (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); - CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), - tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; + app* fact1 = to_app(get_fact(p1)); + app* fact2 = to_app(get_fact(p2)); + SASSERT(fact1->get_num_args() == 2); + SASSERT(fact2->get_num_args() == 2); + CTRACE("mk_transitivity", fact1->get_decl() != fact2->get_decl(), + tout << mk_pp(fact1, *this) << "\n\n" << mk_pp(fact2, *this) << "\n"; + tout << mk_pp(fact1->get_decl(), *this) << "\n"; + tout << mk_pp(fact2->get_decl(), *this) << "\n";); + SASSERT(fact1->get_decl() == fact2->get_decl() || + ( (is_eq(fact1) || is_oeq(fact1)) && + (is_eq(fact2) || is_oeq(fact2)))); + CTRACE("mk_transitivity", fact1->get_arg(1) != fact2->get_arg(0), + tout << mk_pp(fact1, *this) << "\n\n" << mk_pp(fact2, *this) << "\n"; tout << p1->get_id() << ": " << mk_bounded_pp(p1, *this, 5) << "\n\n"; tout << p2->get_id() << ": " << mk_bounded_pp(p2, *this, 5) << "\n\n"; ); - SASSERT(to_app(get_fact(p1))->get_arg(1) == to_app(get_fact(p2))->get_arg(0)); if (is_reflexivity(p1)) return p2; if (is_reflexivity(p2)) return p1; + // local fixup to admit inline simplifications of not(not(e)) to e + expr* e; + if (is_not(fact1->get_arg(1), e) && is_not(e, e) && e == fact2->get_arg(0)) + p1 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0))); + else if (is_not(fact2->get_arg(0), e) && is_not(e, e) && e == fact1->get_arg(1)) + p2 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0))); + else { + SASSERT(fact1->get_arg(1) == fact2->get_arg(0)); + } // OEQ is compatible with EQ for transitivity. - func_decl* f = to_app(get_fact(p1))->get_decl(); - if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); - return mk_app(basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1))); + func_decl* f = fact1->get_decl(); + if (is_oeq(fact2)) + f = fact2->get_decl(); + return mk_app(basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, fact1->get_arg(0), fact2->get_arg(1))); } diff --git a/src/ast/euf/euf_etable.cpp b/src/ast/euf/euf_etable.cpp index e007297ef..6aed46f64 100644 --- a/src/ast/euf/euf_etable.cpp +++ b/src/ast/euf/euf_etable.cpp @@ -203,6 +203,7 @@ namespace euf { SASSERT(n->num_args() > 0); enode * n_prime; void * t = get_table(n); + verbose_stream() << "insert " << n << "\n"; switch (static_cast(GET_TAG(t))) { case UNARY: n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); @@ -223,6 +224,11 @@ namespace euf { void etable::erase(enode * n) { SASSERT(n->num_args() > 0); void * t = get_table(n); + static unsigned count = 0; + verbose_stream() << "erase " << (++count) << " " << n << "\n"; + if (count == 10398) { + verbose_stream() << "here\n"; + } switch (static_cast(GET_TAG(t))) { case UNARY: UNTAG(unary_table*, t)->erase(n); @@ -237,6 +243,7 @@ namespace euf { UNTAG(table*, t)->erase(n); break; } + SASSERT(!contains_ptr(n)); } bool etable::contains(enode* n) const { diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index a35956454..a894f882e 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -28,6 +28,7 @@ Notes: #include "ast/rewriter/var_subst.h" #include "ast/normal_forms/name_exprs.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_pp.h" #include /** @@ -149,7 +150,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); + p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); else p = m.mk_skolemization(q, r); } @@ -644,6 +645,7 @@ struct nnf::imp { m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { + verbose_stream() << mk_pp(t, m) << "\n"; proof * prs[1] = { pr2 }; pr2 = m.mk_oeq_congruence(m.mk_not(t), static_cast(n2.get()), 1, prs); } diff --git a/src/sat/smt/pb_card.cpp b/src/sat/smt/pb_card.cpp index 7356c3e84..30120cb73 100644 --- a/src/sat/smt/pb_card.cpp +++ b/src/sat/smt/pb_card.cpp @@ -176,17 +176,17 @@ namespace pb { return false; } else if (j == bound) { - for (unsigned i = 0; i < bound; ++i) { - s.assign(c, c[i]); - } + for (unsigned i = 0; i < bound; ++i) + s.assign(c, c[i]); return false; } else { - if (c.is_watched()) return true; + if (c.is_watched()) + return true; clear_watch(s); - for (unsigned i = 0; i <= bound; ++i) { - c.watch_literal(s, c[i]); - } + for (unsigned i = 0; i <= bound; ++i) + if (!c.is_watched(s, c[i])) + c.watch_literal(s, c[i]); c.set_watch(); return true; } From 30e8330907cd3eaba47bdb88cdab33d1333553cb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Jul 2023 17:03:44 -0700 Subject: [PATCH 019/428] fix #6813 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index f1db92a57..e2e01bbee 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2926,7 +2926,7 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { if (is_not(fact1->get_arg(1), e) && is_not(e, e) && e == fact2->get_arg(0)) p1 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0))); else if (is_not(fact2->get_arg(0), e) && is_not(e, e) && e == fact1->get_arg(1)) - p2 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0))); + p1 = mk_transitivity(p1, mk_rewrite(fact1->get_arg(1), fact2->get_arg(0))); else { SASSERT(fact1->get_arg(1) == fact2->get_arg(0)); } From 305c1c1dc27a047a2523ce975cea00fe0fd60fb6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Jul 2023 17:52:33 -0700 Subject: [PATCH 020/428] fix build Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index a894f882e..1f0ce6781 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -150,7 +150,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); + p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); else p = m.mk_skolemization(q, r); } @@ -645,7 +645,6 @@ struct nnf::imp { m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { - verbose_stream() << mk_pp(t, m) << "\n"; proof * prs[1] = { pr2 }; pr2 = m.mk_oeq_congruence(m.mk_not(t), static_cast(n2.get()), 1, prs); } From fd5902f76eb312d81bcd12ef7fd140a80e31670a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 16 Jul 2023 11:55:42 -1000 Subject: [PATCH 021/428] relax an assertion in int_solver::patcher --- src/math/lp/int_solver.cpp | 13 +- src/math/lp/lar_solver.cpp | 2 +- src/math/lp/lar_solver.h | 1 + z3.log | 1206 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1215 insertions(+), 7 deletions(-) create mode 100644 z3.log diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index fc1c8d3dc..34666e040 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -150,21 +150,22 @@ namespace lp { } 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); + unsigned bj = lrac.m_r_basis[row_index]; + auto old_val = lia.get_value(bj); auto new_val = old_val - impq(c.coeff()*delta); - if (lia.has_lower(i) && new_val < lra.get_lower_bound(i)) + if (lia.has_lower(bj) && new_val < lra.get_lower_bound(bj)) return false; - if (lia.has_upper(i) && new_val > lra.get_upper_bound(i)) + if (lia.has_upper(bj) && new_val > lra.get_upper_bound(bj)) 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()) + // if bj == v, then, because we are patching the lra.get_value(v), + // we just need to assert that the lra.get_value(v) would be integral. + lp_assert(bj != v || lra.from_model_in_impq_to_mpq(new_val).is_int()); } lra.set_value_for_nbasic_column(j, lia.get_value(j) + impq(delta)); - return true; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index f4067d9bb..dd4f10be8 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1019,7 +1019,7 @@ namespace lp { SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE); SASSERT(m_columns_with_changed_bounds.empty()); numeric_pair const& rp = get_column_value(j); - return rp.x + m_delta * rp.y; + return from_model_in_impq_to_mpq(rp); } mpq lar_solver::get_tv_value(tv const& t) const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index e73e6818c..fc58d0a70 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -497,6 +497,7 @@ class lar_solver : public column_namer { std::ostream& display(std::ostream& out) const; bool init_model() const; + mpq from_model_in_impq_to_mpq(const impq& v) const { return v.x + m_delta * v.y; } 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; diff --git a/z3.log b/z3.log new file mode 100644 index 000000000..54f98cda4 --- /dev/null +++ b/z3.log @@ -0,0 +1,1206 @@ +[tool-version] Z3 4.12.3 +[mk-app] #1 true +[mk-app] #2 false +[mk-app] #1 true +[mk-app] #2 false +[mk-app] #3 pi +[mk-app] #4 euler +[mk-var] datatype#0 0 +[mk-var] datatype#1 1 +[mk-app] datatype#2 insert datatype#0 datatype#1 +[mk-app] datatype#3 pattern datatype#2 +[mk-app] datatype#4 head datatype#2 +[mk-app] datatype#5 = datatype#0 datatype#4 +[mk-quant] datatype#6 constructor_accessor_axiom 2 datatype#3 datatype#5 +[attach-var-names] datatype#6 (;k!0) (;List) +[mk-app] datatype#7 tail datatype#2 +[mk-app] datatype#8 = datatype#1 datatype#7 +[mk-quant] datatype#9 constructor_accessor_axiom 2 datatype#3 datatype#8 +[attach-var-names] datatype#9 (;k!0) (;List) +[mk-app] #5 bv +[attach-meaning] #5 bv #b1 +[mk-app] #6 bv +[attach-meaning] #6 bv #b0 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #6 bv #b0 +[mk-var] #7 0 +[mk-var] #8 1 +[mk-var] #9 2 +[mk-var] #10 3 +[mk-var] #11 4 +[mk-var] #12 5 +[mk-var] #13 6 +[mk-var] #14 7 +[mk-var] #15 8 +[mk-var] #16 9 +[mk-var] #17 10 +[mk-var] #18 11 +[mk-var] #19 12 +[mk-var] #20 13 +[mk-var] #21 14 +[mk-app] #22 + #15 #13 +[attach-enode] #1 0 +[attach-enode] #2 0 +[mk-app] #23 Int +[attach-meaning] #23 arith 0 +[mk-app] #24 d +[mk-app] #25 to_real #23 +[mk-app] #26 = #25 #24 +[mk-app] #27 not #26 +[mk-app] #28 Real +[attach-meaning] #28 arith 0 +[inst-discovered] theory-solving 0000000000000000 arith# ; #25 +[mk-app] #29 = #25 #28 +[instance] 0000000000000000 #29 +[attach-enode] #29 0 +[end-of-instance] +[mk-app] #29 Real +[attach-meaning] #29 arith (- 1) +[mk-app] #30 * #29 #24 +[mk-app] #31 = #24 #28 +[mk-app] #29 = #28 #24 +[inst-discovered] theory-solving 0000000000000000 arith# ; #29 +[mk-app] #30 = #29 #31 +[instance] 0000000000000000 #30 +[attach-enode] #30 0 +[end-of-instance] +[mk-app] #29 not #31 +[mk-app] #30 c +[mk-app] #32 f +[mk-app] #33 = #30 #32 +[mk-app] #34 b +[mk-app] #35 g +[mk-app] #36 to_int #34 +[mk-app] #37 to_int #35 +[mk-app] #38 mod #36 #37 +[mk-app] #39 e +[mk-app] #40 to_real #38 +[mk-app] #41 * #40 #39 +[mk-app] #42 + #41 +[mk-app] #43 * #42 +[mk-app] #44 a +[mk-app] #45 < #43 #28 +[mk-app] #46 < #28 #39 +[mk-app] #47 < #39 #44 +[mk-app] #48 and #45 #46 #47 +[mk-app] #49 <= #28 #34 +[mk-app] #50 <= #34 #35 +[mk-app] #51 and #49 #50 +[mk-app] #52 and #33 #48 #51 +[mk-app] #53 to_int #32 +[mk-app] #54 mod #36 #53 +[mk-app] #55 to_real #54 +[mk-app] #56 + #39 #55 +[mk-app] #57 * #56 +[mk-app] #58 < #25 #57 +[mk-app] #59 < #57 #25 +[mk-app] #60 and #58 #59 +[mk-app] #61 = #52 #60 +[mk-app] #62 not #61 +[mk-app] #63 * #39 #40 +[inst-discovered] theory-solving 0000000000000000 arith# ; #41 +[mk-app] #64 = #41 #63 +[instance] 0000000000000000 #64 +[attach-enode] #64 0 +[end-of-instance] +[mk-app] #64 + #63 +[inst-discovered] theory-solving 0000000000000000 arith# ; #64 +[mk-app] #65 = #64 #63 +[instance] 0000000000000000 #65 +[attach-enode] #65 0 +[end-of-instance] +[mk-app] #64 * #63 +[inst-discovered] theory-solving 0000000000000000 arith# ; #64 +[mk-app] #65 = #64 #63 +[instance] 0000000000000000 #65 +[attach-enode] #65 0 +[end-of-instance] +[mk-app] #64 <= #28 #63 +[mk-app] #65 not #64 +[mk-app] #66 < #63 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #66 +[mk-app] #67 = #66 #65 +[instance] 0000000000000000 #67 +[attach-enode] #67 0 +[end-of-instance] +[mk-app] #66 Real +[attach-meaning] #66 arith (- 1) +[mk-app] #67 * #66 #63 +[mk-app] #68 >= #63 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #64 +[mk-app] #66 = #64 #68 +[instance] 0000000000000000 #66 +[attach-enode] #66 0 +[end-of-instance] +[mk-app] #66 not #68 +[mk-app] #67 <= #39 #28 +[mk-app] #69 not #67 +[inst-discovered] theory-solving 0000000000000000 arith# ; #46 +[mk-app] #70 = #46 #69 +[instance] 0000000000000000 #70 +[attach-enode] #70 0 +[end-of-instance] +[mk-app] #70 <= #44 #39 +[mk-app] #71 not #70 +[inst-discovered] theory-solving 0000000000000000 arith# ; #47 +[mk-app] #72 = #47 #71 +[instance] 0000000000000000 #72 +[attach-enode] #72 0 +[end-of-instance] +[mk-app] #72 Real +[attach-meaning] #72 arith (- 1) +[mk-app] #73 * #72 #39 +[mk-app] #74 + #73 #44 +[attach-meaning] #72 arith (- 1) +[mk-app] #75 * #72 #44 +[mk-app] #76 + #39 #75 +[mk-app] #73 >= #76 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #70 +[mk-app] #74 = #70 #73 +[instance] 0000000000000000 #74 +[attach-enode] #74 0 +[end-of-instance] +[mk-app] #74 not #73 +[attach-meaning] #72 arith (- 1) +[mk-app] #77 * #72 #34 +[mk-app] #78 >= #34 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #49 +[mk-app] #77 = #49 #78 +[instance] 0000000000000000 #77 +[attach-enode] #77 0 +[end-of-instance] +[attach-meaning] #72 arith (- 1) +[mk-app] #77 * #72 #35 +[mk-app] #79 + #34 #77 +[mk-app] #80 <= #79 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #50 +[mk-app] #81 = #50 #80 +[instance] 0000000000000000 #81 +[attach-enode] #81 0 +[end-of-instance] +[mk-app] #81 and #33 #66 #69 #74 #78 #80 +[inst-discovered] theory-solving 0000000000000000 arith# ; #25 +[mk-app] #82 = #25 #28 +[instance] 0000000000000000 #82 +[attach-enode] #82 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# ; #57 +[mk-app] #82 = #57 #56 +[instance] 0000000000000000 #82 +[attach-enode] #82 0 +[end-of-instance] +[mk-app] #82 <= #56 #28 +[mk-app] #83 not #82 +[mk-app] #84 < #28 #56 +[inst-discovered] theory-solving 0000000000000000 arith# ; #84 +[mk-app] #85 = #84 #83 +[instance] 0000000000000000 #85 +[attach-enode] #85 0 +[end-of-instance] +[mk-app] #84 <= #28 #56 +[mk-app] #85 not #84 +[mk-app] #86 < #56 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #86 +[mk-app] #87 = #86 #85 +[instance] 0000000000000000 #87 +[attach-enode] #87 0 +[end-of-instance] +[attach-meaning] #72 arith (- 1) +[mk-app] #86 * #72 #39 +[attach-meaning] #72 arith (- 1) +[mk-app] #87 * #72 #55 +[mk-app] #88 + #86 #87 +[mk-app] #86 >= #56 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #84 +[mk-app] #87 = #84 #86 +[instance] 0000000000000000 #87 +[attach-enode] #87 0 +[end-of-instance] +[mk-app] #87 not #86 +[mk-app] #88 and #83 #87 +[mk-app] #89 = #81 #88 +[mk-app] #90 not #81 +[mk-app] #91 not #89 +[inst-discovered] theory-solving 0000000000000000 basic# ; #91 +[mk-app] #90 = #91 #91 +[instance] 0000000000000000 #90 +[attach-enode] #90 0 +[end-of-instance] +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[inst-discovered] theory-solving 0000000000000000 arith# ; #25 +[mk-app] #90 = #25 #28 +[instance] 0000000000000000 #90 +[attach-enode] #90 0 +[end-of-instance] +[mk-app] #90 = #28 #24 +[mk-app] #92 not #90 +[inst-discovered] theory-solving 0000000000000000 arith# ; #41 +[mk-app] #93 = #41 #63 +[instance] 0000000000000000 #93 +[attach-enode] #93 0 +[end-of-instance] +[mk-app] #93 + #63 +[inst-discovered] theory-solving 0000000000000000 arith# ; #93 +[mk-app] #94 = #93 #63 +[instance] 0000000000000000 #94 +[attach-enode] #94 0 +[end-of-instance] +[mk-app] #93 * #63 +[inst-discovered] theory-solving 0000000000000000 arith# ; #93 +[mk-app] #94 = #93 #63 +[instance] 0000000000000000 #94 +[attach-enode] #94 0 +[end-of-instance] +[mk-app] #93 < #63 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #93 +[mk-app] #94 = #93 #65 +[instance] 0000000000000000 #94 +[attach-enode] #94 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# ; #46 +[mk-app] #93 = #46 #69 +[instance] 0000000000000000 #93 +[attach-enode] #93 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# ; #47 +[mk-app] #93 = #47 #71 +[instance] 0000000000000000 #93 +[attach-enode] #93 0 +[end-of-instance] +[mk-app] #93 and #33 #65 #69 #71 #49 #50 +[inst-discovered] theory-solving 0000000000000000 arith# ; #57 +[mk-app] #94 = #57 #56 +[instance] 0000000000000000 #94 +[attach-enode] #94 0 +[end-of-instance] +[mk-app] #94 < #28 #56 +[inst-discovered] theory-solving 0000000000000000 arith# ; #94 +[mk-app] #95 = #94 #83 +[instance] 0000000000000000 #95 +[attach-enode] #95 0 +[end-of-instance] +[mk-app] #94 < #56 #28 +[inst-discovered] theory-solving 0000000000000000 arith# ; #94 +[mk-app] #95 = #94 #85 +[instance] 0000000000000000 #95 +[attach-enode] #95 0 +[end-of-instance] +[mk-app] #94 and #83 #85 +[mk-app] #95 = #93 #94 +[mk-app] #96 not #93 +[mk-app] #97 not #95 +[inst-discovered] theory-solving 0000000000000000 basic# ; #97 +[mk-app] #96 = #97 #97 +[instance] 0000000000000000 #96 +[attach-enode] #96 0 +[end-of-instance] +[mk-app] #96 not #93 +[inst-discovered] theory-solving 0000000000000000 basic# ; #97 +[mk-app] #96 = #97 #97 +[instance] 0000000000000000 #96 +[attach-enode] #96 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# ; #56 +[mk-app] #96 = #56 #56 +[instance] 0000000000000000 #96 +[attach-enode] #96 0 +[end-of-instance] +[mk-app] #96 not #93 +[inst-discovered] theory-solving 0000000000000000 basic# ; #97 +[mk-app] #96 = #97 #97 +[instance] 0000000000000000 #96 +[attach-enode] #96 0 +[end-of-instance] +[mk-app] #96 Real +[attach-meaning] #96 arith 1 +[mk-app] #98 + #28 #96 +[mk-app] #99 k!0 +[mk-app] #100 if #99 #28 #98 +[mk-app] #101 not #99 +[mk-app] #92 + #32 #96 +[mk-app] #102 k!1 +[mk-app] #103 if #102 #32 #92 +[mk-app] #104 k!2 +[mk-app] #105 + #39 #96 +[mk-app] #106 if #104 #39 #105 +[mk-app] #107 not #104 +[mk-app] #108 and #102 #65 #69 #107 #49 #50 +[mk-app] #109 = #108 #94 +[mk-app] #110 not #109 +[mk-app] #93 k!3 +[mk-app] #95 not #93 +[mk-app] #97 k!4 +[mk-app] #111 not #97 +[mk-app] #112 and #102 #65 #69 #97 #49 #50 +[mk-app] #113 = #112 #94 +[mk-app] #114 not #113 +[attach-meaning] #6 bv #b0 +[attach-enode] #1 0 +[attach-enode] #2 0 +[attach-meaning] #72 arith (- 1) +[mk-app] #107 * #72 #63 +[inst-discovered] theory-solving 0000000000000000 arith# ; #64 +[mk-app] #107 = #64 #68 +[instance] 0000000000000000 #107 +[attach-enode] #107 0 +[end-of-instance] +[attach-meaning] #72 arith (- 1) +[mk-app] #107 * #72 #34 +[inst-discovered] theory-solving 0000000000000000 arith# ; #49 +[mk-app] #107 = #49 #78 +[instance] 0000000000000000 #107 +[attach-enode] #107 0 +[end-of-instance] +[attach-meaning] #72 arith (- 1) +[inst-discovered] theory-solving 0000000000000000 arith# ; #50 +[mk-app] #107 = #50 #80 +[instance] 0000000000000000 #107 +[attach-enode] #107 0 +[end-of-instance] +[mk-app] #107 and #102 #66 #69 #97 #78 #80 +[attach-meaning] #72 arith (- 1) +[mk-app] #101 * #72 #39 +[attach-meaning] #72 arith (- 1) +[mk-app] #90 * #72 #55 +[mk-app] #108 + #101 #90 +[inst-discovered] theory-solving 0000000000000000 arith# ; #84 +[mk-app] #101 = #84 #86 +[instance] 0000000000000000 #101 +[attach-enode] #101 0 +[end-of-instance] +[mk-app] #101 = #107 #88 +[mk-app] #90 not #107 +[mk-app] #108 not #101 +[inst-discovered] theory-solving 0000000000000000 basic# ; #108 +[mk-app] #90 = #108 #108 +[instance] 0000000000000000 #90 +[attach-enode] #90 0 +[end-of-instance] +[begin-check] 0 +[mk-app] #90 Int +[attach-meaning] #90 arith 1 +[attach-enode] #90 0 +[attach-enode] #96 0 +[attach-enode] #23 0 +[attach-enode] #28 0 +[mk-app] #109 not #107 +[inst-discovered] theory-solving 0000000000000000 basic# ; #108 +[mk-app] #109 = #108 #108 +[instance] 0000000000000000 #109 +[attach-enode] #109 0 +[end-of-instance] +[mk-app] #109 not #107 +[inst-discovered] theory-solving 0000000000000000 basic# ; #108 +[mk-app] #109 = #108 #108 +[instance] 0000000000000000 #109 +[attach-enode] #109 0 +[end-of-instance] +[mk-app] #109 not #107 +[inst-discovered] theory-solving 0000000000000000 basic# ; #108 +[mk-app] #109 = #108 #108 +[instance] 0000000000000000 #109 +[attach-enode] #109 0 +[end-of-instance] +[mk-app] #109 not #102 +[mk-app] #110 not #78 +[mk-app] #115 not #80 +[mk-app] #116 or #67 #68 #109 #110 #111 #115 +[mk-app] #117 not #116 +[inst-discovered] theory-solving 0000000000000000 basic# ; #107 +[mk-app] #118 = #107 #117 +[instance] 0000000000000000 #118 +[attach-enode] #118 0 +[end-of-instance] +[mk-app] #118 or #82 #86 +[mk-app] #119 not #118 +[inst-discovered] theory-solving 0000000000000000 basic# ; #88 +[mk-app] #120 = #88 #119 +[instance] 0000000000000000 #120 +[attach-enode] #120 0 +[end-of-instance] +[mk-app] #120 = #116 #118 +[mk-app] #121 = #117 #119 +[inst-discovered] theory-solving 0000000000000000 basic# ; #121 +[mk-app] #122 = #121 #120 +[instance] 0000000000000000 #122 +[attach-enode] #122 0 +[end-of-instance] +[mk-app] #117 not #116 +[mk-app] #121 not #120 +[inst-discovered] theory-solving 0000000000000000 basic# ; #121 +[mk-app] #117 = #121 #121 +[instance] 0000000000000000 #117 +[attach-enode] #117 0 +[end-of-instance] +[mk-app] #107 not #116 +[inst-discovered] theory-solving 0000000000000000 basic# ; #121 +[mk-app] #107 = #121 #121 +[instance] 0000000000000000 #107 +[attach-enode] #107 0 +[end-of-instance] +[mk-app] #107 not #116 +[inst-discovered] theory-solving 0000000000000000 basic# ; #121 +[mk-app] #107 = #121 #121 +[instance] 0000000000000000 #107 +[attach-enode] #107 0 +[end-of-instance] +[assign] #93 justification -1: +[attach-enode] #39 0 +[attach-enode] #34 0 +[attach-enode] #36 0 +[attach-enode] #35 0 +[attach-enode] #37 0 +[attach-enode] #38 0 +[mk-app] #119 mod #36 #23 +[mk-app] #107 mod0 #36 #23 +[mk-app] #101 = #107 #119 +[attach-enode] #107 0 +[attach-enode] #119 0 +[attach-enode] #101 0 +[mk-app] #108 Int +[attach-meaning] #108 arith (- 1) +[mk-app] #117 * #108 #119 +[mk-app] #122 + #107 #117 +[mk-app] #123 <= #122 #23 +[mk-app] #124 >= #122 #23 +[attach-enode] #108 0 +[attach-enode] #117 0 +[attach-enode] #122 0 +[mk-app] #125 to_real #36 +[mk-app] #126 - #125 #34 +[mk-app] #127 <= #126 #28 +[mk-app] #128 - #34 #125 +[mk-app] #129 >= #128 #96 +[attach-enode] #125 0 +[attach-enode] #126 0 +[attach-enode] #127 0 +[attach-enode] #128 0 +[attach-enode] #129 0 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #127 +[assign] #127 justification -1: 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #129 +[assign] (not #129) justification -1: 0 +[end-of-instance] +[assign] #101 axiom +[attach-enode] #40 0 +[attach-enode] #63 0 +[attach-enode] #72 0 +[attach-enode] #77 0 +[attach-enode] #79 0 +[attach-enode] #32 0 +[attach-enode] #53 0 +[attach-enode] #54 0 +[attach-enode] #55 0 +[attach-enode] #56 0 +[assign] (not #120) justification -1: +[assign] #124 bin 4 +[assign] #123 bin 4 +[mk-app] #130 = #122 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #130 +[end-of-instance] +[push] 0 +[assign] (not #86) decision axiom +[assign] #82 bin -15 +[assign] #118 bin 14 +[assign] (not #116) clause -13 -16 +[assign] #80 bin -13 +[assign] #97 bin -13 +[assign] #78 bin -13 +[assign] #102 bin -13 +[assign] (not #68) bin -13 +[assign] (not #67) bin -13 +[mk-app] #130 div #36 #53 +[mk-app] #131 * #53 #130 +[mk-app] #132 + #131 #54 +[mk-app] #133 = #36 #132 +[attach-enode] #130 0 +[mk-app] #134 div0 #36 #53 +[mk-app] #135 = #130 #134 +[attach-enode] #134 0 +[attach-enode] #135 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #136 * #108 #134 +[mk-app] #137 + #130 #136 +[mk-app] #138 <= #137 #23 +[mk-app] #139 >= #137 #23 +[attach-enode] #136 0 +[attach-enode] #137 0 +[assign] #135 axiom +[attach-enode] #131 0 +[attach-enode] #132 0 +[attach-enode] #133 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #140 * #108 #132 +[mk-app] #141 + #36 #140 +[mk-app] #142 <= #141 #23 +[mk-app] #143 >= #141 #23 +[attach-enode] #140 0 +[attach-enode] #141 0 +[mk-app] #144 - #53 +[mk-app] #145 >= #53 #23 +[mk-app] #146 if #145 #53 #144 +[attach-meaning] #108 arith (- 1) +[mk-app] #147 - #54 #146 +[mk-app] #148 = #53 #23 +[attach-enode] #148 0 +[mk-app] #149 = #23 #53 +[mk-app] #150 <= #53 #23 +[attach-enode] #149 0 +[mk-app] #151 >= #54 #23 +[attach-enode] #151 0 +[mk-app] #152 <= #147 #108 +[attach-enode] #145 0 +[attach-enode] #144 0 +[mk-app] #153 = #53 #146 +[mk-app] #154 = #144 #146 +[attach-enode] #146 0 +[attach-enode] #153 0 +[attach-enode] #154 0 +[attach-enode] #147 0 +[attach-enode] #152 0 +[mk-app] #155 - #36 #131 +[mk-app] #156 >= #155 #23 +[attach-meaning] #108 arith (- 1) +[mk-app] #157 * #108 #131 +[mk-app] #158 + #36 #157 +[inst-discovered] theory-solving 0000000000000000 arith# ; #155 +[mk-app] #159 = #155 #158 +[instance] 0000000000000000 #159 +[attach-enode] #159 0 +[end-of-instance] +[mk-app] #159 >= #158 #23 +[attach-enode] #157 0 +[attach-enode] #158 0 +[attach-enode] #159 0 +[mk-app] #155 div #36 #37 +[mk-app] #156 * #37 #155 +[mk-app] #160 + #156 #38 +[mk-app] #161 = #36 #160 +[attach-enode] #155 0 +[mk-app] #162 div0 #36 #37 +[mk-app] #163 = #155 #162 +[attach-enode] #162 0 +[attach-enode] #163 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #164 * #108 #162 +[mk-app] #165 + #155 #164 +[mk-app] #166 <= #165 #23 +[mk-app] #167 >= #165 #23 +[attach-enode] #164 0 +[attach-enode] #165 0 +[assign] #163 axiom +[attach-enode] #156 0 +[attach-enode] #160 0 +[attach-enode] #161 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #168 * #108 #160 +[mk-app] #169 + #36 #168 +[mk-app] #170 <= #169 #23 +[mk-app] #171 >= #169 #23 +[attach-enode] #168 0 +[attach-enode] #169 0 +[mk-app] #172 - #37 +[mk-app] #173 >= #37 #23 +[mk-app] #174 if #173 #37 #172 +[attach-meaning] #108 arith (- 1) +[mk-app] #175 - #38 #174 +[mk-app] #176 = #37 #23 +[attach-enode] #176 0 +[mk-app] #177 = #23 #37 +[mk-app] #178 <= #37 #23 +[attach-enode] #177 0 +[mk-app] #179 >= #38 #23 +[attach-enode] #179 0 +[mk-app] #180 <= #175 #108 +[attach-enode] #173 0 +[attach-enode] #172 0 +[mk-app] #181 = #37 #174 +[mk-app] #182 = #172 #174 +[attach-enode] #174 0 +[attach-enode] #181 0 +[attach-enode] #182 0 +[attach-enode] #175 0 +[attach-enode] #180 0 +[mk-app] #183 - #36 #156 +[mk-app] #184 >= #183 #23 +[attach-meaning] #108 arith (- 1) +[mk-app] #185 * #108 #156 +[mk-app] #186 + #36 #185 +[inst-discovered] theory-solving 0000000000000000 arith# ; #183 +[mk-app] #187 = #183 #186 +[instance] 0000000000000000 #187 +[attach-enode] #187 0 +[end-of-instance] +[mk-app] #187 >= #186 #23 +[attach-enode] #185 0 +[attach-enode] #186 0 +[attach-enode] #187 0 +[mk-app] #183 to_real #53 +[mk-app] #184 - #183 #32 +[mk-app] #188 <= #184 #28 +[mk-app] #189 - #32 #183 +[mk-app] #190 >= #189 #96 +[attach-enode] #183 0 +[attach-enode] #184 0 +[attach-enode] #188 0 +[attach-enode] #189 0 +[attach-enode] #190 0 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #188 +[assign] #188 justification -1: 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #190 +[assign] (not #190) justification -1: 0 +[end-of-instance] +[mk-app] #191 to_real #37 +[mk-app] #192 - #191 #35 +[mk-app] #193 <= #192 #28 +[mk-app] #194 - #35 #191 +[mk-app] #195 >= #194 #96 +[attach-enode] #191 0 +[attach-enode] #192 0 +[attach-enode] #193 0 +[attach-enode] #194 0 +[attach-enode] #195 0 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #193 +[assign] #193 justification -1: 0 +[end-of-instance] +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #195 +[assign] (not #195) justification -1: 0 +[end-of-instance] +[assign] (not #151) clause -28 -14 2 +[assign] #138 clause 19 -18 +[assign] #139 clause 20 -18 +[assign] #166 clause 34 -33 +[assign] #167 clause 35 -33 +[assign] #148 clause 24 28 +[assign] #149 justification -1: 24 +[mk-app] #196 = #54 #119 +[attach-meaning] #108 arith (- 1) +[mk-app] #197 + #54 #117 +[mk-app] #198 <= #197 #23 +[mk-app] #199 >= #197 #23 +[assign] #196 justification -1: 24 +[attach-enode] #196 0 +[attach-enode] #197 0 +[assign] #198 justification -1: 52 +[assign] #199 justification -1: 52 +[mk-app] #200 = #137 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[mk-app] #200 = #165 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[assign] #150 clause 26 -25 +[assign] #145 clause 27 -25 +[assign] #153 clause 30 -27 +[mk-app] #200 = #197 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[mk-app] #200 = #23 #144 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[mk-app] #200 = #28 #183 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[mk-app] #200 = #32 #189 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[assign] #154 justification -1: 30 24 26 27 +[push] 1 +[assign] (not #179) decision axiom +[assign] #176 clause 39 43 +[assign] #177 justification -1: 39 +[mk-app] #200 = #38 #119 +[attach-meaning] #108 arith (- 1) +[mk-app] #201 + #38 #117 +[mk-app] #202 <= #201 #23 +[mk-app] #203 >= #201 #23 +[assign] #200 justification -1: 39 +[attach-enode] #200 0 +[attach-enode] #201 0 +[assign] #202 justification -1: 55 +[assign] #203 justification -1: 55 +[mk-app] #204 = #130 #155 +[attach-meaning] #108 arith (- 1) +[mk-app] #205 * #108 #155 +[mk-app] #206 + #130 #205 +[mk-app] #207 <= #206 #23 +[mk-app] #208 >= #206 #23 +[assign] #204 justification -1: 39 24 +[attach-enode] #204 0 +[attach-enode] #205 0 +[attach-enode] #206 0 +[assign] #207 justification -1: 58 +[assign] #208 justification -1: 58 +[mk-app] #209 = #144 #172 +[attach-meaning] #108 arith (- 1) +[mk-app] #210 * #108 #172 +[mk-app] #211 + #144 #210 +[mk-app] #212 <= #211 #23 +[mk-app] #213 >= #211 #23 +[assign] #209 justification -1: 39 24 +[attach-enode] #209 0 +[attach-enode] #210 0 +[attach-enode] #211 0 +[assign] #212 justification -1: 61 +[assign] #213 justification -1: 61 +[mk-app] #214 = #183 #191 +[attach-meaning] #72 arith (- 1) +[mk-app] #215 * #72 #191 +[mk-app] #216 + #183 #215 +[mk-app] #217 <= #216 #28 +[mk-app] #218 >= #216 #28 +[assign] #214 justification -1: 39 24 +[attach-enode] #214 0 +[attach-enode] #215 0 +[attach-enode] #216 0 +[assign] #217 justification -1: 64 +[assign] #218 justification -1: 64 +[mk-app] #219 = #40 #55 +[attach-meaning] #72 arith (- 1) +[mk-app] #220 * #72 #55 +[mk-app] #221 + #40 #220 +[mk-app] #222 <= #221 #28 +[mk-app] #223 >= #221 #28 +[assign] #219 justification -1: 24 39 +[attach-enode] #219 0 +[attach-enode] #220 0 +[attach-enode] #221 0 +[assign] #222 justification -1: 67 +[assign] #223 justification -1: 67 +[assign] #178 clause 41 -40 +[assign] #173 clause 42 -40 +[assign] #181 clause 45 -42 +[mk-app] #224 = #201 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #206 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #211 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #216 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #221 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #35 #194 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[push] 2 +[assign] (not #152) decision axiom +[push] 3 +[assign] (not #143) decision axiom +[assign] #142 clause 22 23 +[assign] (not #133) clause -21 23 +[assign] (not #159) clause -32 23 -14 2 +[push] 4 +[assign] (not #182) decision axiom +[push] 5 +[assign] (not #187) decision axiom +[push] 6 +[assign] (not #171) decision axiom +[assign] #170 clause 37 38 +[assign] (not #161) clause -36 38 +[push] 7 +[assign] (not #180) decision axiom +[mk-app] #224 = #36 #197 +[attach-enode] #224 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #225 * #108 #197 +[mk-app] #226 + #36 #225 +[mk-app] #227 <= #226 #23 +[mk-app] #228 >= #226 #23 +[attach-enode] #225 0 +[attach-enode] #226 0 +[push] 8 +[assign] #224 decision axiom +[assign] #227 clause 71 -70 +[assign] #228 clause 72 -70 +[mk-app] #229 = #125 #183 +[attach-meaning] #72 arith (- 1) +[mk-app] #230 * #72 #183 +[mk-app] #231 + #125 #230 +[mk-app] #232 <= #231 #28 +[mk-app] #233 >= #231 #28 +[assign] #229 justification -1: 70 24 54 53 +[attach-enode] #229 0 +[attach-enode] #230 0 +[attach-enode] #231 0 +[assign] #232 justification -1: 73 +[assign] #233 justification -1: 73 +[mk-app] #234 = #226 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #234 +[end-of-instance] +[mk-app] #234 = #231 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #234 +[end-of-instance] +[mk-app] #234 not #151 +[mk-app] #235 or #151 #234 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #235 +[end-of-instance] +[mk-app] #234 <= #131 #23 +[assign] #234 justification -1: 26 27 +[mk-app] #235 <= #156 #23 +[assign] #235 justification -1: 41 42 +[resolve-process] true +[resolve-lit] 8 #67 +[resolve-lit] 8 (not #82) +[resolve-lit] 0 (not #234) +[resolve-lit] 5 #143 +[resolve-lit] 0 (not #228) +[resolve-lit] 8 (not #199) +[resolve-process] (not #234) +[resolve-lit] 8 (not #150) +[resolve-lit] 8 (not #145) +[conflict] (not #228) (not #82) #143 +[pop] 5 9 +[attach-enode] #225 0 +[attach-enode] #226 0 +[assign] (not #228) clause -70 23 -14 +[resolve-process] true +[resolve-lit] 3 (not #78) +[resolve-lit] 3 (not #198) +[resolve-lit] 0 #228 +[conflict] #228 (not #78) (not #198) +[pop] 3 4 +[attach-enode] #225 0 +[attach-enode] #226 0 +[assign] #228 clause 55 -10 -53 +[assign] #143 clause 23 -55 -14 +[mk-app] #200 = #226 #36 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #200 +[end-of-instance] +[push] 1 +[assign] (not #179) decision axiom +[assign] #176 clause 39 43 +[assign] #177 justification -1: 39 +[mk-app] #200 = #38 #119 +[attach-meaning] #108 arith (- 1) +[mk-app] #201 + #38 #117 +[mk-app] #202 <= #201 #23 +[mk-app] #203 >= #201 #23 +[assign] #200 justification -1: 39 +[attach-enode] #200 0 +[attach-enode] #201 0 +[assign] #202 justification -1: 56 +[assign] #203 justification -1: 56 +[mk-app] #204 = #130 #155 +[attach-meaning] #108 arith (- 1) +[mk-app] #205 * #108 #155 +[mk-app] #206 + #130 #205 +[mk-app] #207 <= #206 #23 +[mk-app] #208 >= #206 #23 +[assign] #204 justification -1: 39 24 +[attach-enode] #204 0 +[attach-enode] #205 0 +[attach-enode] #206 0 +[assign] #207 justification -1: 59 +[assign] #208 justification -1: 59 +[mk-app] #209 = #144 #172 +[attach-meaning] #108 arith (- 1) +[mk-app] #210 * #108 #172 +[mk-app] #211 + #144 #210 +[mk-app] #212 <= #211 #23 +[mk-app] #213 >= #211 #23 +[assign] #209 justification -1: 39 24 +[attach-enode] #209 0 +[attach-enode] #210 0 +[attach-enode] #211 0 +[assign] #212 justification -1: 62 +[assign] #213 justification -1: 62 +[mk-app] #214 = #183 #191 +[attach-meaning] #72 arith (- 1) +[mk-app] #215 * #72 #191 +[mk-app] #216 + #183 #215 +[mk-app] #217 <= #216 #28 +[mk-app] #218 >= #216 #28 +[assign] #214 justification -1: 39 24 +[attach-enode] #214 0 +[attach-enode] #215 0 +[attach-enode] #216 0 +[assign] #217 justification -1: 65 +[assign] #218 justification -1: 65 +[mk-app] #219 = #40 #55 +[attach-meaning] #72 arith (- 1) +[mk-app] #220 * #72 #55 +[mk-app] #221 + #40 #220 +[mk-app] #222 <= #221 #28 +[mk-app] #223 >= #221 #28 +[assign] #219 justification -1: 24 39 +[attach-enode] #219 0 +[attach-enode] #220 0 +[attach-enode] #221 0 +[assign] #222 justification -1: 68 +[assign] #223 justification -1: 68 +[assign] #178 clause 41 -40 +[assign] #173 clause 42 -40 +[assign] #181 clause 45 -42 +[mk-app] #224 = #201 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #206 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #211 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #216 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #221 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[mk-app] #224 = #35 #194 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #224 +[end-of-instance] +[push] 2 +[assign] (not #152) decision axiom +[push] 3 +[assign] (not #142) decision axiom +[assign] (not #133) clause -21 22 +[push] 4 +[assign] (not #182) decision axiom +[push] 5 +[assign] (not #187) decision axiom +[push] 6 +[assign] (not #171) decision axiom +[assign] #170 clause 37 38 +[assign] (not #161) clause -36 38 +[push] 7 +[assign] (not #180) decision axiom +[push] 8 +[assign] (not #159) decision axiom +[mk-app] #224 = #172 #226 +[attach-enode] #224 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #227 * #108 #226 +[mk-app] #229 + #172 #227 +[mk-app] #230 <= #229 #23 +[mk-app] #231 >= #229 #23 +[attach-enode] #227 0 +[attach-enode] #229 0 +[push] 9 +[assign] #224 decision axiom +[assign] #230 clause 72 -71 +[assign] #231 clause 73 -71 +[mk-app] #232 = #125 #183 +[attach-meaning] #72 arith (- 1) +[mk-app] #233 * #72 #183 +[mk-app] #234 + #125 #233 +[mk-app] #235 <= #234 #28 +[mk-app] #236 >= #234 #28 +[assign] #232 justification -1: 71 24 39 24 53 54 26 27 +[attach-enode] #232 0 +[attach-enode] #233 0 +[attach-enode] #234 0 +[assign] #235 justification -1: 74 +[assign] #236 justification -1: 74 +[mk-app] #237 = #229 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #237 +[end-of-instance] +[mk-app] #237 = #234 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #237 +[end-of-instance] +[mk-app] #237 <= #54 #23 +[assign] #237 justification -1: -28 +[mk-app] #238 not #237 +[mk-app] #239 or #237 #238 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #239 +[end-of-instance] +[mk-app] #238 not #151 +[mk-app] #239 or #151 #238 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #239 +[end-of-instance] +[mk-app] #238 <= #156 #23 +[assign] #238 justification -1: 41 42 +[resolve-process] true +[resolve-lit] 0 (not #238) +[resolve-lit] 8 (not #202) +[resolve-lit] 3 #171 +[resolve-lit] 9 (not #199) +[resolve-lit] 9 #151 +[resolve-lit] 0 (not #230) +[resolve-lit] 8 (not #178) +[resolve-process] (not #238) +[resolve-lit] 8 (not #173) +[conflict] (not #230) (not #202) #171 #151 (not #178) (not #173) +[pop] 3 10 +[attach-enode] #227 0 +[attach-enode] #229 0 +[assign] (not #230) clause -71 38 -57 28 -41 -42 +[resolve-process] true +[resolve-lit] 5 (not #173) +[resolve-lit] 6 (not #228) +[resolve-lit] 0 #230 +[conflict] #230 (not #173) (not #228) +[pop] 5 7 +[attach-enode] #227 0 +[attach-enode] #229 0 +[assign] #230 clause 71 -42 -55 +[assign] #171 clause 38 -71 -57 28 -41 -42 +[push] 2 +[assign] (not #152) decision axiom +[push] 3 +[assign] (not #142) decision axiom +[assign] (not #133) clause -21 22 +[push] 4 +[assign] (not #182) decision axiom +[push] 5 +[assign] (not #187) decision axiom +[push] 6 +[assign] (not #180) decision axiom +[push] 7 +[assign] (not #170) decision axiom +[assign] (not #161) clause -36 37 +[push] 8 +[assign] (not #159) decision axiom +[mk-app] #224 = #197 #226 +[attach-enode] #224 0 +[attach-meaning] #108 arith (- 1) +[mk-app] #231 + #197 #227 +[mk-app] #232 <= #231 #23 +[mk-app] #233 >= #231 #23 +[attach-enode] #231 0 +[push] 9 +[assign] #232 decision axiom +[mk-app] #234 = #231 #229 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #234 +[end-of-instance] +[push] 10 +[assign] #224 decision axiom +[assign] #233 clause 74 -72 +[mk-app] #234 = #125 #183 +[attach-meaning] #72 arith (- 1) +[mk-app] #235 * #72 #183 +[mk-app] #236 + #125 #235 +[mk-app] #237 <= #236 #28 +[mk-app] #238 >= #236 #28 +[assign] #234 justification -1: 72 24 53 54 54 53 +[attach-enode] #234 0 +[attach-enode] #235 0 +[attach-enode] #236 0 +[assign] #237 justification -1: 75 +[assign] #238 justification -1: 75 +[mk-app] #239 = #231 #23 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #239 +[end-of-instance] +[mk-app] #239 = #23 #229 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #239 +[end-of-instance] +[mk-app] #239 = #236 #28 +[inst-discovered] theory-solving 0000000000000000 arith# +[instance] 0000000000000000 #239 +[end-of-instance] +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #72 arith (- 1) +[attach-meaning] #72 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #72 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #72 arith (- 1) +[attach-meaning] #72 arith (- 1) +[attach-meaning] #108 arith (- 1) +[attach-meaning] #108 arith (- 1) +[mk-app] #239 Int +[attach-meaning] #239 arith 2 +[mk-app] #240 Int +[attach-meaning] #240 arith 3 +[mk-app] #241 Real +[attach-meaning] #241 arith 2 +[mk-app] #242 Real +[attach-meaning] #242 arith 3 +[mk-app] #243 Int +[attach-meaning] #243 arith 4 +[mk-app] #244 Int +[attach-meaning] #244 arith 5 +[mk-app] #245 Int +[attach-meaning] #245 arith 6 +[mk-app] #246 Int +[attach-meaning] #246 arith 7 +[mk-app] #247 Int +[attach-meaning] #247 arith 8 +[mk-app] #248 Int +[attach-meaning] #248 arith 9 +[mk-app] #249 Int +[attach-meaning] #249 arith 10 +[mk-app] #250 Real +[attach-meaning] #250 arith 4 +[mk-app] #251 Real +[attach-meaning] #251 arith 5 +[mk-app] #252 Int +[attach-meaning] #252 arith 11 +[mk-app] #253 Real +[attach-meaning] #253 arith 6 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #5 bv #b1 +[attach-meaning] #6 bv #b0 +[attach-meaning] #72 arith (- 1) +[attach-meaning] #72 arith (- 1) +[mk-app] #109 <= #28 #72 +[mk-app] #110 not #109 +[attach-meaning] #72 arith (- 1) +[mk-app] #115 <= #96 #28 +[mk-app] #116 not #115 +[attach-meaning] #72 arith (- 1) +[mk-app] #118 <= #241 #96 +[mk-app] #120 not #118 +[attach-meaning] #72 arith (- 1) +[attach-meaning] #72 arith (- 1) +[mk-app] #155 <= #28 #28 +[mk-app] #130 not #155 +[eof] From 75a9038aa295c106d53271653ce72f24605b83f9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Jul 2023 16:54:26 -0700 Subject: [PATCH 022/428] add missing dependencies in rup Signed-off-by: Nikolaj Bjorner --- src/sat/sat_proof_trim.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index d47819921..a9f36b818 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -278,10 +278,13 @@ namespace sat { auto& [clauses, id, in_core] = m_clauses.find(m_clause); in_core = true; m_result.back().second.push_back(id); - if (l != null_literal && s.lvl(l) == 0) { + if (l != null_literal && s.lvl(l) == 0 && m_clause.size() > 1) { m_clause.reset(); m_clause.push_back(l); - m_clauses.insert_if_not_there(m_clause, {{}, 0, true }).m_in_core = true; + auto& [clauses, id, in_core] = m_clauses.insert_if_not_there(m_clause, {{}, 0, true }); + in_core = true; + if (id != 0) + m_result.back().second.push_back(id); } } From 0a91465e13429008529e0053aac2b8a7ca930c6e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 16 Jul 2023 18:40:53 -1000 Subject: [PATCH 023/428] comment out debug output --- src/ast/euf/euf_etable.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ast/euf/euf_etable.cpp b/src/ast/euf/euf_etable.cpp index 6aed46f64..17be983fe 100644 --- a/src/ast/euf/euf_etable.cpp +++ b/src/ast/euf/euf_etable.cpp @@ -203,7 +203,7 @@ namespace euf { SASSERT(n->num_args() > 0); enode * n_prime; void * t = get_table(n); - verbose_stream() << "insert " << n << "\n"; + //verbose_stream() << "insert " << n << "\n"; switch (static_cast(GET_TAG(t))) { case UNARY: n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); @@ -225,10 +225,10 @@ namespace euf { SASSERT(n->num_args() > 0); void * t = get_table(n); static unsigned count = 0; - verbose_stream() << "erase " << (++count) << " " << n << "\n"; - if (count == 10398) { - verbose_stream() << "here\n"; - } + //verbose_stream() << "erase " << (++count) << " " << n << "\n"; + // if (count == 10398) { + // verbose_stream() << "here\n"; + // } switch (static_cast(GET_TAG(t))) { case UNARY: UNTAG(unary_table*, t)->erase(n); From 013d5dc4db26ae6d1a83cfe14fe50047352798bf Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 17 Jul 2023 08:45:18 +0100 Subject: [PATCH 024/428] remove garbage file --- z3.log | 1206 -------------------------------------------------------- 1 file changed, 1206 deletions(-) delete mode 100644 z3.log diff --git a/z3.log b/z3.log deleted file mode 100644 index 54f98cda4..000000000 --- a/z3.log +++ /dev/null @@ -1,1206 +0,0 @@ -[tool-version] Z3 4.12.3 -[mk-app] #1 true -[mk-app] #2 false -[mk-app] #1 true -[mk-app] #2 false -[mk-app] #3 pi -[mk-app] #4 euler -[mk-var] datatype#0 0 -[mk-var] datatype#1 1 -[mk-app] datatype#2 insert datatype#0 datatype#1 -[mk-app] datatype#3 pattern datatype#2 -[mk-app] datatype#4 head datatype#2 -[mk-app] datatype#5 = datatype#0 datatype#4 -[mk-quant] datatype#6 constructor_accessor_axiom 2 datatype#3 datatype#5 -[attach-var-names] datatype#6 (;k!0) (;List) -[mk-app] datatype#7 tail datatype#2 -[mk-app] datatype#8 = datatype#1 datatype#7 -[mk-quant] datatype#9 constructor_accessor_axiom 2 datatype#3 datatype#8 -[attach-var-names] datatype#9 (;k!0) (;List) -[mk-app] #5 bv -[attach-meaning] #5 bv #b1 -[mk-app] #6 bv -[attach-meaning] #6 bv #b0 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #6 bv #b0 -[mk-var] #7 0 -[mk-var] #8 1 -[mk-var] #9 2 -[mk-var] #10 3 -[mk-var] #11 4 -[mk-var] #12 5 -[mk-var] #13 6 -[mk-var] #14 7 -[mk-var] #15 8 -[mk-var] #16 9 -[mk-var] #17 10 -[mk-var] #18 11 -[mk-var] #19 12 -[mk-var] #20 13 -[mk-var] #21 14 -[mk-app] #22 + #15 #13 -[attach-enode] #1 0 -[attach-enode] #2 0 -[mk-app] #23 Int -[attach-meaning] #23 arith 0 -[mk-app] #24 d -[mk-app] #25 to_real #23 -[mk-app] #26 = #25 #24 -[mk-app] #27 not #26 -[mk-app] #28 Real -[attach-meaning] #28 arith 0 -[inst-discovered] theory-solving 0000000000000000 arith# ; #25 -[mk-app] #29 = #25 #28 -[instance] 0000000000000000 #29 -[attach-enode] #29 0 -[end-of-instance] -[mk-app] #29 Real -[attach-meaning] #29 arith (- 1) -[mk-app] #30 * #29 #24 -[mk-app] #31 = #24 #28 -[mk-app] #29 = #28 #24 -[inst-discovered] theory-solving 0000000000000000 arith# ; #29 -[mk-app] #30 = #29 #31 -[instance] 0000000000000000 #30 -[attach-enode] #30 0 -[end-of-instance] -[mk-app] #29 not #31 -[mk-app] #30 c -[mk-app] #32 f -[mk-app] #33 = #30 #32 -[mk-app] #34 b -[mk-app] #35 g -[mk-app] #36 to_int #34 -[mk-app] #37 to_int #35 -[mk-app] #38 mod #36 #37 -[mk-app] #39 e -[mk-app] #40 to_real #38 -[mk-app] #41 * #40 #39 -[mk-app] #42 + #41 -[mk-app] #43 * #42 -[mk-app] #44 a -[mk-app] #45 < #43 #28 -[mk-app] #46 < #28 #39 -[mk-app] #47 < #39 #44 -[mk-app] #48 and #45 #46 #47 -[mk-app] #49 <= #28 #34 -[mk-app] #50 <= #34 #35 -[mk-app] #51 and #49 #50 -[mk-app] #52 and #33 #48 #51 -[mk-app] #53 to_int #32 -[mk-app] #54 mod #36 #53 -[mk-app] #55 to_real #54 -[mk-app] #56 + #39 #55 -[mk-app] #57 * #56 -[mk-app] #58 < #25 #57 -[mk-app] #59 < #57 #25 -[mk-app] #60 and #58 #59 -[mk-app] #61 = #52 #60 -[mk-app] #62 not #61 -[mk-app] #63 * #39 #40 -[inst-discovered] theory-solving 0000000000000000 arith# ; #41 -[mk-app] #64 = #41 #63 -[instance] 0000000000000000 #64 -[attach-enode] #64 0 -[end-of-instance] -[mk-app] #64 + #63 -[inst-discovered] theory-solving 0000000000000000 arith# ; #64 -[mk-app] #65 = #64 #63 -[instance] 0000000000000000 #65 -[attach-enode] #65 0 -[end-of-instance] -[mk-app] #64 * #63 -[inst-discovered] theory-solving 0000000000000000 arith# ; #64 -[mk-app] #65 = #64 #63 -[instance] 0000000000000000 #65 -[attach-enode] #65 0 -[end-of-instance] -[mk-app] #64 <= #28 #63 -[mk-app] #65 not #64 -[mk-app] #66 < #63 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #66 -[mk-app] #67 = #66 #65 -[instance] 0000000000000000 #67 -[attach-enode] #67 0 -[end-of-instance] -[mk-app] #66 Real -[attach-meaning] #66 arith (- 1) -[mk-app] #67 * #66 #63 -[mk-app] #68 >= #63 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #64 -[mk-app] #66 = #64 #68 -[instance] 0000000000000000 #66 -[attach-enode] #66 0 -[end-of-instance] -[mk-app] #66 not #68 -[mk-app] #67 <= #39 #28 -[mk-app] #69 not #67 -[inst-discovered] theory-solving 0000000000000000 arith# ; #46 -[mk-app] #70 = #46 #69 -[instance] 0000000000000000 #70 -[attach-enode] #70 0 -[end-of-instance] -[mk-app] #70 <= #44 #39 -[mk-app] #71 not #70 -[inst-discovered] theory-solving 0000000000000000 arith# ; #47 -[mk-app] #72 = #47 #71 -[instance] 0000000000000000 #72 -[attach-enode] #72 0 -[end-of-instance] -[mk-app] #72 Real -[attach-meaning] #72 arith (- 1) -[mk-app] #73 * #72 #39 -[mk-app] #74 + #73 #44 -[attach-meaning] #72 arith (- 1) -[mk-app] #75 * #72 #44 -[mk-app] #76 + #39 #75 -[mk-app] #73 >= #76 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #70 -[mk-app] #74 = #70 #73 -[instance] 0000000000000000 #74 -[attach-enode] #74 0 -[end-of-instance] -[mk-app] #74 not #73 -[attach-meaning] #72 arith (- 1) -[mk-app] #77 * #72 #34 -[mk-app] #78 >= #34 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #49 -[mk-app] #77 = #49 #78 -[instance] 0000000000000000 #77 -[attach-enode] #77 0 -[end-of-instance] -[attach-meaning] #72 arith (- 1) -[mk-app] #77 * #72 #35 -[mk-app] #79 + #34 #77 -[mk-app] #80 <= #79 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #50 -[mk-app] #81 = #50 #80 -[instance] 0000000000000000 #81 -[attach-enode] #81 0 -[end-of-instance] -[mk-app] #81 and #33 #66 #69 #74 #78 #80 -[inst-discovered] theory-solving 0000000000000000 arith# ; #25 -[mk-app] #82 = #25 #28 -[instance] 0000000000000000 #82 -[attach-enode] #82 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# ; #57 -[mk-app] #82 = #57 #56 -[instance] 0000000000000000 #82 -[attach-enode] #82 0 -[end-of-instance] -[mk-app] #82 <= #56 #28 -[mk-app] #83 not #82 -[mk-app] #84 < #28 #56 -[inst-discovered] theory-solving 0000000000000000 arith# ; #84 -[mk-app] #85 = #84 #83 -[instance] 0000000000000000 #85 -[attach-enode] #85 0 -[end-of-instance] -[mk-app] #84 <= #28 #56 -[mk-app] #85 not #84 -[mk-app] #86 < #56 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #86 -[mk-app] #87 = #86 #85 -[instance] 0000000000000000 #87 -[attach-enode] #87 0 -[end-of-instance] -[attach-meaning] #72 arith (- 1) -[mk-app] #86 * #72 #39 -[attach-meaning] #72 arith (- 1) -[mk-app] #87 * #72 #55 -[mk-app] #88 + #86 #87 -[mk-app] #86 >= #56 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #84 -[mk-app] #87 = #84 #86 -[instance] 0000000000000000 #87 -[attach-enode] #87 0 -[end-of-instance] -[mk-app] #87 not #86 -[mk-app] #88 and #83 #87 -[mk-app] #89 = #81 #88 -[mk-app] #90 not #81 -[mk-app] #91 not #89 -[inst-discovered] theory-solving 0000000000000000 basic# ; #91 -[mk-app] #90 = #91 #91 -[instance] 0000000000000000 #90 -[attach-enode] #90 0 -[end-of-instance] -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[inst-discovered] theory-solving 0000000000000000 arith# ; #25 -[mk-app] #90 = #25 #28 -[instance] 0000000000000000 #90 -[attach-enode] #90 0 -[end-of-instance] -[mk-app] #90 = #28 #24 -[mk-app] #92 not #90 -[inst-discovered] theory-solving 0000000000000000 arith# ; #41 -[mk-app] #93 = #41 #63 -[instance] 0000000000000000 #93 -[attach-enode] #93 0 -[end-of-instance] -[mk-app] #93 + #63 -[inst-discovered] theory-solving 0000000000000000 arith# ; #93 -[mk-app] #94 = #93 #63 -[instance] 0000000000000000 #94 -[attach-enode] #94 0 -[end-of-instance] -[mk-app] #93 * #63 -[inst-discovered] theory-solving 0000000000000000 arith# ; #93 -[mk-app] #94 = #93 #63 -[instance] 0000000000000000 #94 -[attach-enode] #94 0 -[end-of-instance] -[mk-app] #93 < #63 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #93 -[mk-app] #94 = #93 #65 -[instance] 0000000000000000 #94 -[attach-enode] #94 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# ; #46 -[mk-app] #93 = #46 #69 -[instance] 0000000000000000 #93 -[attach-enode] #93 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# ; #47 -[mk-app] #93 = #47 #71 -[instance] 0000000000000000 #93 -[attach-enode] #93 0 -[end-of-instance] -[mk-app] #93 and #33 #65 #69 #71 #49 #50 -[inst-discovered] theory-solving 0000000000000000 arith# ; #57 -[mk-app] #94 = #57 #56 -[instance] 0000000000000000 #94 -[attach-enode] #94 0 -[end-of-instance] -[mk-app] #94 < #28 #56 -[inst-discovered] theory-solving 0000000000000000 arith# ; #94 -[mk-app] #95 = #94 #83 -[instance] 0000000000000000 #95 -[attach-enode] #95 0 -[end-of-instance] -[mk-app] #94 < #56 #28 -[inst-discovered] theory-solving 0000000000000000 arith# ; #94 -[mk-app] #95 = #94 #85 -[instance] 0000000000000000 #95 -[attach-enode] #95 0 -[end-of-instance] -[mk-app] #94 and #83 #85 -[mk-app] #95 = #93 #94 -[mk-app] #96 not #93 -[mk-app] #97 not #95 -[inst-discovered] theory-solving 0000000000000000 basic# ; #97 -[mk-app] #96 = #97 #97 -[instance] 0000000000000000 #96 -[attach-enode] #96 0 -[end-of-instance] -[mk-app] #96 not #93 -[inst-discovered] theory-solving 0000000000000000 basic# ; #97 -[mk-app] #96 = #97 #97 -[instance] 0000000000000000 #96 -[attach-enode] #96 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# ; #56 -[mk-app] #96 = #56 #56 -[instance] 0000000000000000 #96 -[attach-enode] #96 0 -[end-of-instance] -[mk-app] #96 not #93 -[inst-discovered] theory-solving 0000000000000000 basic# ; #97 -[mk-app] #96 = #97 #97 -[instance] 0000000000000000 #96 -[attach-enode] #96 0 -[end-of-instance] -[mk-app] #96 Real -[attach-meaning] #96 arith 1 -[mk-app] #98 + #28 #96 -[mk-app] #99 k!0 -[mk-app] #100 if #99 #28 #98 -[mk-app] #101 not #99 -[mk-app] #92 + #32 #96 -[mk-app] #102 k!1 -[mk-app] #103 if #102 #32 #92 -[mk-app] #104 k!2 -[mk-app] #105 + #39 #96 -[mk-app] #106 if #104 #39 #105 -[mk-app] #107 not #104 -[mk-app] #108 and #102 #65 #69 #107 #49 #50 -[mk-app] #109 = #108 #94 -[mk-app] #110 not #109 -[mk-app] #93 k!3 -[mk-app] #95 not #93 -[mk-app] #97 k!4 -[mk-app] #111 not #97 -[mk-app] #112 and #102 #65 #69 #97 #49 #50 -[mk-app] #113 = #112 #94 -[mk-app] #114 not #113 -[attach-meaning] #6 bv #b0 -[attach-enode] #1 0 -[attach-enode] #2 0 -[attach-meaning] #72 arith (- 1) -[mk-app] #107 * #72 #63 -[inst-discovered] theory-solving 0000000000000000 arith# ; #64 -[mk-app] #107 = #64 #68 -[instance] 0000000000000000 #107 -[attach-enode] #107 0 -[end-of-instance] -[attach-meaning] #72 arith (- 1) -[mk-app] #107 * #72 #34 -[inst-discovered] theory-solving 0000000000000000 arith# ; #49 -[mk-app] #107 = #49 #78 -[instance] 0000000000000000 #107 -[attach-enode] #107 0 -[end-of-instance] -[attach-meaning] #72 arith (- 1) -[inst-discovered] theory-solving 0000000000000000 arith# ; #50 -[mk-app] #107 = #50 #80 -[instance] 0000000000000000 #107 -[attach-enode] #107 0 -[end-of-instance] -[mk-app] #107 and #102 #66 #69 #97 #78 #80 -[attach-meaning] #72 arith (- 1) -[mk-app] #101 * #72 #39 -[attach-meaning] #72 arith (- 1) -[mk-app] #90 * #72 #55 -[mk-app] #108 + #101 #90 -[inst-discovered] theory-solving 0000000000000000 arith# ; #84 -[mk-app] #101 = #84 #86 -[instance] 0000000000000000 #101 -[attach-enode] #101 0 -[end-of-instance] -[mk-app] #101 = #107 #88 -[mk-app] #90 not #107 -[mk-app] #108 not #101 -[inst-discovered] theory-solving 0000000000000000 basic# ; #108 -[mk-app] #90 = #108 #108 -[instance] 0000000000000000 #90 -[attach-enode] #90 0 -[end-of-instance] -[begin-check] 0 -[mk-app] #90 Int -[attach-meaning] #90 arith 1 -[attach-enode] #90 0 -[attach-enode] #96 0 -[attach-enode] #23 0 -[attach-enode] #28 0 -[mk-app] #109 not #107 -[inst-discovered] theory-solving 0000000000000000 basic# ; #108 -[mk-app] #109 = #108 #108 -[instance] 0000000000000000 #109 -[attach-enode] #109 0 -[end-of-instance] -[mk-app] #109 not #107 -[inst-discovered] theory-solving 0000000000000000 basic# ; #108 -[mk-app] #109 = #108 #108 -[instance] 0000000000000000 #109 -[attach-enode] #109 0 -[end-of-instance] -[mk-app] #109 not #107 -[inst-discovered] theory-solving 0000000000000000 basic# ; #108 -[mk-app] #109 = #108 #108 -[instance] 0000000000000000 #109 -[attach-enode] #109 0 -[end-of-instance] -[mk-app] #109 not #102 -[mk-app] #110 not #78 -[mk-app] #115 not #80 -[mk-app] #116 or #67 #68 #109 #110 #111 #115 -[mk-app] #117 not #116 -[inst-discovered] theory-solving 0000000000000000 basic# ; #107 -[mk-app] #118 = #107 #117 -[instance] 0000000000000000 #118 -[attach-enode] #118 0 -[end-of-instance] -[mk-app] #118 or #82 #86 -[mk-app] #119 not #118 -[inst-discovered] theory-solving 0000000000000000 basic# ; #88 -[mk-app] #120 = #88 #119 -[instance] 0000000000000000 #120 -[attach-enode] #120 0 -[end-of-instance] -[mk-app] #120 = #116 #118 -[mk-app] #121 = #117 #119 -[inst-discovered] theory-solving 0000000000000000 basic# ; #121 -[mk-app] #122 = #121 #120 -[instance] 0000000000000000 #122 -[attach-enode] #122 0 -[end-of-instance] -[mk-app] #117 not #116 -[mk-app] #121 not #120 -[inst-discovered] theory-solving 0000000000000000 basic# ; #121 -[mk-app] #117 = #121 #121 -[instance] 0000000000000000 #117 -[attach-enode] #117 0 -[end-of-instance] -[mk-app] #107 not #116 -[inst-discovered] theory-solving 0000000000000000 basic# ; #121 -[mk-app] #107 = #121 #121 -[instance] 0000000000000000 #107 -[attach-enode] #107 0 -[end-of-instance] -[mk-app] #107 not #116 -[inst-discovered] theory-solving 0000000000000000 basic# ; #121 -[mk-app] #107 = #121 #121 -[instance] 0000000000000000 #107 -[attach-enode] #107 0 -[end-of-instance] -[assign] #93 justification -1: -[attach-enode] #39 0 -[attach-enode] #34 0 -[attach-enode] #36 0 -[attach-enode] #35 0 -[attach-enode] #37 0 -[attach-enode] #38 0 -[mk-app] #119 mod #36 #23 -[mk-app] #107 mod0 #36 #23 -[mk-app] #101 = #107 #119 -[attach-enode] #107 0 -[attach-enode] #119 0 -[attach-enode] #101 0 -[mk-app] #108 Int -[attach-meaning] #108 arith (- 1) -[mk-app] #117 * #108 #119 -[mk-app] #122 + #107 #117 -[mk-app] #123 <= #122 #23 -[mk-app] #124 >= #122 #23 -[attach-enode] #108 0 -[attach-enode] #117 0 -[attach-enode] #122 0 -[mk-app] #125 to_real #36 -[mk-app] #126 - #125 #34 -[mk-app] #127 <= #126 #28 -[mk-app] #128 - #34 #125 -[mk-app] #129 >= #128 #96 -[attach-enode] #125 0 -[attach-enode] #126 0 -[attach-enode] #127 0 -[attach-enode] #128 0 -[attach-enode] #129 0 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #127 -[assign] #127 justification -1: 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #129 -[assign] (not #129) justification -1: 0 -[end-of-instance] -[assign] #101 axiom -[attach-enode] #40 0 -[attach-enode] #63 0 -[attach-enode] #72 0 -[attach-enode] #77 0 -[attach-enode] #79 0 -[attach-enode] #32 0 -[attach-enode] #53 0 -[attach-enode] #54 0 -[attach-enode] #55 0 -[attach-enode] #56 0 -[assign] (not #120) justification -1: -[assign] #124 bin 4 -[assign] #123 bin 4 -[mk-app] #130 = #122 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #130 -[end-of-instance] -[push] 0 -[assign] (not #86) decision axiom -[assign] #82 bin -15 -[assign] #118 bin 14 -[assign] (not #116) clause -13 -16 -[assign] #80 bin -13 -[assign] #97 bin -13 -[assign] #78 bin -13 -[assign] #102 bin -13 -[assign] (not #68) bin -13 -[assign] (not #67) bin -13 -[mk-app] #130 div #36 #53 -[mk-app] #131 * #53 #130 -[mk-app] #132 + #131 #54 -[mk-app] #133 = #36 #132 -[attach-enode] #130 0 -[mk-app] #134 div0 #36 #53 -[mk-app] #135 = #130 #134 -[attach-enode] #134 0 -[attach-enode] #135 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #136 * #108 #134 -[mk-app] #137 + #130 #136 -[mk-app] #138 <= #137 #23 -[mk-app] #139 >= #137 #23 -[attach-enode] #136 0 -[attach-enode] #137 0 -[assign] #135 axiom -[attach-enode] #131 0 -[attach-enode] #132 0 -[attach-enode] #133 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #140 * #108 #132 -[mk-app] #141 + #36 #140 -[mk-app] #142 <= #141 #23 -[mk-app] #143 >= #141 #23 -[attach-enode] #140 0 -[attach-enode] #141 0 -[mk-app] #144 - #53 -[mk-app] #145 >= #53 #23 -[mk-app] #146 if #145 #53 #144 -[attach-meaning] #108 arith (- 1) -[mk-app] #147 - #54 #146 -[mk-app] #148 = #53 #23 -[attach-enode] #148 0 -[mk-app] #149 = #23 #53 -[mk-app] #150 <= #53 #23 -[attach-enode] #149 0 -[mk-app] #151 >= #54 #23 -[attach-enode] #151 0 -[mk-app] #152 <= #147 #108 -[attach-enode] #145 0 -[attach-enode] #144 0 -[mk-app] #153 = #53 #146 -[mk-app] #154 = #144 #146 -[attach-enode] #146 0 -[attach-enode] #153 0 -[attach-enode] #154 0 -[attach-enode] #147 0 -[attach-enode] #152 0 -[mk-app] #155 - #36 #131 -[mk-app] #156 >= #155 #23 -[attach-meaning] #108 arith (- 1) -[mk-app] #157 * #108 #131 -[mk-app] #158 + #36 #157 -[inst-discovered] theory-solving 0000000000000000 arith# ; #155 -[mk-app] #159 = #155 #158 -[instance] 0000000000000000 #159 -[attach-enode] #159 0 -[end-of-instance] -[mk-app] #159 >= #158 #23 -[attach-enode] #157 0 -[attach-enode] #158 0 -[attach-enode] #159 0 -[mk-app] #155 div #36 #37 -[mk-app] #156 * #37 #155 -[mk-app] #160 + #156 #38 -[mk-app] #161 = #36 #160 -[attach-enode] #155 0 -[mk-app] #162 div0 #36 #37 -[mk-app] #163 = #155 #162 -[attach-enode] #162 0 -[attach-enode] #163 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #164 * #108 #162 -[mk-app] #165 + #155 #164 -[mk-app] #166 <= #165 #23 -[mk-app] #167 >= #165 #23 -[attach-enode] #164 0 -[attach-enode] #165 0 -[assign] #163 axiom -[attach-enode] #156 0 -[attach-enode] #160 0 -[attach-enode] #161 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #168 * #108 #160 -[mk-app] #169 + #36 #168 -[mk-app] #170 <= #169 #23 -[mk-app] #171 >= #169 #23 -[attach-enode] #168 0 -[attach-enode] #169 0 -[mk-app] #172 - #37 -[mk-app] #173 >= #37 #23 -[mk-app] #174 if #173 #37 #172 -[attach-meaning] #108 arith (- 1) -[mk-app] #175 - #38 #174 -[mk-app] #176 = #37 #23 -[attach-enode] #176 0 -[mk-app] #177 = #23 #37 -[mk-app] #178 <= #37 #23 -[attach-enode] #177 0 -[mk-app] #179 >= #38 #23 -[attach-enode] #179 0 -[mk-app] #180 <= #175 #108 -[attach-enode] #173 0 -[attach-enode] #172 0 -[mk-app] #181 = #37 #174 -[mk-app] #182 = #172 #174 -[attach-enode] #174 0 -[attach-enode] #181 0 -[attach-enode] #182 0 -[attach-enode] #175 0 -[attach-enode] #180 0 -[mk-app] #183 - #36 #156 -[mk-app] #184 >= #183 #23 -[attach-meaning] #108 arith (- 1) -[mk-app] #185 * #108 #156 -[mk-app] #186 + #36 #185 -[inst-discovered] theory-solving 0000000000000000 arith# ; #183 -[mk-app] #187 = #183 #186 -[instance] 0000000000000000 #187 -[attach-enode] #187 0 -[end-of-instance] -[mk-app] #187 >= #186 #23 -[attach-enode] #185 0 -[attach-enode] #186 0 -[attach-enode] #187 0 -[mk-app] #183 to_real #53 -[mk-app] #184 - #183 #32 -[mk-app] #188 <= #184 #28 -[mk-app] #189 - #32 #183 -[mk-app] #190 >= #189 #96 -[attach-enode] #183 0 -[attach-enode] #184 0 -[attach-enode] #188 0 -[attach-enode] #189 0 -[attach-enode] #190 0 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #188 -[assign] #188 justification -1: 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #190 -[assign] (not #190) justification -1: 0 -[end-of-instance] -[mk-app] #191 to_real #37 -[mk-app] #192 - #191 #35 -[mk-app] #193 <= #192 #28 -[mk-app] #194 - #35 #191 -[mk-app] #195 >= #194 #96 -[attach-enode] #191 0 -[attach-enode] #192 0 -[attach-enode] #193 0 -[attach-enode] #194 0 -[attach-enode] #195 0 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #193 -[assign] #193 justification -1: 0 -[end-of-instance] -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #195 -[assign] (not #195) justification -1: 0 -[end-of-instance] -[assign] (not #151) clause -28 -14 2 -[assign] #138 clause 19 -18 -[assign] #139 clause 20 -18 -[assign] #166 clause 34 -33 -[assign] #167 clause 35 -33 -[assign] #148 clause 24 28 -[assign] #149 justification -1: 24 -[mk-app] #196 = #54 #119 -[attach-meaning] #108 arith (- 1) -[mk-app] #197 + #54 #117 -[mk-app] #198 <= #197 #23 -[mk-app] #199 >= #197 #23 -[assign] #196 justification -1: 24 -[attach-enode] #196 0 -[attach-enode] #197 0 -[assign] #198 justification -1: 52 -[assign] #199 justification -1: 52 -[mk-app] #200 = #137 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[mk-app] #200 = #165 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[assign] #150 clause 26 -25 -[assign] #145 clause 27 -25 -[assign] #153 clause 30 -27 -[mk-app] #200 = #197 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[mk-app] #200 = #23 #144 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[mk-app] #200 = #28 #183 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[mk-app] #200 = #32 #189 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[assign] #154 justification -1: 30 24 26 27 -[push] 1 -[assign] (not #179) decision axiom -[assign] #176 clause 39 43 -[assign] #177 justification -1: 39 -[mk-app] #200 = #38 #119 -[attach-meaning] #108 arith (- 1) -[mk-app] #201 + #38 #117 -[mk-app] #202 <= #201 #23 -[mk-app] #203 >= #201 #23 -[assign] #200 justification -1: 39 -[attach-enode] #200 0 -[attach-enode] #201 0 -[assign] #202 justification -1: 55 -[assign] #203 justification -1: 55 -[mk-app] #204 = #130 #155 -[attach-meaning] #108 arith (- 1) -[mk-app] #205 * #108 #155 -[mk-app] #206 + #130 #205 -[mk-app] #207 <= #206 #23 -[mk-app] #208 >= #206 #23 -[assign] #204 justification -1: 39 24 -[attach-enode] #204 0 -[attach-enode] #205 0 -[attach-enode] #206 0 -[assign] #207 justification -1: 58 -[assign] #208 justification -1: 58 -[mk-app] #209 = #144 #172 -[attach-meaning] #108 arith (- 1) -[mk-app] #210 * #108 #172 -[mk-app] #211 + #144 #210 -[mk-app] #212 <= #211 #23 -[mk-app] #213 >= #211 #23 -[assign] #209 justification -1: 39 24 -[attach-enode] #209 0 -[attach-enode] #210 0 -[attach-enode] #211 0 -[assign] #212 justification -1: 61 -[assign] #213 justification -1: 61 -[mk-app] #214 = #183 #191 -[attach-meaning] #72 arith (- 1) -[mk-app] #215 * #72 #191 -[mk-app] #216 + #183 #215 -[mk-app] #217 <= #216 #28 -[mk-app] #218 >= #216 #28 -[assign] #214 justification -1: 39 24 -[attach-enode] #214 0 -[attach-enode] #215 0 -[attach-enode] #216 0 -[assign] #217 justification -1: 64 -[assign] #218 justification -1: 64 -[mk-app] #219 = #40 #55 -[attach-meaning] #72 arith (- 1) -[mk-app] #220 * #72 #55 -[mk-app] #221 + #40 #220 -[mk-app] #222 <= #221 #28 -[mk-app] #223 >= #221 #28 -[assign] #219 justification -1: 24 39 -[attach-enode] #219 0 -[attach-enode] #220 0 -[attach-enode] #221 0 -[assign] #222 justification -1: 67 -[assign] #223 justification -1: 67 -[assign] #178 clause 41 -40 -[assign] #173 clause 42 -40 -[assign] #181 clause 45 -42 -[mk-app] #224 = #201 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #206 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #211 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #216 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #221 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #35 #194 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[push] 2 -[assign] (not #152) decision axiom -[push] 3 -[assign] (not #143) decision axiom -[assign] #142 clause 22 23 -[assign] (not #133) clause -21 23 -[assign] (not #159) clause -32 23 -14 2 -[push] 4 -[assign] (not #182) decision axiom -[push] 5 -[assign] (not #187) decision axiom -[push] 6 -[assign] (not #171) decision axiom -[assign] #170 clause 37 38 -[assign] (not #161) clause -36 38 -[push] 7 -[assign] (not #180) decision axiom -[mk-app] #224 = #36 #197 -[attach-enode] #224 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #225 * #108 #197 -[mk-app] #226 + #36 #225 -[mk-app] #227 <= #226 #23 -[mk-app] #228 >= #226 #23 -[attach-enode] #225 0 -[attach-enode] #226 0 -[push] 8 -[assign] #224 decision axiom -[assign] #227 clause 71 -70 -[assign] #228 clause 72 -70 -[mk-app] #229 = #125 #183 -[attach-meaning] #72 arith (- 1) -[mk-app] #230 * #72 #183 -[mk-app] #231 + #125 #230 -[mk-app] #232 <= #231 #28 -[mk-app] #233 >= #231 #28 -[assign] #229 justification -1: 70 24 54 53 -[attach-enode] #229 0 -[attach-enode] #230 0 -[attach-enode] #231 0 -[assign] #232 justification -1: 73 -[assign] #233 justification -1: 73 -[mk-app] #234 = #226 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #234 -[end-of-instance] -[mk-app] #234 = #231 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #234 -[end-of-instance] -[mk-app] #234 not #151 -[mk-app] #235 or #151 #234 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #235 -[end-of-instance] -[mk-app] #234 <= #131 #23 -[assign] #234 justification -1: 26 27 -[mk-app] #235 <= #156 #23 -[assign] #235 justification -1: 41 42 -[resolve-process] true -[resolve-lit] 8 #67 -[resolve-lit] 8 (not #82) -[resolve-lit] 0 (not #234) -[resolve-lit] 5 #143 -[resolve-lit] 0 (not #228) -[resolve-lit] 8 (not #199) -[resolve-process] (not #234) -[resolve-lit] 8 (not #150) -[resolve-lit] 8 (not #145) -[conflict] (not #228) (not #82) #143 -[pop] 5 9 -[attach-enode] #225 0 -[attach-enode] #226 0 -[assign] (not #228) clause -70 23 -14 -[resolve-process] true -[resolve-lit] 3 (not #78) -[resolve-lit] 3 (not #198) -[resolve-lit] 0 #228 -[conflict] #228 (not #78) (not #198) -[pop] 3 4 -[attach-enode] #225 0 -[attach-enode] #226 0 -[assign] #228 clause 55 -10 -53 -[assign] #143 clause 23 -55 -14 -[mk-app] #200 = #226 #36 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #200 -[end-of-instance] -[push] 1 -[assign] (not #179) decision axiom -[assign] #176 clause 39 43 -[assign] #177 justification -1: 39 -[mk-app] #200 = #38 #119 -[attach-meaning] #108 arith (- 1) -[mk-app] #201 + #38 #117 -[mk-app] #202 <= #201 #23 -[mk-app] #203 >= #201 #23 -[assign] #200 justification -1: 39 -[attach-enode] #200 0 -[attach-enode] #201 0 -[assign] #202 justification -1: 56 -[assign] #203 justification -1: 56 -[mk-app] #204 = #130 #155 -[attach-meaning] #108 arith (- 1) -[mk-app] #205 * #108 #155 -[mk-app] #206 + #130 #205 -[mk-app] #207 <= #206 #23 -[mk-app] #208 >= #206 #23 -[assign] #204 justification -1: 39 24 -[attach-enode] #204 0 -[attach-enode] #205 0 -[attach-enode] #206 0 -[assign] #207 justification -1: 59 -[assign] #208 justification -1: 59 -[mk-app] #209 = #144 #172 -[attach-meaning] #108 arith (- 1) -[mk-app] #210 * #108 #172 -[mk-app] #211 + #144 #210 -[mk-app] #212 <= #211 #23 -[mk-app] #213 >= #211 #23 -[assign] #209 justification -1: 39 24 -[attach-enode] #209 0 -[attach-enode] #210 0 -[attach-enode] #211 0 -[assign] #212 justification -1: 62 -[assign] #213 justification -1: 62 -[mk-app] #214 = #183 #191 -[attach-meaning] #72 arith (- 1) -[mk-app] #215 * #72 #191 -[mk-app] #216 + #183 #215 -[mk-app] #217 <= #216 #28 -[mk-app] #218 >= #216 #28 -[assign] #214 justification -1: 39 24 -[attach-enode] #214 0 -[attach-enode] #215 0 -[attach-enode] #216 0 -[assign] #217 justification -1: 65 -[assign] #218 justification -1: 65 -[mk-app] #219 = #40 #55 -[attach-meaning] #72 arith (- 1) -[mk-app] #220 * #72 #55 -[mk-app] #221 + #40 #220 -[mk-app] #222 <= #221 #28 -[mk-app] #223 >= #221 #28 -[assign] #219 justification -1: 24 39 -[attach-enode] #219 0 -[attach-enode] #220 0 -[attach-enode] #221 0 -[assign] #222 justification -1: 68 -[assign] #223 justification -1: 68 -[assign] #178 clause 41 -40 -[assign] #173 clause 42 -40 -[assign] #181 clause 45 -42 -[mk-app] #224 = #201 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #206 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #211 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #216 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #221 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[mk-app] #224 = #35 #194 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #224 -[end-of-instance] -[push] 2 -[assign] (not #152) decision axiom -[push] 3 -[assign] (not #142) decision axiom -[assign] (not #133) clause -21 22 -[push] 4 -[assign] (not #182) decision axiom -[push] 5 -[assign] (not #187) decision axiom -[push] 6 -[assign] (not #171) decision axiom -[assign] #170 clause 37 38 -[assign] (not #161) clause -36 38 -[push] 7 -[assign] (not #180) decision axiom -[push] 8 -[assign] (not #159) decision axiom -[mk-app] #224 = #172 #226 -[attach-enode] #224 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #227 * #108 #226 -[mk-app] #229 + #172 #227 -[mk-app] #230 <= #229 #23 -[mk-app] #231 >= #229 #23 -[attach-enode] #227 0 -[attach-enode] #229 0 -[push] 9 -[assign] #224 decision axiom -[assign] #230 clause 72 -71 -[assign] #231 clause 73 -71 -[mk-app] #232 = #125 #183 -[attach-meaning] #72 arith (- 1) -[mk-app] #233 * #72 #183 -[mk-app] #234 + #125 #233 -[mk-app] #235 <= #234 #28 -[mk-app] #236 >= #234 #28 -[assign] #232 justification -1: 71 24 39 24 53 54 26 27 -[attach-enode] #232 0 -[attach-enode] #233 0 -[attach-enode] #234 0 -[assign] #235 justification -1: 74 -[assign] #236 justification -1: 74 -[mk-app] #237 = #229 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #237 -[end-of-instance] -[mk-app] #237 = #234 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #237 -[end-of-instance] -[mk-app] #237 <= #54 #23 -[assign] #237 justification -1: -28 -[mk-app] #238 not #237 -[mk-app] #239 or #237 #238 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #239 -[end-of-instance] -[mk-app] #238 not #151 -[mk-app] #239 or #151 #238 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #239 -[end-of-instance] -[mk-app] #238 <= #156 #23 -[assign] #238 justification -1: 41 42 -[resolve-process] true -[resolve-lit] 0 (not #238) -[resolve-lit] 8 (not #202) -[resolve-lit] 3 #171 -[resolve-lit] 9 (not #199) -[resolve-lit] 9 #151 -[resolve-lit] 0 (not #230) -[resolve-lit] 8 (not #178) -[resolve-process] (not #238) -[resolve-lit] 8 (not #173) -[conflict] (not #230) (not #202) #171 #151 (not #178) (not #173) -[pop] 3 10 -[attach-enode] #227 0 -[attach-enode] #229 0 -[assign] (not #230) clause -71 38 -57 28 -41 -42 -[resolve-process] true -[resolve-lit] 5 (not #173) -[resolve-lit] 6 (not #228) -[resolve-lit] 0 #230 -[conflict] #230 (not #173) (not #228) -[pop] 5 7 -[attach-enode] #227 0 -[attach-enode] #229 0 -[assign] #230 clause 71 -42 -55 -[assign] #171 clause 38 -71 -57 28 -41 -42 -[push] 2 -[assign] (not #152) decision axiom -[push] 3 -[assign] (not #142) decision axiom -[assign] (not #133) clause -21 22 -[push] 4 -[assign] (not #182) decision axiom -[push] 5 -[assign] (not #187) decision axiom -[push] 6 -[assign] (not #180) decision axiom -[push] 7 -[assign] (not #170) decision axiom -[assign] (not #161) clause -36 37 -[push] 8 -[assign] (not #159) decision axiom -[mk-app] #224 = #197 #226 -[attach-enode] #224 0 -[attach-meaning] #108 arith (- 1) -[mk-app] #231 + #197 #227 -[mk-app] #232 <= #231 #23 -[mk-app] #233 >= #231 #23 -[attach-enode] #231 0 -[push] 9 -[assign] #232 decision axiom -[mk-app] #234 = #231 #229 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #234 -[end-of-instance] -[push] 10 -[assign] #224 decision axiom -[assign] #233 clause 74 -72 -[mk-app] #234 = #125 #183 -[attach-meaning] #72 arith (- 1) -[mk-app] #235 * #72 #183 -[mk-app] #236 + #125 #235 -[mk-app] #237 <= #236 #28 -[mk-app] #238 >= #236 #28 -[assign] #234 justification -1: 72 24 53 54 54 53 -[attach-enode] #234 0 -[attach-enode] #235 0 -[attach-enode] #236 0 -[assign] #237 justification -1: 75 -[assign] #238 justification -1: 75 -[mk-app] #239 = #231 #23 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #239 -[end-of-instance] -[mk-app] #239 = #23 #229 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #239 -[end-of-instance] -[mk-app] #239 = #236 #28 -[inst-discovered] theory-solving 0000000000000000 arith# -[instance] 0000000000000000 #239 -[end-of-instance] -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #72 arith (- 1) -[attach-meaning] #72 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #72 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #72 arith (- 1) -[attach-meaning] #72 arith (- 1) -[attach-meaning] #108 arith (- 1) -[attach-meaning] #108 arith (- 1) -[mk-app] #239 Int -[attach-meaning] #239 arith 2 -[mk-app] #240 Int -[attach-meaning] #240 arith 3 -[mk-app] #241 Real -[attach-meaning] #241 arith 2 -[mk-app] #242 Real -[attach-meaning] #242 arith 3 -[mk-app] #243 Int -[attach-meaning] #243 arith 4 -[mk-app] #244 Int -[attach-meaning] #244 arith 5 -[mk-app] #245 Int -[attach-meaning] #245 arith 6 -[mk-app] #246 Int -[attach-meaning] #246 arith 7 -[mk-app] #247 Int -[attach-meaning] #247 arith 8 -[mk-app] #248 Int -[attach-meaning] #248 arith 9 -[mk-app] #249 Int -[attach-meaning] #249 arith 10 -[mk-app] #250 Real -[attach-meaning] #250 arith 4 -[mk-app] #251 Real -[attach-meaning] #251 arith 5 -[mk-app] #252 Int -[attach-meaning] #252 arith 11 -[mk-app] #253 Real -[attach-meaning] #253 arith 6 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #5 bv #b1 -[attach-meaning] #6 bv #b0 -[attach-meaning] #72 arith (- 1) -[attach-meaning] #72 arith (- 1) -[mk-app] #109 <= #28 #72 -[mk-app] #110 not #109 -[attach-meaning] #72 arith (- 1) -[mk-app] #115 <= #96 #28 -[mk-app] #116 not #115 -[attach-meaning] #72 arith (- 1) -[mk-app] #118 <= #241 #96 -[mk-app] #120 not #118 -[attach-meaning] #72 arith (- 1) -[attach-meaning] #72 arith (- 1) -[mk-app] #155 <= #28 #28 -[mk-app] #130 not #155 -[eof] From bfc37bd266d7931d4f8c7820139c935abf4fa468 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 17 Jul 2023 08:00:01 -1000 Subject: [PATCH 025/428] add to m_touched_rows only when bound propagation is required --- src/math/lp/lar_solver.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index dd4f10be8..4aecbf2cc 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -619,11 +619,10 @@ namespace lp { } void lar_solver::add_touched_row(unsigned rid) { - m_touched_rows.insert(rid); + if (m_settings.bound_propagation()) + m_touched_rows.insert(rid); } - - bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } From 3e74989a9d0a0e044ca577db314e0262554facd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Jul 2023 11:00:02 -0700 Subject: [PATCH 026/428] fixup dependencies for trim' Signed-off-by: Nikolaj Bjorner --- src/sat/sat_proof_trim.cpp | 28 ++++++++++++++++++++-------- src/sat/sat_proof_trim.h | 5 ++++- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index a9f36b818..56c4a1b7a 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -56,6 +56,7 @@ namespace sat { 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()}); + m_in_deps.reset(); if (is_initial) continue; conflict_analysis_core(cl, clp); @@ -219,6 +220,7 @@ namespace sat { } void proof_trim::add_dependency(literal lit) { + IF_VERBOSE(3, verbose_stream() << "add dependency " << lit << "\n"); bool_var v = lit.var(); if (m_propagated[v]) { // literal was propagated after assuming ~C if (!s.is_marked(v)) @@ -253,6 +255,12 @@ namespace sat { add_core(lit, j); } + void proof_trim::insert_dep(unsigned dep) { + if (m_in_deps.contains(dep)) + return; + m_in_deps.insert(dep); + m_result.back().second.push_back(dep); + } void proof_trim::add_core(literal l, justification j) { m_clause.reset(); @@ -277,14 +285,18 @@ namespace sat { IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n"); auto& [clauses, id, in_core] = m_clauses.find(m_clause); in_core = true; - m_result.back().second.push_back(id); - if (l != null_literal && s.lvl(l) == 0 && m_clause.size() > 1) { - m_clause.reset(); - m_clause.push_back(l); - auto& [clauses, id, in_core] = m_clauses.insert_if_not_there(m_clause, {{}, 0, true }); - in_core = true; - if (id != 0) - m_result.back().second.push_back(id); + insert_dep(id); + if (m_clause.size() > 1 && l != null_literal && s.lvl(l) == 0) { + for (auto lit : m_clause) { + if (s.lvl(lit) != 0) + continue; + m_clause2.reset(); + m_clause2.push_back(s.value(lit) == l_false ? ~lit : lit); + auto& [clauses, id, in_core] = m_clauses.insert_if_not_there(m_clause2, {{}, UINT_MAX, true }); + in_core = true; + if (id != UINT_MAX) + insert_dep(id); + } } } diff --git a/src/sat/sat_proof_trim.h b/src/sat/sat_proof_trim.h index 24091e69c..11e460eb6 100644 --- a/src/sat/sat_proof_trim.h +++ b/src/sat/sat_proof_trim.h @@ -30,7 +30,8 @@ namespace sat { class proof_trim { solver s; - literal_vector m_clause, m_conflict; + literal_vector m_clause, m_clause2, m_conflict; + uint_set m_in_deps; uint_set m_in_clause; uint_set m_in_coi; clause* m_conflict_clause = nullptr; @@ -72,6 +73,8 @@ namespace sat { void revive(literal_vector const& cl, clause* cp); clause* del(literal_vector const& cl); + void insert_dep(unsigned dep); + 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;} From 3d8f75b3d80c368bafbff9d963efa1fee2b34d6d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Jul 2023 16:59:02 -0700 Subject: [PATCH 027/428] enable on-clause with dependencies Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 2 +- src/api/api_solver.cpp | 4 +-- src/api/python/z3/z3.py | 5 ++-- src/api/z3_api.h | 2 +- src/cmd_context/extra_cmds/proof_cmds.cpp | 32 ++++++++++++++++++----- src/sat/sat_proof_trim.cpp | 13 ++++++--- src/sat/smt/euf_proof.cpp | 2 +- src/smt/smt_clause_proof.cpp | 2 +- src/tactic/user_propagator_base.h | 2 +- 9 files changed, 46 insertions(+), 18 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index dc25f8df2..ad76b9be0 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1919,7 +1919,7 @@ _error_handler_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) _lib.Z3_set_error_handler.restype = None _lib.Z3_set_error_handler.argtypes = [ContextObj, _error_handler_type] -Z3_on_clause_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) +Z3_on_clause_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.c_void_p) Z3_push_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p) Z3_pop_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint) Z3_fresh_eh = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ae77cb4ea..953167fe9 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -984,14 +984,14 @@ extern "C" { Z3_TRY; RESET_ERROR_CODE(); init_solver(c, s); - user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned n, expr* const* _literals) { + user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned nd, unsigned const* deps, unsigned n, expr* const* _literals) { Z3_ast_vector_ref * literals = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(literals); expr_ref pr(proof, mk_c(c)->m()); scoped_ast_vector _sc(literals); for (unsigned i = 0; i < n; ++i) literals->m_ast_vector.push_back(_literals[i]); - on_clause_eh(user_ctx, of_expr(pr.get()), of_ast_vector(literals)); + on_clause_eh(user_ctx, of_expr(pr.get()), nd, deps, of_ast_vector(literals)); }; to_solver_ref(s)->register_on_clause(user_context, _on_clause); auto& solver = *to_solver(s); diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index d1a856174..bac80b33e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11438,11 +11438,12 @@ def to_AstVectorObj(ptr,): # for UserPropagator we use a global dictionary, which isn't great code. _my_hacky_class = None -def on_clause_eh(ctx, p, clause): +def on_clause_eh(ctx, p, n, dep, clause): onc = _my_hacky_class p = _to_expr_ref(to_Ast(p), onc.ctx) clause = AstVector(to_AstVectorObj(clause), onc.ctx) - onc.on_clause(p, clause) + deps = [dep[i] for i in range(n)] + onc.on_clause(p, deps, clause) _on_clause_eh = Z3_on_clause_eh(on_clause_eh) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index a931bc523..36634b8a4 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1437,7 +1437,7 @@ Z3_DECLARE_CLOSURE(Z3_eq_eh, void, (void* ctx, Z3_solver_callback cb, Z3_as Z3_DECLARE_CLOSURE(Z3_final_eh, void, (void* ctx, Z3_solver_callback cb)); Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t)); Z3_DECLARE_CLOSURE(Z3_decide_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t, unsigned idx, bool phase)); -Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, Z3_ast_vector literals)); +Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, unsigned n, unsigned const* deps, Z3_ast_vector literals)); /** diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index e2a19446d..31d708733 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -126,7 +126,7 @@ public: if (m_empty) return; - if (hint && !is_rup(hint) && m_checker.check(hint)) { + if (hint && !is_rup(hint)) { auto clause1 = m_checker.clause(hint); if (clause1.size() != clause.size()) { mk_clause(clause1); @@ -239,8 +239,10 @@ public: class proof_cmds_imp : public proof_cmds { cmd_context& ctx; ast_manager& m; + arith_util m_arith; expr_ref_vector m_lits; app_ref m_proof_hint; + unsigned_vector m_deps; bool m_check = true; bool m_save = false; bool m_trim = false; @@ -266,11 +268,24 @@ class proof_cmds_imp : public proof_cmds { m_del = m.mk_app(symbol("del"), 0, nullptr, m.mk_proof_sort()); return m_del; } + + bool is_dep(expr* e) { + return m.is_proof(e) && symbol("deps") == to_app(e)->get_name(); + } + + void get_deps(expr* e) { + rational n; + bool is_int = false; + for (expr* arg : *to_app(e)) + if (m_arith.is_numeral(arg, n, is_int) && n.is_unsigned()) + m_deps.push_back(n.get_unsigned()); + } public: proof_cmds_imp(cmd_context& ctx): ctx(ctx), - m(ctx.m()), + m(ctx.m()), + m_arith(m), m_lits(m), m_proof_hint(m), m_assumption(m), @@ -280,7 +295,9 @@ public: void add_literal(expr* e) override { if (m.is_proof(e)) { - if (!m_proof_hint) + if (is_dep(e)) + get_deps(e); + else if (!m_proof_hint) m_proof_hint = to_app(e); } else if (!m.is_bool(e)) @@ -297,9 +314,10 @@ public: if (m_trim) trim().assume(m_lits); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, assumption(), m_lits.size(), m_lits.data()); + m_on_clause_eh(m_on_clause_ctx, assumption(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); m_lits.reset(); m_proof_hint.reset(); + m_deps.reset(); } void end_infer() override { @@ -310,9 +328,10 @@ public: if (m_trim) trim().infer(m_lits, m_proof_hint); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_lits.size(), m_lits.data()); + m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); m_lits.reset(); m_proof_hint.reset(); + m_deps.reset(); } void end_deleted() override { @@ -323,9 +342,10 @@ public: if (m_trim) trim().del(m_lits); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, del(), m_lits.size(), m_lits.data()); + m_on_clause_eh(m_on_clause_ctx, del(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); m_lits.reset(); m_proof_hint.reset(); + m_deps.reset(); } void updt_params(params_ref const& p) override { diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index 56c4a1b7a..41f7ea7f7 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -266,7 +266,8 @@ namespace sat { m_clause.reset(); switch (j.get_kind()) { case justification::NONE: - m_clause.push_back(l); + if (l != null_literal) + m_clause.push_back(l); break; case justification::BINARY: m_clause.push_back(l); @@ -282,7 +283,7 @@ namespace sat { break; } std::sort(m_clause.begin(), m_clause.end()); - IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n"); + IF_VERBOSE(3, verbose_stream() << "add core {" << m_clause << "}\n"); auto& [clauses, id, in_core] = m_clauses.find(m_clause); in_core = true; insert_dep(id); @@ -337,7 +338,13 @@ 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()); + unsigned j = 0; + sat::literal prev = null_literal; + for (unsigned i = 0; i < m_clause.size(); ++i) + if (m_clause[i] != prev) + prev = m_clause[j++] = m_clause[i]; + m_clause.shrink(j); if (unit_or_binary_occurs()) return; if (!m_conflict.empty() && m_clause.empty()) { diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index ac9e81311..e96ec91ab 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -312,7 +312,7 @@ namespace euf { for (unsigned i = 0; i < n; ++i) m_clause.push_back(literal2expr(lits[i])); auto hint = status2proof_hint(st); - m_on_clause(m_on_clause_ctx, hint, m_clause.size(), m_clause.data()); + m_on_clause(m_on_clause_ctx, hint, 0, nullptr, m_clause.size(), m_clause.data()); } void solver::on_proof(unsigned n, literal const* lits, sat::status st) { diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 3bb2a1fdf..777961334 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -190,7 +190,7 @@ namespace smt { if (ctx.get_fparams().m_clause_proof) m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, p, v.size(), v.data()); + m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); if (m_has_log) { init_pp_out(); auto& out = *m_pp_out; diff --git a/src/tactic/user_propagator_base.h b/src/tactic/user_propagator_base.h index 40c0fa8fc..58904a12d 100644 --- a/src/tactic/user_propagator_base.h +++ b/src/tactic/user_propagator_base.h @@ -27,7 +27,7 @@ namespace user_propagator { typedef std::function pop_eh_t; typedef std::function created_eh_t; typedef std::function decide_eh_t; - typedef std::function on_clause_eh_t; + typedef std::function on_clause_eh_t; class plugin : public decl_plugin { public: From e8a38c548235d21c9f075cc9cdbe473fc68ef8bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 18 Jul 2023 19:14:45 -0700 Subject: [PATCH 028/428] build fixes Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 3 ++- src/api/c++/z3++.h | 9 ++++++--- src/api/dotnet/OnClause.cs | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index ad76b9be0..5f8db1932 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -426,9 +426,10 @@ def mk_dotnet(dotnet): dotnet.write(' {\n\n') for name, ret, sig in Closures: + sig = sig.replace("unsigned const*","uint[]") sig = sig.replace("void*","voidp").replace("unsigned","uint") sig = sig.replace("Z3_ast*","ref IntPtr").replace("uint*","ref uint").replace("Z3_lbool*","ref int") - ret = ret.replace("void*","voidp").replace("unsigned","uint") + ret = ret.replace("void*","voidp").replace("unsigned","uint") if "*" in sig or "*" in ret: continue dotnet.write(' [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n') diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 4e51e83dd..2f59a6ee7 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -4214,17 +4214,20 @@ namespace z3 { return expr(ctx(), r); } - typedef std::function on_clause_eh_t; + typedef std::function const& deps, expr_vector const& clause)> on_clause_eh_t; class on_clause { context& c; on_clause_eh_t m_on_clause; - static void _on_clause_eh(void* _ctx, Z3_ast _proof, Z3_ast_vector _literals) { + static void _on_clause_eh(void* _ctx, Z3_ast _proof, unsigned n, unsigned const* dep, Z3_ast_vector _literals) { on_clause* ctx = static_cast(_ctx); expr_vector lits(ctx->c, _literals); expr proof(ctx->c, _proof); - ctx->m_on_clause(proof, lits); + std::vector deps; + for (unsigned i = 0; i < n; ++i) + deps.push_back(dep[i]); + ctx->m_on_clause(proof, deps, lits); } public: on_clause(solver& s, on_clause_eh_t& on_clause_eh): c(s.ctx()) { diff --git a/src/api/dotnet/OnClause.cs b/src/api/dotnet/OnClause.cs index 686318928..05defb312 100644 --- a/src/api/dotnet/OnClause.cs +++ b/src/api/dotnet/OnClause.cs @@ -30,6 +30,7 @@ namespace Microsoft.Z3 using Z3_context = System.IntPtr; using Z3_solver = System.IntPtr; using voidp = System.IntPtr; + using uintp = System.IntPtr; using Z3_ast = System.IntPtr; using Z3_ast_vector = System.IntPtr; @@ -60,7 +61,7 @@ namespace Microsoft.Z3 Native.Z3_on_clause_eh on_clause_eh; - static void _on_clause(voidp ctx, Z3_ast _proof_hint, Z3_ast_vector _clause) + static void _on_clause(voidp ctx, Z3_ast _proof_hint, uint n, uint[] deps, Z3_ast_vector _clause) { var onc = (OnClause)GCHandle.FromIntPtr(ctx).Target; using var proof_hint = Expr.Create(onc.ctx, _proof_hint); From 3479cdc10b94d9fd09437e093bcd9755a573d070 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Jul 2023 10:52:58 -0700 Subject: [PATCH 029/428] separate hint literals --- src/sat/smt/euf_proof.cpp | 6 ++--- src/sat/smt/euf_solver.cpp | 46 +++++++++++++++++++++++++++++--------- src/sat/smt/euf_solver.h | 4 +++- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index e96ec91ab..addcec66a 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -52,7 +52,7 @@ namespace euf { * We will here leave it to the EUF checker to perform resolution steps. */ void solver::log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint) { - TRACE("euf", log_antecedents(tout, l, r);); + TRACE("euf", log_antecedents(tout, l, r); tout << mk_pp(hint->get_hint(*this), m) << "\n"); if (!use_drat()) return; literal_vector lits; @@ -79,7 +79,7 @@ namespace euf { } } - eq_proof_hint* solver::mk_hint(symbol const& th, literal conseq, literal_vector const& r) { + eq_proof_hint* solver::mk_hint(symbol const& th, literal conseq) { if (!use_drat()) return nullptr; push(value_trail(m_lit_tail)); @@ -87,7 +87,7 @@ namespace euf { push(restore_vector(m_proof_literals)); if (conseq != sat::null_literal) m_proof_literals.push_back(~conseq); - m_proof_literals.append(r); + m_proof_literals.append(m_hint_lits); m_lit_head = m_lit_tail; m_cc_head = m_cc_tail; m_lit_tail = m_proof_literals.size(); diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 0ae56beb3..471ebac66 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -227,20 +227,25 @@ namespace euf { */ void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) { + bool create_hint = use_drat() && !probing; m_egraph.begin_explain(); m_explain.reset(); - if (use_drat() && !probing) { + if (create_hint) { push(restore_vector(m_explain_cc)); + m_hint_eqs.reset(); + m_hint_lits.reset(); } auto* ext = sat::constraint_base::to_extension(idx); th_proof_hint* hint = nullptr; - bool has_theory = false; + if (ext == this) get_antecedents(l, constraint::from_idx(idx), r, probing); else { ext->get_antecedents(l, idx, r, probing); - has_theory = true; } + if (create_hint && ext != this) + ext->get_antecedents(l, idx, m_hint_lits, probing); + for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { size_t* e = m_explain[qhead]; if (is_literal(e)) @@ -249,24 +254,43 @@ namespace euf { size_t idx = get_justification(e); auto* ext = sat::constraint_base::to_extension(idx); SASSERT(ext != this); + auto& jst = th_explain::from_index(idx); sat::literal lit = sat::null_literal; ext->get_antecedents(lit, idx, r, probing); - has_theory = true; + } + if (create_hint) { + if (is_literal(e)) + m_hint_lits.push_back(get_literal(e)); + else + m_hint_eqs.push_back(th_explain::from_index(get_justification(e)).eq_consequent()); } } - m_egraph.end_explain(); - if (use_drat() && !probing) - hint = mk_hint(has_theory ? m_smt : m_euf, l, r); + m_egraph.end_explain(); + unsigned nv = s().num_vars(); + expr_ref_vector eqs(m); + if (create_hint) { + // add negated equalities to hint. + for (auto const& [a,b] : m_hint_eqs) { + eqs.push_back(m.mk_eq(a->get_expr(), b->get_expr())); + set_tmp_bool_var(nv, eqs.back()); + m_hint_lits.push_back(literal(nv, false)); + ++nv; + } + hint = mk_hint(m_euf, l); + } unsigned j = 0; for (sat::literal lit : r) if (s().lvl(lit) > 0) r[j++] = lit; r.shrink(j); CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n"); + CTRACE("euf", create_hint, tout << "explain " << l << " <- " << m_hint_lits << "\n"); DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true);); if (!probing) log_antecedents(l, r, hint); + for (unsigned v = s().num_vars(); v < nv; ++v) + set_tmp_bool_var(v, nullptr); } void solver::get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing) { @@ -307,7 +331,7 @@ namespace euf { init_ackerman(); if (!probing && use_drat()) cc = &m_explain_cc; - + switch (j.kind()) { case constraint::kind_t::conflict: SASSERT(m_egraph.inconsistent()); @@ -333,8 +357,8 @@ namespace euf { bool_var v = ante->bool_var(); lbool val = ante->value(); SASSERT(val != l_undef); - literal ante(v, val == l_false); - m_explain.push_back(to_ptr(ante)); + literal ante_lit(v, val == l_false); + m_explain.push_back(to_ptr(ante_lit)); } break; } @@ -510,7 +534,7 @@ namespace euf { bool solver::is_self_propagated(th_eq const& e) { if (!e.is_eq()) return false; - + m_egraph.begin_explain(); m_explain.reset(); m_egraph.explain_eq(m_explain, nullptr, e.child(), e.root()); diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 72776b7ff..f1fadac35 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -147,6 +147,8 @@ namespace euf { ptr_vector m_bool_var2expr; ptr_vector m_explain; euf::cc_justification m_explain_cc; + enode_pair_vector m_hint_eqs; + sat::literal_vector m_hint_lits; unsigned m_num_scopes = 0; unsigned_vector m_var_trail; svector m_scopes; @@ -228,7 +230,7 @@ namespace euf { void log_justification(literal l, th_explain const& jst); - eq_proof_hint* mk_hint(symbol const& th, literal lit, literal_vector const& r); + eq_proof_hint* mk_hint(symbol const& th, literal lit); From 4d31ff7a38ecbb7d56afec76f70f3e5d7333d8a1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Jul 2023 08:35:09 -0700 Subject: [PATCH 030/428] remove unused variable Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_solver.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 471ebac66..89a8433e1 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -240,9 +240,9 @@ namespace euf { if (ext == this) get_antecedents(l, constraint::from_idx(idx), r, probing); - else { + else ext->get_antecedents(l, idx, r, probing); - } + if (create_hint && ext != this) ext->get_antecedents(l, idx, m_hint_lits, probing); @@ -254,7 +254,6 @@ namespace euf { size_t idx = get_justification(e); auto* ext = sat::constraint_base::to_extension(idx); SASSERT(ext != this); - auto& jst = th_explain::from_index(idx); sat::literal lit = sat::null_literal; ext->get_antecedents(lit, idx, r, probing); } @@ -278,12 +277,12 @@ namespace euf { } hint = mk_hint(m_euf, l); } - + + CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n"); unsigned j = 0; for (sat::literal lit : r) if (s().lvl(lit) > 0) r[j++] = lit; r.shrink(j); - CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n"); CTRACE("euf", create_hint, tout << "explain " << l << " <- " << m_hint_lits << "\n"); DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true);); From df8ccce08e5ea7b329765a9570a0e348ccd1eaa8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Jul 2023 11:03:20 -0700 Subject: [PATCH 031/428] #6822 string matching against version number of glibc to ensure inclusino Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 073b6b99a..5020792fc 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,8 +24,7 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'glibc' : ('so', 'linux-x64'), - #'glibc-2.35' : ('so', 'linux-x64'), + 'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), From a0892c666939a22beabc19e22ed4c52e1eef0968 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Jul 2023 11:30:34 -0700 Subject: [PATCH 032/428] rename antecedent utilities for clarity Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_solver.cpp | 2 +- src/sat/smt/bv_solver.cpp | 6 +++--- src/sat/smt/dt_solver.cpp | 2 +- src/sat/smt/euf_proof.cpp | 10 +++------- src/sat/smt/euf_solver.cpp | 18 +++++++++++------- src/sat/smt/euf_solver.h | 6 +++--- src/sat/smt/user_solver.cpp | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index ccbe2591b..2803e6c06 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1460,7 +1460,7 @@ namespace arith { void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) { auto& jst = euf::th_explain::from_index(idx); - ctx.get_antecedents(l, jst, r, probing); + ctx.get_th_antecedents(l, jst, r, probing); } bool solver::include_func_interp(func_decl* f) const { diff --git a/src/sat/smt/bv_solver.cpp b/src/sat/smt/bv_solver.cpp index a3aa5f7b3..53a004140 100644 --- a/src/sat/smt/bv_solver.cpp +++ b/src/sat/smt/bv_solver.cpp @@ -308,7 +308,7 @@ namespace bv { case bv_justification::kind_t::eq2bit: SASSERT(s().value(c.m_antecedent) == l_true); r.push_back(c.m_antecedent); - ctx.add_antecedent(probing, var2enode(c.m_v1), var2enode(c.m_v2)); + ctx.add_eq_antecedent(probing, var2enode(c.m_v1), var2enode(c.m_v2)); break; case bv_justification::kind_t::ne2bit: { r.push_back(c.m_antecedent); @@ -376,8 +376,8 @@ namespace bv { break; } case bv_justification::kind_t::bv2int: { - ctx.add_antecedent(probing, c.a, c.b); - ctx.add_antecedent(probing, c.a, c.c); + ctx.add_eq_antecedent(probing, c.a, c.b); + ctx.add_eq_antecedent(probing, c.a, c.c); break; } } diff --git a/src/sat/smt/dt_solver.cpp b/src/sat/smt/dt_solver.cpp index 56a224d36..22e446a13 100644 --- a/src/sat/smt/dt_solver.cpp +++ b/src/sat/smt/dt_solver.cpp @@ -757,7 +757,7 @@ namespace dt { void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) { auto& jst = euf::th_explain::from_index(idx); - ctx.get_antecedents(l, jst, r, probing); + ctx.get_th_antecedents(l, jst, r, probing); } void solver::add_value(euf::enode* n, model& mdl, expr_ref_vector& values) { diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index addcec66a..62a9a11e8 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -197,7 +197,6 @@ namespace euf { if (!literal2expr(lits[i])) IF_VERBOSE(0, verbose_stream() << lits[i] << "\n"; display(verbose_stream())); - SASSERT(literal2expr(lits[i])); m_proof_literals.push_back(lits[i]); } @@ -260,10 +259,7 @@ namespace euf { auto const& [a, b] = s.m_proof_deqs[i]; args.push_back(m.mk_not(m.mk_eq(a, b))); } - for (auto * arg : args) - sorts.push_back(arg->get_sort()); - func_decl* f = m.mk_func_decl(m_name, sorts.size(), sorts.data(), proof); - return m.mk_app(f, args); + return m.mk_app(m_name, args.size(), args.data(), proof); } void solver::set_tmp_bool_var(bool_var b, expr* e) { @@ -298,7 +294,7 @@ namespace euf { } void solver::on_clause(unsigned n, literal const* lits, sat::status st) { - TRACE("euf", tout << "on-clause " << n << "\n"); + TRACE("euf_verbose", tout << "on-clause " << n << "\n"); on_lemma(n, lits, st); on_proof(n, lits, st); on_check(n, lits, st); @@ -417,7 +413,7 @@ namespace euf { if (proof_hint) return display_expr(out << " ", proof_hint); else - return out; + return out; } app_ref solver::status2proof_hint(sat::status st) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 89a8433e1..c5db070de 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -239,7 +239,7 @@ namespace euf { th_proof_hint* hint = nullptr; if (ext == this) - get_antecedents(l, constraint::from_idx(idx), r, probing); + get_euf_antecedents(l, constraint::from_idx(idx), r, probing); else ext->get_antecedents(l, idx, r, probing); @@ -260,8 +260,11 @@ namespace euf { if (create_hint) { if (is_literal(e)) m_hint_lits.push_back(get_literal(e)); - else - m_hint_eqs.push_back(th_explain::from_index(get_justification(e)).eq_consequent()); + else { + auto const& eq = th_explain::from_index(get_justification(e)).eq_consequent(); + TRACE("euf", tout << "consequent " << bpp(eq.first) << " " << bpp(eq.second) << "\n"; ); + m_hint_eqs.push_back(eq); + } } } m_egraph.end_explain(); @@ -292,17 +295,17 @@ namespace euf { set_tmp_bool_var(v, nullptr); } - void solver::get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing) { + void solver::get_th_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing) { for (auto lit : euf::th_explain::lits(jst)) r.push_back(lit); for (auto eq : euf::th_explain::eqs(jst)) - add_antecedent(probing, eq.first, eq.second); + add_eq_antecedent(probing, eq.first, eq.second); if (!probing && use_drat()) log_justification(l, jst); } - void solver::add_antecedent(bool probing, enode* a, enode* b) { + void solver::add_eq_antecedent(bool probing, enode* a, enode* b) { cc_justification* cc = (!probing && use_drat()) ? &m_explain_cc : nullptr; m_egraph.explain_eq(m_explain, cc, a, b); } @@ -321,7 +324,7 @@ namespace euf { return true; } - void solver::get_antecedents(literal l, constraint& j, literal_vector& r, bool probing) { + void solver::get_euf_antecedents(literal l, constraint& j, literal_vector& r, bool probing) { expr* e = nullptr; euf::enode* n = nullptr; cc_justification* cc = nullptr; @@ -357,6 +360,7 @@ namespace euf { lbool val = ante->value(); SASSERT(val != l_undef); literal ante_lit(v, val == l_false); + TRACE("euf", tout << "explain " << bpp(n) << " by " << bpp(ante) << "\n"); m_explain.push_back(to_ptr(ante_lit)); } break; diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index f1fadac35..f2add1e87 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -220,7 +220,7 @@ namespace euf { void propagate_literal(enode* n, enode* ante); void propagate_th_eqs(); bool is_self_propagated(th_eq const& e); - void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing); + void get_euf_antecedents(literal l, constraint& j, literal_vector& r, bool probing); void new_diseq(enode* a, enode* b, literal lit); bool merge_shared_bools(); @@ -365,8 +365,8 @@ namespace euf { void flush_roots() override; void get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) override; - void get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing); - void add_antecedent(bool probing, enode* a, enode* b); + void get_th_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing); + void add_eq_antecedent(bool probing, enode* a, enode* b); void add_diseq_antecedent(ptr_vector& ex, cc_justification* cc, enode* a, enode* b); void add_explain(size_t* p) { m_explain.push_back(p); } void reset_explain() { m_explain.reset(); } diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 2823c81f8..01c1786ec 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -221,7 +221,7 @@ namespace user_solver { for (unsigned id : prop.m_ids) r.append(m_id2justification[id]); for (auto const& p : prop.m_eqs) - ctx.add_antecedent(probing, expr2enode(p.first), expr2enode(p.second)); + ctx.add_eq_antecedent(probing, expr2enode(p.first), expr2enode(p.second)); } /* From d0f2b00f96e0549a69fd9a71b0d3f9c05d99e8a2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Jul 2023 12:24:30 -0700 Subject: [PATCH 033/428] fix build warnings Signed-off-by: Nikolaj Bjorner --- src/ast/euf/euf_etable.cpp | 5 ----- src/sat/sat_ddfw.cpp | 2 +- src/sat/smt/arith_diagnostics.cpp | 3 +++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ast/euf/euf_etable.cpp b/src/ast/euf/euf_etable.cpp index 17be983fe..d79944c9b 100644 --- a/src/ast/euf/euf_etable.cpp +++ b/src/ast/euf/euf_etable.cpp @@ -224,11 +224,6 @@ namespace euf { void etable::erase(enode * n) { SASSERT(n->num_args() > 0); void * t = get_table(n); - static unsigned count = 0; - //verbose_stream() << "erase " << (++count) << " " << n << "\n"; - // if (count == 10398) { - // verbose_stream() << "here\n"; - // } switch (static_cast(GET_TAG(t))) { case UNARY: UNTAG(unary_table*, t)->erase(n); diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index ca274be51..f15c241f2 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -494,7 +494,7 @@ namespace sat { unsigned h = value_hash(); unsigned occs = 0; bool contains = m_models.find(h, occs); - if (!m_models.contains(h)) { + if (!contains) { for (unsigned v = 0; v < num_vars(); ++v) bias(v) += value(v) ? 1 : -1; if (m_models.size() > m_config.m_max_num_models) diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 783f268bd..e621ee9d7 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -199,6 +199,9 @@ namespace arith { name = "implied-eq"; args.push_back(arith.mk_int(m_num_le)); break; + default: + name = "unknown-arithmetic"; + break; } rational lc(1); for (unsigned i = m_lit_head; i < m_lit_tail; ++i) From e64bab4bb87093ce277917f834dee18cf43cf9e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Jul 2023 13:19:03 -0700 Subject: [PATCH 034/428] simplify code Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_solver.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index c5db070de..94ff9db38 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -268,18 +268,6 @@ namespace euf { } } m_egraph.end_explain(); - unsigned nv = s().num_vars(); - expr_ref_vector eqs(m); - if (create_hint) { - // add negated equalities to hint. - for (auto const& [a,b] : m_hint_eqs) { - eqs.push_back(m.mk_eq(a->get_expr(), b->get_expr())); - set_tmp_bool_var(nv, eqs.back()); - m_hint_lits.push_back(literal(nv, false)); - ++nv; - } - hint = mk_hint(m_euf, l); - } CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n"); unsigned j = 0; @@ -289,10 +277,21 @@ namespace euf { CTRACE("euf", create_hint, tout << "explain " << l << " <- " << m_hint_lits << "\n"); DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true);); - if (!probing) + if (create_hint) { + unsigned nv = s().num_vars(); + expr_ref_vector eqs(m); + // add equalities to hint. + for (auto const& [a,b] : m_hint_eqs) { + eqs.push_back(m.mk_eq(a->get_expr(), b->get_expr())); + set_tmp_bool_var(nv, eqs.back()); + m_hint_lits.push_back(literal(nv, false)); + ++nv; + } + hint = mk_hint(m_euf, l); log_antecedents(l, r, hint); - for (unsigned v = s().num_vars(); v < nv; ++v) - set_tmp_bool_var(v, nullptr); + for (unsigned v = s().num_vars(); v < nv; ++v) + set_tmp_bool_var(v, nullptr); + } } void solver::get_th_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing) { From 48deb4d3e02bceeb1f5bb11b46e8b1798811dae7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 23 Jul 2023 14:31:44 -0700 Subject: [PATCH 035/428] fix proof generation for euf-solver Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_diagnostics.cpp | 4 +- src/sat/smt/euf_proof.cpp | 84 +++++++++++++++++++++++++++---- src/sat/smt/euf_solver.cpp | 56 ++++++++------------- src/sat/smt/euf_solver.h | 4 +- src/sat/smt/q_ematch.cpp | 2 +- src/sat/smt/sat_th.cpp | 7 ++- 6 files changed, 108 insertions(+), 49 deletions(-) diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index e621ee9d7..9fafbcc80 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -213,7 +213,9 @@ namespace arith { args.push_back(s.literal2expr(lit)); } for (unsigned i = m_eq_head; i < m_eq_tail; ++i) { - auto const& [x, y, is_eq] = a.m_arith_hint.eq(i); + auto [x, y, is_eq] = a.m_arith_hint.eq(i); + if (x->get_id() > y->get_id()) + std::swap(x, y); expr_ref eq(m.mk_eq(x->get_expr(), y->get_expr()), m); if (!is_eq) eq = m.mk_not(eq); args.push_back(arith.mk_int(1)); diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 62a9a11e8..39c9879a6 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -44,17 +44,73 @@ namespace euf { } /** - * \brief logs antecedents to a proof trail. - * - * NB with theories, this is not a pure EUF justification, - * It is true modulo EUF and previously logged certificates - * so it isn't necessarily an axiom over EUF, - * We will here leave it to the EUF checker to perform resolution steps. - */ + * Log justifications. + * is_euf - true if l is justified by congruence closure. In this case create a congruence closure proof. + * explain_size - the relevant portion of premises for the congruence closure proof. + * The EUF solver manages equality propagation. Each propagated equality is justified by a congruence closure. + */ + void solver::log_justifications(literal l, unsigned explain_size, bool is_euf) { + + unsigned nv = s().num_vars(); + expr_ref_vector eqs(m); + + auto add_hint_literals = [&](unsigned sz) { + eqs.reset(); + m_hint_lits.reset(); + nv = s().num_vars(); + for (unsigned i = 0; i < sz; ++i) { + size_t* e = m_explain[i]; + if (is_literal(e)) + m_hint_lits.push_back(get_literal(e)); + else { + auto [x, y] = th_explain::from_index(get_justification(e)).eq_consequent(); + eqs.push_back(m.mk_eq(x->get_expr(), y->get_expr())); + set_tmp_bool_var(nv, eqs.back()); + m_hint_lits.push_back(literal(nv, false)); + ++nv; + } + } + }; + + auto clear_hint_literals = [&]() { + for (unsigned v = s().num_vars(); v < nv; ++v) + set_tmp_bool_var(v, nullptr); + }; + + // log EUF justifications + if (is_euf) { + add_hint_literals(explain_size); + auto* hint = mk_hint(m_euf, l); + log_antecedents(l, m_hint_lits, hint); + clear_hint_literals(); + } + + // explain equalities + for (auto const& [a, b] : m_hint_eqs) { + m_egraph.begin_explain(); + m_explain.reset(); + m_egraph.explain_eq(m_explain, &m_explain_cc, a, b); + m_egraph.end_explain(); + // Detect shortcut if equality is explained directly by a theory + if (m_explain.size() == 1 && !is_literal(m_explain[0])) { + auto const& [x, y] = th_explain::from_index(get_justification(m_explain[0])).eq_consequent(); + if (x == a && y == b) + continue; + } + add_hint_literals(m_explain.size()); + eqs.push_back(m.mk_eq(a->get_expr(), b->get_expr())); + set_tmp_bool_var(nv, eqs.back()); + sat::literal eql = literal(nv, false); + ++nv; + auto* hint = mk_hint(m_euf, eql); + log_antecedents(eql, m_hint_lits, hint); + clear_hint_literals(); + } + } + void solver::log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint) { + SASSERT(hint && use_drat()); TRACE("euf", log_antecedents(tout, l, r); tout << mk_pp(hint->get_hint(*this), m) << "\n"); - if (!use_drat()) - return; literal_vector lits; for (literal lit : r) lits.push_back(~lit); @@ -63,6 +119,15 @@ namespace euf { get_drat().add(lits, sat::status::th(true, get_id(), hint)); } + void solver::log_rup(literal l, literal_vector const& r) { + literal_vector lits; + for (literal lit : r) + lits.push_back(~lit); + if (l != sat::null_literal) + lits.push_back(l); + get_drat().add(lits, sat::status::redundant()); + } + void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) { for (sat::literal l : r) { expr* n = m_bool_var2expr[l.var()]; @@ -159,6 +224,7 @@ namespace euf { }; for (unsigned i = m_lit_head; i < m_lit_tail; ++i) args.push_back(s.literal2expr(s.m_proof_literals[i])); + std::sort(s.m_explain_cc.data() + m_cc_head, s.m_explain_cc.data() + m_cc_tail, compare_ts); for (unsigned i = m_cc_head; i < m_cc_tail; ++i) { auto const& [a, b, ts, comm] = s.m_explain_cc[i]; diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 94ff9db38..8f0f29445 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -228,69 +228,52 @@ namespace euf { void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) { bool create_hint = use_drat() && !probing; - m_egraph.begin_explain(); - m_explain.reset(); if (create_hint) { push(restore_vector(m_explain_cc)); m_hint_eqs.reset(); - m_hint_lits.reset(); } auto* ext = sat::constraint_base::to_extension(idx); - th_proof_hint* hint = nullptr; + bool is_euf = ext == this; + bool multiple_theories = false; - if (ext == this) + m_egraph.begin_explain(); + m_explain.reset(); + if (is_euf) get_euf_antecedents(l, constraint::from_idx(idx), r, probing); else ext->get_antecedents(l, idx, r, probing); - if (create_hint && ext != this) - ext->get_antecedents(l, idx, m_hint_lits, probing); + unsigned ez = m_explain.size(); for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { size_t* e = m_explain[qhead]; if (is_literal(e)) r.push_back(get_literal(e)); else { + multiple_theories = true; size_t idx = get_justification(e); auto* ext = sat::constraint_base::to_extension(idx); SASSERT(ext != this); sat::literal lit = sat::null_literal; ext->get_antecedents(lit, idx, r, probing); } - if (create_hint) { - if (is_literal(e)) - m_hint_lits.push_back(get_literal(e)); - else { - auto const& eq = th_explain::from_index(get_justification(e)).eq_consequent(); - TRACE("euf", tout << "consequent " << bpp(eq.first) << " " << bpp(eq.second) << "\n"; ); - m_hint_eqs.push_back(eq); - } - } } m_egraph.end_explain(); CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n"); unsigned j = 0; - for (sat::literal lit : r) - if (s().lvl(lit) > 0) r[j++] = lit; + for (auto lit : r) + if (s().lvl(lit) > 0) + r[j++] = lit; + bool reduced = j < r.size(); r.shrink(j); - CTRACE("euf", create_hint, tout << "explain " << l << " <- " << m_hint_lits << "\n"); + DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true);); - if (create_hint) { - unsigned nv = s().num_vars(); - expr_ref_vector eqs(m); - // add equalities to hint. - for (auto const& [a,b] : m_hint_eqs) { - eqs.push_back(m.mk_eq(a->get_expr(), b->get_expr())); - set_tmp_bool_var(nv, eqs.back()); - m_hint_lits.push_back(literal(nv, false)); - ++nv; - } - hint = mk_hint(m_euf, l); - log_antecedents(l, r, hint); - for (unsigned v = s().num_vars(); v < nv; ++v) - set_tmp_bool_var(v, nullptr); + if (create_hint) { + log_justifications(l, ez, is_euf); + if (reduced || multiple_theories) + log_rup(l, r); } } @@ -305,11 +288,12 @@ namespace euf { } void solver::add_eq_antecedent(bool probing, enode* a, enode* b) { - cc_justification* cc = (!probing && use_drat()) ? &m_explain_cc : nullptr; - m_egraph.explain_eq(m_explain, cc, a, b); + if (!probing && use_drat()) + m_hint_eqs.push_back({a, b}); + m_egraph.explain_eq(m_explain, nullptr, a, b); } - void solver::add_diseq_antecedent(ptr_vector& ex, cc_justification* cc, enode* a, enode* b) { + void solver::explain_diseq(ptr_vector& ex, cc_justification* cc, enode* a, enode* b) { sat::bool_var v = get_egraph().explain_diseq(ex, cc, a, b); SASSERT(v == sat::null_bool_var || s().value(v) == l_false); if (v != sat::null_bool_var) diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index f2add1e87..a9dc20e0e 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -228,6 +228,8 @@ namespace euf { void log_antecedents(std::ostream& out, literal l, literal_vector const& r); void log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint); void log_justification(literal l, th_explain const& jst); + void log_justifications(literal l, unsigned explain_size, bool is_euf); + void log_rup(literal l, literal_vector const& r); eq_proof_hint* mk_hint(symbol const& th, literal lit); @@ -367,7 +369,7 @@ namespace euf { void get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) override; void get_th_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing); void add_eq_antecedent(bool probing, enode* a, enode* b); - void add_diseq_antecedent(ptr_vector& ex, cc_justification* cc, enode* a, enode* b); + void explain_diseq(ptr_vector& ex, cc_justification* cc, enode* a, enode* b); void add_explain(size_t* p) { m_explain.push_back(p); } void reset_explain() { m_explain.reset(); } void set_eliminated(bool_var v) override; diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index df832a675..ec10426a7 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -125,7 +125,7 @@ namespace q { if (a->get_root() == b->get_root()) ctx.get_egraph().explain_eq(m_explain, cc, a, b); else - ctx.add_diseq_antecedent(m_explain, cc, a, b); + ctx.explain_diseq(m_explain, cc, a, b); } ctx.get_egraph().end_explain(); diff --git a/src/sat/smt/sat_th.cpp b/src/sat/smt/sat_th.cpp index 17d167829..21e3883e8 100644 --- a/src/sat/smt/sat_th.cpp +++ b/src/sat/smt/sat_th.cpp @@ -228,6 +228,8 @@ namespace euf { th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, th_proof_hint const* pma) { m_consequent = c; m_eq = p; + if (m_eq.first && m_eq.first->get_id() > m_eq.second->get_id()) + std::swap(m_eq.first, m_eq.second); m_proof_hint = pma; m_num_literals = n_lits; m_num_eqs = n_eqs; @@ -238,8 +240,11 @@ namespace euf { m_literals[i] = lits[i]; base_ptr += sizeof(literal) * n_lits; m_eqs = reinterpret_cast(base_ptr); - for (i = 0; i < n_eqs; ++i) + for (i = 0; i < n_eqs; ++i) { m_eqs[i] = eqs[i]; + if (m_eqs[i].first->get_id() > m_eqs[i].second->get_id()) + std::swap(m_eqs[i].first, m_eqs[i].second); + } } th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma) { From 6c8b8609eec5f4402bf54d5883f930fadd3770f0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 23 Jul 2023 18:16:00 -0700 Subject: [PATCH 036/428] tweak control flow for empty clauses Signed-off-by: Nikolaj Bjorner --- src/sat/sat_proof_trim.cpp | 9 ++++++--- src/sat/smt/euf_solver.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index 41f7ea7f7..d346fb015 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -40,7 +40,6 @@ namespace sat { conflict_analysis_core(m_conflict, m_conflict_clause); 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) { @@ -384,13 +383,17 @@ namespace sat { return false; }; + if (all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) + return; 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); + if (s.inconsistent() || all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) { + IF_VERBOSE(3, verbose_stream() << "conflict " << m_clause << "\n"); + set_conflict(m_clause, cl); + } } /** diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8f0f29445..6a6b43650 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -272,7 +272,7 @@ namespace euf { if (create_hint) { log_justifications(l, ez, is_euf); - if (reduced || multiple_theories) + if (l != sat::null_literal && (reduced || multiple_theories)) log_rup(l, r); } } From 8cc696951006a96578c0fc64a1571d2fd8841461 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 24 Jul 2023 09:38:57 +0700 Subject: [PATCH 037/428] Remove `Z3_literals` remnants. (#6829) The bulk of the functionality using these was removed between Z3 4.4.1 and 4.5.0, back in 2015. Co-authored-by: Bruce Mitchener --- src/api/z3_api.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 36634b8a4..9a93611b4 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5,7 +5,6 @@ #pragma once DEFINE_TYPE(Z3_symbol); -DEFINE_TYPE(Z3_literals); DEFINE_TYPE(Z3_config); DEFINE_TYPE(Z3_context); DEFINE_TYPE(Z3_sort); @@ -1398,7 +1397,6 @@ typedef enum def_Type('FUNC_DECL', 'Z3_func_decl', 'FuncDecl') def_Type('PATTERN', 'Z3_pattern', 'Pattern') def_Type('MODEL', 'Z3_model', 'ModelObj') - def_Type('LITERALS', 'Z3_literals', 'Literals') def_Type('CONSTRUCTOR', 'Z3_constructor', 'Constructor') def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList') def_Type('SOLVER', 'Z3_solver', 'SolverObj') From 68a437e615048315948d185b54a6a927dc61d35f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jul 2023 09:45:35 -0700 Subject: [PATCH 038/428] revert to logging conflict to get EUF trim to work Signed-off-by: Nikolaj Bjorner --- src/cmd_context/extra_cmds/proof_cmds.cpp | 4 +++- src/sat/sat_proof_trim.cpp | 8 ++++++-- src/sat/smt/arith_diagnostics.cpp | 3 +++ src/sat/smt/arith_solver.cpp | 2 +- src/sat/smt/arith_solver.h | 1 + src/sat/smt/euf_proof.cpp | 1 + 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/extra_cmds/proof_cmds.cpp b/src/cmd_context/extra_cmds/proof_cmds.cpp index 31d708733..ea585bfae 100644 --- a/src/cmd_context/extra_cmds/proof_cmds.cpp +++ b/src/cmd_context/extra_cmds/proof_cmds.cpp @@ -350,9 +350,9 @@ public: void updt_params(params_ref const& p) override { solver_params sp(p); - m_check = sp.proof_check(); m_save = sp.proof_save(); m_trim = sp.proof_trim(); + m_check = sp.proof_check() && !m_trim && !m_save && !m_on_clause_eh; if (m_trim) trim().updt_params(p); } @@ -360,6 +360,8 @@ public: void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause_eh) override { m_on_clause_ctx = ctx; m_on_clause_eh = on_clause_eh; + if (m_on_clause_eh) + m_check = false; } }; diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index d346fb015..51fc44cd7 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -383,14 +383,18 @@ namespace sat { return false; }; - if (all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) + if (all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) { + IF_VERBOSE(3, verbose_stream() << "conflict " << m_clause << "\n"); + set_conflict(m_clause, cl); return; + } + 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; })) { + if (s.inconsistent()) { IF_VERBOSE(3, verbose_stream() << "conflict " << m_clause << "\n"); set_conflict(m_clause, cl); } diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 9fafbcc80..bdcddc03d 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -192,6 +192,9 @@ namespace arith { case hint_type::farkas_h: name = "farkas"; break; + case hint_type::cut_h: + name = "cut"; + break; case hint_type::bound_h: name = "bound"; break; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 2803e6c06..89bccf76b 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1147,7 +1147,7 @@ namespace arith { app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); IF_VERBOSE(4, verbose_stream() << "cut " << b << "\n"); literal lit = expr2literal(b); - assign(lit, m_core, m_eqs, explain(hint_type::bound_h, lit)); + assign(lit, m_core, m_eqs, explain(hint_type::cut_h, lit)); lia_check = l_false; break; } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 2364bb16e..8922673c0 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -51,6 +51,7 @@ namespace arith { enum class hint_type { farkas_h, bound_h, + cut_h, implied_eq_h }; diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 39c9879a6..55b5eff72 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -255,6 +255,7 @@ namespace euf { smt_proof_hint* solver::mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne, expr_pair const* eqs, unsigned nd, expr_pair const* deqs) { if (!use_drat()) return nullptr; + TRACE("euf", tout << "SMT hint " << n << "\n"); push(value_trail(m_lit_tail)); push(restore_vector(m_proof_literals)); From 423a7b6888001bb9f696dba184833d715e4b5600 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jul 2023 09:46:59 -0700 Subject: [PATCH 039/428] also add separate cut rule Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_proof.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 55b5eff72..39c9879a6 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -255,7 +255,6 @@ namespace euf { smt_proof_hint* solver::mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne, expr_pair const* eqs, unsigned nd, expr_pair const* deqs) { if (!use_drat()) return nullptr; - TRACE("euf", tout << "SMT hint " << n << "\n"); push(value_trail(m_lit_tail)); push(restore_vector(m_proof_literals)); From 0f2fe6031a3bd7fafb1cb8e605d978e2ea678805 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jul 2023 11:32:20 -0700 Subject: [PATCH 040/428] patching up trim Signed-off-by: Nikolaj Bjorner --- src/sat/sat_proof_trim.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sat/sat_proof_trim.cpp b/src/sat/sat_proof_trim.cpp index 51fc44cd7..7531da99f 100644 --- a/src/sat/sat_proof_trim.cpp +++ b/src/sat/sat_proof_trim.cpp @@ -177,7 +177,8 @@ namespace sat { IF_VERBOSE(3, verbose_stream() << "core " << cl << "\n"); unsigned trail_size0 = s.m_trail.size(); - if (!cl.empty()) { + bool probe = !cl.empty() && !s.inconsistent(); + if (probe) { SASSERT(!s.inconsistent()); s.push(); unsigned lvl = s.scope_lvl(); @@ -214,7 +215,7 @@ namespace sat { s.reset_mark(v); add_dependency(s.get_justification(v)); } - if (!cl.empty()) + if (probe) s.pop(1); } @@ -384,7 +385,7 @@ namespace sat { }; if (all_of(m_clause, [&](sat::literal lit) { return s.value(lit) == l_false; })) { - IF_VERBOSE(3, verbose_stream() << "conflict " << m_clause << "\n"); + IF_VERBOSE(3, verbose_stream() << "false clause " << m_clause << "\n"); set_conflict(m_clause, cl); return; } From c6aab8966214e32b035c08b702c70a0ee5253198 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jul 2023 14:57:27 -0700 Subject: [PATCH 041/428] add rewrite for partially interpreted arithmetic functions --- src/ast/arith_decl_plugin.cpp | 23 +++++++++++++++++++++++ src/ast/arith_decl_plugin.h | 2 ++ src/model/model_evaluator.cpp | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index a1d067a32..ba2b4b4a7 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -801,6 +801,29 @@ expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) { return result; } +bool arith_util::is_considered_partially_interpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out) { + if (is_decl_of(f, arith_family_id, OP_DIV) && n == 2 && !is_numeral(args[1])) { + f_out = mk_div0(); + return true; + } + if (is_decl_of(f, arith_family_id, OP_IDIV) && n == 2 && !is_numeral(args[1])) { + sort* rs[2] = { mk_int(), mk_int() }; + f_out = m_manager.mk_func_decl(arith_family_id, OP_IDIV0, 0, nullptr, 2, rs, mk_int()); + return true; + } + if (is_decl_of(f, arith_family_id, OP_MOD) && n == 2 && !is_numeral(args[1])) { + sort* rs[2] = { mk_int(), mk_int() }; + f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); + return true; + } + if (is_decl_of(f, arith_family_id, OP_REM) && n == 2 && !is_numeral(args[1])) { + sort* rs[2] = { mk_int(), mk_int() }; + f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); + return true; + } + return false; +} + bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out) { rational r; if (is_decl_of(f, arith_family_id, OP_DIV) && n == 2 && is_numeral(args[1], r) && r.is_zero()) { diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 5dbf3e8cf..fa359a9a7 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -517,6 +517,8 @@ public: bool is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out); + bool is_considered_partially_interpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out); + bool is_underspecified(expr* e) const; bool is_bounded(expr* e) const; diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 0c2a09e78..125380930 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -421,6 +421,15 @@ struct evaluator_cfg : public default_rewriter_cfg { return BR_DONE; } } + else if (!fi && m_au.is_considered_partially_interpreted(f, num, args, f_ui)) { + fi = m_model.get_func_interp(f_ui); + if (fi) { + var_subst vs(m, false); + result = vs(fi->get_interp(), num, args); + result = m.mk_ite(m.mk_eq(m_au.mk_real(rational(0)), args[1]), result, m.mk_app(f, num, args)); + return BR_DONE; + } + } else if (!fi && m_fpau.is_considered_uninterpreted(f, num, args)) { result = m.get_some_value(f->get_range()); return BR_DONE; From 249f0de80b68c0bf203f8f8492256d9c81fadb14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Jul 2023 10:06:41 -0700 Subject: [PATCH 042/428] fix order for inequalities in arithmetic justifications such that implied bound literal is last. The self-checker uses this property to identify the implied bound Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_diagnostics.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index bdcddc03d..77c51d87c 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -209,12 +209,6 @@ namespace arith { rational lc(1); for (unsigned i = m_lit_head; i < m_lit_tail; ++i) lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first)); - - for (unsigned i = m_lit_head; i < m_lit_tail; ++i) { - auto const& [coeff, lit] = a.m_arith_hint.lit(i); - args.push_back(arith.mk_int(abs(coeff*lc))); - args.push_back(s.literal2expr(lit)); - } for (unsigned i = m_eq_head; i < m_eq_tail; ++i) { auto [x, y, is_eq] = a.m_arith_hint.eq(i); if (x->get_id() > y->get_id()) @@ -224,6 +218,11 @@ namespace arith { args.push_back(arith.mk_int(1)); args.push_back(eq); } + for (unsigned i = m_lit_head; i < m_lit_tail; ++i) { + auto const& [coeff, lit] = a.m_arith_hint.lit(i); + args.push_back(arith.mk_int(abs(coeff*lc))); + args.push_back(s.literal2expr(lit)); + } return m.mk_app(symbol(name), args.size(), args.data(), m.mk_proof_sort()); } } From f0184c3fde1eb8f28fdfdc140afc8286fd7c87dc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Jul 2023 13:21:45 -0700 Subject: [PATCH 043/428] update format and checker for implied-eq Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_bound_propagator.h | 3 +- src/sat/smt/arith_axioms.cpp | 8 +- src/sat/smt/arith_diagnostics.cpp | 33 +++-- src/sat/smt/arith_solver.cpp | 9 +- src/sat/smt/arith_solver.h | 7 +- src/sat/smt/arith_theory_checker.h | 221 +++++++++++++++++++---------- src/sat/smt/euf_proof.cpp | 2 + src/sat/smt/sat_th.cpp | 3 + 8 files changed, 192 insertions(+), 94 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index da2e4488d..6056444e8 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -612,7 +612,8 @@ class lp_bound_propagator { constraint_index lc, uc; lp().get_bound_constraint_witnesses_for_column(j, lc, uc); ex.push_back(lc); - ex.push_back(uc); + if (lc != uc) + ex.push_back(uc); } vector connect_in_tree(const vertex* u, const vertex* v) const { diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index 93917042e..c2a96177d 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -539,10 +539,12 @@ namespace arith { if (x->get_root() == y->get_root()) return; reset_evidence(); - set_evidence(ci1); - set_evidence(ci2); + m_explanation.clear(); + consume(rational::one(), ci1); + consume(rational::one(), ci2); ++m_stats.m_fixed_eqs; - auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y); + auto* hint = explain_implied_eq(m_explanation, x, y); + auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y, hint); ctx.propagate(x, y, jst->to_index()); } diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 77c51d87c..9fe7540e0 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -32,7 +32,7 @@ namespace arith { } arith_proof_hint* arith_proof_hint_builder::mk(euf::solver& s) { - return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); + return new (s.get_region()) arith_proof_hint(m_ty, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); } std::ostream& solver::display(std::ostream& out) const { @@ -164,7 +164,6 @@ namespace arith { return nullptr; m_arith_hint.set_type(ctx, hint_type::implied_eq_h); explain_assumptions(e); - m_arith_hint.set_num_le(1); // TODO m_arith_hint.add_diseq(a, b); return m_arith_hint.mk(ctx); } @@ -173,13 +172,19 @@ namespace arith { if (!ctx.use_drat()) return nullptr; m_arith_hint.set_type(ctx, hint_type::implied_eq_h); - m_arith_hint.set_num_le(1); m_arith_hint.add_lit(rational(1), le); m_arith_hint.add_lit(rational(1), ge); m_arith_hint.add_lit(rational(1), ~eq); return m_arith_hint.mk(ctx); } + /** + * The expected format is: + * 1. all equalities + * 2. all inequalities + * 3. optional disequalities (used for the steps that propagate equalities) + */ + expr* arith_proof_hint::get_hint(euf::solver& s) const { ast_manager& m = s.get_manager(); family_id fid = m.get_family_id("arith"); @@ -200,29 +205,39 @@ namespace arith { break; case hint_type::implied_eq_h: name = "implied-eq"; - args.push_back(arith.mk_int(m_num_le)); break; default: name = "unknown-arithmetic"; break; } - rational lc(1); - for (unsigned i = m_lit_head; i < m_lit_tail; ++i) - lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first)); - for (unsigned i = m_eq_head; i < m_eq_tail; ++i) { - auto [x, y, is_eq] = a.m_arith_hint.eq(i); + + auto push_eq = [&](bool is_eq, enode* x, enode* y) { if (x->get_id() > y->get_id()) std::swap(x, y); expr_ref eq(m.mk_eq(x->get_expr(), y->get_expr()), m); if (!is_eq) eq = m.mk_not(eq); args.push_back(arith.mk_int(1)); args.push_back(eq); + }; + rational lc(1); + for (unsigned i = m_lit_head; i < m_lit_tail; ++i) + lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first)); + for (unsigned i = m_eq_head; i < m_eq_tail; ++i) { + auto [x, y, is_eq] = a.m_arith_hint.eq(i); + if (is_eq) + push_eq(is_eq, x, y); } for (unsigned i = m_lit_head; i < m_lit_tail; ++i) { auto const& [coeff, lit] = a.m_arith_hint.lit(i); args.push_back(arith.mk_int(abs(coeff*lc))); args.push_back(s.literal2expr(lit)); } + for (unsigned i = m_eq_head; i < m_eq_tail; ++i) { + auto [x, y, is_eq] = a.m_arith_hint.eq(i); + if (!is_eq) + push_eq(is_eq, x, y); + } + return m.mk_app(symbol(name), args.size(), args.data(), m.mk_proof_sort()); } } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 89bccf76b..f30c29872 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -713,10 +713,11 @@ namespace arith { ++m_stats.m_fixed_eqs; reset_evidence(); - set_evidence(ci1); - set_evidence(ci2); - set_evidence(ci3); - set_evidence(ci4); + m_explanation.clear(); + consume(rational::one(), ci1); + consume(rational::one(), ci2); + consume(rational::one(), ci3); + consume(rational::one(), ci4); enode* x = var2enode(v1); enode* y = var2enode(v2); auto* ex = explain_implied_eq(m_explanation, x, y); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 8922673c0..4ff46ba13 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -57,10 +57,9 @@ namespace arith { struct arith_proof_hint : public euf::th_proof_hint { hint_type m_ty; - unsigned m_num_le; unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail; - arith_proof_hint(hint_type t, unsigned num_le, unsigned lh, unsigned lt, unsigned eh, unsigned et): - m_ty(t), m_num_le(num_le), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {} + arith_proof_hint(hint_type t, unsigned lh, unsigned lt, unsigned eh, unsigned et): + m_ty(t), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {} expr* get_hint(euf::solver& s) const override; }; @@ -68,7 +67,6 @@ namespace arith { vector> m_literals; svector> m_eqs; hint_type m_ty; - unsigned m_num_le = 0; unsigned m_lit_head = 0, m_lit_tail = 0, m_eq_head = 0, m_eq_tail = 0; void reset() { m_lit_head = m_lit_tail; m_eq_head = m_eq_tail; } void add(euf::enode* a, euf::enode* b, bool is_eq) { @@ -80,7 +78,6 @@ namespace arith { } public: void set_type(euf::solver& ctx, hint_type ty); - void set_num_le(unsigned n) { m_num_le = n; } void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); } void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); } void add_lit(rational const& coeff, literal lit) { diff --git a/src/sat/smt/arith_theory_checker.h b/src/sat/smt/arith_theory_checker.h index 65c647658..87868d940 100644 --- a/src/sat/smt/arith_theory_checker.h +++ b/src/sat/smt/arith_theory_checker.h @@ -35,6 +35,15 @@ The module assumes a limited repertoire of arithmetic proof rules. namespace arith { class theory_checker : public euf::theory_checker_plugin { + + enum rule_type_t { + cut_t, + farkas_t, + implied_eq_t, + bound_t, + none_t + }; + struct row { obj_map m_coeffs; rational m_coeff; @@ -42,6 +51,9 @@ namespace arith { m_coeffs.reset(); m_coeff = 0; } + bool is_zero() const { + return m_coeffs.empty() && m_coeff == 0; + } }; ast_manager& m; @@ -50,10 +62,24 @@ namespace arith { bool m_strict = false; row m_ineq; row m_conseq; - vector m_eqs; - symbol m_farkas; - symbol m_implied_eq; - symbol m_bound; + vector m_eqs, m_ineqs; + symbol m_farkas = symbol("farkas"); + symbol m_implied_eq = symbol("implied-eq"); + symbol m_bound = symbol("bound"); + symbol m_cut = symbol("cut"); + + rule_type_t rule_type(app* jst) const { + if (jst->get_name() == m_cut) + return cut_t; + if (jst->get_name() == m_bound) + return bound_t; + if (jst->get_name() == m_implied_eq) + return implied_eq_t; + if (jst->get_name() == m_farkas) + return farkas_t; + return none_t; + } + void add(row& r, expr* v, rational const& coeff) { rational coeff1; @@ -90,10 +116,10 @@ namespace arith { // X = lcm(a,b)/b, Y = -lcm(a,b)/a if v is integer // X = 1/b, Y = -1/a if v is real // - void resolve(expr* v, row& dst, rational const& A, row const& src) { + bool resolve(expr* v, row& dst, rational const& A, row const& src) { rational B, x, y; if (!dst.m_coeffs.find(v, B)) - return; + return false; if (a.is_int(v)) { rational lc = lcm(abs(A), abs(B)); x = lc / abs(B); @@ -109,6 +135,7 @@ namespace arith { y.neg(); mul(dst, x); add(dst, src, y); + return true; } void cut(row& r) { @@ -197,6 +224,8 @@ namespace arith { resolve(v, m_eqs[j], coeff, r); resolve(v, m_ineq, coeff, r); resolve(v, m_conseq, coeff, r); + for (auto& ineq : m_ineqs) + resolve(v, ineq, coeff, r); } return true; } @@ -269,6 +298,81 @@ namespace arith { return false; } + /** + Check implied equality lemma: + + inequalities & equalities => equality + + + We may assume the set of inequality assumptions we are given are all tight, non-strict and imply equalities. + In other words, given a set of inequalities a1x + b1 <= 0, ..., anx + bn <= 0 + the equalities a1x + b1 = 0, ..., anx + bn = 0 are all consequences. + + We use a weaker property: We derive implied equalities by applying exhaustive Fourier-Motzkin + elimination and then collect the tight 0 <= 0 inequalities that are derived. + + Claim: the set of inequalities used to derive 0 <= 0 are all tight equalities. + */ + + svector> m_deps; + unsigned_vector m_tight_inequalities; + uint_set m_ineqs_that_are_eqs; + + bool check_implied_eq() { + if (!reduce_eq()) + return true; + if (m_conseq.is_zero()) + return true; + + m_eqs.reset(); + m_deps.reset(); + unsigned orig_size = m_ineqs.size(); + m_deps.reserve(orig_size); + for (unsigned i = 0; i < m_ineqs.size(); ++i) { + row& r = m_ineqs[i]; + if (r.is_zero()) { + m_tight_inequalities.push_back(i); + continue; + } + auto const& [v, coeff] = *r.m_coeffs.begin(); + unsigned sz = m_ineqs.size(); + + for (unsigned j = i + 1; j < sz; ++j) { + rational B; + row& r2 = m_ineqs[j]; + if (!r2.m_coeffs.find(v, B) || (coeff > 0 && B > 0) || (coeff < 0 && B < 0)) + continue; + row& r3 = fresh(m_ineqs); + add(r3, m_ineqs[j], rational::one()); + resolve(v, r3, coeff, m_ineqs[i]); + m_deps.push_back({i, j}); + } + SASSERT(m_deps.size() == m_ineqs.size()); + } + + m_ineqs_that_are_eqs.reset(); + while (!m_tight_inequalities.empty()) { + unsigned j = m_tight_inequalities.back(); + m_tight_inequalities.pop_back(); + if (m_ineqs_that_are_eqs.contains(j)) + continue; + m_ineqs_that_are_eqs.insert(j); + if (j < orig_size) { + m_eqs.push_back(m_ineqs[j]); + } + else { + auto [a, b] = m_deps[j]; + m_tight_inequalities.push_back(a); + m_tight_inequalities.push_back(b); + } + } + m_ineqs.reset(); + + VERIFY (reduce_eq()); + + return m_conseq.is_zero(); + } + std::ostream& display_row(std::ostream& out, row const& r) { bool first = true; for (auto const& [v, coeff] : r.m_coeffs) { @@ -306,22 +410,21 @@ namespace arith { public: theory_checker(ast_manager& m): m(m), - a(m), - m_farkas("farkas"), - m_implied_eq("implied-eq"), - m_bound("bound") {} + a(m) {} void reset() { m_ineq.reset(); m_conseq.reset(); m_eqs.reset(); + m_ineqs.reset(); m_strict = false; } - bool add_ineq(rational const& coeff, expr* e, bool sign) { - return add_literal(m_ineq, abs(coeff), e, sign); + bool add_ineq(rule_type_t rt, rational const& coeff, expr* e, bool sign) { + row& r = rt == implied_eq_t ? fresh(m_ineqs) : m_ineq; + return add_literal(r, abs(coeff), e, sign); } - + bool add_conseq(rational const& coeff, expr* e, bool sign) { return add_literal(m_conseq, abs(coeff), e, sign); } @@ -332,11 +435,17 @@ namespace arith { linearize(r, rational(-1), b); } - bool check() { - if (m_conseq.m_coeffs.empty()) + bool check(rule_type_t rt) { + switch (rt) { + case farkas_t: return check_farkas(); - else + case bound_t: return check_bound(); + case implied_eq_t: + return check_implied_eq(); + default: + return check_bound(); + } } std::ostream& display(std::ostream& out) { @@ -359,7 +468,7 @@ namespace arith { /** Add implied equality as an inequality */ - bool add_implied_ineq(bool sign, app* jst) { + bool add_implied_diseq(bool sign, app* jst) { unsigned n = jst->get_num_args(); if (n < 2) return false; @@ -374,90 +483,57 @@ namespace arith { return false; if (!sign) coeff.neg(); - auto& r = m_ineq; + auto& r = m_conseq; linearize(r, coeff, arg1); linearize(r, -coeff, arg2); - m_strict = true; return true; } bool check(app* jst) override { reset(); - bool is_bound = jst->get_name() == m_bound; - bool is_implied_eq = jst->get_name() == m_implied_eq; - bool is_farkas = jst->get_name() == m_farkas; - if (!is_farkas && !is_bound && !is_implied_eq) { + + auto rt = rule_type(jst); + switch (rt) { + case cut_t: + return false; + case none_t: IF_VERBOSE(0, verbose_stream() << "unhandled inference " << mk_pp(jst, m) << "\n"); return false; + default: + break; } bool even = true; rational coeff; expr* x, * y; - unsigned j = 0, num_le = 0; - + unsigned j = 0; for (expr* arg : *jst) { + if (even) { if (!a.is_numeral(arg, coeff)) { IF_VERBOSE(0, verbose_stream() << "not numeral " << mk_pp(jst, m) << "\n"); return false; } - if (is_implied_eq) { - is_implied_eq = false; - if (!coeff.is_unsigned()) { - IF_VERBOSE(0, verbose_stream() << "not unsigned " << mk_pp(jst, m) << "\n"); - return false; - } - num_le = coeff.get_unsigned(); - if (!add_implied_ineq(false, jst)) { - IF_VERBOSE(0, display(verbose_stream() << "did not add implied eq")); - return false; - } - ++j; - continue; - } } else { bool sign = m.is_not(arg, arg); if (a.is_le(arg) || a.is_lt(arg) || a.is_ge(arg) || a.is_gt(arg)) { - if (is_bound && j + 1 == jst->get_num_args()) + if (rt == bound_t && j + 1 == jst->get_num_args()) add_conseq(coeff, arg, sign); - else if (num_le > 0) { - add_ineq(coeff, arg, sign); - --num_le; - if (num_le == 0) { - // we processed all the first inequalities, - // check that they imply one half of the implied equality. - if (!check()) { - // we might have added the wrong direction of the implied equality. - // so try the opposite inequality. - add_implied_ineq(true, jst); - add_implied_ineq(true, jst); - if (check()) { - reset(); - add_implied_ineq(false, jst); - } - else { - IF_VERBOSE(0, display(verbose_stream() << "failed to check implied eq ")); - return false; - } - } - else { - reset(); - VERIFY(add_implied_ineq(true, jst)); - } - } - } else - add_ineq(coeff, arg, sign); + add_ineq(rt, coeff, arg, sign); } else if (m.is_eq(arg, x, y)) { - if (is_bound && j + 1 == jst->get_num_args()) + if (rt == bound_t && j + 1 == jst->get_num_args()) add_conseq(coeff, arg, sign); - else if (sign) - return check(); // it should be an implied equality - else + else if (rt == implied_eq_t && j + 1 == jst->get_num_args()) + return add_implied_diseq(sign, jst) && check(rt); + else if (!sign) add_eq(x, y); + else { + IF_VERBOSE(0, verbose_stream() << "unexpected disequality in justification " << mk_pp(arg, m) << "\n"); + return false; + } } else { IF_VERBOSE(0, verbose_stream() << "not a recognized arithmetical relation " << mk_pp(arg, m) << "\n"); @@ -467,13 +543,14 @@ namespace arith { even = !even; ++j; } - return check(); + return check(rt); } void register_plugins(euf::theory_checker& pc) override { pc.register_plugin(m_farkas, this); pc.register_plugin(m_bound, this); pc.register_plugin(m_implied_eq, this); + pc.register_plugin(m_cut, this); } }; diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index 39c9879a6..e55726af6 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -465,6 +465,8 @@ namespace euf { void solver::display_inferred(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) { expr_ref hint(proof_hint, m); + if (!proof_hint) + verbose_stream() << hint << "\n"; if (!hint) hint = m.mk_const(m_smt, m.mk_proof_sort()); visit_expr(out, hint); diff --git a/src/sat/smt/sat_th.cpp b/src/sat/smt/sat_th.cpp index 21e3883e8..51783f0a5 100644 --- a/src/sat/smt/sat_th.cpp +++ b/src/sat/smt/sat_th.cpp @@ -240,6 +240,9 @@ namespace euf { m_literals[i] = lits[i]; base_ptr += sizeof(literal) * n_lits; m_eqs = reinterpret_cast(base_ptr); + if (!pma) { + verbose_stream() << "null\n"; + } for (i = 0; i < n_eqs; ++i) { m_eqs[i] = eqs[i]; if (m_eqs[i].first->get_id() > m_eqs[i].second->get_id()) From 7135283135a23bc1d4c41043b1c2a1a58ddd7615 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Jul 2023 13:23:17 -0700 Subject: [PATCH 044/428] update format and checker for implied-eq Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_proof.cpp | 2 -- src/sat/smt/sat_th.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/sat/smt/euf_proof.cpp b/src/sat/smt/euf_proof.cpp index e55726af6..39c9879a6 100644 --- a/src/sat/smt/euf_proof.cpp +++ b/src/sat/smt/euf_proof.cpp @@ -465,8 +465,6 @@ namespace euf { void solver::display_inferred(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) { expr_ref hint(proof_hint, m); - if (!proof_hint) - verbose_stream() << hint << "\n"; if (!hint) hint = m.mk_const(m_smt, m.mk_proof_sort()); visit_expr(out, hint); diff --git a/src/sat/smt/sat_th.cpp b/src/sat/smt/sat_th.cpp index 51783f0a5..21e3883e8 100644 --- a/src/sat/smt/sat_th.cpp +++ b/src/sat/smt/sat_th.cpp @@ -240,9 +240,6 @@ namespace euf { m_literals[i] = lits[i]; base_ptr += sizeof(literal) * n_lits; m_eqs = reinterpret_cast(base_ptr); - if (!pma) { - verbose_stream() << "null\n"; - } for (i = 0; i < n_eqs; ++i) { m_eqs[i] = eqs[i]; if (m_eqs[i].first->get_id() > m_eqs[i].second->get_id()) From 1b2b8809c05b778589b400acc30f87297aef442f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Jul 2023 11:10:14 -0700 Subject: [PATCH 045/428] try to add Ubuntu ARM64 to nightly #6835 Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index f4573877e..9717a363f 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -63,6 +63,20 @@ stages: artifactName: 'Ubuntu' targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuArm64 + displayName: "Ubuntu ARM64 build" + pool: + vmImage: "ubuntu-latest" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: git clone https://github.com/z3prover/z3test z3test + - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 + - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'Ubuntu' + targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuDoc displayName: "Ubuntu Doc build" pool: @@ -238,6 +252,11 @@ stages: inputs: artifact: 'Ubuntu' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu ARM64 Build' + inputs: + artifact: 'UbuntuArm64' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: @@ -521,6 +540,11 @@ stages: inputs: artifactName: 'MacArm64' targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download Ubuntu Arm64" + inputs: + artifactName: 'UbuntuArm64' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Ubuntu" inputs: From 6c5434f9888e1f9aabc1d994edfb6f5c9be1a60a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Jul 2023 11:40:37 -0700 Subject: [PATCH 046/428] rename artifacts apart Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 9717a363f..d3e0737b5 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -74,7 +74,7 @@ stages: - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: - artifactName: 'Ubuntu' + artifactName: 'UbuntuArm64' targetPath: $(Build.ArtifactStagingDirectory) - job: UbuntuDoc From de1cf30ea85f6cef801504aa69951278726345f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Jul 2023 16:54:33 -0700 Subject: [PATCH 047/428] strengthen Tseitin checker to take true/false constants into account Signed-off-by: Nikolaj Bjorner --- src/sat/smt/tseitin_theory_checker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/tseitin_theory_checker.cpp b/src/sat/smt/tseitin_theory_checker.cpp index 74f4e55b0..ffddc9063 100644 --- a/src/sat/smt/tseitin_theory_checker.cpp +++ b/src/sat/smt/tseitin_theory_checker.cpp @@ -72,7 +72,7 @@ namespace tseitin { complement_mark(arg); for (expr* arg : *to_app(main_expr)) - if (!is_complement(arg)) + if (!is_complement(arg) && !m.is_true(arg)) return false; return true; @@ -178,7 +178,7 @@ namespace tseitin { for (expr* arg : *jst) mark(arg); for (expr* arg : *to_app(a)) - if (!is_marked(arg)) + if (!is_marked(arg) && !m.is_false(arg)) return false; return true; } From 0606ca15d987722bfecde1a4572f1f2866c29ea3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Jul 2023 17:40:56 -0700 Subject: [PATCH 048/428] track lia conflicts as cuts Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_diagnostics.cpp | 7 +++++-- src/sat/smt/arith_solver.cpp | 16 ++++++++-------- src/sat/smt/arith_solver.h | 9 +++++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 9fe7540e0..e9d989545 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -133,7 +133,7 @@ namespace arith { return m_arith_hint.mk(ctx); } - arith_proof_hint const* solver::explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { + arith_proof_hint const* solver::explain_conflict(hint_type ty, sat::literal_vector const& core, euf::enode_pair_vector const& eqs) { arith_proof_hint* hint = nullptr; if (ctx.use_drat()) { m_coeffs.reset(); @@ -142,7 +142,7 @@ namespace arith { m_coeffs.push_back(e.coeff()); } - m_arith_hint.set_type(ctx, hint_type::farkas_h); + m_arith_hint.set_type(ctx, ty); if (m_coeffs.size() == core.size()) { unsigned i = 0; for (auto lit : core) @@ -206,6 +206,9 @@ namespace arith { case hint_type::implied_eq_h: name = "implied-eq"; break; + case hint_type::nla_h: + name = "nla"; + break; default: name = "unknown-arithmetic"; break; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index f30c29872..5784962a5 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1155,7 +1155,7 @@ namespace arith { case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core - set_conflict(); + set_conflict(hint_type::cut_h); return l_false; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); @@ -1187,15 +1187,15 @@ namespace arith { void solver::get_infeasibility_explanation_and_set_conflict() { m_explanation.clear(); lp().get_infeasibility_explanation(m_explanation); - set_conflict(); + set_conflict(hint_type::farkas_h); } - void solver::set_conflict() { + void solver::set_conflict(hint_type ty) { literal_vector core; - set_conflict_or_lemma(core, true); + set_conflict_or_lemma(ty, core, true); } - void solver::set_conflict_or_lemma(literal_vector const& core, bool is_conflict) { + void solver::set_conflict_or_lemma(hint_type ty, literal_vector const& core, bool is_conflict) { reset_evidence(); m_core.append(core); for (auto ev : m_explanation) @@ -1212,7 +1212,7 @@ namespace arith { for (auto p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root())); ++m_num_conflicts; ++m_stats.m_conflicts; - auto* hint = explain_conflict(m_core, m_eqs); + auto* hint = explain_conflict(ty, m_core, m_eqs); ctx.set_conflict(euf::th_explain::conflict(*this, m_core, m_eqs, hint)); } else { @@ -1221,7 +1221,7 @@ namespace arith { for (literal& c : m_core) c.neg(); - add_redundant(m_core, explain(hint_type::farkas_h)); + add_redundant(m_core, explain(ty)); } } @@ -1428,7 +1428,7 @@ namespace arith { lit = ctx.expr2literal(mk_bound(ineq.term(), ineq.rs(), is_lower)); core.push_back(pos ? lit : ~lit); } - set_conflict_or_lemma(core, false); + set_conflict_or_lemma(hint_type::nla_h, core, false); } lbool solver::check_nla() { diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 4ff46ba13..e5ba06ae6 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -52,7 +52,8 @@ namespace arith { farkas_h, bound_h, cut_h, - implied_eq_h + implied_eq_h, + nla_h }; struct arith_proof_hint : public euf::th_proof_hint { @@ -457,8 +458,8 @@ namespace arith { void term2coeffs(lp::lar_term const& term, u_map& coeffs); void get_infeasibility_explanation_and_set_conflict(); - void set_conflict(); - void set_conflict_or_lemma(literal_vector const& core, bool is_conflict); + void set_conflict(hint_type ty); + void set_conflict_or_lemma(hint_type ty, literal_vector const& core, bool is_conflict); void set_evidence(lp::constraint_index idx); void assign(literal lit, literal_vector const& core, svector const& eqs, euf::th_proof_hint const* pma); @@ -470,7 +471,7 @@ namespace arith { arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal); arith_proof_hint const* explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b); arith_proof_hint const* explain_trichotomy(sat::literal le, sat::literal ge, sat::literal eq); - arith_proof_hint const* explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs); + arith_proof_hint const* explain_conflict(hint_type ty, sat::literal_vector const& core, euf::enode_pair_vector const& eqs); void explain_assumptions(lp::explanation const& e); From afe1218bc6ae3cb89bd073846152160548e5cf0d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 31 Jul 2023 10:46:16 -0700 Subject: [PATCH 049/428] update release.yml with linux-arm64 Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/release.yml b/scripts/release.yml index a1306d87f..561b3084d 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -114,6 +114,20 @@ stages: artifactName: 'UbuntuBuild20' targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuArm64 + displayName: "Ubuntu ARM64 build" + pool: + vmImage: "ubuntu-latest" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: git clone https://github.com/z3prover/z3test z3test + - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 + - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'UbuntuArm64' + targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuDoc displayName: "Ubuntu Doc build" pool: @@ -225,6 +239,11 @@ stages: inputs: artifact: 'UbuntuBuild20' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu ARM64 Build' + inputs: + artifact: 'UbuntuArm64' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: @@ -480,6 +499,11 @@ stages: inputs: artifact: 'UbuntuBuild' path: $(Agent.TempDirectory) + - task: DownloadPipelineArtifact@2 + displayName: "Download Ubuntu Arm64" + inputs: + artifactName: 'UbuntuArm64' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Doc" inputs: From 403340498c4c0a150893b3b61cd70e4ec3302af5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 31 Jul 2023 19:41:11 -0700 Subject: [PATCH 050/428] format --- src/math/lp/lp_core_solver_base_def.h | 29 +++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 1687786a8..101f9dbea 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -115,12 +115,11 @@ pretty_print(std::ostream & out) { template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { - m_x[entering] += delta; - - for (const auto & c : m_A.m_columns[entering]) { - unsigned i = c.var(); - m_x[m_basis[i]] -= delta * m_A.get_val(c); - } + m_x[entering] += delta; + for (const auto & c : m_A.m_columns[entering]) { + unsigned i = c.var(); + m_x[m_basis[i]] -= delta * m_A.get_val(c); + } } @@ -404,17 +403,17 @@ template void lp_core_solver_base::transpose_row transpose_basis(i, j); m_A.transpose_rows(i, j); } + // j is the new basic column, j_basic - the leaving column template bool lp_core_solver_base::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w) { - lp_assert(m_basis_heading[j] < 0); - lp_assert(m_basis_heading[j_basic] >= 0); - unsigned row_index = m_basis_heading[j_basic]; - // the tableau case - if (pivot_column_tableau(j, row_index)) - change_basis(j, j_basic); - else return false; - - return true; + lp_assert(m_basis_heading[j] < 0); + lp_assert(m_basis_heading[j_basic] >= 0); + unsigned row_index = m_basis_heading[j_basic]; + // the tableau case + if (!pivot_column_tableau(j, row_index)) + return false; + change_basis(j, j_basic); + return true; } From adad468cd7ea30f0530f86fcce8eeeb1cb46ee55 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 31 Jul 2023 19:46:08 -0700 Subject: [PATCH 051/428] allow copy within a user scope #6827 this will allow copying the solver state within a scope. The new solver state has its state at level 0. It is not possible to pop scopes from the new solver (you can still pop scopes from the original solver). The reason for this semantics is the relative difficulty of implementing (getting it right) of a state copy that preserves scopes. --- src/sat/sat_solver.cpp | 3 ++- src/sat/sat_solver/inc_sat_solver.cpp | 3 --- src/smt/smt_context.cpp | 5 ++--- src/smt/smt_internalizer.cpp | 5 +++-- src/smt/smt_kernel.cpp | 4 ++-- src/smt/smt_kernel.h | 2 +- src/smt/smt_solver.cpp | 16 ++++++++-------- src/solver/assertions/asserted_formulas.cpp | 6 +++++- src/solver/tactic2solver.cpp | 3 --- 9 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index e636ab110..b899ee479 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -238,7 +238,8 @@ namespace sat { } m_user_scope_literals.reset(); - m_user_scope_literals.append(src.m_user_scope_literals); + for (auto lit : src.m_user_scope_literals) + assign_unit(~lit); m_mc = src.m_mc; m_stats.m_units = init_trail_size(); diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 8bf665ebf..4574d3da3 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -115,9 +115,6 @@ public: } solver* translate(ast_manager& dst_m, params_ref const& p) override { - if (m_num_scopes > 0) { - throw default_exception("Cannot translate sat solver at non-base level"); - } ast_translation tr(m, dst_m); m_solver.pop_to_base_level(); inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental()); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 7902a3440..27494cb58 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -154,9 +154,8 @@ namespace smt { src_af.get_macro_manager().copy_to(dst_af.get_macro_manager()); - if (!src_ctx.m_setup.already_configured()) { + if (!src_ctx.m_setup.already_configured()) return; - } for (unsigned i = 0; !src_m.proofs_enabled() && i < src_ctx.m_assigned_literals.size(); ++i) { literal lit = src_ctx.m_assigned_literals[i]; @@ -3233,7 +3232,7 @@ namespace smt { } expr * f = m_asserted_formulas.get_formula(qhead); proof * pr = m_asserted_formulas.get_formula_proof(qhead); - SASSERT(!pr || f == m.get_fact(pr)); + SASSERT(!pr || f == m.get_fact(pr)); internalize_assertion(f, pr, 0); ++qhead; } diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 68879b8ac..0e9e39996 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -297,9 +297,10 @@ namespace smt { void context::assert_default(expr * n, proof * pr) { internalize(n, true); literal l = get_literal(n); - if (l == false_literal) { + if (l == false_literal) set_conflict(mk_justification(justification_proof_wrapper(*this, pr))); - } + else if (l == true_literal) + return; else { justification* j = mk_justification(justification_proof_wrapper(*this, pr)); m_clause_proof.add(l, CLS_AUX, j); diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index c4ecf6787..74f0bded6 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -63,8 +63,8 @@ namespace smt { return m_imp->m_kernel.get_manager(); } - void kernel::copy(kernel& src, kernel& dst) { - context::copy(src.m_imp->m_kernel, dst.m_imp->m_kernel); + void kernel::copy(kernel& src, kernel& dst, bool override_base) { + context::copy(src.m_imp->m_kernel, dst.m_imp->m_kernel, override_base); } bool kernel::set_logic(symbol logic) { diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index ccea5caf8..dacbb525e 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -50,7 +50,7 @@ namespace smt { ~kernel(); - static void copy(kernel& src, kernel& dst); + static void copy(kernel& src, kernel& dst, bool override_base); ast_manager & m() const; diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index d415812b0..f91a31111 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -20,7 +20,7 @@ Notes: #include "util/dec_ref_util.h" #include "ast/reg_decl_plugins.h" #include "ast/for_each_expr.h" -#include "ast/ast_smt2_pp.h" +#include "ast/ast_pp.h" #include "ast/func_decl_dependencies.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" @@ -88,14 +88,14 @@ namespace { ast_translation translator(get_manager(), m); smt_solver * result = alloc(smt_solver, m, p, m_logic); - smt::kernel::copy(m_context, result->m_context); + smt::kernel::copy(m_context, result->m_context, true); if (mc0()) result->set_model_converter(mc0()->translate(translator)); - for (auto & kv : m_name2assertion) { - expr* val = translator(kv.m_value); - expr* key = translator(kv.m_key); + for (auto & [k, v] : m_name2assertion) { + expr* val = translator(k); + expr* key = translator(v); result->assert_expr(val, key); } @@ -104,9 +104,9 @@ namespace { ~smt_solver() override { dealloc(m_cuber); - for (auto& kv : m_name2assertion) { - get_manager().dec_ref(kv.m_key); - get_manager().dec_ref(kv.m_value); + for (auto& [k,v] : m_name2assertion) { + get_manager().dec_ref(k); + get_manager().dec_ref(v); } } diff --git a/src/solver/assertions/asserted_formulas.cpp b/src/solver/assertions/asserted_formulas.cpp index 4e64ee39f..ee94ac355 100644 --- a/src/solver/assertions/asserted_formulas.cpp +++ b/src/solver/assertions/asserted_formulas.cpp @@ -161,6 +161,9 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { if (inconsistent()) return; + if (m.is_true(e)) + return; + if (m_smt_params.m_preprocess) { TRACE("assert_expr_bug", tout << r << "\n";); set_eliminate_and(false); // do not eliminate and before nnf. @@ -507,7 +510,8 @@ void asserted_formulas::simplify_fmls::operator()() { else { af.push_assertion(result, result_pr, new_fmls); } - if (af.canceled()) return; + if (af.canceled()) + return; } af.swap_asserted_formulas(new_fmls); TRACE("asserted_formulas", af.display(tout);); diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 8ecfb3680..861a83185 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -294,9 +294,6 @@ solver* tactic2solver::translate(ast_manager& m, params_ref const& p) { tactic* t = m_tactic->translate(m); tactic2solver* r = alloc(tactic2solver, m, t, p, m_produce_proofs, m_produce_models, m_produce_unsat_cores, m_logic); r->m_result = nullptr; - if (!m_scopes.empty()) { - throw default_exception("translation of contexts is only supported at base level"); - } ast_translation tr(m_assertions.get_manager(), m, false); for (unsigned i = 0; i < get_num_assertions(); ++i) { From 5b2519d7a36e2a3c1bd95e759fb7345c0bd4871c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Aug 2023 08:41:26 -0700 Subject: [PATCH 052/428] #6523 attach original variable to pb expression. --- src/ast/simplifiers/solve_eqs.cpp | 7 ++++--- src/sat/smt/pb_internalize.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index fbc6fbb02..c97cab7c9 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -212,12 +212,13 @@ namespace euf { auto [f, p, d] = m_fmls[i](); auto [new_f, new_dep] = rp->replace_with_dep(f); proof_ref new_pr(m); - m_rewriter(new_f, new_f, new_pr); - if (new_f == f) + expr_ref tmp(m); + m_rewriter(new_f, tmp, new_pr); + if (tmp == f) continue; new_dep = m.mk_join(d, new_dep); old_fmls.push_back(m_fmls[i]); - m_fmls.update(i, dependent_expr(m, new_f, mp(p, new_pr), new_dep)); + m_fmls.update(i, dependent_expr(m, tmp, mp(p, new_pr), new_dep)); } } diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 42b06872a..391b643f5 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -30,7 +30,7 @@ namespace pb { if (m_pb.is_pb(e)) { sat::literal lit = internalize_pb(e, sign, root); if (m_ctx && !root && lit != sat::null_literal) - m_ctx->attach_lit(lit, e); + m_ctx->attach_lit(literal(lit.var(), false), e); return lit; } UNREACHABLE(); From 51d3c279d0656a729c1fe27249f7fd5f3f59e71c Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 2 Aug 2023 12:34:06 -0400 Subject: [PATCH 053/428] QEL: Fast Approximated Quantifier Elimination (#6820) * qe_lite: cleanup and comment no change to code * mbp_arrays: refactor out partial equality (peq) Partial array equality, PEQ, is used as an intermediate expression during MBP for arrays. We need to factor it out so that it can be shared between MBP-QEL and existing MBP. Partial array equality (peq) is used in MBP for arrays. Factoring this out to be used by multiple MBP implementations. * rewriter: new rewrite rules These rules are specializes for terms that are created in QEL. QEL commit is comming later * datatype_rw: new rewrite rule for ADTs The rule handles this special case: (cons (head x) (tail x)) --> x * array_rewriter rules for rewriting PEQs Special rules to simplify PEQs * th_rewriter: wire PEQ simplifications * spacer_iuc: avoid terms with default in IUC Spacer prfers to not have a term representing default value of an array. This guides IUC from picking such terms in interpolation * mbp_term_graph: replace root with repr * mbp_term_graph: formatting * mbp_term_graph: class_props, getters, setters Class properties allow to keep information for an equivalence class. Getters and setters for terms allow accessing information * mbp_term_graph: auxiliary methods for qel QEL commit is comming later in the history * mbp_term_graph: bug fix * mbp_term_graph: pick, refine repr, compute cgrnd * mbp_term_graph: internalize deq * mbp_term_graph: constructor * mbp_term_graph: optionally internalize equalities Reperesent equalities explicitly by nodes in the term_graph * qel * formatting * comments on term_lt * get terms and other api for mbp_qel * plugins for mbp_qel * mbp_qel_util: utilities for mbp_qel * qe_mbp: QEL-based mbp * qel: expose QEL API * spacer: replace qe_lite in qe_project_spacer by qel This changes the default projection engine that spacer uses. * cmd_context: debug commands for qel and mbp_qel New commands are mbp-qel -- MBP with term graphs qel -- QEL with term graphs qe-lite -- older qelite * qe_mbp: model-based rewriters for arrays * qe_mbp: QEL-based projection functions * qsat: wire in QEL-based mbp * qsat: debug code * qsat: maybe a bug fix Changed the code to follow the paper by adding all predicates above a given level, not just predicates of immediately preceding level. * chore: use new api to create solver in qsat * mbp_term_graph use all_of idiom * feat: solver for integer multiplication * array_peq: formatting, no change to code * mbp_qel_util: block comment + format * mbt_term_graph: clang-format * bug fix. Move dt rewrite to qe_mbp * array_peq: add header * run clang format on mbp plugins * clang format on mul solver * format do-while * format * format do-while * update release notes --------- Co-authored-by: hgvk94 Co-authored-by: Isabel Garcia --- RELEASE_NOTES.md | 1 + src/ast/CMakeLists.txt | 1 + src/ast/array_peq.cpp | 107 + src/ast/array_peq.h | 91 + src/ast/rewriter/array_rewriter.cpp | 43 +- src/ast/rewriter/datatype_rewriter.cpp | 3 +- src/ast/rewriter/th_rewriter.cpp | 5 + src/cmd_context/extra_cmds/dbg_cmds.cpp | 196 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 2 +- src/muz/spacer/spacer_util.cpp | 20 +- src/muz/spacer/spacer_util.h | 1 + src/qe/lite/CMakeLists.txt | 2 + src/qe/lite/qe_lite_tactic.cpp | 6 +- src/qe/lite/qe_lite_tactic.h | 1 + src/qe/lite/qel.cpp | 54 + src/qe/lite/qel.h | 49 + src/qe/mbp/CMakeLists.txt | 5 + src/qe/mbp/mbp_arrays.cpp | 142 +- src/qe/mbp/mbp_arrays_tg.cpp | 394 +++ src/qe/mbp/mbp_arrays_tg.h | 47 + src/qe/mbp/mbp_basic_tg.cpp | 101 + src/qe/mbp/mbp_basic_tg.h | 40 + src/qe/mbp/mbp_dt_tg.cpp | 202 ++ src/qe/mbp/mbp_dt_tg.h | 44 + src/qe/mbp/mbp_qel.cpp | 226 ++ src/qe/mbp/mbp_qel.h | 41 + src/qe/mbp/mbp_qel_util.cpp | 110 + src/qe/mbp/mbp_qel_util.h | 41 + src/qe/mbp/mbp_solve_plugin.cpp | 92 +- src/qe/mbp/mbp_term_graph.cpp | 2926 +++++++++++-------- src/qe/mbp/mbp_term_graph.h | 355 ++- src/qe/mbp/mbp_tg_plugins.h | 34 + src/qe/qe_mbp.cpp | 256 +- src/qe/qsat.cpp | 46 +- src/smt/params/smt_params_helper.pyg | 3 +- 35 files changed, 4170 insertions(+), 1517 deletions(-) create mode 100644 src/ast/array_peq.cpp create mode 100644 src/ast/array_peq.h create mode 100644 src/qe/lite/qel.cpp create mode 100644 src/qe/lite/qel.h create mode 100644 src/qe/mbp/mbp_arrays_tg.cpp create mode 100644 src/qe/mbp/mbp_arrays_tg.h create mode 100644 src/qe/mbp/mbp_basic_tg.cpp create mode 100644 src/qe/mbp/mbp_basic_tg.h create mode 100644 src/qe/mbp/mbp_dt_tg.cpp create mode 100644 src/qe/mbp/mbp_dt_tg.h create mode 100644 src/qe/mbp/mbp_qel.cpp create mode 100644 src/qe/mbp/mbp_qel.h create mode 100644 src/qe/mbp/mbp_qel_util.cpp create mode 100644 src/qe/mbp/mbp_qel_util.h create mode 100644 src/qe/mbp/mbp_tg_plugins.h diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9e92195d4..f46f32a1a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,6 +9,7 @@ Version 4.next - polysat - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. + - Light quantifier elimination based on term graphs (egraphs), and corresponding Model Based Projection for arrays and ADTs. Used by Spacer and QSAT. Version 4.12.3 ============== diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index 9df3ff001..7a4a03a27 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(ast act_cache.cpp arith_decl_plugin.cpp array_decl_plugin.cpp + array_peq.cpp ast.cpp ast_ll_pp.cpp ast_lt.cpp diff --git a/src/ast/array_peq.cpp b/src/ast/array_peq.cpp new file mode 100644 index 000000000..9f4f1b10d --- /dev/null +++ b/src/ast/array_peq.cpp @@ -0,0 +1,107 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + array_peq.cpp + +Abstract: + + Partial equality for arrays + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + Hari Govind V K + +Revision History: + +--*/ +#include "ast/array_peq.h" + +#define PARTIAL_EQ "!partial_eq" +bool is_partial_eq(const func_decl *f) { + SASSERT(f); + return f->get_name() == PARTIAL_EQ; +} + +bool is_partial_eq(const app *a) { + SASSERT(a); + return is_partial_eq(a->get_decl()); +} + +app_ref mk_peq(expr *e0, expr *e1, vector const &indices, + ast_manager &m) { + peq p(e0, e1, indices, m); + return p.mk_peq(); +} + +app_ref peq::mk_eq(app_ref_vector &aux_consts, bool stores_on_rhs) { + if (!m_eq) { + expr_ref lhs(m_lhs, m), rhs(m_rhs, m); + if (!stores_on_rhs) { std::swap(lhs, rhs); } + // lhs = (...(store (store rhs i0 v0) i1 v1)...) + sort *val_sort = get_array_range(lhs->get_sort()); + for (expr_ref_vector const &diff : m_diff_indices) { + ptr_vector store_args; + store_args.push_back(rhs); + store_args.append(diff.size(), diff.data()); + app_ref val(m.mk_fresh_const("diff", val_sort), m); + store_args.push_back(val); + aux_consts.push_back(val); + rhs = m_arr_u.mk_store(store_args); + } + m_eq = m.mk_eq(lhs, rhs); + } + return m_eq; +} + +app_ref peq::mk_peq() { + if (!m_peq) { + ptr_vector args; + args.push_back(m_lhs); + args.push_back(m_rhs); + for (auto const &v : m_diff_indices) { + args.append(v.size(), v.data()); + } + m_peq = m.mk_app(m_decl, args.size(), args.data()); + } + return m_peq; +} + +peq::peq(expr *lhs, expr *rhs, vector const &diff_indices, + ast_manager &m) + : m(m), m_lhs(lhs, m), m_rhs(rhs, m), m_diff_indices(diff_indices), + m_decl(m), m_peq(m), m_eq(m), m_arr_u(m) { + SASSERT(m_arr_u.is_array(lhs)); + SASSERT(m_arr_u.is_array(rhs)); + SASSERT(lhs->get_sort() == rhs->get_sort()); + ptr_vector sorts; + sorts.push_back(m_lhs->get_sort()); + sorts.push_back(m_rhs->get_sort()); + + for (auto const &v : diff_indices) { + SASSERT(v.size() == get_array_arity(m_lhs->get_sort())); + for (expr *e : v) sorts.push_back(e->get_sort()); + } + m_decl = m.mk_func_decl(symbol(PARTIAL_EQ), sorts.size(), sorts.data(), + m.mk_bool_sort()); +} + +peq::peq(app *p, ast_manager &m) + : m(m), m_lhs(p->get_arg(0), m), m_rhs(p->get_arg(1), m), + m_decl(p->get_decl(), m), m_peq(p, m), m_eq(m), m_arr_u(m), + m_name(symbol(PARTIAL_EQ)) { + SASSERT(is_partial_eq(p)); + + SASSERT(m_arr_u.is_array(m_lhs)); + SASSERT(m_arr_u.is_array(m_rhs)); + SASSERT(m_lhs->get_sort() == m_rhs->get_sort()); + unsigned arity = get_array_arity(m_lhs->get_sort()); + for (unsigned i = 2; i < p->get_num_args(); i += arity) { + SASSERT(arity + i <= p->get_num_args()); + expr_ref_vector vec(m); + vec.append(arity, p->get_args() + i); + m_diff_indices.push_back(std::move(vec)); + } +} diff --git a/src/ast/array_peq.h b/src/ast/array_peq.h new file mode 100644 index 000000000..9e71791c1 --- /dev/null +++ b/src/ast/array_peq.h @@ -0,0 +1,91 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + array_peq.h + +Abstract: + + Partial equality for arrays + +Author: + + Nikolaj Bjorner (nbjorner) 2015-06-13 + Hari Govind V K + +Revision History: + +--*/ +#pragma once + +#include "ast/array_decl_plugin.h" +#include "ast/ast.h" + +/** + * \brief utility class for partial equalities + * + * A partial equality (a ==I b), for two arrays a, b and a finite set of indices + * I holds iff (forall i :: i \not\in I => a[i] == b[i]). In other words, peq is + * a restricted form of the extensionality axiom + * + * Using this class, we denote (a =I b) as f(a,b,i0,i1,...), + * where f is an uninterpreted predicate with the name PARTIAL_EQ and + * I = {i0,i1,...} + */ + +class peq { + ast_manager &m; + expr_ref m_lhs; + expr_ref m_rhs; + vector m_diff_indices; + func_decl_ref m_decl; // the partial equality declaration + app_ref m_peq; // partial equality application + app_ref m_eq; // equivalent std equality using def. of partial eq + array_util m_arr_u; + symbol m_name; + + public: + peq(app *p, ast_manager &m); + + peq(expr *lhs, expr *rhs, vector const &diff_indices, + ast_manager &m); + + expr_ref lhs() { return m_lhs; } + + expr_ref rhs() { return m_rhs; } + + void get_diff_indices(vector &result) { + result.append(m_diff_indices); + } + + /** Convert peq into a peq expression */ + app_ref mk_peq(); + + /** Convert peq into an equality + + For peq of the form (a =I b) returns (a = b[i0 := v0, i1 := v1, ...]) + where i0, i1 \in I, and v0, v1 are fresh skolem constants + + Skolems are returned in aux_consts + + The left and right hand arguments are reversed when stores_on_rhs is + false + */ + app_ref mk_eq(app_ref_vector &aux_consts, bool stores_on_rhs = true); +}; + +/** + * mk (e0 ==indices e1) + * + * result has stores if either e0 or e1 or an index term has stores + */ +app_ref mk_peq(expr *e0, expr *e1, vector const &indices, + ast_manager &m); + +bool is_partial_eq(const func_decl *f); + +bool is_partial_eq(const app *a); + +inline bool is_peq(const func_decl *f) { return is_partial_eq(f); } +inline bool is_peq(const app *a) { return is_partial_eq(a); } diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index e580eb82d..228ba9914 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -24,6 +24,7 @@ Notes: #include "ast/rewriter/var_subst.h" #include "params/array_rewriter_params.hpp" #include "util/util.h" +#include "ast/array_peq.h" void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); @@ -40,8 +41,48 @@ void array_rewriter::get_param_descrs(param_descrs & r) { } br_status array_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { + br_status st = BR_FAILED; + + // BEGIN: rewrite rules for PEQs + if (is_partial_eq(f)) { + SASSERT(num_args >= 2); + expr *e0, *e1; + e0 = args[0]; + e1 = args[1]; + + expr_ref a(m()), val(m()); + expr_ref_vector vindex(m()); + + if (e0 == e1) { + // t peq t --> true + result = m().mk_true(); + st = BR_DONE; + } + else if (m_util.is_store_ext(e0, a, vindex, val)) { + if (num_args == 2 && a == e1) { + // (a[i := x] peq_{\emptyset} a) ---> a[i] == x + mk_select(vindex.size(), vindex.data(), result); + result = m().mk_eq(result, val); + st = BR_REWRITE_FULL; + } + else if (a == e1 && vindex.size() == num_args + 2) { + // a [i: = x] peq_{i} a -- > true + bool all_eq = true; + for (unsigned i = 0, sz = vindex.size(); all_eq && i < sz; + ++i) { + all_eq &= vindex.get(i) == args[2+i]; + } + if (all_eq) { + result = m().mk_true(); + st = BR_DONE; + } + } + } + return st; + } + // END: rewrite rules for PEQs + SASSERT(f->get_family_id() == get_fid()); - br_status st; switch (f->get_decl_kind()) { case OP_SELECT: st = mk_select_core(num_args, args, result); diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp index ba0155e97..001b697e4 100644 --- a/src/ast/rewriter/datatype_rewriter.cpp +++ b/src/ast/rewriter/datatype_rewriter.cpp @@ -21,7 +21,8 @@ Notes: br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { - case OP_DT_CONSTRUCTOR: return BR_FAILED; + case OP_DT_CONSTRUCTOR: + return BR_FAILED; case OP_DT_RECOGNISER: SASSERT(num_args == 1); result = m_util.mk_is(m_util.get_recognizer_constructor(f), args[0]); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index d4e302a5d..18b335ecb 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -39,6 +39,7 @@ Notes: #include "ast/ast_util.h" #include "ast/well_sorted.h" #include "ast/for_each_expr.h" +#include "ast/array_peq.h" namespace { struct th_rewriter_cfg : public default_rewriter_cfg { @@ -644,6 +645,10 @@ struct th_rewriter_cfg : public default_rewriter_cfg { else st = pull_ite(result); } + if (st == BR_FAILED && f->get_family_id() == null_family_id && is_partial_eq(f)) { + st = m_ar_rw.mk_app_core(f, num, args, result); + } + CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index b18d43528..c8f5deb7d 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -16,6 +16,7 @@ Notes: --*/ #include +#include "ast/ast.h" #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "ast/rewriter/rewriter.h" @@ -34,7 +35,9 @@ Notes: #include "qe/qe_mbp.h" #include "qe/qe_mbi.h" #include "qe/mbp/mbp_term_graph.h" - +#include "qe/mbp/mbp_qel.h" +#include "qe/lite/qe_lite_tactic.h" +#include "qe/lite/qel.h" BINARY_SYM_CMD(get_quantifier_body_cmd, "dbg-get-qbody", @@ -369,7 +372,7 @@ public: } vars.push_back(to_app(v)); } - qe::mbproj mbp(m); + qe::mbproj mbp(m, gparams::get_module("smt")); expr_ref fml(m_fml, m); mbp.spacer(vars, *mdl.get(), fml); ctx.regular_stream() << fml << "\n"; @@ -572,8 +575,192 @@ public: }; +class mbp_qel_cmd : public cmd { + unsigned m_arg_index; + ptr_vector m_lits; + ptr_vector m_vars; -void install_dbg_cmds(cmd_context & ctx) { + public: + mbp_qel_cmd() : cmd("mbp-qel"){}; + char const *get_usage() const override { return "(exprs) (vars)"; } + char const *get_descr(cmd_context &ctx) const override { + return "Model based projection using e-graphs"; + } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context &ctx) const override { + return CPK_EXPR_LIST; + } + void set_next_arg(cmd_context &ctx, unsigned num, + expr *const *args) override { + if (m_arg_index == 0) { + m_lits.append(num, args); + m_arg_index = 1; + } + else { m_vars.append(num, args); } + } + void prepare(cmd_context &ctx) override { + m_arg_index = 0; + m_lits.reset(); + m_vars.reset(); + } + void execute(cmd_context &ctx) override { + ast_manager &m = ctx.m(); + app_ref_vector vars(m); + expr_ref fml(m); + expr_ref_vector lits(m); + for (expr *v : m_vars) vars.push_back(to_app(v)); + for (expr *e : m_lits) lits.push_back(e); + fml = mk_and(lits); + solver_factory &sf = ctx.get_solver_factory(); + params_ref pa; + solver_ref s = sf(m, pa, false, true, true, symbol::null); + s->assert_expr(fml); + lbool r = s->check_sat(); + if (r != l_true) return; + model_ref mdl; + s->get_model(mdl); + mbp::mbp_qel mbptg(m, pa); + mbptg(vars, fml, *mdl.get()); + + ctx.regular_stream() << "------------------------------ " << std::endl; + ctx.regular_stream() << "Orig tg: " << mk_and(lits) << std::endl; + ctx.regular_stream() << "To elim: "; + for (expr *v : m_vars) { + ctx.regular_stream() << to_app(v)->get_decl()->get_name() << " "; + } + ctx.regular_stream() << std::endl; + ctx.regular_stream() << "output " << fml << std::endl; + } +}; + +class qel_cmd : public cmd { + unsigned m_arg_index; + ptr_vector m_lits; + ptr_vector m_vars; + + public: + qel_cmd() : cmd("qel"){}; + char const *get_usage() const override { return "(lits) (vars)"; } + char const *get_descr(cmd_context &ctx) const override { + return "QE lite over e-graphs"; + } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context &ctx) const override { + if (m_arg_index == 0) return CPK_EXPR_LIST; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context &ctx, unsigned num, + expr *const *args) override { + m_lits.append(num, args); + m_arg_index = 1; + } + void set_next_arg(cmd_context &ctx, unsigned num, + func_decl *const *ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context &ctx) override { + m_arg_index = 0; + m_lits.reset(); + m_vars.reset(); + } + void execute(cmd_context &ctx) override { + ast_manager &m = ctx.m(); + func_decl_ref_vector vars(m); + app_ref_vector vars_apps(m); + expr_ref_vector lits(m); + + ctx.regular_stream() << "------------------------------ " << std::endl; + + for (func_decl *v : m_vars) { + vars.push_back(v); + vars_apps.push_back(m.mk_const(v)); + } + for (expr *e : m_lits) lits.push_back(e); + + expr_ref fml(m.mk_and(lits), m); + ctx.regular_stream() << "[tg] Before: " << fml << std::endl + << "[tg] Vars: "; + for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " "; + + ctx.regular_stream() << std::endl; + + params_ref pa; + + // the following is the same code as in qe_mbp in spacer + qel qe(m, pa); + qe(vars_apps, fml); + ctx.regular_stream() << "[tg] After: " << fml << std::endl + << "[tg] Vars: "; + for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " "; + + ctx.regular_stream() << std::endl; + } +}; + +class qe_lite_cmd : public cmd { + unsigned m_arg_index; + ptr_vector m_lits; + ptr_vector m_vars; + + public: + qe_lite_cmd() : cmd("qe-lite"){}; + char const *get_usage() const override { return "(lits) (vars)"; } + char const *get_descr(cmd_context &ctx) const override { + return "QE lite over e-graphs"; + } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context &ctx) const override { + if (m_arg_index == 0) return CPK_EXPR_LIST; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context &ctx, unsigned num, + expr *const *args) override { + m_lits.append(num, args); + m_arg_index = 1; + } + void set_next_arg(cmd_context &ctx, unsigned num, + func_decl *const *ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context &ctx) override { + m_arg_index = 0; + m_lits.reset(); + m_vars.reset(); + } + void execute(cmd_context &ctx) override { + ast_manager &m = ctx.m(); + func_decl_ref_vector vars(m); + app_ref_vector vars_apps(m); + expr_ref_vector lits(m); + + ctx.regular_stream() << "------------------------------ " << std::endl; + + for (func_decl *v : m_vars) { + vars.push_back(v); + vars_apps.push_back(m.mk_const(v)); + } + for (expr *e : m_lits) lits.push_back(e); + + expr_ref fml(m.mk_and(lits), m); + ctx.regular_stream() << "[der] Before: " << fml << std::endl + << "[der] Vars: "; + for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " "; + + ctx.regular_stream() << std::endl; + + params_ref pa; + // the following is the same code as in qe_mbp in spacer + qe_lite qe(m, pa, false); + qe(vars_apps, fml); + ctx.regular_stream() << "[der] After: " << fml << std::endl + << "[der] Vars: "; + for (app *a : vars_apps) ctx.regular_stream() << app_ref(a, m) << " "; + + ctx.regular_stream() << std::endl; + } +}; + +void install_dbg_cmds(cmd_context &ctx) { ctx.insert(alloc(print_dimacs_cmd)); ctx.insert(alloc(get_quantifier_body_cmd)); ctx.insert(alloc(set_cmd)); @@ -598,7 +785,10 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(set_next_id)); ctx.insert(alloc(get_interpolant_cmd)); ctx.insert(alloc(mbp_cmd)); + ctx.insert(alloc(mbp_qel_cmd)); ctx.insert(alloc(mbi_cmd)); ctx.insert(alloc(euf_project_cmd)); ctx.insert(alloc(eufi_cmd)); + ctx.insert(alloc(qel_cmd)); + ctx.insert(alloc(qe_lite_cmd)); } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 5523326f4..c0a9d7e6f 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -73,7 +73,7 @@ namespace spacer { // the current step needs to be interpolated: expr* fact = m.get_fact(pf); // if we trust the current step and we are able to use it - if (m_ctx.is_b_pure (pf) && (m.is_asserted(pf) || spacer::is_literal(m, fact))) { + if (m_ctx.is_b_pure (pf) && (m.is_asserted(pf) || spacer::is_literal(m, fact)) && !spacer::contains_defaults(fact, m)) { // just add it to the core m_ctx.add_lemma_to_core(fact); } diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 4e1da5770..3959bd655 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -34,6 +34,7 @@ Notes: #include "ast/ast_pp.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/expr_functors.h" #include "ast/for_each_expr.h" #include "ast/occurs.h" #include "ast/rewriter/bool_rewriter.h" @@ -51,7 +52,7 @@ Notes: #include "model/model_smt2_pp.h" #include "smt/params/smt_params.h" -#include "qe/lite/qe_lite_tactic.h" +#include "qe/lite/qel.h" #include "qe/mbp/mbp_plugin.h" #include "qe/mbp/mbp_term_graph.h" #include "qe/qe_mbp.h" @@ -69,6 +70,21 @@ Notes: namespace spacer { +class contains_def_pred : public i_expr_pred { + array_util m_autil; + public: + contains_def_pred(ast_manager& m): m_autil(m) {} + bool operator()(expr* e) override { + return m_autil.is_default(e); + } +}; + +bool contains_defaults(expr *fml, ast_manager &m) { + contains_def_pred pred(m); + check_pred check(pred, m, false); + return check(fml); +} + bool is_clause(ast_manager &m, expr *n) { if (spacer::is_literal(m, n)) return true; if (m.is_or(n)) { @@ -173,7 +189,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml, while (true) { params_ref p; - qe_lite qe(m, p, false); + qel qe(m, p); qe(vars, fml); rw(fml); diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index dbc3083a2..272c6cf30 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -121,6 +121,7 @@ void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars); void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml); bool contains_selects(expr *fml, ast_manager &m); +bool contains_defaults(expr *fml, ast_manager &m); void get_select_indices(expr *fml, app_ref_vector &indices); void find_decls(expr *fml, app_ref_vector &decls, std::string &prefix); diff --git a/src/qe/lite/CMakeLists.txt b/src/qe/lite/CMakeLists.txt index fc942d4ae..9b9f5a45a 100644 --- a/src/qe/lite/CMakeLists.txt +++ b/src/qe/lite/CMakeLists.txt @@ -1,9 +1,11 @@ z3_add_component(qe_lite SOURCES qe_lite_tactic.cpp + qel.cpp COMPONENT_DEPENDENCIES tactic mbp TACTIC_HEADERS qe_lite_tactic.h + qel.h ) diff --git a/src/qe/lite/qe_lite_tactic.cpp b/src/qe/lite/qe_lite_tactic.cpp index 32d11786c..03ebc8c4e 100644 --- a/src/qe/lite/qe_lite_tactic.cpp +++ b/src/qe/lite/qe_lite_tactic.cpp @@ -487,7 +487,7 @@ namespace qel { ptr_vector vs; expr_ref_vector ts(m); expr_ref t(m); - if (is_var_def(is_exists, args[i], vs, ts)) { + if (is_var_def(is_exists, args[i], vs, ts)) { // vs is the variable, ts is the definition for (unsigned j = 0; j < vs.size(); ++j) { var* v = vs[j]; t = ts.get(j); @@ -2376,7 +2376,7 @@ public: m_array_der.set_is_variable_proc(is_var); m_der(fmls); m_fm(fmls); - // AG: disalble m_array_der() since it interferes with other array handling + // AG: disable m_array_der() since it interferes with other array handling if (m_use_array_der) m_array_der(fmls); TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) tout << mk_pp(fmls[i].get(), m) << "\n";); } @@ -2392,7 +2392,7 @@ qe_lite::~qe_lite() { } void qe_lite::operator()(app_ref_vector& vars, expr_ref& fml) { - (*m_impl)(vars, fml); + (*m_impl)(vars, fml); } diff --git a/src/qe/lite/qe_lite_tactic.h b/src/qe/lite/qe_lite_tactic.h index 07ce60f35..e45c6f752 100644 --- a/src/qe/lite/qe_lite_tactic.h +++ b/src/qe/lite/qe_lite_tactic.h @@ -30,6 +30,7 @@ class tactic; class qe_lite { class impl; impl * m_impl; + public: /** use_array_der controls whether equalities over array reads are simplified diff --git a/src/qe/lite/qel.cpp b/src/qe/lite/qel.cpp new file mode 100644 index 000000000..583f51cc9 --- /dev/null +++ b/src/qe/lite/qel.cpp @@ -0,0 +1,54 @@ +/*++ + + Module Name: + + qel.cpp + +Abstract: + Light weight quantifier elimination (QEL) based on term graph. + + The implementation is based on the following paper: + + Isabel Garcia-Contreras, Hari Govind V. K., Sharon Shoham, Arie Gurfinkel: + Fast Approximations of Quantifier Elimination. Computer-Aided Verification + (CAV). 2023. URL: https://arxiv.org/abs/2306.10009 + +Author: + + Hari Govind V K (hgvk94) + Isabel Garcia (igcontreras) + +Revision History: + + +--*/ +#include "qe/lite/qel.h" +#include "qe/mbp/mbp_term_graph.h" + +class qel::impl { + private: + ast_manager &m; + + public: + impl(ast_manager &m, params_ref const &p) : m(m) {} + + void operator()(app_ref_vector &vars, expr_ref &fml) { + if (vars.empty()) return; + + mbp::term_graph tg(m); + tg.set_vars(vars); + + expr_ref_vector lits(m); + flatten_and(fml, lits); + tg.add_lits(lits); + tg.qel(vars, fml); + } +}; + +qel::qel(ast_manager &m, params_ref const &p) { m_impl = alloc(impl, m, p); } + +qel::~qel() { dealloc(m_impl); } + +void qel::operator()(app_ref_vector &vars, expr_ref &fml) { + (*m_impl)(vars, fml); +} diff --git a/src/qe/lite/qel.h b/src/qe/lite/qel.h new file mode 100644 index 000000000..9db6a32b4 --- /dev/null +++ b/src/qe/lite/qel.h @@ -0,0 +1,49 @@ +/*++ + + Module Name: + + qel.h + +Abstract: + + Light weight quantifier elimination (QEL) based on term graph. + + The implementation is based on the following paper: + + Isabel Garcia-Contreras, Hari Govind V. K., Sharon Shoham, Arie Gurfinkel: + Fast Approximations of Quantifier Elimination. Computer-Aided Verification + (CAV). 2023. URL: https://arxiv.org/abs/2306.10009 + +Author: + + Hari Govind V K (hgvk94) + Isabel Garcia (igcontreras) + +Revision History: + + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "ast/ast_util.h" +#include "util/params.h" +#include "util/uint_set.h" + +class qel { + class impl; + impl *m_impl; + + public: + qel(ast_manager &m, params_ref const &p); + + ~qel(); + + /** + \brief Applies light-weight elimination of `vars` provided as vector + of expressions to the cube `fml`. Returns the updated formula and updated + set of variables that were not eliminated. + */ + void operator()(app_ref_vector &vars, expr_ref &fml); +}; diff --git a/src/qe/mbp/CMakeLists.txt b/src/qe/mbp/CMakeLists.txt index 69d5dfd20..a5c7e7702 100644 --- a/src/qe/mbp/CMakeLists.txt +++ b/src/qe/mbp/CMakeLists.txt @@ -2,7 +2,12 @@ z3_add_component(mbp SOURCES mbp_arith.cpp mbp_arrays.cpp + mbp_arrays_tg.cpp + mbp_basic_tg.cpp mbp_datatypes.cpp + mbp_dt_tg.cpp + mbp_qel.cpp + mbp_qel_util.cpp mbp_plugin.cpp mbp_solve_plugin.cpp mbp_term_graph.cpp diff --git a/src/qe/mbp/mbp_arrays.cpp b/src/qe/mbp/mbp_arrays.cpp index 0f4c805b7..bf3ad08ed 100644 --- a/src/qe/mbp/mbp_arrays.cpp +++ b/src/qe/mbp/mbp_arrays.cpp @@ -17,7 +17,6 @@ Revision History: --*/ - #include "util/lbool.h" #include "ast/rewriter/rewriter_def.h" #include "ast/expr_functors.h" @@ -26,134 +25,11 @@ Revision History: #include "ast/rewriter/th_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" +#include "ast/array_peq.h" #include "model/model_evaluator.h" #include "qe/mbp/mbp_arrays.h" #include "qe/mbp/mbp_term_graph.h" - -namespace { - bool is_partial_eq (app* a); - - /** - * \brief utility class for partial equalities - * - * A partial equality (a ==I b), for two arrays a,b and a finite set of indices I holds - * iff (Forall i. i \not\in I => a[i] == b[i]); in other words, it is a - * restricted form of the extensionality axiom - * - * using this class, we denote (a =I b) as f(a,b,i0,i1,...) - * where f is an uninterpreted predicate with name PARTIAL_EQ and - * I = {i0,i1,...} - */ - - // TBD: make work for arrays with multiple arguments. - class peq { - ast_manager& m; - expr_ref m_lhs; - expr_ref m_rhs; - vector m_diff_indices; - func_decl_ref m_decl; // the partial equality declaration - app_ref m_peq; // partial equality application - app_ref m_eq; // equivalent std equality using def. of partial eq - array_util m_arr_u; - - public: - static const char* PARTIAL_EQ; - - peq (app* p, ast_manager& m): - m (m), - m_lhs (p->get_arg (0), m), - m_rhs (p->get_arg (1), m), - m_decl (p->get_decl (), m), - m_peq (p, m), - m_eq (m), - m_arr_u (m) - { - VERIFY (is_partial_eq (p)); - SASSERT (m_arr_u.is_array (m_lhs) && - m_arr_u.is_array (m_rhs) && - m_lhs->get_sort() == m_rhs->get_sort()); - unsigned arity = get_array_arity(m_lhs->get_sort()); - for (unsigned i = 2; i < p->get_num_args (); i += arity) { - SASSERT(arity + i <= p->get_num_args()); - expr_ref_vector vec(m); - vec.append(arity, p->get_args() + i); - m_diff_indices.push_back (vec); - } - } - - peq (expr* lhs, expr* rhs, vector const& diff_indices, ast_manager& m): - m (m), - m_lhs (lhs, m), - m_rhs (rhs, m), - m_diff_indices (diff_indices), - m_decl (m), - m_peq (m), - m_eq (m), - m_arr_u (m) { - SASSERT (m_arr_u.is_array (lhs) && - m_arr_u.is_array (rhs) && - lhs->get_sort() == rhs->get_sort()); - ptr_vector sorts; - sorts.push_back (m_lhs->get_sort ()); - sorts.push_back (m_rhs->get_sort ()); - for (auto const& v : diff_indices) { - SASSERT(v.size() == get_array_arity(m_lhs->get_sort())); - for (expr* e : v) - sorts.push_back (e->get_sort()); - } - m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.data (), m.mk_bool_sort ()); - } - - expr_ref lhs () { return m_lhs; } - - expr_ref rhs () { return m_rhs; } - - void get_diff_indices (vector& result) { result.append(m_diff_indices); } - - app_ref mk_peq () { - if (!m_peq) { - ptr_vector args; - args.push_back (m_lhs); - args.push_back (m_rhs); - for (auto const& v : m_diff_indices) { - args.append (v.size(), v.data()); - } - m_peq = m.mk_app (m_decl, args.size (), args.data ()); - } - return m_peq; - } - - app_ref mk_eq (app_ref_vector& aux_consts, bool stores_on_rhs = true) { - if (!m_eq) { - expr_ref lhs (m_lhs, m), rhs (m_rhs, m); - if (!stores_on_rhs) { - std::swap (lhs, rhs); - } - // lhs = (...(store (store rhs i0 v0) i1 v1)...) - sort* val_sort = get_array_range (lhs->get_sort()); - for (expr_ref_vector const& diff : m_diff_indices) { - ptr_vector store_args; - store_args.push_back (rhs); - store_args.append (diff.size(), diff.data()); - app_ref val(m.mk_fresh_const ("diff", val_sort), m); - store_args.push_back (val); - aux_consts.push_back (val); - rhs = m_arr_u.mk_store (store_args); - } - m_eq = m.mk_eq (lhs, rhs); - } - return m_eq; - } - }; - - const char* peq::PARTIAL_EQ = "!partial_eq"; - - bool is_partial_eq (app* a) { - return a->get_decl ()->get_name () == peq::PARTIAL_EQ; - } -} - namespace mbp { @@ -366,20 +242,10 @@ namespace mbp { } } - /** - * mk (e0 ==indices e1) - * - * result has stores if either e0 or e1 or an index term has stores - */ - app_ref mk_peq (expr* e0, expr* e1, vector const& indices) { - peq p (e0, e1, indices, m); - return p.mk_peq (); - } - void find_subst_term (app* eq) { SASSERT(m.is_eq(eq)); vector empty; - app_ref p_exp = mk_peq (eq->get_arg (0), eq->get_arg (1), empty); + app_ref p_exp = mk_peq (eq->get_arg (0), eq->get_arg (1), empty, m); bool subst_eq_found = false; while (true) { TRACE ("qe", tout << "processing peq:\n" << p_exp << "\n";); @@ -434,7 +300,7 @@ namespace mbp { ); // arr0 ==I arr1 - p_exp = mk_peq (arr0, arr1, I); + p_exp = mk_peq (arr0, arr1, I, m); TRACE ("qe", tout << "new peq:\n"; @@ -445,7 +311,7 @@ namespace mbp { m_idx_lits_v.append (idx_diseq); // arr0 ==I+idx arr1 I.push_back (idxs); - p_exp = mk_peq (arr0, arr1, I); + p_exp = mk_peq (arr0, arr1, I, m); TRACE ("qe", tout << "new peq:\n" << p_exp << "\n"; ); diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp new file mode 100644 index 000000000..8f127819d --- /dev/null +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -0,0 +1,394 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_arrays_tg.cpp + +Abstract: + + Apply rules for model based projection for arrays on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#include "qe/mbp/mbp_arrays_tg.h" +#include "ast/array_decl_plugin.h" +#include "ast/array_peq.h" +#include "qe/mbp/mbp_qel_util.h" +#include "util/obj_hashtable.h" +#include "util/obj_pair_hashtable.h" + +namespace mbp { + +struct mbp_array_tg::impl { + typedef std::pair expr_pair; + ast_manager &m; + array_util m_array_util; + mbp::term_graph &m_tg; + // TODO: cache mdl evaluation eventhough we extend m_mdl + model &m_mdl; + + // set of variables on which to apply MBP rules + obj_hashtable &m_vars_set; + + // variables created in the last iteration of MBP application + app_ref_vector m_new_vars; + + expr_sparse_mark &m_seen; + obj_pair_hashtable m_seenp; + + // apply rules that split on model + bool m_use_mdl; + + // m_has_store.is_marked(t) if t has a subterm store(v) where v is a + // variable to be eliminated + ast_mark m_has_stores; + // variables required for applying rules + vector indices; + expr_ref_vector terms, rdTerms; + + bool has_var(expr *t) { return contains_vars(t, m_vars_set, m); } + + bool has_arr_var(expr *t) { + return contains_vars(t, m_vars_set, m, m_array_util.get_family_id(), + ARRAY_SORT); + } + + bool is_var(expr *t) { return is_uninterp_const(t) && has_var(t); } + + bool is_wr_on_rhs(expr *e) { + return is_app(e) && is_partial_eq(to_app(e)) && + is_wr_on_rhs(to_app(e)->get_arg(0), to_app(e)->get_arg(1)); + } + + bool is_wr_on_rhs(expr *lhs, expr *rhs) { + return (is_arr_write(rhs) && !is_arr_write(lhs)); + } + + bool is_arr_write(expr *t) { + if (!m_array_util.is_store(t)) return false; + return has_var(to_app(t)); + } + + // Returns true if e has a subterm store(v) where v is a variable to be + // eliminated. Assumes that has_store has already been called for + // subexpressions of e + bool has_stores(expr *e) { + if (m_has_stores.is_marked(e)) return true; + if (!is_app(e)) return false; + if (m_array_util.is_store(e) && is_var(to_app(e)->get_arg(0))) { + m_has_stores.mark(e, true); + return true; + } + for (auto c : *(to_app(e))) { + if (m_has_stores.is_marked(c)) { + m_has_stores.mark(e, true); + return true; + } + } + return false; + } + + bool is_rd_wr(expr *t) { + if (!m_array_util.is_select(t)) return false; + return m_array_util.is_store(to_app(t)->get_arg(0)) && + has_stores(to_app(t)->get_arg(0)); + } + + bool is_implicit_peq(expr *e) { + return m.is_eq(e) && + is_implicit_peq(to_app(e)->get_arg(0), to_app(e)->get_arg(1)); + } + + bool is_implicit_peq(expr *lhs, expr *rhs) { + return m_array_util.is_array(lhs) && m_array_util.is_array(rhs) && + (has_var(lhs) || has_var(rhs)); + } + + void mark_seen(expr *t) { m_seen.mark(t); } + bool is_seen(expr *t) { return m_seen.is_marked(t); } + void mark_seen(expr *t1, expr *t2) { m_seenp.insert(expr_pair(t1, t2)); } + bool is_seen(expr *t1, expr *t2) { + return m_seenp.contains(expr_pair(t1, t2)) || + m_seenp.contains(expr_pair(t2, t1)); + } + + impl(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen) + : m(man), m_array_util(m), m_tg(tg), m_mdl(mdl), m_vars_set(vars_set), + m_new_vars(m), m_seen(seen), m_use_mdl(false), terms(m), rdTerms(m) {} + + // create a peq where write terms are preferred on the left hand side + peq mk_wr_peq(expr *e1, expr *e2) { + vector empty; + return mk_wr_peq(e1, e2, empty); + } + + // create a peq where write terms are preferred on the left hand side + peq mk_wr_peq(expr *e1, expr *e2, vector &indices) { + expr *n_lhs = e1, *n_rhs = e2; + if (is_wr_on_rhs(e1, e2)) std::swap(n_lhs, n_rhs); + return peq(n_lhs, n_rhs, indices, m); + } + + // rewrite store(x, j, elem) \peq_{indices} y + // into either j = i && x \peq_{indices} y (for some i in + // indices) or &&_{i \in indices} j \neq i && + // x \peq_{indices, j} y && + // select(y, j) = elem + // rewrite negation !(store(x, j, elem) \peq_{indices} y) into + // into either j = i && !(x \peq_{indices} y) (for some i in + // indices) or &&_{i \in indices} j \neq i && + // !(x \peq_{indices, j} y) && + // or &&_{i \in indices} j \neq i && + // !(select(y, j) = elem) + void elimwreq(peq p, bool is_neg) { + SASSERT(is_arr_write(p.lhs())); + TRACE("mbp_tg", + tout << "applying elimwreq on " << expr_ref(p.mk_peq(), m);); + vector indices; + expr *j = to_app(p.lhs())->get_arg(1); + expr *elem = to_app(p.lhs())->get_arg(2); + bool in = false; + p.get_diff_indices(indices); + expr_ref eq_index(m); + expr_ref_vector deq(m); + for (expr_ref_vector &e : indices) { + for (expr *i : e) { + if (m_mdl.are_equal(j, i)) { + in = true; + // save for later + eq_index = i; + break; + } else + deq.push_back(i); + } + } + if (in) { + SASSERT(m_mdl.are_equal(j, eq_index)); + peq p_new = + mk_wr_peq(to_app(p.lhs())->get_arg(0), p.rhs(), indices); + m_tg.add_eq(j, eq_index); + expr_ref p_new_expr(m); + p_new_expr = is_neg ? m.mk_not(p_new.mk_peq()) : p_new.mk_peq(); + m_tg.add_lit(p_new_expr); + m_tg.add_eq(p_new_expr, p.mk_peq()); + return; + } + for (expr *d : deq) { m_tg.add_deq(j, d); } + expr_ref_vector setOne(m); + setOne.push_back(j); + indices.push_back(setOne); + peq p_new = mk_wr_peq(to_app(p.lhs())->get_arg(0), p.rhs(), indices); + expr *args[2] = {p.rhs(), j}; + expr_ref rd(m_array_util.mk_select(2, args), m); + if (!is_neg) { + m_tg.add_lit(p_new.mk_peq()); + m_tg.add_eq(rd, elem); + m_tg.add_eq(p.mk_peq(), p_new.mk_peq()); + } else { + SASSERT(m_mdl.is_false(p_new.mk_peq()) || + !m_mdl.are_equal(rd, elem)); + if (m_mdl.is_false(p_new.mk_peq())) { + expr_ref npeq(mk_not(p_new.mk_peq()), m); + m_tg.add_lit(npeq); + m_tg.add_eq(p.mk_peq(), p_new.mk_peq()); + } + if (!m_mdl.are_equal(rd, elem)) { m_tg.add_deq(rd, elem); } + } + } + + // add equality v = rd where v is a fresh variable + void add_rdVar(expr *rd) { + // do not assign new variable if rd is already equal to a value + if (m_tg.has_val_in_class(rd)) return; + TRACE("mbp_tg", tout << "applying add_rdVar on " << expr_ref(rd, m);); + app_ref u = new_var(to_app(rd)->get_sort(), m); + m_new_vars.push_back(u); + m_tg.add_var(u); + m_tg.add_eq(u, rd); + m_mdl.register_decl(u->get_decl(), m_mdl(rd)); + } + + // given a \peq_{indices} t, where a is a variable, merge equivalence class + // of a with store(t, indices, elems) where elems are fresh constants + void elimeq(peq p) { + TRACE("mbp_tg", + tout << "applying elimeq on " << expr_ref(p.mk_peq(), m);); + app_ref_vector aux_consts(m); + expr_ref eq(m); + expr_ref sel(m); + eq = p.mk_eq(aux_consts, true); + vector indices; + p.get_diff_indices(indices); + vector::iterator itr = indices.begin(); + unsigned i = 0; + for (app *a : aux_consts) { + m_new_vars.push_back(a); + m_tg.add_var(a); + auto const &indx = std::next(itr, i); + SASSERT(indx->size() == 1); + expr *args[2] = {to_app(p.lhs()), to_app(indx->get(0))}; + sel = m_array_util.mk_select(2, args); + m_mdl.register_decl(a->get_decl(), m_mdl(sel)); + i++; + } + m_tg.add_lit(eq); + m_tg.add_eq(p.mk_peq(), m.mk_true()); + TRACE("mbp_tg", tout << "added lit " << eq;); + } + + // rewrite select(store(a, i, k), j) into either select(a, j) or k + void elimrdwr(expr *term) { + SASSERT(is_rd_wr(term)); + TRACE("mbp_tg", tout << "applying elimrdwr on " << expr_ref(term, m);); + expr *wr_ind = to_app(to_app(term)->get_arg(0))->get_arg(1); + expr *rd_ind = to_app(term)->get_arg(1); + expr *e; + if (m_mdl.are_equal(wr_ind, rd_ind)) { + m_tg.add_eq(wr_ind, rd_ind); + e = to_app(to_app(term)->get_arg(0))->get_arg(2); + } else { + m_tg.add_deq(wr_ind, rd_ind); + expr *args[2] = {to_app(to_app(term)->get_arg(0))->get_arg(0), + to_app(term)->get_arg(1)}; + e = m_array_util.mk_select(2, args); + } + m_tg.add_eq(term, e); + } + + // iterate through all terms in m_tg and apply all array MBP rules once + // returns true if any rules were applied + bool apply() { + TRACE("mbp_tg", tout << "Iterating over terms of tg";); + indices.reset(); + rdTerms.reset(); + m_new_vars.reset(); + expr_ref e(m), rdEq(m), rdDeq(m); + expr *nt, *term; + bool progress = false, is_neg = false; + + // Not resetting terms because get_terms calls resize on terms + m_tg.get_terms(terms, false); + for (unsigned i = 0; i < terms.size(); i++) { + term = terms.get(i); + SASSERT(!m.is_distinct(term)); + if (m_seen.is_marked(term)) continue; + if (m_tg.is_cgr(term)) continue; + TRACE("mbp_tg", tout << "processing " << expr_ref(term, m);); + if (is_implicit_peq(term)) { + // rewrite array eq as peq + mark_seen(term); + progress = true; + e = mk_wr_peq(to_app(term)->get_arg(0), + to_app(term)->get_arg(1)) + .mk_peq(); + m_tg.add_lit(e); + m_tg.add_eq(term, e); + continue; + } + nt = term; + is_neg = m.is_not(term, nt); + if (is_app(nt) && is_partial_eq(to_app(nt))) { + peq p(to_app(nt), m); + if (m_use_mdl && is_arr_write(p.lhs())) { + mark_seen(nt); + mark_seen(term); + progress = true; + elimwreq(p, is_neg); + continue; + } + if (!m_array_util.is_store(p.lhs()) && has_var(p.lhs())) { + // TODO: don't apply this rule if vars in p.lhs() also + // appear in p.rhs() + mark_seen(p.lhs()); + mark_seen(nt); + mark_seen(term); + progress = true; + elimeq(p); + continue; + } + // eliminate eq when the variable is on the rhs + if (!m_array_util.is_store(p.rhs()) && has_var(p.rhs())) { + mark_seen(p.rhs()); + p.get_diff_indices(indices); + peq p_new = mk_wr_peq(p.rhs(), p.lhs(), indices); + mark_seen(nt); + mark_seen(term); + progress = true; + elimeq(p_new); + continue; + } + } + if (m_use_mdl && is_rd_wr(term)) { + mark_seen(term); + progress = true; + elimrdwr(term); + continue; + } + } + + // iterate over term graph again to collect read terms + // irrespective of whether they have been marked or not + rdTerms.reset(); + for (unsigned i = 0; i < terms.size(); i++) { + term = terms.get(i); + if (m_array_util.is_select(term) && + has_var(to_app(term)->get_arg(0))) { + rdTerms.push_back(term); + if (is_seen(term)) continue; + add_rdVar(term); + mark_seen(term); + } + } + if (!m_use_mdl) return progress; + expr *e1, *e2, *a1, *a2, *i1, *i2; + for (unsigned i = 0; i < rdTerms.size(); i++) { + e1 = rdTerms.get(i); + a1 = to_app(e1)->get_arg(0); + i1 = to_app(e1)->get_arg(1); + for (unsigned j = i + 1; j < rdTerms.size(); j++) { + e2 = rdTerms.get(j); + a2 = to_app(e2)->get_arg(0); + i2 = to_app(e2)->get_arg(1); + if (!is_seen(e1, e2) && a1->get_id() == a2->get_id()) { + mark_seen(e1, e2); + progress = true; + if (m_mdl.are_equal(i1, i2)) { + m_tg.add_eq(i1, i2); + } else { + SASSERT(!m_mdl.are_equal(i1, i2)); + m_tg.add_deq(i1, i2); + } + continue; + } + } + } + return progress; + } +}; + +void mbp_array_tg::use_model() { m_impl->m_use_mdl = true; } +bool mbp_array_tg::apply() { return m_impl->apply(); } +void mbp_array_tg::reset() { + m_impl->m_seen.reset(); + m_impl->m_vars_set.reset(); +} +void mbp_array_tg::get_new_vars(app_ref_vector *&t) { t = &m_impl->m_new_vars; } +family_id mbp_array_tg::get_family_id() const { + return m_impl->m_array_util.get_family_id(); +} +mbp_array_tg::mbp_array_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, + expr_sparse_mark &seen) { + m_impl = alloc(mbp_array_tg::impl, man, tg, mdl, vars_set, seen); +} +mbp_array_tg::~mbp_array_tg() { dealloc(m_impl); } + +} // namespace mbp diff --git a/src/qe/mbp/mbp_arrays_tg.h b/src/qe/mbp/mbp_arrays_tg.h new file mode 100644 index 000000000..0c634bdd0 --- /dev/null +++ b/src/qe/mbp/mbp_arrays_tg.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_arrays_tg.h + +Abstract: + + Apply rules for model based projection for arrays on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "qe/mbp/mbp_qel_util.h" +#include "qe/mbp/mbp_term_graph.h" +#include "qe/mbp/mbp_tg_plugins.h" +#include "util/memory_manager.h" +#include "util/obj_hashtable.h" +#include "util/obj_pair_hashtable.h" + +namespace mbp { +class mbp_array_tg : public mbp_tg_plugin { + struct impl; + impl *m_impl; + + public: + mbp_array_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen); + void use_model() override; + void reset(); + // iterate through all terms in m_tg and apply all array MBP rules once + // returns true if any rules were applied + bool apply() override; + ~mbp_array_tg() override; + void get_new_vars(app_ref_vector *&t) override; + family_id get_family_id() const override; +}; +} // namespace mbp diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp new file mode 100644 index 000000000..ee83012a7 --- /dev/null +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -0,0 +1,101 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_basic_tg.cpp + +Abstract: + + Apply rules for model based projection for basic types, on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#include "qe/mbp/mbp_basic_tg.h" +#include "ast/ast.h" +#include "ast/expr_functors.h" +#include "util/debug.h" +#include "util/memory_manager.h" + +struct mbp_basic_tg::impl { + ast_manager &m; + mbp::term_graph &m_tg; + // TODO: cache mdl evaluation eventhough we extend m_mdl + model &m_mdl; + + // set of variables on which to apply MBP rules + obj_hashtable &m_vars_set; + + // variables created in the last iteration of MBP application + app_ref_vector m_new_vars; + + expr_sparse_mark &m_seen; + + expr_ref_vector terms; + bool m_use_mdl; + + void mark_seen(expr *t) { m_seen.mark(t); } + bool is_seen(expr *t) { return m_seen.is_marked(t); } + + bool apply() { + if (!m_use_mdl) return false; + expr *term, *c, *th, *el; + expr_ref nterm(m); + bool progress = false; + TRACE("mbp_tg", tout << "Iterating over terms of tg";); + // Not resetting terms because get_terms calls resize on terms + m_tg.get_terms(terms, false); + for (unsigned i = 0; i < terms.size(); i++) { + term = terms.get(i); + // Unsupported operators + SASSERT(!m.is_and(term)); + SASSERT(!m.is_or(term)); + SASSERT(!m.is_distinct(term)); + SASSERT(!m.is_implies(term)); + + if (is_seen(term)) continue; + if (m_tg.is_cgr(term)) continue; + if (m.is_ite(term, c, th, el)) { + mark_seen(term); + progress = true; + if (m_mdl.is_true(c)) { + m_tg.add_lit(c); + m_tg.add_eq(term, th); + } else { + if (m.is_not(c)) + nterm = to_app(c)->get_arg(0); + else + nterm = m.mk_not(c); + m_tg.add_lit(nterm); + m_tg.add_eq(term, el); + } + continue; + } + } + return progress; + } + + impl(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen) + : m(man), m_tg(tg), m_mdl(mdl), m_vars_set(vars_set), m_new_vars(m), + m_seen(seen), terms(m), m_use_mdl(false) {} +}; + +bool mbp_basic_tg::apply() { return m_impl->apply(); } +void mbp_basic_tg::use_model() { m_impl->m_use_mdl = true; } +void mbp_basic_tg::get_new_vars(app_ref_vector *&t) { t = &m_impl->m_new_vars; } +family_id mbp_basic_tg::get_family_id() const { + return m_impl->m.get_basic_family_id(); +} +mbp_basic_tg::mbp_basic_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, + expr_sparse_mark &seen) { + m_impl = alloc(mbp_basic_tg::impl, man, tg, mdl, vars_set, seen); +} +mbp_basic_tg::~mbp_basic_tg() { dealloc(m_impl); } diff --git a/src/qe/mbp/mbp_basic_tg.h b/src/qe/mbp/mbp_basic_tg.h new file mode 100644 index 000000000..af7c624c0 --- /dev/null +++ b/src/qe/mbp/mbp_basic_tg.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_basic_tg.h + +Abstract: + + Apply rules for model based projection for basic types, on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ +#pragma once + +#include "qe/mbp/mbp_qel_util.h" +#include "qe/mbp/mbp_term_graph.h" +#include "qe/mbp/mbp_tg_plugins.h" +#include "util/obj_hashtable.h" + +class mbp_basic_tg : public mbp_tg_plugin { + struct impl; + impl *m_impl; + + public: + mbp_basic_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen); + // iterate through all terms in m_tg and apply all basic MBP rules once + // returns true if any rules were applied + bool apply() override; + ~mbp_basic_tg() override; + void use_model() override; + void get_new_vars(app_ref_vector *&t) override; + family_id get_family_id() const override; +}; diff --git a/src/qe/mbp/mbp_dt_tg.cpp b/src/qe/mbp/mbp_dt_tg.cpp new file mode 100644 index 000000000..b3b24617d --- /dev/null +++ b/src/qe/mbp/mbp_dt_tg.cpp @@ -0,0 +1,202 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_dt_tg.cpp + +Abstract: + + Apply rules for model based projection for datatypes on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ +#include "qe/mbp/mbp_dt_tg.h" +#include "qe/mbp/mbp_qel_util.h" +#include "util/memory_manager.h" + +namespace mbp { + +struct mbp_dt_tg::impl { + ast_manager &m; + datatype_util m_dt_util; + mbp::term_graph &m_tg; + // TODO: cache mdl evaluation eventhough we extend m_mdl + model &m_mdl; + + // set of variables on which to apply MBP rules + obj_hashtable &m_vars_set; + + // variables created in the last iteration of MBP application + app_ref_vector m_new_vars; + + expr_sparse_mark &m_seen; + + expr_ref_vector terms; + bool m_use_mdl; + + void mark_seen(expr *t) { m_seen.mark(t); } + bool is_seen(expr *t) { return m_seen.is_marked(t); } + + bool is_var(expr *t) { return is_uninterp_const(t) && has_var(t); } + + bool has_var(expr *t) { return contains_vars(t, m_vars_set, m); } + + bool is_constructor(expr *t) { + return is_app(t) && m_dt_util.is_constructor(to_app(t)->get_decl()) && + has_var(t); + } + + bool is_constructor_app(expr *e, expr *&cons, expr *&rhs) { + if (!m.is_eq(e, cons, rhs)) return false; + // TODO: does it matter whether vars in cons appear in rhs? + if (is_constructor(cons)) { + return true; + } else if (is_constructor(rhs)) { + cons = rhs; + rhs = to_app(e)->get_arg(0); + return true; + } + return false; + } + + impl(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen) + : m(man), m_dt_util(m), m_tg(tg), m_mdl(mdl), m_vars_set(vars_set), + m_new_vars(m), m_seen(seen), terms(m), m_use_mdl(false) {} + + // rewrite head(x) with y + // and x with list(y, z) + void rm_select(expr *term) { + SASSERT(is_app(term) && + m_dt_util.is_accessor(to_app(term)->get_decl()) && + is_var(to_app(term)->get_arg(0))); + TRACE("mbp_tg", tout << "applying rm_select on " << expr_ref(term, m);); + expr *v = to_app(term)->get_arg(0); + expr_ref sel(m); + app_ref u(m); + app_ref_vector new_vars(m); + func_decl *cons = + m_dt_util.get_accessor_constructor(to_app(term)->get_decl()); + ptr_vector const *accessors = + m_dt_util.get_constructor_accessors(cons); + for (unsigned i = 0; i < accessors->size(); i++) { + func_decl *d = accessors->get(i); + sel = m.mk_app(d, v); + u = m_tg.get_const_in_class(sel); + if (u) { + new_vars.push_back(u); + continue; + } + u = new_var(d->get_range(), m); + m_new_vars.push_back(u); + m_tg.add_var(u); + new_vars.push_back(u); + m_tg.add_eq(sel, u); + m_mdl.register_decl(u->get_decl(), m_mdl(sel)); + } + expr_ref new_cons(m.mk_app(cons, new_vars), m); + m_tg.add_eq(v, new_cons); + } + + // rewrite cons(v, u) = x with v = head(x) and u = tail(x) + // where u or v contain variables + void deconstruct_eq(expr *cons, expr *rhs) { + TRACE("mbp_tg", + tout << "applying deconstruct_eq on " << expr_ref(cons, m);); + ptr_vector const *accessors = + m_dt_util.get_constructor_accessors(to_app(cons)->get_decl()); + for (unsigned i = 0; i < accessors->size(); i++) { + expr_ref a(m.mk_app(accessors->get(i), rhs), m); + expr *newRhs = to_app(cons)->get_arg(i); + m_tg.add_eq(a, newRhs); + } + func_decl *is_cons = + m_dt_util.get_constructor_recognizer(to_app(cons)->get_decl()); + expr_ref is(m.mk_app(is_cons, rhs), m); + m_tg.add_lit(is); + } + + // rewrite cons(v, u) != x into one of !cons(x) or v != head(x) or u != + // tail(x) where u or v contain variables + void deconstruct_neq(expr *cons, expr *rhs) { + TRACE("mbp_tg", + tout << "applying deconstruct_neq on " << expr_ref(cons, m);); + ptr_vector const *accessors = + m_dt_util.get_constructor_accessors(to_app(cons)->get_decl()); + func_decl *is_cons = + m_dt_util.get_constructor_recognizer(to_app(cons)->get_decl()); + expr_ref a(m.mk_app(is_cons, rhs), m); + if (m_mdl.is_false(a)) { + expr_ref not_cons(m.mk_not(a), m); + m_tg.add_lit(not_cons); + return; + } + m_tg.add_lit(a); + + for (unsigned i = 0; i < accessors->size(); i++) { + expr_ref a(m.mk_app(accessors->get(i), rhs), m); + expr *newRhs = to_app(cons)->get_arg(i); + if (!m_mdl.are_equal(a, newRhs)) { + m_tg.add_deq(a, newRhs); + break; + } + } + } + + bool apply() { + expr *cons, *rhs, *f, *term; + bool progress = false; + m_new_vars.reset(); + TRACE("mbp_tg", tout << "Iterating over terms of tg";); + // Not resetting terms because get_terms calls resize on terms + m_tg.get_terms(terms, false); + for (unsigned i = 0; i < terms.size(); i++) { + term = terms.get(i); + SASSERT(!m.is_distinct(term)); + if (is_seen(term)) continue; + if (m_tg.is_cgr(term)) continue; + if (is_app(term) && + m_dt_util.is_accessor(to_app(term)->get_decl()) && + is_var(to_app(term)->get_arg(0))) { + mark_seen(term); + progress = true; + rm_select(term); + continue; + } + if (is_constructor_app(term, cons, rhs)) { + mark_seen(term); + progress = true; + deconstruct_eq(cons, rhs); + continue; + } + if (m_use_mdl && m.is_not(term, f) && + is_constructor_app(f, cons, rhs)) { + mark_seen(term); + progress = true; + deconstruct_neq(cons, rhs); + continue; + } + } + return progress; + } +}; + +bool mbp_dt_tg::apply() { return m_impl->apply(); } +mbp_dt_tg::mbp_dt_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen) { + m_impl = alloc(mbp_dt_tg::impl, man, tg, mdl, vars_set, seen); +} +void mbp_dt_tg::use_model() { m_impl->m_use_mdl = true; } +void mbp_dt_tg::get_new_vars(app_ref_vector *&t) { t = &m_impl->m_new_vars; } +family_id mbp_dt_tg::get_family_id() const { + return m_impl->m_dt_util.get_family_id(); +} +mbp_dt_tg::~mbp_dt_tg() { dealloc(m_impl); } + +} // namespace mbp diff --git a/src/qe/mbp/mbp_dt_tg.h b/src/qe/mbp/mbp_dt_tg.h new file mode 100644 index 000000000..8ea0d8e48 --- /dev/null +++ b/src/qe/mbp/mbp_dt_tg.h @@ -0,0 +1,44 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_dt_tg.h + +Abstract: + + Apply rules for model based projection for datatypes on a term graph + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#pragma once + +#include "ast/datatype_decl_plugin.h" +#include "qe/mbp/mbp_qel_util.h" +#include "qe/mbp/mbp_term_graph.h" +#include "qe/mbp/mbp_tg_plugins.h" +#include "util/obj_hashtable.h" + +namespace mbp { +class mbp_dt_tg : public mbp_tg_plugin { + struct impl; + impl *m_impl; + + public: + mbp_dt_tg(ast_manager &man, mbp::term_graph &tg, model &mdl, + obj_hashtable &vars_set, expr_sparse_mark &seen); + // iterate through all terms in m_tg and apply all datatype MBP rules once + // returns true if any rules were applied + bool apply() override; + ~mbp_dt_tg() override; + void use_model() override; + void get_new_vars(app_ref_vector *&t) override; + family_id get_family_id() const override; +}; +} // namespace mbp diff --git a/src/qe/mbp/mbp_qel.cpp b/src/qe/mbp/mbp_qel.cpp new file mode 100644 index 000000000..d50a8c1f6 --- /dev/null +++ b/src/qe/mbp/mbp_qel.cpp @@ -0,0 +1,226 @@ +/*++ + + Module Name: + + mbp_qel.cpp + +Abstract: + + Model Based Projection based on term graph + +Author: + + Hari Govind V K (hgvk94) 2022-07-12 + +Revision History: + + +--*/ +#include "qe/mbp/mbp_qel.h" +#include "ast/array_decl_plugin.h" +#include "ast/array_peq.h" +#include "ast/datatype_decl_plugin.h" +#include "model/model.h" +#include "qe/mbp/mbp_arrays.h" +#include "qe/mbp/mbp_arrays_tg.h" +#include "qe/mbp/mbp_basic_tg.h" +#include "qe/mbp/mbp_dt_tg.h" +#include "qe/mbp/mbp_term_graph.h" +#include "qe/mbp/mbp_tg_plugins.h" +#include "util/obj_hashtable.h" + +namespace mbp { + +class mbp_qel::impl { + private: + ast_manager &m; + array_util m_array_util; + datatype_util m_dt_util; + params_ref m_params; + mbp::term_graph m_tg; + + ptr_vector m_plugins; + + // set of non_basic variables to be projected. MBP rules are applied to + // terms containing these variables + obj_hashtable m_non_basic_vars; + + // Utilities to keep track of which terms have been processed + expr_sparse_mark m_seen; + void mark_seen(expr *t) { m_seen.mark(t); } + bool is_seen(expr *t) { return m_seen.is_marked(t); } + + bool is_non_basic(app *v) { + return m_dt_util.is_datatype(v->get_sort()) || m_array_util.is_array(v); + } + + void add_vars(mbp_tg_plugin *p, app_ref_vector &vars) { + app_ref_vector *new_vars; + p->get_new_vars(new_vars); + for (auto v : *new_vars) { + if (is_non_basic(v)) m_non_basic_vars.insert(v); + vars.push_back(v); + } + } + + // apply all plugins till saturation + void saturate(app_ref_vector &vars) { + bool progress; + do { + progress = false; + for (auto *p : m_plugins) { + if (p->apply()) { + progress = true; + add_vars(p, vars); + } + } + } + while (progress); + } + + void init(app_ref_vector &vars, expr_ref &fml, model &mdl) { + // variables to apply projection rules on + for (auto v : vars) + if (is_non_basic(v)) m_non_basic_vars.insert(v); + + // mark vars as non-ground. + m_tg.add_vars(vars); + // treat eq literals as term in the egraph + m_tg.set_explicit_eq(); + + expr_ref_vector fmls(m); + flatten_and(fml, fmls); + m_tg.add_lits(fmls); + + add_plugin(alloc(mbp_array_tg, m, m_tg, mdl, m_non_basic_vars, m_seen)); + add_plugin(alloc(mbp_dt_tg, m, m_tg, mdl, m_non_basic_vars, m_seen)); + add_plugin(alloc(mbp_basic_tg, m, m_tg, mdl, m_non_basic_vars, m_seen)); + } + + void add_plugin(mbp_tg_plugin *p) { m_plugins.push_back(p); } + + void enable_model_splitting() { + for (auto p : m_plugins) p->use_model(); + } + + mbp_tg_plugin *get_plugin(family_id fid) { + for (auto p : m_plugins) + if (p->get_family_id() == fid) return p; + return nullptr; + } + + public: + impl(ast_manager &m, params_ref const &p) + : m(m), m_array_util(m), m_dt_util(m), m_params(p), m_tg(m) {} + + ~impl() { + std::for_each(m_plugins.begin(), m_plugins.end(), + delete_proc()); + } + + void operator()(app_ref_vector &vars, expr_ref &fml, model &mdl) { + if (vars.empty()) return; + + init(vars, fml, mdl); + // Apply MBP rules till saturation + + // First, apply rules without splitting on model + saturate(vars); + + enable_model_splitting(); + + // Do complete mbp + saturate(vars); + + TRACE("mbp_tg", + tout << "mbp tg " << m_tg.get_lits() << " and vars " << vars;); + TRACE("mbp_tg_verbose", obj_hashtable vars_tmp; + collect_uninterp_consts(mk_and(m_tg.get_lits()), vars_tmp); + for (auto a + : vars_tmp) tout + << mk_pp(a->get_decl(), m) << "\n"; + for (auto b + : m_tg.get_lits()) tout + << expr_ref(b, m) << "\n"; + for (auto a + : vars) tout + << expr_ref(a, m) << " ";); + + // 1. Apply qe_lite to remove all c-ground variables + // 2. Collect all core variables in the output (variables used as array + // indices/values) + // 3. Re-apply qe_lite to remove non-core variables + + // Step 1. + m_tg.qel(vars, fml); + + // Step 2. + // Variables that appear as array indices or values cannot be + // eliminated if they are not c-ground. They are core variables All + // other Array/ADT variables can be eliminated, they are redundant. + obj_hashtable core_vars; + collect_selstore_vars(fml, core_vars, m); + + std::function is_red = [&](app *v) { + if (!m_dt_util.is_datatype(v->get_sort()) && + !m_array_util.is_array(v)) + return false; + return !core_vars.contains(v); + }; + expr_sparse_mark red_vars; + for (auto v : vars) + if (is_red(v)) red_vars.mark(v); + CTRACE("mbp_tg", !core_vars.empty(), tout << "vars not redundant "; + for (auto v + : core_vars) tout + << " " << app_ref(v, m); + tout << "\n";); + + std::function non_core = [&](expr *e) { + if (is_app(e) && is_partial_eq(to_app(e))) return true; + if (m.is_ite(e)) return true; + return red_vars.is_marked(e); + }; + + // Step 3. + m_tg.qel(vars, fml, &non_core); + + CTRACE("mbp_tg", !vars.empty(), + tout << "before substitution " << fml << "\n";); + // for all remaining non-cgr bool, dt, array variables, add v = mdl(v) + expr_sparse_mark s_vars; + for (auto v : vars) { + if (m_dt_util.is_datatype(v->get_sort()) || + m_array_util.is_array(v) || m.is_bool(v)) { + CTRACE("mbp_tg", + m_array_util.is_array(v) || + m_dt_util.is_datatype(v->get_sort()), + tout << "Could not eliminate " << v->get_name() + << "\n";); + s_vars.mark(v); + m_tg.add_eq(v, mdl(v)); + } + } + + std::function substituted = [&](expr *e) { + if (is_app(e) && is_partial_eq(to_app(e))) return true; + if (m.is_ite(e)) return true; + return red_vars.is_marked(e) || s_vars.is_marked(e); + }; + + // remove all substituted variables + m_tg.qel(vars, fml, &substituted); + } +}; + +mbp_qel::mbp_qel(ast_manager &m, params_ref const &p) { + m_impl = alloc(impl, m, p); +} + +mbp_qel::~mbp_qel() { dealloc(m_impl); } + +void mbp_qel::operator()(app_ref_vector &vars, expr_ref &fml, model &mdl) { + (*m_impl)(vars, fml, mdl); +} + +} // namespace mbp diff --git a/src/qe/mbp/mbp_qel.h b/src/qe/mbp/mbp_qel.h new file mode 100644 index 000000000..6246ec01f --- /dev/null +++ b/src/qe/mbp/mbp_qel.h @@ -0,0 +1,41 @@ +/*++ + + Module Name: + + mbp_qel.h + +Abstract: + + Model Based Projection based on term graph + +Author: + + Hari Govind V K (hgvk94) 2022-07-12 + +Revision History: + + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "model/model.h" +#include "util/params.h" + +namespace mbp { +class mbp_qel { + class impl; + impl *m_impl; + + public: + mbp_qel(ast_manager &m, params_ref const &p); + + ~mbp_qel(); + + /** + Do model based projection + */ + void operator()(app_ref_vector &vars, expr_ref &fml, model &mdl); +}; +} // namespace mbp diff --git a/src/qe/mbp/mbp_qel_util.cpp b/src/qe/mbp/mbp_qel_util.cpp new file mode 100644 index 000000000..e6537a1d0 --- /dev/null +++ b/src/qe/mbp/mbp_qel_util.cpp @@ -0,0 +1,110 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_qel_util.h + +Abstract: + + Utility methods for mbp_qel + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#include "qe/mbp/mbp_qel_util.h" +#include "ast/array_decl_plugin.h" +#include "ast/ast.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/expr_functors.h" +#include "ast/for_each_expr.h" +#include "ast/pp.h" + +class check_uninterp_consts : public i_expr_pred { + obj_hashtable const &m_vars; + family_id m_fid; + decl_kind m_decl_kind; + + public: + check_uninterp_consts(obj_hashtable const &vars, ast_manager &man, + family_id fid = null_family_id, + decl_kind dk = null_decl_kind) + : m_vars(vars), m_fid(fid), m_decl_kind(dk) {} + bool operator()(expr *n) override { + return (is_app(n) && is_uninterp_const(n) && + m_vars.contains(to_app(n))) && + ((m_fid == null_family_id || m_decl_kind == null_decl_kind) || + (is_sort_of(to_app(n)->get_sort(), m_fid, m_decl_kind))); + } +}; + +// check if e contains any apps from vars +// if fid and dk are not null, check if the variable is of desired sort +bool contains_vars(expr *e, obj_hashtable const &vars, ast_manager &man, + family_id fid, decl_kind dk) { + check_uninterp_consts pred(vars, man, fid, dk); + check_pred check(pred, man, false); + return check(e); +} + +app_ref new_var(sort *s, ast_manager &m) { + return app_ref(m.mk_fresh_const("mbptg", s), m); +} + +namespace collect_uninterp_consts_ns { +struct proc { + obj_hashtable &m_out; + proc(obj_hashtable &out) : m_out(out) {} + void operator()(expr *n) const {} + void operator()(app *n) { + if (is_uninterp_const(n)) m_out.insert(n); + } +}; +} // namespace collect_uninterp_consts_ns + +// Return all uninterpreted constants of \p q +void collect_uninterp_consts(expr *e, obj_hashtable &out) { + collect_uninterp_consts_ns::proc proc(out); + for_each_expr(proc, e); +} + +namespace collect_selstore_vars_ns { +struct proc { + ast_manager &m; + obj_hashtable &m_vars; + array_util m_array_util; + datatype_util m_dt_util; + proc(obj_hashtable &vars, ast_manager &man) + : m(man), m_vars(vars), m_array_util(m), m_dt_util(m) {} + void operator()(expr *n) const {} + void operator()(app *n) { + if (m_array_util.is_select(n)) { + expr *idx = n->get_arg(1); + if (is_app(idx) && m_dt_util.is_accessor(to_app(idx)->get_decl())) + return; + collect_uninterp_consts(idx, m_vars); + } else if (m_array_util.is_store(n)) { + expr *idx = n->get_arg(1), *elem = n->get_arg(2); + if (!(is_app(idx) && + m_dt_util.is_accessor(to_app(idx)->get_decl()))) + collect_uninterp_consts(idx, m_vars); + if (!(is_app(elem) && + m_dt_util.is_accessor(to_app(elem)->get_decl()))) + collect_uninterp_consts(elem, m_vars); + } + } +}; +} // namespace collect_selstore_vars_ns + +// collect all uninterpreted consts used as array indices or values +void collect_selstore_vars(expr *fml, obj_hashtable &vars, + ast_manager &man) { + collect_selstore_vars_ns::proc proc(vars, man); + quick_for_each_expr(proc, fml); +} diff --git a/src/qe/mbp/mbp_qel_util.h b/src/qe/mbp/mbp_qel_util.h new file mode 100644 index 000000000..dd1a7795a --- /dev/null +++ b/src/qe/mbp/mbp_qel_util.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2022 Microsoft Corporation + +Module Name: + + mbp_qel_util.h + +Abstract: + + Utility methods for mbp_qel + +Author: + + Hari Govind V K (hgvk94) 2023-03-07 + +Revision History: + +--*/ + +#pragma once + +#include "ast/array_decl_plugin.h" +#include "ast/ast.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "ast/pp.h" + +// check if e contains any apps from vars +// if fid and dk are not null, check if the variable is of desired sort +bool contains_vars(expr *e, obj_hashtable const &vars, ast_manager &man, + family_id fid = null_family_id, + decl_kind dk = null_decl_kind); + +app_ref new_var(sort *s, ast_manager &m); + +// Return all uninterpreted constants of \p q +void collect_uninterp_consts(expr *e, obj_hashtable &out); +// collect all uninterpreted consts used as array indices or values +void collect_selstore_vars(expr *fml, obj_hashtable &vars, + ast_manager &man); diff --git a/src/qe/mbp/mbp_solve_plugin.cpp b/src/qe/mbp/mbp_solve_plugin.cpp index d80a3665d..d46a09284 100644 --- a/src/qe/mbp/mbp_solve_plugin.cpp +++ b/src/qe/mbp/mbp_solve_plugin.cpp @@ -248,30 +248,80 @@ namespace mbp { return false; } + // returns `true` if a rewritting happened + bool try_int_mul_solve(expr *atom, bool is_pos, expr_ref &res) { + + if (!is_pos) + return false; // negation of multiplication is not a cube for + // integers + + // we want k*y == x -----> y = x div k && x mod k == 0 + expr *lhs = nullptr, *rhs = nullptr; + if (!m.is_eq(atom, lhs, rhs)) return false; + + if (!a.is_int(lhs)) { return false; } + + if (!a.is_mul(rhs)) { + if (a.is_mul(lhs)) + std::swap(lhs, rhs); + else + return false; // no muls + } + + // of the form v = k*expr + expr *first = nullptr, *second = nullptr; + if (!a.is_mul(rhs, first, second)) return false; + + if (!(is_app(first) && a.plugin().is_value(to_app(first)))) { + if (is_app(second) && a.plugin().is_value(to_app(second))) { + std::swap(first, second); + } else { + return false; + } + } + + if (a.is_zero(first)) { + // SASSERT(a.is_int(lhs)); + res = m.mk_eq(lhs, a.mk_int(0)); + return true; + }; + + // `first` is a value, different from 0 + res = m.mk_and(m.mk_eq(second, a.mk_idiv(lhs, first)), + m.mk_eq(a.mk_int(0), a.mk_mod(lhs, first))); + + return true; + } + expr_ref solve(expr* atom, bool is_pos) override { - expr *e1, *e2; - expr_ref res(atom, m); - if (m.is_eq (atom, e1, e2)) { - expr_ref v(m), t(m); - v = e1; t = e2; - // -- attempt to solve using arithmetic - solve(e1, e2, v, t); - // -- normalize equality - res = mk_eq_core(v, t); - } - else if (a.is_le(atom, e1, e2)) { - mk_le_core(e1, e2, res); - } - else if (a.is_ge(atom, e1, e2)) { - mk_ge_core(e1, e2, res); - } + expr_ref res(atom, m); - // restore negation - if (!is_pos) { - res = mk_not(m, res); - } - return res; + if (try_int_mul_solve(atom, is_pos, res)) return res; + + expr *e1, *e2; + + if (m.is_eq(atom, e1, e2)) { + expr_ref v(m), t(m); + v = e1; + t = e2; + // -- attempt to solve using arithmetic + solve(e1, e2, v, t); + // -- normalize equality + res = mk_eq_core(v, t); + } + else if (a.is_le(atom, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (a.is_ge(atom, e1, e2)) { + mk_ge_core(e1, e2, res); + } + + // restore negation + if (!is_pos) { + res = mk_not(m, res); + } + return res; } }; diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 624febffd..ee371bdfa 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -12,1291 +12,1811 @@ Abstract: Author: Arie Gurfinkel + Hari Govind V K (hgvk94) + Isabel Garcia (igcontreras) + +Revision History: + + Added implementation of qe_lite using term graph Notes: --*/ -#include "util/util.h" -#include "util/uint_set.h" -#include "util/obj_pair_hashtable.h" +#include "qe/mbp/mbp_term_graph.h" +#include "ast/array_peq.h" +#include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/occurs.h" #include "ast/rewriter/th_rewriter.h" #include "model/model_evaluator.h" -#include "qe/mbp/mbp_term_graph.h" +#include "qe/mbp/mbp_arrays.h" +#include "util/bit_vector.h" +#include "util/obj_pair_hashtable.h" +#include "util/uint_set.h" +#include "util/util.h" namespace mbp { - static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) { - expr *t = nullptr; - // x != !x == true - if ((m.is_not(e1, t) && t == e2) || (m.is_not(e2, t) && t == e1)) - return expr_ref(m.mk_true(), m); - else if (m.are_distinct(e1, e2)) - return expr_ref(m.mk_true(), m); - return expr_ref(m.mk_not(m.mk_eq(e1, e2)), m); - } +static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) { + expr *t = nullptr; + // x != !x == true + if ((m.is_not(e1, t) && t == e2) || (m.is_not(e2, t) && t == e1)) + return expr_ref(m.mk_true(), m); + else if (m.are_distinct(e1, e2)) + return expr_ref(m.mk_true(), m); + return expr_ref(m.mk_not(m.mk_eq(e1, e2)), m); +} - namespace { - struct sort_lt_proc { - bool operator()(const expr* a, const expr *b) const { - return a->get_sort()->get_id() < b->get_sort()->get_id(); - } - }; +namespace { +struct sort_lt_proc { // for representatives in model_complete + bool operator()(const expr *a, const expr *b) const { + return a->get_sort()->get_id() < b->get_sort()->get_id(); } +}; +struct mark_all_sub_expr { + expr_sparse_mark &m_mark; + mark_all_sub_expr(expr_sparse_mark &mark) : m_mark(mark) {} + void operator()(var *n) const {} + void operator()(app *n) const { m_mark.mark(n); } + void operator()(quantifier *n) const {} +}; +} // namespace - namespace is_pure_ns { - struct found{}; - struct proc { - is_variable_proc &m_is_var; - proc(is_variable_proc &is_var) : m_is_var(is_var) {} - void operator()(var *n) const {if (m_is_var(n)) throw found();} - void operator()(app const *n) const {if (m_is_var(n)) throw found();} - void operator()(quantifier *n) const {} - }; +namespace is_pure_ns { +struct found {}; +struct proc { + is_variable_proc &m_is_var; + proc(is_variable_proc &is_var) : m_is_var(is_var) {} + void operator()(var *n) const { + if (m_is_var(n)) throw found(); } - - bool is_pure(is_variable_proc &is_var, expr *e) { - try { - is_pure_ns::proc v(is_var); - quick_for_each_expr(v, e); - } - catch (const is_pure_ns::found &) { - return false; - } - return true; + void operator()(app const *n) const { + if (m_is_var(n)) throw found(); } + void operator()(quantifier *n) const {} +}; +} // namespace is_pure_ns - class term { - // -- an app represented by this term - expr_ref m_expr; // NSB: to make usable with exprs - // -- root of the equivalence class - term* m_root; - // -- next element in the equivalence class (cyclic linked list) - term* m_next; +bool is_pure(is_variable_proc &is_var, expr *e) { + try { + is_pure_ns::proc v(is_var); + quick_for_each_expr(v, e); + } catch (const is_pure_ns::found &) { return false; } + return true; +} + +bool term_graph::is_ground(expr *e) { + try { + is_ground_ns::proc v(m_is_var); + quick_for_each_expr(v, e); + } catch (const is_ground_ns::found &) { return false; } + return true; +} + +class term { + // -- an app represented by this term + expr_ref m_expr; // NSB: to make usable with exprs + // -- root of the equivalence class + term *m_root; + // -- representative of the equivalence class + term *m_repr; + // -- next element in the equivalence class (cyclic linked list) + term *m_next; + // -- general purpose mark + unsigned m_mark : 1; + // -- general purpose second mark + unsigned m_mark2 : 1; + // -- is an interpreted constant + unsigned m_interpreted : 1; + // caches whether m_expr is an equality + unsigned m_is_eq : 1; + // caches whether m_expr is an inequality + unsigned m_is_neq : 1; + // caches whether m_expr is a distinct + unsigned m_is_distinct : 1; + // caches whether m_expr is a partial equality + unsigned m_is_peq : 1; + // caches whether m_expr is the child of not + unsigned m_is_neq_child : 1; + + // -- the term is a compound term can be rewritten to be ground or it is a + // ground constant + unsigned m_cgr : 1; + // -- the term is ground + unsigned m_gr : 1; + + // -- terms that contain this term as a child (only maintained for root + // nodes) + ptr_vector m_parents; + + // arguments of term. + ptr_vector m_children; + + struct class_props { + // TODO: parents should be here + // -- the class has a ground representative + unsigned m_gr_class : 1; // -- eq class size unsigned m_class_size; - // -- general purpose mark - unsigned m_mark:1; - // -- general purpose second mark - unsigned m_mark2:1; - // -- is an interpreted constant - unsigned m_interpreted:1; + // -- disequality sets that the class belongs to + term_graph::deqs m_deqs; - // -- terms that contain this term as a child - ptr_vector m_parents; - - // arguments of term. - ptr_vector m_children; - - public: - term(expr_ref const& v, u_map& app2term) : - m_expr(v), - m_root(this), - m_next(this), - m_class_size(1), - m_mark(false), - m_mark2(false), - m_interpreted(false) { - if (!is_app(m_expr)) return; - for (expr* e : *to_app(m_expr)) { - term* t = app2term[e->get_id()]; - t->get_root().m_parents.push_back(this); - m_children.push_back(t); - } - } - - ~term() {} - - class parents { - term const& t; - public: - parents(term const& _t):t(_t) {} - parents(term const* _t):t(*_t) {} - ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } - ptr_vector::const_iterator end() const { return t.m_parents.end(); } - }; - - class children { - term const& t; - public: - children(term const& _t):t(_t) {} - children(term const* _t):t(*_t) {} - ptr_vector::const_iterator begin() const { return t.m_children.begin(); } - ptr_vector::const_iterator end() const { return t.m_children.end(); } - }; - - // Congruence table hash function is based on - // roots of children and function declaration. - - unsigned get_hash() const { - unsigned a, b, c; - a = b = c = get_decl_id(); - for (term * ch : children(this)) { - a = ch->get_root().get_id(); - mix(a, b, c); - } - return c; - } - - static bool cg_eq(term const * t1, term const * t2) { - if (t1->get_decl_id() != t2->get_decl_id()) return false; - if (t1->m_children.size() != t2->m_children.size()) return false; - for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++ i) { - if (t1->m_children[i]->get_root().get_id() != t2->m_children[i]->get_root().get_id()) return false; - } - return true; - } - - unsigned get_id() const { return m_expr->get_id();} - - unsigned get_decl_id() const { return is_app(m_expr) ? to_app(m_expr)->get_decl()->get_id() : m_expr->get_id(); } - - bool is_marked() const {return m_mark;} - void set_mark(bool v){m_mark = v;} - bool is_marked2() const {return m_mark2;} // NSB: where is this used? - void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? - - bool is_interpreted() const {return m_interpreted;} - bool is_theory() const { return !is_app(m_expr) || to_app(m_expr)->get_family_id() != null_family_id; } - void mark_as_interpreted() {m_interpreted=true;} - expr* get_expr() const {return m_expr;} - unsigned get_num_args() const { return is_app(m_expr) ? to_app(m_expr)->get_num_args() : 0; } - - term &get_root() const {return *m_root;} - bool is_root() const {return m_root == this;} - void set_root(term &r) {m_root = &r;} - term &get_next() const {return *m_next;} - void add_parent(term* p) { m_parents.push_back(p); } - - unsigned get_class_size() const {return m_class_size;} - - void merge_eq_class(term &b) { - std::swap(this->m_next, b.m_next); - m_class_size += b.get_class_size(); + class_props() : m_gr_class(0), m_class_size(1) {} + void merge(class_props &b) { + m_class_size += b.m_class_size; + m_gr_class |= b.m_gr_class; + m_deqs |= b.m_deqs; // merge disequalities // -- reset (useful for debugging) b.m_class_size = 0; + b.m_gr_class = false; + b.m_deqs.reset(); } - - // -- make this term the root of its equivalence class - void mk_root() { - if (is_root()) return; - - term *curr = this; - do { - if (curr->is_root()) { - // found previous root - SASSERT(curr != this); - m_class_size = curr->get_class_size(); - curr->m_class_size = 0; - } - curr->set_root(*this); - curr = &curr->get_next(); - } - while (curr != this); + void transfer(class_props &b) { + // TODO replace by std::swap of the whole struct? + m_class_size = b.m_class_size; + b.m_class_size = 0; + std::swap(m_deqs, b.m_deqs); + m_gr_class = b.m_gr_class; + b.m_gr_class = false; } + }; + class_props m_class_props; - std::ostream& display(std::ostream& out) const { - out << get_id() << ": " << m_expr - << (is_root() ? " R" : "") << " - "; - term const* r = &this->get_next(); - while (r != this) { - out << r->get_id() << " "; - r = &r->get_next(); - } - out << "\n"; - return out; + public: + term(expr_ref const &v, u_map &app2term) + : m_expr(v), m_root(this), m_repr(nullptr), m_next(this), m_mark(false), + m_mark2(false), m_interpreted(false), + m_is_eq(m_expr.get_manager().is_eq(m_expr)), m_is_peq(false), + m_is_neq_child(false), m_cgr(0), m_gr(0) { + m_is_neq = m_expr.get_manager().is_not(m_expr) && + m_expr.get_manager().is_eq(to_app(m_expr)->get_arg(0)); + m_is_distinct = m_expr.get_manager().is_distinct(m_expr); + m_children.reset(); + if (!is_app(m_expr)) return; + for (expr *e : *to_app(m_expr)) { + term *t = app2term[e->get_id()]; + t->get_root().m_parents.push_back(this); + m_children.push_back(t); + } + m_is_peq = is_partial_eq(to_app(m_expr)); + } + + ~term() {} + + class parents { + term const &t; + + public: + parents(term const &_t) : t(_t) {} + parents(term const *_t) : t(*_t) {} + ptr_vector::const_iterator begin() const { + return t.m_parents.begin(); + } + ptr_vector::const_iterator end() const { + return t.m_parents.end(); } }; - static std::ostream& operator<<(std::ostream& out, term const& t) { - return t.display(out); - } + class children { + term const &t; - bool term_graph::is_variable_proc::operator()(const expr * e) const { - if (!is_app(e)) return false; - const app *a = ::to_app(e); - TRACE("qe_verbose", tout << a->get_family_id() << " " << m_solved.contains(a->get_decl()) << " " << m_decls.contains(a->get_decl()) << "\n";); - return - a->get_family_id() == null_family_id && - !m_solved.contains(a->get_decl()) && - m_exclude == m_decls.contains(a->get_decl()); - } - - bool term_graph::is_variable_proc::operator()(const term &t) const { - return (*this)(t.get_expr()); - } - - void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { - reset(); - m_exclude = exclude; - for (auto *d : decls) m_decls.insert(d); - } - void term_graph::is_variable_proc::mark_solved(const expr *e) { - if ((*this)(e) && is_app(e)) - m_solved.insert(::to_app(e)->get_decl()); - } - - - unsigned term_graph::term_hash::operator()(term const* t) const { return t->get_hash(); } - - bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); } - - term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m), m_projector(nullptr) { - m_plugins.register_plugin(mbp::mk_basic_solve_plugin(m, m_is_var)); - m_plugins.register_plugin(mbp::mk_arith_solve_plugin(m, m_is_var)); - } - - term_graph::~term_graph() { - dealloc(m_projector); - reset(); - } - - bool term_graph::is_pure_def(expr *atom, expr*& v) { - expr *e = nullptr; - return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); - } - - static family_id get_family_id(ast_manager &m, expr *lit) { - if (m.is_not(lit, lit)) - return get_family_id(m, lit); - - expr *a = nullptr, *b = nullptr; - // deal with equality using sort of range - if (m.is_eq (lit, a, b)) { - return a->get_sort()->get_family_id(); + public: + children(term const &_t) : t(_t) {} + children(term const *_t) : t(*_t) {} + ptr_vector::const_iterator begin() const { + return t.m_children.begin(); } - // extract family_id of top level app - else if (is_app(lit)) { - return to_app(lit)->get_decl()->get_family_id(); + ptr_vector::const_iterator end() const { + return t.m_children.end(); } - else { - return null_family_id; - } - } - void term_graph::add_lit(expr *l) { - expr_ref lit(m); - expr_ref_vector lits(m); - lits.push_back(l); - for (unsigned i = 0; i < lits.size(); ++i) { - l = lits.get(i); - family_id fid = get_family_id(m, l); - mbp::solve_plugin *pin = m_plugins.get_plugin(fid); - lit = pin ? (*pin)(l) : l; - if (m.is_and(lit)) { - lits.append(::to_app(lit)->get_num_args(), ::to_app(lit)->get_args()); - } - else { - m_lits.push_back(lit); - internalize_lit(lit); - } + }; + + // Congruence table hash function is based on + // roots of children and function declaration. + + unsigned get_hash() const { + unsigned a, b, c; + a = b = c = get_decl_id(); + for (term *ch : children(this)) { + a = ch->get_root().get_id(); + mix(a, b, c); } + return c; } - bool term_graph::is_internalized(expr *a) { - return m_app2term.contains(a->get_id()); - } - - term* term_graph::get_term(expr *a) { - term *res; - return m_app2term.find (a->get_id(), res) ? res : nullptr; - } - - term *term_graph::mk_term(expr *a) { - expr_ref e(a, m); - term * t = alloc(term, e, m_app2term); - if (t->get_num_args() == 0 && m.is_unique_value(a)) - t->mark_as_interpreted(); - - m_terms.push_back(t); - m_app2term.insert(a->get_id(), t); - return t; - } - - term* term_graph::internalize_term(expr *t) { - term* res = get_term(t); - if (res) return res; - ptr_buffer todo; - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - res = get_term(t); - if (res) { - todo.pop_back(); - continue; - } - unsigned sz = todo.size(); - if (is_app(t)) { - for (expr * arg : *::to_app(t)) { - if (!get_term(arg)) - todo.push_back(arg); - } - } - if (sz < todo.size()) continue; - todo.pop_back(); - res = mk_term(t); - } - SASSERT(res); - return res; - } - - void term_graph::internalize_eq(expr *a1, expr* a2) { - SASSERT(m_merge.empty()); - merge(*internalize_term(a1), *internalize_term(a2)); - merge_flush(); - SASSERT(m_merge.empty()); - } - - void term_graph::internalize_lit(expr* lit) { - expr *e1 = nullptr, *e2 = nullptr, *v = nullptr; - if (m.is_eq (lit, e1, e2)) { - internalize_eq (e1, e2); - } - else { - internalize_term(lit); - } - if (is_pure_def(lit, v)) { - m_is_var.mark_solved(v); - } - } - - void term_graph::merge_flush() { - while (!m_merge.empty()) { - term* t1 = m_merge.back().first; - term* t2 = m_merge.back().second; - m_merge.pop_back(); - merge(*t1, *t2); - } - } - - void term_graph::merge(term &t1, term &t2) { - term *a = &t1.get_root(); - term *b = &t2.get_root(); - - if (a == b) return; - - // -- merge might invalidate term2app cache - m_term2app.reset(); - m_pinned.reset(); - - if (a->get_class_size() > b->get_class_size()) { - std::swap(a, b); - } - - // Remove parents of b from the cg table. - for (term* p : term::parents(b)) { - if (!p->is_marked()) { - p->set_mark(true); - m_cg_table.erase(p); - } - } - // make 'a' be the root of the equivalence class of 'b' - b->set_root(*a); - for (term *it = &b->get_next(); it != b; it = &it->get_next()) { - it->set_root(*a); - } - - // merge equivalence classes - a->merge_eq_class(*b); - - // Insert parents of b's old equilvalence class into the cg table - for (term* p : term::parents(b)) { - if (p->is_marked()) { - term* p_old = m_cg_table.insert_if_not_there(p); - p->set_mark(false); - a->add_parent(p); - // propagate new equalities. - if (p->get_root().get_id() != p_old->get_root().get_id()) { - m_merge.push_back(std::make_pair(p, p_old)); - } - } - } - SASSERT(marks_are_clear()); - } - - expr* term_graph::mk_app_core (expr *e) { - if (is_app(e)) { - expr_ref_buffer kids(m); - app* a = ::to_app(e); - for (expr * arg : *a) { - kids.push_back (mk_app(arg)); - } - app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.data()); - m_pinned.push_back(res); - return res; - } - else { - return e; - } - } - - expr_ref term_graph::mk_app(term const &r) { - SASSERT(r.is_root()); - - if (r.get_num_args() == 0) { - return expr_ref(r.get_expr(), m); - } - - expr* res = nullptr; - if (m_term2app.find(r.get_id(), res)) { - return expr_ref(res, m); - } - - res = mk_app_core (r.get_expr()); - m_term2app.insert(r.get_id(), res); - return expr_ref(res, m); - - } - - expr_ref term_graph::mk_app(expr *a) { - term *t = get_term(a); - if (!t) - return expr_ref(a, m); - else - return mk_app(t->get_root()); - - } - - void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { - SASSERT(t.is_root()); - expr_ref rep(mk_app(t), m); - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - expr* mem = mk_app_core(it->get_expr()); - out.push_back (m.mk_eq (rep, mem)); - } - } - - void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { - mk_equalities(t, out); - - for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { - expr* a1 = mk_app_core (it->get_expr()); - for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { - expr* a2 = mk_app_core(it2->get_expr()); - out.push_back (m.mk_eq (a1, a2)); - } - } - } - - void term_graph::reset_marks() { - for (term * t : m_terms) { - t->set_mark(false); - } - } - - bool term_graph::marks_are_clear() { - for (term * t : m_terms) { - if (t->is_marked()) return false; + static bool cg_eq(term const *t1, term const *t2) { + if (t1->get_decl_id() != t2->get_decl_id()) return false; + if (t1->m_children.size() != t2->m_children.size()) return false; + for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++i) { + if (t1->m_children[i]->get_root().get_id() != + t2->m_children[i]->get_root().get_id()) + return false; } return true; } - /// Order of preference for roots of equivalence classes - /// XXX This should be factored out to let clients control the preference - bool term_graph::term_lt(term const &t1, term const &t2) { - // prefer constants over applications - // prefer uninterpreted constants over values - // prefer smaller expressions over larger ones - if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { - if (t1.get_num_args() == t2.get_num_args()) { - // t1.get_num_args() == t2.get_num_args() == 0 - if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) - return t1.get_id() < t2.get_id(); - return m.is_value(t2.get_expr()); - } - return t1.get_num_args() < t2.get_num_args(); - } - - unsigned sz1 = get_num_exprs(t1.get_expr()); - unsigned sz2 = get_num_exprs(t2.get_expr()); - return sz1 < sz2; + unsigned deg() const { return m_children.size(); } + unsigned get_id() const { return m_expr->get_id(); } + bool is_eq_or_neq() const { return m_is_eq || m_is_neq || m_is_distinct; } + bool is_eq_or_peq() const { return m_is_eq || m_is_peq; } + bool is_neq() const { return m_is_neq; } + void set_neq_child() { m_is_neq_child = true; } + bool is_neq_child() const { return m_is_neq_child; } + unsigned get_decl_id() const { + return is_app(m_expr) ? to_app(m_expr)->get_decl()->get_id() + : m_expr->get_id(); } - void term_graph::pick_root (term &t) { - term *r = &t; - for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - it->set_mark(true); - if (term_lt(*it, *r)) { r = it; } - } + bool is_marked() const { return m_mark; } + void set_mark(bool v) { m_mark = v; } + bool is_marked2() const { return m_mark2; } // NSB: where is this used? + void set_mark2(bool v) { m_mark2 = v; } // NSB: where is this used? - // -- if found something better, make it the new root - if (r != &t) { - r->mk_root(); + bool is_cgr() const { return m_cgr; } + void set_cgr(bool v) { m_cgr = v; } + + bool is_gr() const { return m_gr; } + void set_gr(bool v) { m_gr = v; } + + bool is_class_gr_root() const { + SASSERT(is_root()); + return m_class_props.m_gr_class; + } + void set_class_gr_root(bool v) { + SASSERT(is_root()); + m_class_props.m_gr_class = v; + } + bool is_class_gr() const { return m_root->is_class_gr_root(); } + void set_class_gr(bool v) { m_root->set_class_gr_root(v); } + + static bool are_deq(const term &t1, const term &t2) { + term_graph::deqs const &ds1 = t1.get_root().get_deqs(); + term_graph::deqs const &ds2 = t2.get_root().get_deqs(); + + term_graph::deqs tmp(ds1); // copy + + tmp &= ds2; + return tmp != 0; + } + + static void set_deq(term_graph::deqs &ds, unsigned idx) { + ds.resize(idx + 1); + ds.set(idx); + } + + bool all_children_ground() { + SASSERT(deg() != 0); + return all_of(m_children, + [&](const term *t) { return t->is_class_gr(); }); + } + + void set_mark2_terms_class(bool v) { // TODO: remove + if (is_marked2()) return; + term *curr = this; + do { + curr->set_mark2(v); + curr = &curr->get_next(); + } + while (curr != this); + } + + bool is_interpreted() const { return m_interpreted; } + bool is_theory() const { + return !is_app(m_expr) || + to_app(m_expr)->get_family_id() != null_family_id; + } + void mark_as_interpreted() { m_interpreted = true; } + expr *get_expr() const { return m_expr; } + unsigned get_num_args() const { + return is_app(m_expr) ? to_app(m_expr)->get_num_args() : 0; + } + + term &get_root() const { return *m_root; } + bool is_root() const { return m_root == this; } + void set_root(term &r) { m_root = &r; } + term *get_repr() { return m_repr; } + bool is_repr() const { return m_repr == this; } + void set_repr(term *t) { + SASSERT(get_root().get_id() == t->get_root().get_id()); + m_repr = t; + } + void reset_repr() { m_repr = nullptr; } + term &get_next() const { return *m_next; } + void add_parent(term *p) { m_parents.push_back(p); } + + unsigned get_class_size() const { return m_class_props.m_class_size; } + + void merge_eq_class(term &b) { + std::swap(this->m_next, b.m_next); + m_class_props.merge(b.m_class_props); + } + + // -- make this term the repr of its equivalence class + void mk_repr() { + term *curr = this; + do { + curr->set_repr(this); + curr = &curr->get_next(); + } + while (curr != this); + } + + std::ostream &display(std::ostream &out) const { + out << get_id() << ": " << m_expr << (is_repr() ? " R" : "") + << (is_gr() ? " G" : "") << (is_class_gr() ? " clsG" : "") + << (is_cgr() ? " CG" : "") << " deg:" << deg() << " - "; + term const *r = &this->get_next(); + while (r != this) { + out << r->get_id() << " " << (r->is_cgr() ? " CG" : "") << " "; + r = &r->get_next(); + } + out << "\n"; + return out; + } + + term_graph::deqs &get_deqs() { return m_class_props.m_deqs; } +}; + +static std::ostream &operator<<(std::ostream &out, term const &t) { + return t.display(out); +} + +// t1 != t2 +void term_graph::add_deq_proc::operator()(term *t1, term *t2) { + ptr_vector ts(2); + ts[0] = t1; + ts[1] = t2; + (*this)(ts); +} + +// distinct(ts) +void term_graph::add_deq_proc::operator()(ptr_vector &ts) { + for (auto t : ts) { term::set_deq(t->get_root().get_deqs(), m_deq_cnt); } + SASSERT(m_deq_cnt < UINT64_MAX); + m_deq_cnt++; +} + +bool term_graph::is_variable_proc::operator()(const expr *e) const { + if (!is_app(e)) return false; + const app *a = ::to_app(e); + TRACE("qe_verbose", tout << a->get_family_id() << " " + << m_solved.contains(a->get_decl()) << " " + << m_decls.contains(a->get_decl()) << "\n";); + return a->get_family_id() == null_family_id && + !m_solved.contains(a->get_decl()) && + m_exclude == m_decls.contains(a->get_decl()); +} + +bool term_graph::is_variable_proc::operator()(const term &t) const { + return (*this)(t.get_expr()); +} + +void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, + bool exclude) { + reset(); + m_exclude = exclude; + for (auto *d : decls) m_decls.insert(d); +} + +void term_graph::is_variable_proc::add_decls(const app_ref_vector &decls) { + for (auto *d : decls) m_decls.insert(d->get_decl()); +} + +void term_graph::is_variable_proc::add_decl(app *d) { + m_decls.insert(d->get_decl()); +} + +void term_graph::is_variable_proc::set_decls(const app_ref_vector &vars, + bool exclude) { + reset(); + m_exclude = exclude; + for (auto *v : vars) m_decls.insert(v->get_decl()); +} + +void term_graph::is_variable_proc::mark_solved(const expr *e) { + if ((*this)(e) && is_app(e)) m_solved.insert(::to_app(e)->get_decl()); +} + +unsigned term_graph::term_hash::operator()(term const *t) const { + return t->get_hash(); +} + +bool term_graph::term_eq::operator()(term const *a, term const *b) const { + return term::cg_eq(a, b); +} + +term_graph::term_graph(ast_manager &man) + : m(man), m_lits(m), m_pinned(m), m_projector(nullptr), + m_explicit_eq(false), m_repick_repr(false) { + m_is_var.reset(); + m_plugins.register_plugin(mbp::mk_basic_solve_plugin(m, m_is_var)); + m_plugins.register_plugin(mbp::mk_arith_solve_plugin(m, m_is_var)); +} + +term_graph::~term_graph() { + dealloc(m_projector); + reset(); +} + +bool term_graph::is_pure_def(expr *atom, expr *&v) { + expr *e = nullptr; + return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); +} + +static family_id get_family_id(ast_manager &m, expr *lit) { + if (m.is_not(lit, lit)) return get_family_id(m, lit); + + expr *a = nullptr, *b = nullptr; + // deal with equality using sort of range + if (m.is_eq(lit, a, b)) { return a->get_sort()->get_family_id(); } + // extract family_id of top level app + else if (is_app(lit)) { return to_app(lit)->get_decl()->get_family_id(); } + else { return null_family_id; } +} +void term_graph::add_lit(expr *l) { + expr_ref lit(m); + expr_ref_vector lits(m); + lits.push_back(l); + for (unsigned i = 0; i < lits.size(); ++i) { + l = lits.get(i); + family_id fid = get_family_id(m, l); + mbp::solve_plugin *pin = m_plugins.get_plugin(fid); + lit = pin ? (*pin)(l) : l; + if (m.is_and(lit)) { + lits.append(::to_app(lit)->get_num_args(), + ::to_app(lit)->get_args()); + } + else { + m_lits.push_back(lit); + internalize_lit(lit); + } + } +} + +// collect expressions of all terms in the term graph +// optionally, exclude constructively ground nodes that are not equalities +// overwrites res +void term_graph::get_terms(expr_ref_vector &res, bool exclude_cground) { + std::function fil = nullptr; + if (exclude_cground) { + fil = [](term *t) { + return !t->is_neq_child() && (t->is_eq_or_peq() || !t->is_cgr()); + }; + } + else { + fil = [](term *t) { return !t->is_neq_child(); }; + } + auto terms = m_terms.filter_pure(fil); + res.resize(terms.size()); + unsigned i = 0; + for (term *t : terms) res[i++] = t->get_expr(); +} + +bool term_graph::is_cgr(expr *e) { + if (!is_internalized(e)) return false; + term *t = get_term(e); + return (!t->is_eq_or_peq() && t->is_cgr()); +} + +bool term_graph::is_internalized(expr *a) { + return m_app2term.contains(a->get_id()); +} + +term *term_graph::get_term(expr *a) { + term *res; + return m_app2term.find(a->get_id(), res) ? res : nullptr; +} + +term *term_graph::mk_term(expr *a) { + expr_ref e(a, m); + term *t = alloc(term, e, m_app2term); + if (is_ground(a)) { + t->set_gr(true); + t->set_cgr(true); + t->set_class_gr(true); + } + else if (t->deg() > 0 && t->all_children_ground()) { + t->set_cgr(true); + t->set_class_gr(true); + } + if (t->get_num_args() == 0 && m.is_unique_value(a)) + t->mark_as_interpreted(); + + m_terms.push_back(t); + m_app2term.insert(a->get_id(), t); + return t; +} + +term *term_graph::internalize_term(expr *t) { + term *res = get_term(t); + if (res) return res; + ptr_buffer todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + res = get_term(t); + if (res) { + todo.pop_back(); + continue; + } + unsigned sz = todo.size(); + if (is_app(t)) { + for (expr *arg : *::to_app(t)) { + if (!get_term(arg)) todo.push_back(arg); + } + } + if (sz < todo.size()) continue; + todo.pop_back(); + res = mk_term(t); + + // the term was not internalized in this syntactic form, but it + // could be congruent with some other term, if that is the case, we + // need to merge them. + term *res_old = m_cg_table.insert_if_not_there(res); + if (res->is_cgr()) res_old->set_cgr(true); + SASSERT(res_old->is_cgr() == res->is_cgr()); + if (res_old->get_root().get_id() != res->get_root().get_id()) { + m_merge.push_back(std::make_pair(res, res_old)); + } + } + merge_flush(); + SASSERT(res); + return res; +} + +void term_graph::internalize_eq(expr *a1, expr *a2) { + SASSERT(m_merge.empty()); + merge(*internalize_term(a1), *internalize_term(a2)); + merge_flush(); + SASSERT(m_merge.empty()); + if (!m_explicit_eq) return; + expr_ref eq(m.mk_eq(a1, a2), m); + term *res = get_term(eq); + if (!res) mk_term(eq); +} + +void term_graph::internalize_distinct(expr *d) { + app *a = to_app(d); + ptr_vector ts(a->get_decl()->get_arity()); + auto tsit = ts.begin(); + for (auto arg : *a) { + *tsit = internalize_term(arg); + tsit++; + } + m_add_deq(ts); + m_deq_distinct.push_back(ts); + if (!m_explicit_eq) return; + term *t = get_term(d); + if (!t) mk_term(d); +} + +// Assumes that a1 != a2 is satisfiable +void term_graph::internalize_deq(expr *a1, expr *a2) { + term *t1 = internalize_term(a1); + term *t2 = internalize_term(a2); + m_add_deq(t1, t2); + m_deq_pairs.push_back({t1, t2}); + if (!m_explicit_eq) return; + expr_ref eq(m.mk_eq(a1, a2), m); + term *eq_term = mk_term(eq); + eq_term->set_neq_child(); + expr_ref deq(m.mk_not(eq), m); + term *res = get_term(deq); + if (!res) mk_term(deq); +} + +void term_graph::internalize_lit(expr *lit) { + expr *e1 = nullptr, *e2 = nullptr, *ne = nullptr, *v = nullptr; + if (m.is_eq(lit, e1, e2)) { // internalize equality + internalize_eq(e1, e2); + } + else if (m.is_distinct(lit)) { internalize_distinct(lit); } + else if (m.is_not(lit, ne) && m.is_eq(ne, e1, e2)) { + internalize_deq(e1, e2); + } + else { internalize_term(lit); } + if (is_pure_def(lit, v)) { m_is_var.mark_solved(v); } +} + +void term_graph::merge_flush() { + while (!m_merge.empty()) { + term *t1 = m_merge.back().first; + term *t2 = m_merge.back().second; + m_merge.pop_back(); + merge(*t1, *t2); + } +} + +void term_graph::merge(term &t1, term &t2) { + term *a = &t1.get_root(); + term *b = &t2.get_root(); + + if (a == b) return; + + // -- merge might invalidate term2app cache + m_term2app.reset(); + m_pinned.reset(); + m_repick_repr = true; + + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } + + // Remove parents of b from the cg table + for (term *p : term::parents(b)) { + if (!p->is_marked()) { + p->set_mark(true); + m_cg_table.erase(p); } } - /// Choose better roots for equivalence classes - void term_graph::pick_roots() { - SASSERT(marks_are_clear()); - for (term* t : m_terms) { - if (!t->is_marked() && t->is_root()) - pick_root(*t); - } - reset_marks(); + bool prop_cgroundness = (b->is_class_gr() != a->is_class_gr()); + // make 'a' be the root of the equivalence class of 'b' + b->set_root(*a); + for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + it->set_root(*a); } - void term_graph::display(std::ostream &out) { - for (term * t : m_terms) { - out << *t; + // merge equivalence classes + a->merge_eq_class(*b); + + // Insert parents of b's old equivalence class into the cg table + // bottom-up merge of parents + for (term *p : term::parents(b)) { + if (p->is_marked()) { + term *p_old = m_cg_table.insert_if_not_there(p); + p->set_mark(false); + a->add_parent(p); + // propagate new equalities. + if (p->get_root().get_id() != p_old->get_root().get_id()) { + m_merge.push_back(std::make_pair(p, p_old)); + } + } + } + if (prop_cgroundness) cground_percolate_up(a); + + SASSERT(marks_are_clear()); +} + +expr *term_graph::mk_app_core(expr *e) { + if (is_app(e)) { + expr_ref_buffer kids(m); + app *a = ::to_app(e); + for (expr *arg : *a) { kids.push_back(mk_app(arg)); } + app *res = m.mk_app(a->get_decl(), a->get_num_args(), kids.data()); + m_pinned.push_back(res); + return res; + } + else { return e; } +} + +expr_ref term_graph::mk_app(term &r) { + SASSERT(r.is_repr()); + + if (r.get_num_args() == 0) { return expr_ref(r.get_expr(), m); } + + expr *res = nullptr; + if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } + + res = mk_app_core(r.get_expr()); + m_term2app.insert(r.get_id(), res); + return expr_ref(res, m); +} + +expr_ref term_graph::mk_app(expr *a) { + term *t = get_term(a); + if (!t) + return expr_ref(a, m); + else { + SASSERT(t->get_repr()); + return mk_app(*t->get_repr()); + } +} + +void term_graph::mk_equalities(term &t, expr_ref_vector &out) { + SASSERT(t.is_repr()); + if (t.get_class_size() == 1) return; + expr_ref rep(mk_app(t), m); + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr *mem = mk_app_core(it->get_expr()); + out.push_back(m.mk_eq(rep, mem)); + } +} + +void term_graph::mk_all_equalities(term &t, expr_ref_vector &out) { + if (t.get_class_size() == 1) return; + + mk_equalities(t, out); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr *a1 = mk_app_core(it->get_expr()); + for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { + expr *a2 = mk_app_core(it2->get_expr()); + out.push_back(m.mk_eq(a1, a2)); + } + } +} + +void term_graph::mk_qe_lite_equalities(term &t, expr_ref_vector &out, + check_pred &contains_nc) { + SASSERT(t.is_repr()); + if (t.get_class_size() == 1) return; + expr_ref rep(m); + rep = mk_app(t); + if (contains_nc(rep)) { + TRACE( + "qe_debug", tout << "repr not in core " << t; + for (term *it = &t.get_next(); it != &t; + it = &it->get_next()) { tout << *it << "\n"; };); + DEBUG_CODE( + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) + SASSERT(!it->is_cgr() || + contains_nc(mk_app_core(it->get_expr())));); + return; + } + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr *e = it->get_expr(); + SASSERT(is_app(e)); + app *a = to_app(e); + // don't add equalities for vars to eliminate + if (m_is_var.contains(a->get_decl())) continue; + expr *mem = mk_app_core(e); + if (rep != mem && !contains_nc(mem)) out.push_back(m.mk_eq(rep, mem)); + } +} + +void term_graph::reset_marks() { + for (term *t : m_terms) { t->set_mark(false); } +} + +void term_graph::reset_marks2() { + for (term *t : m_terms) { t->set_mark2(false); } +} + +bool term_graph::marks_are_clear() { + for (term *t : m_terms) { + if (t->is_marked()) return false; + } + return true; +} + +/// Order of preference for roots of equivalence classes +/// XXX This should be factored out to let clients control the preference +bool term_graph::term_lt(term const &t1, term const &t2) { + // prefer constants over applications (ground) + // prefer applications over variables (for non-ground) + // prefer uninterpreted constants over values + // prefer smaller expressions over larger ones + + if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { + if (t1.get_num_args() == t2.get_num_args()) { + if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) + return t1.get_id() < t2.get_id(); + return m.is_value(t2.get_expr()); + } + return t1.get_num_args() < t2.get_num_args(); + } + + // XXX this is the internalized size, not the size with the new + // representatives + unsigned sz1 = get_num_exprs(t1.get_expr()); + unsigned sz2 = get_num_exprs(t2.get_expr()); + return sz1 < sz2; +} + +bool all_children_picked(term *t) { + if (t->deg() == 0) return true; + for (term *c : term::children(t)) { + if (!c->get_repr()) return false; + } + return true; +} + +// pick representatives for all terms in todo. Then, pick representatives for +// all terms whose children have representatives +void term_graph::pick_repr_percolate_up(ptr_vector &todo) { + term *t; + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (t->get_repr()) continue; + pick_repr_class(t); + for (auto it : term::parents(t->get_root())) + if (all_children_picked(it)) todo.push_back(it); + } +} + +// iterate through all terms in a class and pick a representative that: +// 1. is cgr and 2. least according to term_lt +void term_graph::pick_repr_class(term *t) { + SASSERT(all_children_picked(t)); + term *r = t; + for (term *it = &t->get_next(); it != t; it = &it->get_next()) { + if (!all_children_picked(it)) continue; + if ((it->is_cgr() && !r->is_cgr()) || + (it->is_cgr() == r->is_cgr() && term_lt(*it, *r))) + r = it; + } + r->mk_repr(); +} + +// Choose repr for equivalence classes +// repr has the following properties: +// 1. acyclicity (mk_app terminates) +// 2. maximal wrt cgr +// 3. each class has exactly one repr +// assumes that cgroundness has been computed +void term_graph::pick_repr() { + // invalidates cache + m_term2app.reset(); + DEBUG_CODE(for (term *t + : m_terms) + SASSERT(t->deg() == 0 || !t->all_children_ground() || + t->is_cgr());); + for (term *t : m_terms) t->reset_repr(); + ptr_vector todo; + for (term *t : m_terms) { + if (t->deg() == 0 && t->is_cgr()) todo.push_back(t); + } + pick_repr_percolate_up(todo); + DEBUG_CODE(for (term *t : m_terms) SASSERT(!t->is_cgr() || t->get_repr());); + + for (term *t : m_terms) { + if (t->get_repr()) continue; + if (t->deg() == 0) todo.push_back(t); + } + pick_repr_percolate_up(todo); + DEBUG_CODE(for (term *t : m_terms) SASSERT(t->get_repr());); + DEBUG_CODE(for (auto t + : m_terms) + SASSERT(!t->is_cgr() || t->get_repr()->is_cgr());); +} + +// if t is a variable, attempt to pick non-var +void term_graph::refine_repr_class(term *t) { + SASSERT(t->is_repr()); + auto is_var = [&](term *p) { + SASSERT(is_app(p->get_expr())); + return m_is_var.contains(to_app(p->get_expr())->get_decl()); + }; + if (!is_var(t)) return; + term *r = t; + for (term *it = &t->get_next(); it != t; it = &it->get_next()) { + if (makes_cycle(it)) continue; + if (is_var(r) && !is_var(it)) r = it; + } + r->mk_repr(); +} + +// check if t makes a cycle if chosen as repr. This function assumes that the +// current repr doesn't have cycles. If there is a cycle in a child class the +// function doesn't terminate. +bool term_graph::makes_cycle(term *t) { + term &r = t->get_root(); + ptr_vector todo; + for (auto *it : term::children(t)) { todo.push_back(it->get_repr()); } + term *it; + while (!todo.empty()) { + it = todo.back(); + todo.pop_back(); + if (it->get_root().get_id() == r.get_id()) return true; + for (auto *ch : term::children(it)) { todo.push_back(ch->get_repr()); } + } + return false; +} + +void term_graph::refine_repr() { + // invalidates cache + m_term2app.reset(); + for (term *t : m_terms) { + if (!t->get_repr()->is_cgr()) refine_repr_class(t->get_repr()); + } +} + +// returns true if tg ==> e = v where v is a value +bool term_graph::has_val_in_class(expr *e) { + term *r = get_term(e); + if (!r) return false; + auto is_val = [&](term *t) { return m.is_value(t->get_expr()); }; + if (is_val(r)) return true; + for (term *it = &r->get_next(); it != r; it = &it->get_next()) + if (is_val(it)) return true; + return false; +} + +// if there exists an uninterpreted const c s.t. tg ==> e = c, return c +// else return nullptr +app *term_graph::get_const_in_class(expr *e) { + term *r = get_term(e); + if (!r) return nullptr; + auto is_const = [](term *t) { return is_uninterp_const(t->get_expr()); }; + if (is_const(r)) return ::to_app(r->get_expr()); + for (term *it = &r->get_next(); it != r; it = &it->get_next()) + if (is_const(it)) return ::to_app(it->get_expr()); + return nullptr; +} + +void term_graph::display(std::ostream &out) { + for (term *t : m_terms) { out << *t; } +} + +void term_graph::to_lits(expr_ref_vector &lits, bool all_equalities, + bool repick_repr) { + if (m_repick_repr || repick_repr) pick_repr(); + + for (expr *a : m_lits) { + if (is_internalized(a)) { + if (m_explicit_eq && get_term(a)->is_eq_or_neq()) continue; + lits.push_back(::to_app(mk_app(a))); } } - void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { - pick_roots(); - - for (expr * a : m_lits) { - if (is_internalized(a)) { - lits.push_back (::to_app(mk_app(a))); - } - } - - for (term * t : m_terms) { - if (!t->is_root()) - continue; - else if (all_equalities) - mk_all_equalities (*t, lits); - else - mk_equalities(*t, lits); - } + for (term *t : m_terms) { + if (t->is_eq_or_neq()) continue; + if (!t->is_repr()) + continue; + else if (all_equalities) + mk_all_equalities(*t, lits); + else + mk_equalities(*t, lits); } - expr_ref term_graph::to_expr() { - expr_ref_vector lits(m); - to_lits(lits); - return mk_and(lits); + // TODO: use seen to prevent duplicate disequalities + for (auto p : m_deq_pairs) { + lits.push_back(mk_neq(m, mk_app(p.first->get_expr()), + mk_app(p.second->get_expr()))); } - void term_graph::reset() { - m_term2app.reset(); - m_pinned.reset(); - m_app2term.reset(); - std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); - m_terms.reset(); - m_lits.reset(); - m_cg_table.reset(); + for (auto t : m_deq_distinct) { + ptr_vector args(t.size()); + for (auto c : t) args.push_back(mk_app(c->get_expr())); + lits.push_back(m.mk_distinct(args.size(), args.data())); + } +} + +// assumes that representatives have already been picked +void term_graph::to_lits_qe_lite(expr_ref_vector &lits, + std::function *non_core) { + DEBUG_CODE(for (auto t : m_terms) SASSERT(t->get_repr());); + DEBUG_CODE(for (auto t + : m_terms) + SASSERT(!t->is_cgr() || t->get_repr()->is_cgr());); + is_non_core not_in_core(non_core); + check_pred contains_nc(not_in_core, m, false); + // literals other than eq, neq, distinct + for (expr *a : m_lits) { + if (!is_internalized(a)) continue; + if (m_explicit_eq && get_term(a)->is_eq_or_neq()) continue; + expr_ref r(m); + r = mk_app(a); + if (non_core == nullptr || !contains_nc(r)) lits.push_back(r); } - class term_graph::projector { - term_graph &m_tg; - ast_manager &m; - u_map m_term2app; - u_map m_root2rep; - th_rewriter m_rewriter; + // equalities + for (term *t : m_terms) { + if (t->is_eq_or_neq()) continue; + if (!t->is_repr()) continue; + mk_qe_lite_equalities(*t, lits, contains_nc); + } + // disequalities and distinct + // TODO: use seen to prevent duplicate disequalities + expr_ref e1(m), e2(m), d(m), distinct(m); + expr_ref_vector args(m); + for (auto p : m_deq_pairs) { + e1 = mk_app(*(p.first->get_repr())); + e2 = mk_app(*(p.second->get_repr())); + if (non_core == nullptr || (!contains_nc(e1) && !contains_nc(e2))) + lits.push_back(mk_neq(m, e1, e2)); + } - model_ref m_model; - expr_ref_vector m_pinned; // tracks expr in the maps + for (auto t : m_deq_distinct) { + args.reset(); + for (auto c : t) { + d = mk_app(*(c->get_repr())); + if (non_core == nullptr || !contains_nc(d)) args.push_back(d); + } + if (args.size() < 2) continue; + if (args.size() == 2) + distinct = mk_neq(m, args.get(0), args.get(1)); + else + distinct = m.mk_distinct(args.size(), args.data()); + lits.push_back(distinct); + } +} - expr* mk_pure(term const& t) { - TRACE("qe", t.display(tout);); - expr* e = nullptr; - if (find_term2app(t, e)) return e; - e = t.get_expr(); - if (!is_app(e)) return nullptr; - app* a = ::to_app(e); - expr_ref_buffer kids(m); - for (term* ch : term::children(t)) { - // prefer a node that resembles current child, - // otherwise, pick a root representative, if present. - if (find_term2app(*ch, e)) { - kids.push_back(e); - } - else if (m_root2rep.find(ch->get_root().get_id(), e)) { - kids.push_back(e); - } - else { - return nullptr; - } - TRACE("qe_verbose", tout << *ch << " -> " << mk_pp(e, m) << "\n";); +expr_ref term_graph::to_expr(bool repick_repr) { + expr_ref_vector lits(m); + to_lits(lits, false, repick_repr); + return mk_and(lits); +} + +void term_graph::reset() { + m_term2app.reset(); + m_pinned.reset(); + m_app2term.reset(); + std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); + m_terms.reset(); + m_lits.reset(); + m_cg_table.reset(); +} + +class term_graph::projector { + term_graph &m_tg; + ast_manager &m; + u_map m_term2app; + u_map m_root2rep; + th_rewriter m_rewriter; + + model_ref m_model; + expr_ref_vector m_pinned; // tracks expr in the maps + + expr *mk_pure(term const &t) { + TRACE("qe", t.display(tout);); + expr *e = nullptr; + if (find_term2app(t, e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app *a = ::to_app(e); + expr_ref_buffer kids(m); + for (term *ch : term::children(t)) { + // prefer a node that resembles current child, + // otherwise, pick a root representative, if present. + if (find_term2app(*ch, e)) { kids.push_back(e); } + else if (m_root2rep.find(ch->get_root().get_id(), e)) { + kids.push_back(e); } - expr_ref pure = m_rewriter.mk_app(a->get_decl(), kids.size(), kids.data()); - m_pinned.push_back(pure); - add_term2app(t, pure); - return pure; + else { return nullptr; } + TRACE("qe_verbose", tout << *ch << " -> " << mk_pp(e, m) << "\n";); } + expr_ref pure = + m_rewriter.mk_app(a->get_decl(), kids.size(), kids.data()); + m_pinned.push_back(pure); + add_term2app(t, pure); + return pure; + } + bool is_better_rep(expr *t1, expr *t2) { + if (!t2) return t1 != nullptr; + return m.is_unique_value(t1) && !m.is_unique_value(t2); + } - bool is_better_rep(expr *t1, expr *t2) { - if (!t2) return t1 != nullptr; - return m.is_unique_value(t1) && !m.is_unique_value(t2); + struct term_depth { + bool operator()(term const *t1, term const *t2) const { + return get_depth(t1->get_expr()) < get_depth(t2->get_expr()); } + }; - struct term_depth { - bool operator()(term const* t1, term const* t2) const { - return get_depth(t1->get_expr()) < get_depth(t2->get_expr()); - } - }; - - - void solve_core() { - ptr_vector worklist; - for (term * t : m_tg.m_terms) { - // skip pure terms - if (!in_term2app(*t)) { - worklist.push_back(t); - t->set_mark(true); - } - } - term_depth td; - std::sort(worklist.begin(), worklist.end(), td); - - for (unsigned i = 0; i < worklist.size(); ++i) { - term* t = worklist[i]; - t->set_mark(false); - if (in_term2app(*t)) - continue; - - expr* pure = mk_pure(*t); - if (!pure) - continue; - - add_term2app(*t, pure); - expr* rep = nullptr; - // ensure that the root has a representative - m_root2rep.find(t->get_root().get_id(), rep); - - if (!rep) { - m_root2rep.insert(t->get_root().get_id(), pure); - for (term * p : term::parents(t->get_root())) { - SASSERT(!in_term2app(*p)); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } - } - } - } - m_tg.reset_marks(); - } - - bool find_app(term &t, expr *&res) { - return - find_term2app(t, res) || - m_root2rep.find(t.get_root().get_id(), res); - } - - bool find_app(expr *lit, expr *&res) { - term const* t = m_tg.get_term(lit); - return - find_term2app(*t, res) || - m_root2rep.find(t->get_root().get_id(), res); - } - - void mk_lits(expr_ref_vector &res) { - expr *e = nullptr; - for (auto *lit : m_tg.m_lits) { - if (!m.is_eq(lit) && find_app(lit, e)) - res.push_back(e); - } - TRACE("qe", tout << "literals: " << res << "\n";); - } - - void lits2pure(expr_ref_vector& res) { - expr *e1 = nullptr, *e2 = nullptr, *p1 = nullptr, *p2 = nullptr; - for (auto *lit : m_tg.m_lits) { - if (m.is_eq(lit, e1, e2)) { - if (find_app(e1, p1) && find_app(e2, p2)) { - if (p1 != p2) - res.push_back(m.mk_eq(p1, p2)); - } - else - TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); - } - else if (m.is_distinct(lit)) { - ptr_buffer diff; - for (expr* arg : *to_app(lit)) - if (find_app(arg, p1)) - diff.push_back(p1); - if (diff.size() > 1) - res.push_back(m.mk_distinct(diff.size(), diff.data())); - else - TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); - } - else if (find_app(lit, p1)) - res.push_back(p1); - else - TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); - } - remove_duplicates(res); - TRACE("qe", tout << "literals: " << res << "\n";); - } - - void remove_duplicates(expr_ref_vector& v) { - obj_hashtable seen; - unsigned j = 0; - for (expr* e : v) { - if (!seen.contains(e)) { - v[j++] = e; - seen.insert(e); - } - } - v.shrink(j); - } - - vector> m_decl2terms; // terms that use function f - ptr_vector m_decls; - - void collect_decl2terms() { - // Collect the projected function symbols. - m_decl2terms.reset(); - m_decls.reset(); - for (term *t : m_tg.m_terms) { - expr* e = t->get_expr(); - if (!is_app(e)) continue; - if (!is_projected(*t)) continue; - app* a = to_app(e); - func_decl* d = a->get_decl(); - if (d->get_arity() == 0) continue; - unsigned id = d->get_small_id(); - m_decl2terms.reserve(id+1); - if (m_decl2terms[id].empty()) m_decls.push_back(d); - m_decl2terms[id].push_back(t); - } - } - - void args_are_distinct(expr_ref_vector& res) { - // - // for each projected function that occurs - // (may occur) in multiple congruence classes, - // produce assertions that non-congruent arguments - // are distinct. - // - for (func_decl* d : m_decls) { - unsigned id = d->get_small_id(); - ptr_vector const& terms = m_decl2terms[id]; - if (terms.size() <= 1) continue; - unsigned arity = d->get_arity(); - for (unsigned i = 0; i < arity; ++i) { - obj_hashtable roots, root_vals; - expr_ref_vector pinned(m); - for (term* t : terms) { - expr* arg = to_app(t->get_expr())->get_arg(i); - term const& root = m_tg.get_term(arg)->get_root(); - expr* r = root.get_expr(); - // if a model is given, then use the equivalence class induced - // by the model. Otherwise, use the congruence class. - if (m_model) { - expr_ref tmp(m); - tmp = (*m_model)(r); - if (!root_vals.contains(tmp)) { - root_vals.insert(tmp); - roots.insert(r); - pinned.push_back(tmp); - } - } - else { - roots.insert(r); - } - } - if (roots.size() > 1) { - ptr_buffer args; - for (expr* r : roots) { - args.push_back(r); - } - TRACE("qe", tout << "function: " << d->get_name() << "\n";); - res.push_back(m.mk_distinct(args.size(), args.data())); - } - } - } - } - - void mk_distinct(expr_ref_vector& res) { - collect_decl2terms(); - args_are_distinct(res); - TRACE("qe", tout << res << "\n";); - } - - void mk_pure_equalities(const term &t, expr_ref_vector &res) { - SASSERT(t.is_root()); - expr *rep = nullptr; - if (!m_root2rep.find(t.get_id(), rep)) return; - obj_hashtable members; - members.insert(rep); - term const * r = &t; - do { - expr* member = nullptr; - if (find_term2app(*r, member) && !members.contains(member)) { - res.push_back (m.mk_eq (rep, member)); - members.insert(member); - } - r = &r->get_next(); - } - while (r != &t); - } - - bool is_projected(const term &t) { - return m_tg.m_is_var(t); - } - - void mk_unpure_equalities(const term &t, expr_ref_vector &res) { - expr *rep = nullptr; - if (!m_root2rep.find(t.get_id(), rep)) return; - obj_hashtable members; - members.insert(rep); - term const * r = &t; - do { - expr* member = mk_pure(*r); - SASSERT(member); - if (!members.contains(member) && - (!is_projected(*r) || !is_solved_eq(rep, member))) { - res.push_back(m.mk_eq(rep, member)); - members.insert(member); - } - r = &r->get_next(); - } - while (r != &t); - } - - template - void mk_equalities(expr_ref_vector &res) { - for (term *t : m_tg.m_terms) { - if (!t->is_root()) continue; - if (!m_root2rep.contains(t->get_id())) continue; - if (pure) - mk_pure_equalities(*t, res); - else - mk_unpure_equalities(*t, res); - } - TRACE("qe", tout << "literals: " << res << "\n";); - } - - void mk_pure_equalities(expr_ref_vector &res) { - mk_equalities(res); - } - - void mk_unpure_equalities(expr_ref_vector &res) { - mk_equalities(res); - } - - // TBD: generalize for also the case of a (:var n) - bool is_solved_eq(expr *lhs, expr* rhs) { - return is_uninterp_const(rhs) && !occurs(rhs, lhs); - } - - /// Add equalities and disequalities for all pure representatives - /// based on their equivalence in the model - void model_complete(expr_ref_vector &res) { - if (!m_model) return; - obj_map val2rep; - model_evaluator mev(*m_model); - for (auto &kv : m_root2rep) { - expr *rep = kv.m_value; - expr_ref val(m); - expr *u = nullptr; - if (!mev.eval(rep, val)) continue; - if (val2rep.find(val, u)) { - res.push_back(m.mk_eq(u, rep)); - } - else { - val2rep.insert(val, rep); - } - } - - // TBD: optimize further based on implied values (e.g., - // some literals are forced to be true/false) and based on - // unique_values (e.g., (x=1 & y=1) does not require - // (x!=y) to be added - ptr_buffer reps; - for (auto &kv : val2rep) { - expr *rep = kv.m_value; - if (!m.is_unique_value(rep)) - reps.push_back(kv.m_value); - } - - if (reps.size() <= 1) return; - - // -- sort representatives, call mk_distinct on any range - // -- of the same sort longer than 1 - std::sort(reps.data(), reps.data() + reps.size(), sort_lt_proc()); - unsigned i = 0; - unsigned sz = reps.size(); - while (i < sz) { - sort* last_sort = res.get(i)->get_sort(); - unsigned j = i + 1; - while (j < sz && last_sort == reps.get(j)->get_sort()) {++j;} - if (j - i == 2) { - expr_ref d(m); - d = mk_neq(m, reps.get(i), reps.get(i+1)); - if (!m.is_true(d)) res.push_back(d); - } - else if (j - i > 2) - res.push_back(m.mk_distinct(j - i, reps.data() + i)); - i = j; - } - TRACE("qe", tout << "after distinct: " << res << "\n";); - } - - std::ostream& display(std::ostream& out) const { - m_tg.display(out); - out << "term2app:\n"; - for (auto const& kv : m_term2app) { - out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; - } - out << "root2rep:\n"; - for (auto const& kv : m_root2rep) { - out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; - } - return out; - } - - public: - projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_rewriter(m), m_pinned(m) {} - - void add_term2app(term const& t, expr* a) { - m_term2app.insert(t.get_id(), a); - } - - void del_term2app(term const& t) { - m_term2app.remove(t.get_id()); - } - - bool find_term2app(term const& t, expr*& r) { - return m_term2app.find(t.get_id(), r); - } - - expr* find_term2app(term const& t) { - expr* r = nullptr; - find_term2app(t, r); - return r; - } - - bool in_term2app(term const& t) { - return m_term2app.contains(t.get_id()); - } - - void set_model(model &mdl) { m_model = &mdl; } - - void reset() { - m_tg.reset_marks(); - m_term2app.reset(); - m_root2rep.reset(); - m_pinned.reset(); - m_model.reset(); - } - - expr_ref_vector project() { - expr_ref_vector res(m); - purify(); - lits2pure(res); - mk_distinct(res); - reset(); - return res; - } - - expr_ref_vector get_ackerman_disequalities() { - expr_ref_vector res(m); - purify(); - lits2pure(res); - unsigned sz = res.size(); - mk_distinct(res); - reset(); - unsigned j = 0; - for (unsigned i = sz; i < res.size(); ++i) { - res[j++] = res.get(i); - } - res.shrink(j); - return res; - } - - expr_ref_vector solve() { - expr_ref_vector res(m); - purify(); - solve_core(); - mk_lits(res); - mk_unpure_equalities(res); - reset(); - return res; - } - - vector get_partition(model& mdl, bool include_bool) { - vector result; - expr_ref_vector pinned(m); - obj_map pid; - auto insert_val = [&](expr* a, expr* val) { - unsigned p = 0; - // NB. works for simple domains Integers, Rationals, - // but not for algebraic numerals. - if (!pid.find(val, p)) { - p = pid.size(); - pid.insert(val, p); - pinned.push_back(val); - result.push_back(expr_ref_vector(m)); - } - result[p].push_back(a); - }; - model::scoped_model_completion _smc(mdl, true); - for (term *t : m_tg.m_terms) { - expr* a = t->get_expr(); - if (!is_app(a)) - continue; - if (m.is_bool(a) && !include_bool) - continue; - expr_ref val = mdl(a); - insert_val(a, val); - } - - return result; - } - - expr_ref_vector shared_occurrences(family_id fid) { - expr_ref_vector result(m); - for (term *t : m_tg.m_terms) { - expr* e = t->get_expr(); - if (e->get_sort()->get_family_id() != fid) continue; - for (term * p : term::parents(t->get_root())) { - expr* pe = p->get_expr(); - if (!is_app(pe)) continue; - if (to_app(pe)->get_family_id() == fid) continue; - if (to_app(pe)->get_family_id() == m.get_basic_family_id()) continue; - result.push_back(e); - break; - } - } - return result; - } - - void purify() { - // - propagate representatives up over parents. - // use work-list + marking to propagate. - // - produce equalities over represented classes. - // - produce other literals over represented classes - // (walk disequalities in m_lits and represent - // lhs/rhs over decls or excluding decls) - - ptr_vector worklist; - for (term * t : m_tg.m_terms) { + void solve_core() { + ptr_vector worklist; + for (term *t : m_tg.m_terms) { + // skip pure terms + if (!in_term2app(*t) && !t->is_eq_or_neq()) { worklist.push_back(t); t->set_mark(true); } - // traverse worklist in order of depth. - term_depth td; - std::sort(worklist.begin(), worklist.end(), td); - - for (unsigned i = 0; i < worklist.size(); ++i) { - term* t = worklist[i]; - t->set_mark(false); - if (in_term2app(*t)) - continue; - if (!t->is_theory() && is_projected(*t)) - continue; - - expr* pure = mk_pure(*t); - if (!pure) continue; - - add_term2app(*t, pure); - TRACE("qe_verbose", tout << "purified " << *t << " " << mk_pp(pure, m) << "\n";); - expr* rep = nullptr; // ensure that the root has a representative - m_root2rep.find(t->get_root().get_id(), rep); - - // update rep with pure if it is better - if (pure != rep && is_better_rep(pure, rep)) { - m_root2rep.insert(t->get_root().get_id(), pure); - for (term * p : term::parents(t->get_root())) { - del_term2app(*p); - if (!p->is_marked()) { - p->set_mark(true); - worklist.push_back(p); - } - } - } - } - - // Here we could also walk equivalence classes that - // contain interpreted values by sort and extract - // disequalities between non-unique value - // representatives. these disequalities are implied - // and can be mined using other means, such as theory - // aware core minimization - m_tg.reset_marks(); - TRACE("qe", display(tout << "after purify\n");); } + term_depth td; + std::sort(worklist.begin(), worklist.end(), td); - }; + for (unsigned i = 0; i < worklist.size(); ++i) { + term *t = worklist[i]; + t->set_mark(false); + if (in_term2app(*t)) continue; - void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { - m_is_var.set_decls(decls, exclude); - } + expr *pure = mk_pure(*t); + if (!pure) continue; - expr_ref_vector term_graph::project() { - // reset solved vars so that they are not considered pure by projector - m_is_var.reset_solved(); - term_graph::projector p(*this); - return p.project(); - } + add_term2app(*t, pure); + expr *rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); - expr_ref_vector term_graph::project(model &mdl) { - m_is_var.reset_solved(); - term_graph::projector p(*this); - p.set_model(mdl); - return p.project(); - } - - expr_ref_vector term_graph::solve() { - // reset solved vars so that they are not considered pure by projector - m_is_var.reset_solved(); - term_graph::projector p(*this); - return p.solve(); - } - - expr_ref_vector term_graph::get_ackerman_disequalities() { - m_is_var.reset_solved(); - dealloc(m_projector); - m_projector = alloc(term_graph::projector, *this); - return m_projector->get_ackerman_disequalities(); - } - - vector term_graph::get_partition(model& mdl) { - dealloc(m_projector); - m_projector = alloc(term_graph::projector, *this); - return m_projector->get_partition(mdl, false); - } - - expr_ref_vector term_graph::shared_occurrences(family_id fid) { - term_graph::projector p(*this); - return p.shared_occurrences(fid); - } - - void term_graph::add_model_based_terms(model& mdl, expr_ref_vector const& terms) { - for (expr* t : terms) { - internalize_term(t); - } - m_is_var.reset_solved(); - - SASSERT(!m_projector); - m_projector = alloc(term_graph::projector, *this); - - // retrieve partition of terms - vector equivs = m_projector->get_partition(mdl, true); - - // merge term graph on equal terms. - for (auto const& cs : equivs) { - term* t0 = get_term(cs[0]); - for (unsigned i = 1; i < cs.size(); ++i) { - merge(*t0, *get_term(cs[i])); - } - } - TRACE("qe", - for (auto & es : equivs) { - tout << "equiv: "; - for (expr* t : es) tout << expr_ref(t, m) << " "; - tout << "\n"; - } - display(tout);); - // create representatives for shared/projected variables. - m_projector->set_model(mdl); - m_projector->purify(); - - } - - expr* term_graph::rep_of(expr* e) { - SASSERT(m_projector); - term* t = get_term(e); - SASSERT(t && "only get representatives"); - return m_projector->find_term2app(*t); - } - - expr_ref_vector term_graph::dcert(model& mdl, expr_ref_vector const& lits) { - TRACE("qe", tout << "dcert " << lits << "\n";); - struct pair_t { - expr* a, *b; - pair_t(): a(nullptr), b(nullptr) {} - pair_t(expr* _a, expr* _b):a(_a), b(_b) { - if (a->get_id() > b->get_id()) std::swap(a, b); - } - struct hash { - unsigned operator()(pair_t const& p) const { return mk_mix(p.a ? p.a->hash() : 0, p.b ? p.b->hash() : 0, 1); } - }; - struct eq { - bool operator()(pair_t const& a, pair_t const& b) const { return a.a == b.a && a.b == b.b; } - }; - }; - hashtable diseqs; - expr_ref_vector result(m); - add_lits(lits); - svector todo; - - for (expr* e : lits) { - expr* ne, *a, *b; - if (m.is_not(e, ne) && m.is_eq(ne, a, b) && (is_uninterp(a) || is_uninterp(b))) { - diseqs.insert(pair_t(a, b)); - } - else if (is_uninterp(e)) { - diseqs.insert(pair_t(e, m.mk_false())); - } - else if (m.is_not(e, ne) && is_uninterp(ne)) { - diseqs.insert(pair_t(ne, m.mk_true())); - } - } - for (auto& p : diseqs) todo.push_back(p); - - auto const partitions = get_partition(mdl); - obj_map term2pid; - unsigned id = 0; - for (auto const& vec : partitions) { - for (expr* e : vec) term2pid.insert(e, id); - ++id; - } - expr_ref_vector empty(m); - auto partition_of = [&](expr* e) { - unsigned pid; - if (!term2pid.find(e, pid)) - return empty; - return partitions[pid]; - }; - auto in_table = [&](expr* a, expr* b) { - return diseqs.contains(pair_t(a, b)); - }; - auto same_function = [](expr* a, expr* b) { - return is_app(a) && is_app(b) && - to_app(a)->get_decl() == to_app(b)->get_decl() && to_app(a)->get_family_id() == null_family_id; - }; - - // make sure that diseqs is closed under function applications - // of uninterpreted functions. - for (unsigned idx = 0; idx < todo.size(); ++idx) { - auto p = todo[idx]; - for (expr* t1 : partition_of(p.a)) { - for (expr* t2 : partition_of(p.b)) { - if (same_function(t1, t2)) { - unsigned sz = to_app(t1)->get_num_args(); - bool found = false; - pair_t q(t1, t2); - for (unsigned i = 0; i < sz; ++i) { - expr* arg1 = to_app(t1)->get_arg(i); - expr* arg2 = to_app(t2)->get_arg(i); - if (mdl(arg1) == mdl(t2)) { - continue; - } - if (in_table(arg1, arg2)) { - found = true; - break; - } - q = pair_t(arg1, arg2); - } - if (!found) { - diseqs.insert(q); - todo.push_back(q); - result.push_back(m.mk_not(m.mk_eq(q.a, q.b))); - } + if (!rep) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term *p : term::parents(t->get_root())) { + SASSERT(!in_term2app(*p)); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); } } } } - for (auto const& terms : partitions) { - expr* a = nullptr; - for (expr* b : terms) { - if (is_uninterp(b)) { - if (a) - result.push_back(m.mk_eq(a, b)); - else - a = b; + m_tg.reset_marks(); + } + + bool find_app(term &t, expr *&res) { + return find_term2app(t, res) || + m_root2rep.find(t.get_root().get_id(), res); + } + + bool find_app(expr *lit, expr *&res) { + term const *t = m_tg.get_term(lit); + return find_term2app(*t, res) || + m_root2rep.find(t->get_root().get_id(), res); + } + + void mk_lits(expr_ref_vector &res) { + expr *e = nullptr; + for (auto *lit : m_tg.m_lits) { + if (!m.is_eq(lit) && find_app(lit, e)) res.push_back(e); + } + TRACE("qe", tout << "literals: " << res << "\n";); + } + + void lits2pure(expr_ref_vector &res) { + expr *e1 = nullptr, *e2 = nullptr, *p1 = nullptr, *p2 = nullptr; + for (auto *lit : m_tg.m_lits) { + if (m.is_eq(lit, e1, e2)) { + if (find_app(e1, p1) && find_app(e2, p2)) { + if (p1 != p2) res.push_back(m.mk_eq(p1, p2)); + } + else + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + else if (m.is_distinct(lit)) { + ptr_buffer diff; + for (expr *arg : *to_app(lit)) + if (find_app(arg, p1)) diff.push_back(p1); + if (diff.size() > 1) + res.push_back(m.mk_distinct(diff.size(), diff.data())); + else + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + else if (find_app(lit, p1)) + res.push_back(p1); + else + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + remove_duplicates(res); + TRACE("qe", tout << "literals: " << res << "\n";); + } + + void remove_duplicates(expr_ref_vector &v) { + obj_hashtable seen; + unsigned j = 0; + for (expr *e : v) { + if (!seen.contains(e)) { + v[j++] = e; + seen.insert(e); + } + } + v.shrink(j); + } + + vector> m_decl2terms; // terms that use function f + ptr_vector m_decls; + + void collect_decl2terms() { + // Collect the projected function symbols. + m_decl2terms.reset(); + m_decls.reset(); + for (term *t : m_tg.m_terms) { + if (t->is_eq_or_neq()) continue; + expr *e = t->get_expr(); + if (!is_app(e)) continue; + if (!is_projected(*t)) continue; + app *a = to_app(e); + func_decl *d = a->get_decl(); + if (d->get_arity() == 0) continue; + unsigned id = d->get_small_id(); + m_decl2terms.reserve(id + 1); + if (m_decl2terms[id].empty()) m_decls.push_back(d); + m_decl2terms[id].push_back(t); + } + } + + void args_are_distinct(expr_ref_vector &res) { + // + // for each projected function that occurs + // (may occur) in multiple congruence classes, + // produce assertions that non-congruent arguments + // are distinct. + // + for (func_decl *d : m_decls) { + unsigned id = d->get_small_id(); + ptr_vector const &terms = m_decl2terms[id]; + if (terms.size() <= 1) continue; + unsigned arity = d->get_arity(); + for (unsigned i = 0; i < arity; ++i) { + obj_hashtable roots, root_vals; + expr_ref_vector pinned(m); + for (term *t : terms) { + expr *arg = to_app(t->get_expr())->get_arg(i); + term const &root = m_tg.get_term(arg)->get_root(); + expr *r = root.get_expr(); + // if a model is given, then use the equivalence class + // induced by the model. Otherwise, use the congruence + // class. + if (m_model) { + expr_ref tmp(m); + tmp = (*m_model)(r); + if (!root_vals.contains(tmp)) { + root_vals.insert(tmp); + roots.insert(r); + pinned.push_back(tmp); + } + } + else { roots.insert(r); } + } + if (roots.size() > 1) { + ptr_buffer args; + for (expr *r : roots) { args.push_back(r); } + TRACE("qe", tout << "function: " << d->get_name() << "\n";); + res.push_back(m.mk_distinct(args.size(), args.data())); } } } - TRACE("qe", tout << result << "\n";); + } + + void mk_distinct(expr_ref_vector &res) { + collect_decl2terms(); + args_are_distinct(res); + TRACE("qe", tout << res << "\n";); + } + + void mk_pure_equalities(const term &t, expr_ref_vector &res) { + SASSERT(t.is_root()); + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const *r = &t; + do { + expr *member = nullptr; + if (find_term2app(*r, member) && !members.contains(member)) { + res.push_back(m.mk_eq(rep, member)); + members.insert(member); + } + r = &r->get_next(); + } + while (r != &t); + } + + bool is_projected(const term &t) { return m_tg.m_is_var(t); } + + void mk_unpure_equalities(const term &t, expr_ref_vector &res) { + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const *r = &t; + do { + expr *member = mk_pure(*r); + SASSERT(member); + if (!members.contains(member) && + (!is_projected(*r) || !is_solved_eq(rep, member))) { + res.push_back(m.mk_eq(rep, member)); + members.insert(member); + } + r = &r->get_next(); + } + while (r != &t); + } + + template void mk_equalities(expr_ref_vector &res) { + for (term *t : m_tg.m_terms) { + if (t->is_eq_or_neq()) continue; + if (!t->is_root()) continue; + if (!m_root2rep.contains(t->get_id())) continue; + if (pure) + mk_pure_equalities(*t, res); + else + mk_unpure_equalities(*t, res); + } + TRACE("qe", tout << "literals: " << res << "\n";); + } + + void mk_pure_equalities(expr_ref_vector &res) { mk_equalities(res); } + + void mk_unpure_equalities(expr_ref_vector &res) { + mk_equalities(res); + } + + // TBD: generalize for also the case of a (:var n) + bool is_solved_eq(expr *lhs, expr *rhs) { + return is_uninterp_const(rhs) && !occurs(rhs, lhs); + } + + /// Add equalities and disequalities for all pure representatives + /// based on their equivalence in the model + void model_complete(expr_ref_vector &res) { + if (!m_model) return; + obj_map val2rep; + model_evaluator mev(*m_model); + for (auto &kv : m_root2rep) { + expr *rep = kv.m_value; + expr_ref val(m); + expr *u = nullptr; + if (!mev.eval(rep, val)) continue; + if (val2rep.find(val, u)) { res.push_back(m.mk_eq(u, rep)); } + else { val2rep.insert(val, rep); } + } + + // TBD: optimize further based on implied values (e.g., + // some literals are forced to be true/false) and based on + // unique_values (e.g., (x=1 & y=1) does not require + // (x!=y) to be added + ptr_buffer reps; + for (auto &kv : val2rep) { + expr *rep = kv.m_value; + if (!m.is_unique_value(rep)) reps.push_back(kv.m_value); + } + + if (reps.size() <= 1) return; + + // -- sort representatives, call mk_distinct on any range + // -- of the same sort longer than 1 + std::sort(reps.data(), reps.data() + reps.size(), sort_lt_proc()); + unsigned i = 0; + unsigned sz = reps.size(); + while (i < sz) { + sort *last_sort = res.get(i)->get_sort(); + unsigned j = i + 1; + while (j < sz && last_sort == reps.get(j)->get_sort()) { ++j; } + if (j - i == 2) { + expr_ref d(m); + d = mk_neq(m, reps.get(i), reps.get(i + 1)); + if (!m.is_true(d)) res.push_back(d); + } + else if (j - i > 2) + res.push_back(m.mk_distinct(j - i, reps.data() + i)); + i = j; + } + TRACE("qe", tout << "after distinct: " << res << "\n";); + } + + std::ostream &display(std::ostream &out) const { + m_tg.display(out); + out << "term2app:\n"; + for (auto const &kv : m_term2app) { + out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; + } + out << "root2rep:\n"; + for (auto const &kv : m_root2rep) { + out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; + } + return out; + } + + public: + projector(term_graph &tg) + : m_tg(tg), m(m_tg.m), m_rewriter(m), m_pinned(m) {} + + void add_term2app(term const &t, expr *a) { + m_term2app.insert(t.get_id(), a); + } + + void del_term2app(term const &t) { m_term2app.remove(t.get_id()); } + + bool find_term2app(term const &t, expr *&r) { + return m_term2app.find(t.get_id(), r); + } + + expr *find_term2app(term const &t) { + expr *r = nullptr; + find_term2app(t, r); + return r; + } + + bool in_term2app(term const &t) { return m_term2app.contains(t.get_id()); } + + void set_model(model &mdl) { m_model = &mdl; } + + void reset() { + m_tg.reset_marks(); + m_term2app.reset(); + m_root2rep.reset(); + m_pinned.reset(); + m_model.reset(); + } + + expr_ref_vector project() { + expr_ref_vector res(m); + purify(); + lits2pure(res); + mk_distinct(res); + reset(); + return res; + } + + expr_ref_vector get_ackerman_disequalities() { + expr_ref_vector res(m); + purify(); + lits2pure(res); + unsigned sz = res.size(); + mk_distinct(res); + reset(); + unsigned j = 0; + for (unsigned i = sz; i < res.size(); ++i) { res[j++] = res.get(i); } + res.shrink(j); + return res; + } + + expr_ref_vector solve() { + expr_ref_vector res(m); + purify(); + solve_core(); + mk_lits(res); + mk_unpure_equalities(res); + reset(); + return res; + } + + vector get_partition(model &mdl, bool include_bool) { + vector result; + expr_ref_vector pinned(m); + obj_map pid; + auto insert_val = [&](expr *a, expr *val) { + unsigned p = 0; + // NB. works for simple domains Integers, Rationals, + // but not for algebraic numerals. + if (!pid.find(val, p)) { + p = pid.size(); + pid.insert(val, p); + pinned.push_back(val); + result.push_back(expr_ref_vector(m)); + } + result[p].push_back(a); + }; + model::scoped_model_completion _smc(mdl, true); + for (term *t : m_tg.m_terms) { + if (t->is_eq_or_neq()) continue; + expr *a = t->get_expr(); + if (!is_app(a)) continue; + if (m.is_bool(a) && !include_bool) continue; + expr_ref val = mdl(a); + insert_val(a, val); + } return result; } + expr_ref_vector shared_occurrences(family_id fid) { + expr_ref_vector result(m); + for (term *t : m_tg.m_terms) { + if (t->is_eq_or_neq()) continue; + expr *e = t->get_expr(); + if (e->get_sort()->get_family_id() != fid) continue; + for (term *p : term::parents(t->get_root())) { + expr *pe = p->get_expr(); + if (!is_app(pe)) continue; + if (to_app(pe)->get_family_id() == fid) continue; + if (to_app(pe)->get_family_id() == m.get_basic_family_id()) + continue; + result.push_back(e); + break; + } + } + return result; + } + + void purify() { + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes + // (walk disequalities in m_lits and represent + // lhs/rhs over decls or excluding decls) + + ptr_vector worklist; + for (term *t : m_tg.m_terms) { + if (t->is_eq_or_neq()) continue; + worklist.push_back(t); + t->set_mark(true); + } + // traverse worklist in order of depth. + term_depth td; + std::sort(worklist.begin(), worklist.end(), td); + + for (unsigned i = 0; i < worklist.size(); ++i) { + term *t = worklist[i]; + t->set_mark(false); + if (in_term2app(*t)) continue; + if (!t->is_theory() && is_projected(*t)) continue; + + expr *pure = mk_pure(*t); + if (!pure) continue; + + add_term2app(*t, pure); + TRACE("qe_verbose", + tout << "purified " << *t << " " << mk_pp(pure, m) << "\n";); + expr *rep = nullptr; // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + // update rep with pure if it is better + if (pure != rep && is_better_rep(pure, rep)) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term *p : term::parents(t->get_root())) { + del_term2app(*p); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + } + } + + // Here we could also walk equivalence classes that + // contain interpreted values by sort and extract + // disequalities between non-unique value + // representatives. these disequalities are implied + // and can be mined using other means, such as theory + // aware core minimization + m_tg.reset_marks(); + TRACE("qe", display(tout << "after purify\n");); + } +}; + +// produce a quantifier reduction of the formula stored in the term graph +// removes from `vars` the variables that have a ground representative +// modifies `vars` to keep the variables that could not be eliminated +void term_graph::qel(app_ref_vector &vars, expr_ref &fml, + std::function *non_core) { + unsigned i = 0; + for (auto v : vars) { + if (is_internalized(v)) { vars[i++] = v; } + } + vars.shrink(i); + pick_repr(); + refine_repr(); + + expr_ref_vector lits(m); + to_lits_qe_lite(lits, non_core); + if (lits.size() == 0) + fml = m.mk_true(); + else if (lits.size() == 1) + fml = lits[0].get(); + else + fml = m.mk_and(lits); + + // Remove all variables that are do not appear in the formula + expr_sparse_mark mark; + mark_all_sub_expr marker(mark); + quick_for_each_expr(marker, fml); + i = 0; + for (auto v : vars) { + if (mark.is_marked(v)) vars[i++] = v; + } + vars.shrink(i); } + +void term_graph::set_vars(func_decl_ref_vector const &decls, bool exclude) { + m_is_var.set_decls(decls, exclude); +} + +void term_graph::set_vars(app_ref_vector const &vars, bool exclude) { + m_is_var.set_decls(vars, exclude); +} + +void term_graph::add_vars(app_ref_vector const &vars) { + m_is_var.add_decls(vars); +} + +void term_graph::add_var(app *var) { m_is_var.add_decl(var); } + +expr_ref_vector term_graph::project() { + // reset solved vars so that they are not considered pure by projector + m_is_var.reset_solved(); + term_graph::projector p(*this); + return p.project(); +} + +expr_ref_vector term_graph::project(model &mdl) { + m_is_var.reset_solved(); + term_graph::projector p(*this); + p.set_model(mdl); + return p.project(); +} + +expr_ref_vector term_graph::solve() { + // reset solved vars so that they are not considered pure by projector + m_is_var.reset_solved(); + term_graph::projector p(*this); + return p.solve(); +} + +expr_ref_vector term_graph::get_ackerman_disequalities() { + m_is_var.reset_solved(); + dealloc(m_projector); + m_projector = alloc(term_graph::projector, *this); + return m_projector->get_ackerman_disequalities(); +} + +vector term_graph::get_partition(model &mdl) { + dealloc(m_projector); + m_projector = alloc(term_graph::projector, *this); + return m_projector->get_partition(mdl, false); +} + +expr_ref_vector term_graph::shared_occurrences(family_id fid) { + term_graph::projector p(*this); + return p.shared_occurrences(fid); +} + +void term_graph::add_model_based_terms(model &mdl, + expr_ref_vector const &terms) { + for (expr *t : terms) { internalize_term(t); } + m_is_var.reset_solved(); + + SASSERT(!m_projector); + m_projector = alloc(term_graph::projector, *this); + + // retrieve partition of terms + vector equivs = m_projector->get_partition(mdl, true); + + // merge term graph on equal terms. + for (auto const &cs : equivs) { + term *t0 = get_term(cs[0]); + for (unsigned i = 1; i < cs.size(); ++i) { + merge(*t0, *get_term(cs[i])); + } + } + TRACE( + "qe", for (auto &es + : equivs) { + tout << "equiv: "; + for (expr *t : es) tout << expr_ref(t, m) << " "; + tout << "\n"; + } display(tout);); + // create representatives for shared/projected variables. + m_projector->set_model(mdl); + m_projector->purify(); +} + +expr *term_graph::rep_of(expr *e) { + SASSERT(m_projector); + term *t = get_term(e); + SASSERT(t && "only get representatives"); + return m_projector->find_term2app(*t); +} + +expr_ref_vector term_graph::dcert(model &mdl, expr_ref_vector const &lits) { + TRACE("qe", tout << "dcert " << lits << "\n";); + struct pair_t { + expr *a, *b; + pair_t() : a(nullptr), b(nullptr) {} + pair_t(expr *_a, expr *_b) : a(_a), b(_b) { + if (a->get_id() > b->get_id()) std::swap(a, b); + } + struct hash { + unsigned operator()(pair_t const &p) const { + return mk_mix(p.a ? p.a->hash() : 0, p.b ? p.b->hash() : 0, 1); + } + }; + struct eq { + bool operator()(pair_t const &a, pair_t const &b) const { + return a.a == b.a && a.b == b.b; + } + }; + }; + hashtable diseqs; + expr_ref_vector result(m); + add_lits(lits); + svector todo; + + for (expr *e : lits) { + expr *ne, *a, *b; + if (m.is_not(e, ne) && m.is_eq(ne, a, b) && + (is_uninterp(a) || is_uninterp(b))) { + diseqs.insert(pair_t(a, b)); + } + else if (is_uninterp(e)) { diseqs.insert(pair_t(e, m.mk_false())); } + else if (m.is_not(e, ne) && is_uninterp(ne)) { + diseqs.insert(pair_t(ne, m.mk_true())); + } + } + for (auto &p : diseqs) todo.push_back(p); + + auto const partitions = get_partition(mdl); + obj_map term2pid; + unsigned id = 0; + for (auto const &vec : partitions) { + for (expr *e : vec) term2pid.insert(e, id); + ++id; + } + expr_ref_vector empty(m); + auto partition_of = [&](expr *e) { + unsigned pid; + if (!term2pid.find(e, pid)) return empty; + return partitions[pid]; + }; + auto in_table = [&](expr *a, expr *b) { + return diseqs.contains(pair_t(a, b)); + }; + auto same_function = [](expr *a, expr *b) { + return is_app(a) && is_app(b) && + to_app(a)->get_decl() == to_app(b)->get_decl() && + to_app(a)->get_family_id() == null_family_id; + }; + + // make sure that diseqs is closed under function applications + // of uninterpreted functions. + for (unsigned idx = 0; idx < todo.size(); ++idx) { + auto p = todo[idx]; + for (expr *t1 : partition_of(p.a)) { + for (expr *t2 : partition_of(p.b)) { + if (same_function(t1, t2)) { + unsigned sz = to_app(t1)->get_num_args(); + bool found = false; + pair_t q(t1, t2); + for (unsigned i = 0; i < sz; ++i) { + expr *arg1 = to_app(t1)->get_arg(i); + expr *arg2 = to_app(t2)->get_arg(i); + if (mdl(arg1) == mdl(t2)) { continue; } + if (in_table(arg1, arg2)) { + found = true; + break; + } + q = pair_t(arg1, arg2); + } + if (!found) { + diseqs.insert(q); + todo.push_back(q); + result.push_back(m.mk_not(m.mk_eq(q.a, q.b))); + } + } + } + } + } + for (auto const &terms : partitions) { + expr *a = nullptr; + for (expr *b : terms) { + if (is_uninterp(b)) { + if (a) + result.push_back(m.mk_eq(a, b)); + else + a = b; + } + } + } + TRACE("qe", tout << result << "\n";); + return result; +} + +void term_graph::cground_percolate_up(term *t) { + SASSERT(t->is_class_gr()); + term *it = t; + // there is a cgr term in all ground classes + while (!it->is_cgr()) { + it = &it->get_next(); + SASSERT(it != t); + } + + ptr_vector todo; + todo.push_back(it); + cground_percolate_up(todo); +} + +void term_graph::cground_percolate_up(ptr_vector &todo) { + term *t; + + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + t->set_cgr(true); + t->set_class_gr(true); + for (auto p : term::parents(t->get_root())) + if (!p->is_cgr() && p->all_children_ground()) todo.push_back(p); + } +} + +void term_graph::compute_cground() { + for (auto t : m_terms) { + t->set_cgr(false); + t->set_class_gr(false); + } + ptr_vector todo; + for (auto t : m_terms) { + if (t->is_gr()) { todo.push_back(t); } + } + cground_percolate_up(todo); + DEBUG_CODE(for (auto t + : m_terms) { + bool isclsg = true; + for (auto c : term::children(t)) isclsg &= c->is_class_gr(); + SASSERT(t->deg() == 0 || !isclsg || t->is_cgr()); + SASSERT(t->deg() == 0 || isclsg || !t->is_cgr()); + }); +} +} // namespace mbp diff --git a/src/qe/mbp/mbp_term_graph.h b/src/qe/mbp/mbp_term_graph.h index 37d1e7b8d..1300034a6 100644 --- a/src/qe/mbp/mbp_term_graph.h +++ b/src/qe/mbp/mbp_term_graph.h @@ -12,6 +12,12 @@ Abstract: Author: Arie Gurfinkel + Hari Govind V K (hgvk94) + Isabel Garcia (igcontreras) + +Revision History: + + Added implementation of qe_lite using term graph Notes: @@ -19,138 +25,237 @@ Notes: #pragma once #include "ast/ast.h" +#include "ast/expr_functors.h" #include "ast/is_variable_test.h" -#include "util/plugin_manager.h" -#include "qe/mbp/mbp_solve_plugin.h" #include "model/model.h" +#include "qe/mbp/mbp_solve_plugin.h" +#include "util/plugin_manager.h" namespace mbp { +namespace is_ground_ns { +struct proc; +struct found; +} // namespace is_ground_ns +class term; - class term; +class term_graph { + class projector; + friend struct is_ground_ns::proc; + friend struct is_ground_ns::found; - class term_graph { - class projector; + class is_variable_proc : public ::is_variable_proc { + bool m_exclude; + obj_hashtable m_decls, m_solved; - class is_variable_proc : public ::is_variable_proc { - bool m_exclude; - obj_hashtable m_decls, m_solved; - public: - bool operator()(const expr *e) const override; - bool operator()(const term &t) const; - - void set_decls(const func_decl_ref_vector &decls, bool exclude); - void mark_solved(const expr *e); - void reset_solved() {m_solved.reset();} - void reset() {m_decls.reset(); m_solved.reset(); m_exclude = true;} - }; - - struct term_hash { unsigned operator()(term const* t) const; }; - struct term_eq { bool operator()(term const* a, term const* b) const; }; - ast_manager & m; - ptr_vector m_terms; - expr_ref_vector m_lits; // NSB: expr_ref_vector? - u_map m_app2term; - ast_ref_vector m_pinned; - projector* m_projector; - u_map m_term2app; - plugin_manager m_plugins; - ptr_hashtable m_cg_table; - vector> m_merge; - - term_graph::is_variable_proc m_is_var; - void merge(term &t1, term &t2); - void merge_flush(); - - term *mk_term(expr *t); - term *get_term(expr *t); - - term *internalize_term(expr *t); - void internalize_eq(expr *a1, expr *a2); - void internalize_lit(expr *lit); - - bool is_internalized(expr *a); - - bool term_lt(term const &t1, term const &t2); - void pick_root (term &t); - void pick_roots(); - - void reset_marks(); - bool marks_are_clear(); - - expr* mk_app_core(expr* a); - expr_ref mk_app(term const &t); - expr* mk_pure(term& t); - expr_ref mk_app(expr *a); - void mk_equalities(term const &t, expr_ref_vector &out); - void mk_all_equalities(term const &t, expr_ref_vector &out); - void display(std::ostream &out); - - bool is_pure_def(expr* atom, expr *& v); - - public: - term_graph(ast_manager &m); - ~term_graph(); - - void set_vars(func_decl_ref_vector const& decls, bool exclude); - - ast_manager& get_ast_manager() const { return m;} - - void add_lit(expr *lit); - void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } - void add_eq(expr* a, expr* b) { internalize_eq(a, b); } - - void reset(); - - // deprecate? - void to_lits(expr_ref_vector &lits, bool all_equalities = false); - expr_ref to_expr(); - - /** - * Return literals obtained by projecting added literals - * onto the vocabulary of decls (if exclude is false) or outside the - * vocabulary of decls (if exclude is true). - */ - expr_ref_vector project(); - expr_ref_vector solve(); - expr_ref_vector project(model &mdl); - - /** - * Return disequalities to ensure that disequalities between - * excluded functions are preserved. - * For example if f(a) = b, f(c) = d, and b and d are not - * congruent, then produce the disequality a != c. - */ - expr_ref_vector get_ackerman_disequalities(); - - /** - * Produce model-based disequality - * certificate corresponding to - * definition in BGVS 2020. - * A disequality certificate is a reduced set of - * disequalities, true under mdl, such that the literals - * can be satisfied when non-shared symbols are projected. - */ - expr_ref_vector dcert(model& mdl, expr_ref_vector const& lits); - - /** - * Produce a model-based partition. - */ - vector get_partition(model& mdl); - - /** - * Extract shared occurrences of terms whose sort are - * fid, but appear in a context that is not fid. - * for example f(x + y) produces the shared occurrence - * x + y when f is uninterpreted and x + y has sort Int or Real. - */ - expr_ref_vector shared_occurrences(family_id fid); - - /** - * Map expression that occurs in added literals into representative if it exists. - */ - void add_model_based_terms(model& mdl, expr_ref_vector const& terms); - expr* rep_of(expr* e); + public: + bool operator()(const expr *e) const override; + bool operator()(const term &t) const; + void set_decls(const func_decl_ref_vector &decls, bool exclude); + void set_decls(const app_ref_vector &vars, bool exclude); + void add_decls(const app_ref_vector &vars); + void add_decl(app *var); + void mark_solved(const expr *e); + void reset_solved() { m_solved.reset(); } + void reset() { + m_decls.reset(); + m_solved.reset(); + m_exclude = true; + } + bool contains(func_decl *f) { return m_decls.contains(f) == m_exclude; } }; -} + class is_non_core : public i_expr_pred { + std::function *m_non_core; + + public: + is_non_core(std::function *nc) : m_non_core(nc) {} + bool operator()(expr *n) override { + if (m_non_core == nullptr) return false; + return (*m_non_core)(n); + } + }; + + struct term_hash { + unsigned operator()(term const *t) const; + }; + struct term_eq { + bool operator()(term const *a, term const *b) const; + }; + ast_manager &m; + ptr_vector m_terms; + expr_ref_vector m_lits; // NSB: expr_ref_vector? + u_map m_app2term; + ast_ref_vector m_pinned; + projector *m_projector; + bool m_explicit_eq; + bool m_repick_repr; + u_map + m_term2app; // any representative change invalidates this cache + plugin_manager m_plugins; + ptr_hashtable m_cg_table; + vector> m_merge; + + term_graph::is_variable_proc m_is_var; + + void merge(term &t1, term &t2); + void merge_flush(); + + term *mk_term(expr *t); + term *get_term(expr *t); + term *get_term(func_decl *f); + + term *internalize_term(expr *t); + void internalize_eq(expr *a1, expr *a2); + void internalize_lit(expr *lit); + void internalize_distinct(expr *d); + void internalize_deq(expr *a1, expr *a2); + + bool is_internalized(expr *a); + bool is_ground(expr *e); + + bool term_lt(term const &t1, term const &t2); + void pick_repr_percolate_up(ptr_vector &todo); + void pick_repr_class(term *t); + void pick_repr(); + + void reset_marks(); + void reset_marks2(); + bool marks_are_clear(); + + expr *mk_app_core(expr *a); + expr_ref mk_app(term &t); + expr *mk_pure(term &t); + expr_ref mk_app(expr *a); + void mk_equalities(term &t, expr_ref_vector &out); + void mk_all_equalities(term &t, expr_ref_vector &out); + void mk_qe_lite_equalities(term &t, expr_ref_vector &out, + check_pred ¬_in_core); + void display(std::ostream &out); + + bool is_pure_def(expr *atom, expr *&v); + void cground_percolate_up(ptr_vector &); + void cground_percolate_up(term *t); + void compute_cground(); + + public: + term_graph(ast_manager &m); + ~term_graph(); + + const expr_ref_vector &get_lits() const { return m_lits; } + void get_terms(expr_ref_vector &res, bool exclude_cground = true); + bool is_cgr(expr *e); + unsigned size() { return m_terms.size(); } + + void set_vars(func_decl_ref_vector const &decls, bool exclude = true); + void set_vars(app_ref_vector const &vars, bool exclude = true); + void add_vars(app_ref_vector const &vars); + void add_var(app *var); + + ast_manager &get_ast_manager() const { return m; } + + void add_lit(expr *lit); + void add_lits(expr_ref_vector const &lits) { + for (expr *e : lits) add_lit(e); + } + void add_eq(expr *a, expr *b) { internalize_eq(a, b); } + void add_deq(expr *a, expr *b) { internalize_deq(a, b); } + + void reset(); + + // deprecate? + void to_lits(expr_ref_vector &lits, bool all_equalities = false, + bool repick_repr = true); + void to_lits_qe_lite(expr_ref_vector &lits, + std::function *non_core = nullptr); + expr_ref to_expr(bool repick_repr = true); + + /** + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the + * vocabulary of decls (if exclude is true). + */ + expr_ref_vector project(); + expr_ref_vector solve(); + expr_ref_vector project(model &mdl); + + /** + * Return disequalities to ensure that disequalities between + * excluded functions are preserved. + * For example if f(a) = b, f(c) = d, and b and d are not + * congruent, then produce the disequality a != c. + */ + expr_ref_vector get_ackerman_disequalities(); + + /** + * Produce model-based disequality + * certificate corresponding to + * definition in BGVS 2020. + * A disequality certificate is a reduced set of + * disequalities, true under mdl, such that the literals + * can be satisfied when non-shared symbols are projected. + */ + expr_ref_vector dcert(model &mdl, expr_ref_vector const &lits); + + /** + * Produce a model-based partition. + */ + vector get_partition(model &mdl); + + /** + * Extract shared occurrences of terms whose sort are + * fid, but appear in a context that is not fid. + * for example f(x + y) produces the shared occurrence + * x + y when f is uninterpreted and x + y has sort Int or Real. + */ + expr_ref_vector shared_occurrences(family_id fid); + + /** + * Map expression that occurs in added literals into representative if it + * exists. + */ + void add_model_based_terms(model &mdl, expr_ref_vector const &terms); + expr *rep_of(expr *e); + + using deqs = bit_vector; + struct add_deq_proc { + uint64_t m_deq_cnt = 0; + void operator()(term *t1, term *t2); + void operator()(ptr_vector &ts); + }; + + // -- disequalities added for output + vector> m_deq_pairs; + // -- maybe they are not necessary since they are in the original formula + vector> m_deq_distinct; + + expr_ref_vector non_ground_terms(); + void gr_terms_to_lits(expr_ref_vector &lits, bool all_equalities); + // produce a quantifier reduction of the formula stored in the term graph + // output of qel will not contain expression e s.t. non_core(e) == true + void qel(app_ref_vector &vars, expr_ref &fml, + std::function *non_core = nullptr); + bool has_val_in_class(expr *e); + app *get_const_in_class(expr *e); + void set_explicit_eq() { m_explicit_eq = true; } + + private: + add_deq_proc m_add_deq; + void refine_repr_class(term *t); + void refine_repr(); + bool makes_cycle(term *t); +}; + +namespace is_ground_ns { +struct found {}; +struct proc { + term_graph::is_variable_proc &m_is_var; + proc(term_graph::is_variable_proc &is_var) : m_is_var(is_var) {} + void operator()(var *n) const {} + void operator()(app const *n) const { + if (m_is_var.contains(n->get_decl())) throw found(); + } + void operator()(quantifier *n) const {} +}; +} // namespace is_ground_ns +} // namespace mbp diff --git a/src/qe/mbp/mbp_tg_plugins.h b/src/qe/mbp/mbp_tg_plugins.h new file mode 100644 index 000000000..ab4a54333 --- /dev/null +++ b/src/qe/mbp/mbp_tg_plugins.h @@ -0,0 +1,34 @@ +/*++ + + Module Name: + + mbp_tg_plugins.h + +Abstract: + + Model Based Projection for a theory + +Author: + + Hari Govind V K (hgvk94) 2022-07-12 + +Revision History: + + +--*/ +#pragma once +#include "ast/ast.h" +#include "qe/mbp/mbp_qel_util.h" +#include "qe/mbp/mbp_term_graph.h" +#include "util/obj_hashtable.h" + +class mbp_tg_plugin { + public: + // iterate through all terms in m_tg and apply all theory MBP rules once + // returns true if any rules were applied + virtual bool apply() { return false; }; + virtual ~mbp_tg_plugin() = default; + virtual void use_model() { }; + virtual void get_new_vars(app_ref_vector*&) { }; + virtual family_id get_family_id() const { return null_family_id; }; +}; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 9f5d9063c..d53684100 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -18,25 +18,127 @@ Revision History: --*/ -#include "ast/rewriter/expr_safe_replace.h" +#include "qe/qe_mbp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" -#include "ast/occurs.h" -#include "ast/rewriter/th_rewriter.h" #include "ast/expr_functors.h" #include "ast/for_each_expr.h" +#include "ast/occurs.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/rewriter/rewriter.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/scoped_proof.h" -#include "qe/qe_mbp.h" +#include "util/gparams.h" +#include "model/model_evaluator.h" +#include "model/model_pp.h" +#include "qe/lite/qe_lite_tactic.h" +#include "qe/lite/qel.h" #include "qe/mbp/mbp_arith.h" #include "qe/mbp/mbp_arrays.h" +#include "qe/mbp/mbp_qel.h" #include "qe/mbp/mbp_datatypes.h" -#include "qe/lite/qe_lite_tactic.h" -#include "model/model_pp.h" -#include "model/model_evaluator.h" - using namespace qe; +namespace { +// rewrite select(store(a, i, k), j) into k if m \models i = j and select(a, j) if m \models i != j + struct rd_over_wr_rewriter : public default_rewriter_cfg { + ast_manager &m; + array_util m_arr; + model_evaluator m_eval; + expr_ref_vector m_sc; + + rd_over_wr_rewriter(ast_manager& man, model& mdl): m(man), m_arr(m), m_eval(mdl), m_sc(m) { + m_eval.set_model_completion(false); + } + + br_status reduce_app(func_decl *f, unsigned num, expr *const *args, + expr_ref &result, proof_ref &result_pr) { + if (m_arr.is_select(f) && m_arr.is_store(args[0])) { + expr_ref ind1(m), ind2(m); + ind1 = m_eval(args[1]); + ind2 = m_eval(to_app(args[0])->get_arg(1)); + if (ind1 == ind2) { + result = to_app(args[0])->get_arg(2); + m_sc.push_back(m.mk_eq(args[1], to_app(args[0])->get_arg(1))); + return BR_DONE; + } + m_sc.push_back(m.mk_not(m.mk_eq(args[1], to_app(args[0])->get_arg(1)))); + expr_ref_vector new_args(m); + new_args.push_back(to_app(args[0])->get_arg(0)); + new_args.push_back(args[1]); + result = m_arr.mk_select(new_args); + return BR_REWRITE1; + } + return BR_FAILED; + } + }; +// rewrite all occurrences of (as const arr c) to (as const arr v) where v = m_eval(c) + struct app_const_arr_rewriter : public default_rewriter_cfg { + ast_manager &m; + array_util m_arr; + datatype_util m_dt_util; + model_evaluator m_eval; + expr_ref val; + + app_const_arr_rewriter(ast_manager& man, model& mdl): m(man), m_arr(m), m_dt_util(m), m_eval(mdl), val(m) { + m_eval.set_model_completion(false); + } + br_status reduce_app(func_decl *f, unsigned num, expr *const *args, + expr_ref &result, proof_ref &result_pr) { + if (m_arr.is_const(f) && !m.is_value(args[0])) { + val = m_eval(args[0]); + SASSERT(m.is_value(val)); + result = m_arr.mk_const_array(f->get_range(), val); + return BR_DONE; + } + if (m_dt_util.is_constructor(f)) { + // cons(head(x), tail(x)) --> x + ptr_vector const *accessors = + m_dt_util.get_constructor_accessors(f); + + SASSERT(num == accessors->size()); + // -- all accessors must have exactly one argument + if (any_of(*accessors, [&](const func_decl* acc) { return acc->get_arity() != 1; })) { + return BR_FAILED; + } + + if (num >= 1 && is_app(args[0]) && to_app(args[0])->get_decl() == accessors->get(0)) { + bool is_all = true; + expr* t = to_app(args[0])->get_arg(0); + for(unsigned i = 1; i < num && is_all; ++i) { + is_all &= (is_app(args[i]) && + to_app(args[i])->get_decl() == accessors->get(i) && + to_app(args[i])->get_arg(0) == t); + } + if (is_all) { + result = t; + return BR_DONE; + } + } + } + return BR_FAILED; + } + }; +} +void rewrite_as_const_arr(expr* in, model& mdl, expr_ref& out) { + app_const_arr_rewriter cfg(out.m(), mdl); + rewriter_tpl rw(out.m(), false, cfg); + rw(in, out); +} + +void rewrite_read_over_write(expr *in, model &mdl, expr_ref &out) { + rd_over_wr_rewriter cfg(out.m(), mdl); + rewriter_tpl rw(out.m(), false, cfg); + rw(in, out); + if (cfg.m_sc.empty()) return; + expr_ref_vector sc(out.m()); + SASSERT(out.m().is_and(out)); + flatten_and(out, sc); + sc.append(cfg.m_sc); + out = mk_and(sc); +} class mbproj::impl { ast_manager& m; @@ -47,6 +149,7 @@ class mbproj::impl { // parameters bool m_reduce_all_selects; bool m_dont_sub; + bool m_use_qel; void add_plugin(mbp::project_plugin* p) { family_id fid = p->get_family_id(); @@ -253,9 +356,35 @@ public: m_params.append(p); m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); m_dont_sub = m_params.get_bool("dont_sub", false); + auto q = gparams::get_module("smt"); + m_params.append(q); + m_use_qel = m_params.get_bool("qsat_use_qel", true); } void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + if (m_use_qel) { + extract_literals(model, vars, fmls); + expr_ref e(m); + bool change = true; + while (change && !vars.empty()) { + change = false; + e = mk_and(fmls); + do_qel(vars, e); + fmls.reset(); + flatten_and(e, fmls); + for (auto* p : m_plugins) { + if (p && p->solve(model, vars, fmls)) { + change = true; + } + } + } + //rewrite as_const_arr terms + expr_ref fml(m); + fml = mk_and(fmls); + rewrite_as_const_arr(fml, model, fml); + flatten_and(fml, fmls); + } + else { extract_literals(model, vars, fmls); bool change = true; while (change && !vars.empty()) { @@ -267,6 +396,7 @@ public: } } } + } bool validate_model(model& model, expr_ref_vector const& fmls) { for (expr* f : fmls) { @@ -276,6 +406,22 @@ public: } void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { + if (m_use_qel) { + bool dsub = m_dont_sub; + m_dont_sub = !force_elim; + expr_ref fml(m); + fml = mk_and(fmls); + spacer_qel(vars, model, fml); + fmls.reset(); + flatten_and(fml, fmls); + m_dont_sub = dsub; + } + else { + mbp(force_elim, vars, model, fmls); + } + } + + void mbp(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { SASSERT(validate_model(model, fmls)); expr_ref val(m), tmp(m); app_ref var(m); @@ -341,6 +487,17 @@ public: SASSERT(!m.is_false(fml)); } + + void do_qel(app_ref_vector &vars, expr_ref &fml) { + qel qe(m, m_params); + qe(vars, fml); + m_rw(fml); + TRACE("qe", tout << "After qel:\n" + << fml << "\n" + << "Vars: " << vars << "\n";); + SASSERT(!m.is_false(fml)); + } + void do_qe_bool(model& mdl, app_ref_vector& vars, expr_ref& fml) { expr_ref_vector fmls(m); fmls.push_back(fml); @@ -348,7 +505,86 @@ public: fml = mk_and(fmls); } + void qel_project(app_ref_vector &vars, model &mdl, expr_ref &fml, bool reduce_all_selects) { + flatten_and(fml); + mbp::mbp_qel mbptg(m, m_params); + mbptg(vars, fml, mdl); + if (reduce_all_selects) rewrite_read_over_write(fml, mdl, fml); + m_rw(fml); + TRACE("qe", tout << "After mbp_tg:\n" + << fml << " models " << mdl.is_true(fml) << "\n" + << "Vars: " << vars << "\n";); + } + + void spacer_qel(app_ref_vector& vars, model& mdl, expr_ref& fml) { + TRACE("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";); + + model_evaluator eval(mdl, m_params); + eval.set_model_completion(true); + app_ref_vector other_vars(m); + app_ref_vector sub_vars(m); + array_util arr_u(m); + arith_util ari_u(m); + datatype_util dt_u(m); + + do_qel(vars, fml); + qel_project(vars, mdl, fml, m_reduce_all_selects); + flatten_and(fml); + m_rw(fml); + rewrite_as_const_arr(fml, mdl, fml); + + for (app* v : vars) { + SASSERT(!arr_u.is_array(v) && !dt_u.is_datatype(v->get_sort())); + other_vars.push_back(v); + } + + // project reals, ints and other variables. + if (!other_vars.empty()) { + TRACE("qe", tout << "Other vars: " << other_vars << "\n" << mdl;); + + expr_ref_vector fmls(m); + flatten_and(fml, fmls); + + mbp(false, other_vars, mdl, fmls); + fml = mk_and(fmls); + m_rw(fml); + + TRACE("qe", + tout << "Projected other vars:\n" << fml << "\n"; + tout << "Remaining other vars:\n" << other_vars << "\n";); + SASSERT(!m.is_false(fml)); + } + + if (!other_vars.empty()) { + project_vars(mdl, other_vars, fml); + m_rw(fml); + } + + // substitute any remaining other vars + if (!m_dont_sub && !other_vars.empty()) { + subst_vars(eval, other_vars, fml); + TRACE("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";); + // an extra round of simplification because subst_vars is not simplifying + m_rw(fml); + other_vars.reset(); + } + + SASSERT(!eval.is_false(fml)); + + vars.reset(); + vars.append(other_vars); + } + void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { + if (m_use_qel) { + spacer_qel(vars, mdl, fml); + } + else { + spacer_qe_lite(vars, mdl, fml); + } + } + + void spacer_qe_lite(app_ref_vector& vars, model& mdl, expr_ref& fml) { TRACE("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";); model_evaluator eval(mdl, m_params); @@ -428,7 +664,6 @@ public: vars.reset(); vars.append(other_vars); } - }; mbproj::mbproj(ast_manager& m, params_ref const& p) { @@ -447,6 +682,7 @@ void mbproj::updt_params(params_ref const& p) { void mbproj::get_param_descrs(param_descrs& r) { r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); + r.insert("use_qel", CPK_BOOL, "(default: true) use egraph based QEL"); } void mbproj::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { @@ -468,3 +704,5 @@ opt::inf_eps mbproj::maximize(expr_ref_vector const& fmls, model& mdl, app* t, e scoped_no_proof _sp(fmls.get_manager()); return m_impl->maximize(fmls, mdl, t, ge, gt); } +template class rewriter_tpl; +template class rewriter_tpl; diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 7e28437f6..fe4c4945c 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -39,6 +39,7 @@ Notes: #include "qe/qe_mbp.h" #include "qe/qe.h" #include "ast/rewriter/label_rewriter.h" +#include "util/params.h" namespace qe { @@ -164,8 +165,9 @@ namespace qe { TRACE("qe_assumptions", model_v2_pp(tout, *mdl);); expr_ref val(m); - for (unsigned j = 0; j < m_preds[level - 1].size(); ++j) { - app* p = m_preds[level - 1][j].get(); + for (unsigned i = 0; i <= level-1; ++i) { + for (unsigned j = 0; j < m_preds[i].size(); ++j) { + app* p = m_preds[i][j].get(); eval(p, val); if (!m.inc()) return; @@ -176,6 +178,7 @@ namespace qe { SASSERT(m.is_true(val)); m_asms.push_back(p); } + } } asms.append(m_asms); @@ -529,11 +532,14 @@ namespace qe { ast_manager& m; params_ref m_params; ref m_solver; + + expr_ref m_last_assert; public: kernel(ast_manager& m): m(m), - m_solver(nullptr) + m_solver(nullptr), + m_last_assert(m) { m_params.set_bool("model", true); m_params.set_uint("relevancy", 0); @@ -544,7 +550,7 @@ namespace qe { solver const& s() const { return *m_solver; } void init() { - m_solver = mk_smt_solver(m, m_params, symbol::null); + m_solver = mk_smt2_solver(m, m_params, symbol::null); } void collect_statistics(statistics & st) const { if (m_solver) @@ -561,7 +567,23 @@ namespace qe { void clear() { m_solver = nullptr; } - void assert_expr(expr* e) { + + void assert_expr(expr *e) { + if (!m.is_true(e)) + m_solver->assert_expr(e); + } + void assert_blocking_fml(expr* e) { + if (m.is_true(e)) return; + if (m_last_assert) { + if (e == m_last_assert) { + verbose_stream() << "Asserting this expression twice in a row:\n " << m_last_assert << "\n"; + SASSERT(false); + std::exit(3); + } + + } + m_last_assert = e; + m_solver->assert_expr(e); } @@ -618,7 +640,9 @@ namespace qe { lbool check_sat() { while (true) { ++m_stats.m_num_rounds; - IF_VERBOSE(3, verbose_stream() << "(check-qsat level: " << m_level << " round: " << m_stats.m_num_rounds << ")\n";); + IF_VERBOSE(1, verbose_stream() << "(check-qsat level: " << m_level << " round: " << m_stats.m_num_rounds << ")\n";); + TRACE("qe", + tout << "level: " << m_level << " round: " << m_stats.m_num_rounds << "\n"); check_cancel(); expr_ref_vector asms(m_asms); m_pred_abs.get_assumptions(m_model.get(), asms); @@ -951,7 +975,8 @@ namespace qe { } else { fml = m_pred_abs.mk_abstract(fml); - get_kernel(m_level).assert_expr(fml); + TRACE("qe_block", tout << "Blocking fml at level: " << m_level << "\n" << fml << "\n";); + get_kernel(m_level).assert_blocking_fml(fml); } SASSERT(!m_model.get()); return true; @@ -1235,8 +1260,11 @@ namespace qe { m_value(nullptr), m_was_sat(false), m_gt(m) - { - } + { + params_ref q = params_ref(); + q.set_bool("use_qel", false); + m_mbp.updt_params(q); + } ~qsat() override { clear(); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 7bc9bc3fa..2bb3d4197 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -132,6 +132,7 @@ def_module_params(module_name='smt', ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'), ('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body'), ('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none'), - ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy') + ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy'), + ('qsat_use_qel', BOOL, True, 'Use QEL for lite quantifier elimination and model-based projection in QSAT') )) From 606940e60c4edda87e504e7fab30411481143c70 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 10:29:48 -0700 Subject: [PATCH 054/428] nits Signed-off-by: Nikolaj Bjorner --- src/qe/lite/qel.cpp | 6 +- src/qe/lite/qel.h | 7 +- src/qe/mbp/mbp_term_graph.cpp | 117 ++++++++++++++++++++-------------- src/qe/mbp/mbp_term_graph.h | 32 +++++----- 4 files changed, 90 insertions(+), 72 deletions(-) diff --git a/src/qe/lite/qel.cpp b/src/qe/lite/qel.cpp index 583f51cc9..addd33c3c 100644 --- a/src/qe/lite/qel.cpp +++ b/src/qe/lite/qel.cpp @@ -18,18 +18,16 @@ Author: Hari Govind V K (hgvk94) Isabel Garcia (igcontreras) -Revision History: - --*/ #include "qe/lite/qel.h" #include "qe/mbp/mbp_term_graph.h" class qel::impl { - private: +private: ast_manager &m; - public: +public: impl(ast_manager &m, params_ref const &p) : m(m) {} void operator()(app_ref_vector &vars, expr_ref &fml) { diff --git a/src/qe/lite/qel.h b/src/qe/lite/qel.h index 9db6a32b4..ab960bc47 100644 --- a/src/qe/lite/qel.h +++ b/src/qe/lite/qel.h @@ -19,9 +19,6 @@ Author: Hari Govind V K (hgvk94) Isabel Garcia (igcontreras) -Revision History: - - --*/ #pragma once @@ -34,8 +31,8 @@ Revision History: class qel { class impl; impl *m_impl; - - public: + +public: qel(ast_manager &m, params_ref const &p); ~qel(); diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index ee371bdfa..3dc8a20f7 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -364,19 +364,25 @@ static std::ostream &operator<<(std::ostream &out, term const &t) { return t.display(out); } + // t1 != t2 void term_graph::add_deq_proc::operator()(term *t1, term *t2) { - ptr_vector ts(2); - ts[0] = t1; - ts[1] = t2; - (*this)(ts); + term::set_deq(t1->get_root().get_deqs(), m_deq_cnt); + term::set_deq(t2->get_root().get_deqs(), m_deq_cnt); + inc_count(); } // distinct(ts) void term_graph::add_deq_proc::operator()(ptr_vector &ts) { - for (auto t : ts) { term::set_deq(t->get_root().get_deqs(), m_deq_cnt); } - SASSERT(m_deq_cnt < UINT64_MAX); + for (auto t : ts) + term::set_deq(t->get_root().get_deqs(), m_deq_cnt); + inc_count(); +} + +void term_graph::add_deq_proc::inc_count() { m_deq_cnt++; + if (m_deq_cnt == 0) + throw default_exception("unexpected wrap-around on m_deq_cnt"); } bool term_graph::is_variable_proc::operator()(const expr *e) const { @@ -429,8 +435,7 @@ bool term_graph::term_eq::operator()(term const *a, term const *b) const { } term_graph::term_graph(ast_manager &man) - : m(man), m_lits(m), m_pinned(m), m_projector(nullptr), - m_explicit_eq(false), m_repick_repr(false) { + : m(man), m_lits(m), m_pinned(m) { m_is_var.reset(); m_plugins.register_plugin(mbp::mk_basic_solve_plugin(m, m_is_var)); m_plugins.register_plugin(mbp::mk_arith_solve_plugin(m, m_is_var)); @@ -447,15 +452,18 @@ bool term_graph::is_pure_def(expr *atom, expr *&v) { } static family_id get_family_id(ast_manager &m, expr *lit) { - if (m.is_not(lit, lit)) return get_family_id(m, lit); + if (m.is_not(lit, lit)) + return get_family_id(m, lit); expr *a = nullptr, *b = nullptr; - // deal with equality using sort of range - if (m.is_eq(lit, a, b)) { return a->get_sort()->get_family_id(); } - // extract family_id of top level app - else if (is_app(lit)) { return to_app(lit)->get_decl()->get_family_id(); } - else { return null_family_id; } + if (m.is_eq(lit, a, b)) // deal with equality using sort of range + return a->get_sort()->get_family_id(); + else if (is_app(lit)) // extract family_id of top level app + return to_app(lit)->get_decl()->get_family_id(); + else + return null_family_id; } + void term_graph::add_lit(expr *l) { expr_ref lit(m); expr_ref_vector lits(m); @@ -492,7 +500,8 @@ void term_graph::get_terms(expr_ref_vector &res, bool exclude_cground) { auto terms = m_terms.filter_pure(fil); res.resize(terms.size()); unsigned i = 0; - for (term *t : terms) res[i++] = t->get_expr(); + for (term *t : terms) + res[i++] = t->get_expr(); } bool term_graph::is_cgr(expr *e) { @@ -543,12 +552,13 @@ term *term_graph::internalize_term(expr *t) { continue; } unsigned sz = todo.size(); - if (is_app(t)) { - for (expr *arg : *::to_app(t)) { - if (!get_term(arg)) todo.push_back(arg); - } - } - if (sz < todo.size()) continue; + if (is_app(t)) + for (expr *arg : *::to_app(t)) + if (!get_term(arg)) + todo.push_back(arg); + + if (sz < todo.size()) + continue; todo.pop_back(); res = mk_term(t); @@ -556,11 +566,11 @@ term *term_graph::internalize_term(expr *t) { // could be congruent with some other term, if that is the case, we // need to merge them. term *res_old = m_cg_table.insert_if_not_there(res); - if (res->is_cgr()) res_old->set_cgr(true); + if (res->is_cgr()) + res_old->set_cgr(true); SASSERT(res_old->is_cgr() == res->is_cgr()); - if (res_old->get_root().get_id() != res->get_root().get_id()) { - m_merge.push_back(std::make_pair(res, res_old)); - } + if (res_old->get_root().get_id() != res->get_root().get_id()) + m_merge.push_back({res, res_old}); } merge_flush(); SASSERT(res); @@ -572,10 +582,12 @@ void term_graph::internalize_eq(expr *a1, expr *a2) { merge(*internalize_term(a1), *internalize_term(a2)); merge_flush(); SASSERT(m_merge.empty()); - if (!m_explicit_eq) return; + if (!m_explicit_eq) + return; expr_ref eq(m.mk_eq(a1, a2), m); term *res = get_term(eq); - if (!res) mk_term(eq); + if (!res) + mk_term(eq); } void term_graph::internalize_distinct(expr *d) { @@ -599,26 +611,29 @@ void term_graph::internalize_deq(expr *a1, expr *a2) { term *t2 = internalize_term(a2); m_add_deq(t1, t2); m_deq_pairs.push_back({t1, t2}); - if (!m_explicit_eq) return; + if (!m_explicit_eq) + return; expr_ref eq(m.mk_eq(a1, a2), m); term *eq_term = mk_term(eq); eq_term->set_neq_child(); expr_ref deq(m.mk_not(eq), m); term *res = get_term(deq); - if (!res) mk_term(deq); + if (!res) + mk_term(deq); } void term_graph::internalize_lit(expr *lit) { expr *e1 = nullptr, *e2 = nullptr, *ne = nullptr, *v = nullptr; - if (m.is_eq(lit, e1, e2)) { // internalize equality + if (m.is_eq(lit, e1, e2)) // internalize equality internalize_eq(e1, e2); - } - else if (m.is_distinct(lit)) { internalize_distinct(lit); } - else if (m.is_not(lit, ne) && m.is_eq(ne, e1, e2)) { + else if (m.is_distinct(lit)) + internalize_distinct(lit); + else if (m.is_not(lit, ne) && m.is_eq(ne, e1, e2)) internalize_deq(e1, e2); - } - else { internalize_term(lit); } - if (is_pure_def(lit, v)) { m_is_var.mark_solved(v); } + else + internalize_term(lit); + if (is_pure_def(lit, v)) + m_is_var.mark_solved(v); } void term_graph::merge_flush() { @@ -674,7 +689,8 @@ void term_graph::merge(term &t1, term &t2) { } } } - if (prop_cgroundness) cground_percolate_up(a); + if (prop_cgroundness) + cground_percolate_up(a); SASSERT(marks_are_clear()); } @@ -694,10 +710,12 @@ expr *term_graph::mk_app_core(expr *e) { expr_ref term_graph::mk_app(term &r) { SASSERT(r.is_repr()); - if (r.get_num_args() == 0) { return expr_ref(r.get_expr(), m); } + if (r.get_num_args() == 0) + return expr_ref(r.get_expr(), m); expr *res = nullptr; - if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } + if (m_term2app.find(r.get_id(), res)) + return expr_ref(res, m); res = mk_app_core(r.get_expr()); m_term2app.insert(r.get_id(), res); @@ -716,7 +734,8 @@ expr_ref term_graph::mk_app(expr *a) { void term_graph::mk_equalities(term &t, expr_ref_vector &out) { SASSERT(t.is_repr()); - if (t.get_class_size() == 1) return; + if (t.get_class_size() == 1) + return; expr_ref rep(mk_app(t), m); for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { expr *mem = mk_app_core(it->get_expr()); @@ -725,7 +744,8 @@ void term_graph::mk_equalities(term &t, expr_ref_vector &out) { } void term_graph::mk_all_equalities(term &t, expr_ref_vector &out) { - if (t.get_class_size() == 1) return; + if (t.get_class_size() == 1) + return; mk_equalities(t, out); @@ -762,22 +782,25 @@ void term_graph::mk_qe_lite_equalities(term &t, expr_ref_vector &out, // don't add equalities for vars to eliminate if (m_is_var.contains(a->get_decl())) continue; expr *mem = mk_app_core(e); - if (rep != mem && !contains_nc(mem)) out.push_back(m.mk_eq(rep, mem)); + if (rep != mem && !contains_nc(mem)) + out.push_back(m.mk_eq(rep, mem)); } } void term_graph::reset_marks() { - for (term *t : m_terms) { t->set_mark(false); } + for (term *t : m_terms) t->set_mark(false); } void term_graph::reset_marks2() { - for (term *t : m_terms) { t->set_mark2(false); } + for (term *t : m_terms) t->set_mark2(false); } bool term_graph::marks_are_clear() { - for (term *t : m_terms) { - if (t->is_marked()) return false; - } + return all_of(m_terms, [](term* t) { return !t->is_marked(); }); + + for (term *t : m_terms) + if (t->is_marked()) + return false; return true; } diff --git a/src/qe/mbp/mbp_term_graph.h b/src/qe/mbp/mbp_term_graph.h index 1300034a6..58afcaf02 100644 --- a/src/qe/mbp/mbp_term_graph.h +++ b/src/qe/mbp/mbp_term_graph.h @@ -47,7 +47,7 @@ class term_graph { bool m_exclude; obj_hashtable m_decls, m_solved; - public: + public: bool operator()(const expr *e) const override; bool operator()(const term &t) const; @@ -68,7 +68,7 @@ class term_graph { class is_non_core : public i_expr_pred { std::function *m_non_core; - public: + public: is_non_core(std::function *nc) : m_non_core(nc) {} bool operator()(expr *n) override { if (m_non_core == nullptr) return false; @@ -82,16 +82,15 @@ class term_graph { struct term_eq { bool operator()(term const *a, term const *b) const; }; - ast_manager &m; - ptr_vector m_terms; - expr_ref_vector m_lits; // NSB: expr_ref_vector? - u_map m_app2term; - ast_ref_vector m_pinned; - projector *m_projector; - bool m_explicit_eq; - bool m_repick_repr; - u_map - m_term2app; // any representative change invalidates this cache + ast_manager & m; + ptr_vector m_terms; + expr_ref_vector m_lits; + u_map m_app2term; + ast_ref_vector m_pinned; + projector * m_projector = nullptr; + bool m_explicit_eq = false; + bool m_repick_repr = false; + u_map m_term2app; // any representative change invalidates this cache plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; @@ -138,7 +137,7 @@ class term_graph { void cground_percolate_up(term *t); void compute_cground(); - public: +public: term_graph(ast_manager &m); ~term_graph(); @@ -218,8 +217,9 @@ class term_graph { expr *rep_of(expr *e); using deqs = bit_vector; - struct add_deq_proc { - uint64_t m_deq_cnt = 0; + struct add_deq_proc { + unsigned m_deq_cnt = 0; + void inc_count(); void operator()(term *t1, term *t2); void operator()(ptr_vector &ts); }; @@ -239,7 +239,7 @@ class term_graph { app *get_const_in_class(expr *e); void set_explicit_eq() { m_explicit_eq = true; } - private: +private: add_deq_proc m_add_deq; void refine_repr_class(term *t); void refine_repr(); From 9b5727addedab1dc4d027e0c7d3cd5c40b9a739e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 10:51:52 -0700 Subject: [PATCH 055/428] enable arm for non-osx Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 2 +- src/qe/mbp/mbp_term_graph.cpp | 93 ++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index fbb4b3590..595673647 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2664,7 +2664,7 @@ def mk_config(): LDFLAGS = '%s -static-libgcc -static-libstdc++' % LDFLAGS if sysname == 'Linux' and machine.startswith('armv7') or machine.startswith('armv8'): CXXFLAGS = '%s -fpic' % CXXFLAGS - if IS_OSX and IS_ARCH_ARM64: + if IS_ARCH_ARM64: print("Setting arm64") CXXFLAGS = '%s -arch arm64' % CXXFLAGS LDFLAGS = '%s -arch arm64' % LDFLAGS diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 3dc8a20f7..8c525d2f7 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -649,14 +649,16 @@ void term_graph::merge(term &t1, term &t2) { term *a = &t1.get_root(); term *b = &t2.get_root(); - if (a == b) return; + if (a == b) + return; // -- merge might invalidate term2app cache m_term2app.reset(); m_pinned.reset(); m_repick_repr = true; - if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } + if (a->get_class_size() > b->get_class_size()) + std::swap(a, b); // Remove parents of b from the cg table for (term *p : term::parents(b)) { @@ -669,9 +671,8 @@ void term_graph::merge(term &t1, term &t2) { bool prop_cgroundness = (b->is_class_gr() != a->is_class_gr()); // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); - for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + for (term *it = &b->get_next(); it != b; it = &it->get_next()) it->set_root(*a); - } // merge equivalence classes a->merge_eq_class(*b); @@ -684,9 +685,8 @@ void term_graph::merge(term &t1, term &t2) { p->set_mark(false); a->add_parent(p); // propagate new equalities. - if (p->get_root().get_id() != p_old->get_root().get_id()) { - m_merge.push_back(std::make_pair(p, p_old)); - } + if (p->get_root().get_id() != p_old->get_root().get_id()) + m_merge.push_back({p, p_old}); } } if (prop_cgroundness) @@ -724,12 +724,11 @@ expr_ref term_graph::mk_app(term &r) { expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); + SASSERT(!t || t->get_repr()); if (!t) return expr_ref(a, m); - else { - SASSERT(t->get_repr()); + else return mk_app(*t->get_repr()); - } } void term_graph::mk_equalities(term &t, expr_ref_vector &out) { @@ -797,11 +796,6 @@ void term_graph::reset_marks2() { bool term_graph::marks_are_clear() { return all_of(m_terms, [](term* t) { return !t->is_marked(); }); - - for (term *t : m_terms) - if (t->is_marked()) - return false; - return true; } /// Order of preference for roots of equivalence classes @@ -829,11 +823,9 @@ bool term_graph::term_lt(term const &t1, term const &t2) { } bool all_children_picked(term *t) { - if (t->deg() == 0) return true; - for (term *c : term::children(t)) { - if (!c->get_repr()) return false; - } - return true; + if (t->deg() == 0) + return true; + return all_of(term::children(t), [](term* c) { return c->get_repr(); }); } // pick representatives for all terms in todo. Then, pick representatives for @@ -843,10 +835,12 @@ void term_graph::pick_repr_percolate_up(ptr_vector &todo) { while (!todo.empty()) { t = todo.back(); todo.pop_back(); - if (t->get_repr()) continue; + if (t->get_repr()) + continue; pick_repr_class(t); for (auto it : term::parents(t->get_root())) - if (all_children_picked(it)) todo.push_back(it); + if (all_children_picked(it)) + todo.push_back(it); } } @@ -856,7 +850,8 @@ void term_graph::pick_repr_class(term *t) { SASSERT(all_children_picked(t)); term *r = t; for (term *it = &t->get_next(); it != t; it = &it->get_next()) { - if (!all_children_picked(it)) continue; + if (!all_children_picked(it)) + continue; if ((it->is_cgr() && !r->is_cgr()) || (it->is_cgr() == r->is_cgr() && term_lt(*it, *r))) r = it; @@ -879,15 +874,17 @@ void term_graph::pick_repr() { t->is_cgr());); for (term *t : m_terms) t->reset_repr(); ptr_vector todo; - for (term *t : m_terms) { - if (t->deg() == 0 && t->is_cgr()) todo.push_back(t); - } + for (term *t : m_terms) + if (t->deg() == 0 && t->is_cgr()) + todo.push_back(t); pick_repr_percolate_up(todo); DEBUG_CODE(for (term *t : m_terms) SASSERT(!t->is_cgr() || t->get_repr());); for (term *t : m_terms) { - if (t->get_repr()) continue; - if (t->deg() == 0) todo.push_back(t); + if (t->get_repr()) + continue; + if (t->deg() == 0) + todo.push_back(t); } pick_repr_percolate_up(todo); DEBUG_CODE(for (term *t : m_terms) SASSERT(t->get_repr());); @@ -903,12 +900,12 @@ void term_graph::refine_repr_class(term *t) { SASSERT(is_app(p->get_expr())); return m_is_var.contains(to_app(p->get_expr())->get_decl()); }; - if (!is_var(t)) return; + if (!is_var(t)) + return; term *r = t; - for (term *it = &t->get_next(); it != t; it = &it->get_next()) { - if (makes_cycle(it)) continue; - if (is_var(r) && !is_var(it)) r = it; - } + for (term *it = &t->get_next(); it != t; it = &it->get_next()) + if (!makes_cycle(it) && is_var(r) && !is_var(it)) + r = it; r->mk_repr(); } @@ -918,13 +915,16 @@ void term_graph::refine_repr_class(term *t) { bool term_graph::makes_cycle(term *t) { term &r = t->get_root(); ptr_vector todo; - for (auto *it : term::children(t)) { todo.push_back(it->get_repr()); } + for (auto *it : term::children(t)) + todo.push_back(it->get_repr()); term *it; while (!todo.empty()) { it = todo.back(); todo.pop_back(); - if (it->get_root().get_id() == r.get_id()) return true; - for (auto *ch : term::children(it)) { todo.push_back(ch->get_repr()); } + if (it->get_root().get_id() == r.get_id()) + return true; + for (auto *ch : term::children(it)) + todo.push_back(ch->get_repr()); } return false; } @@ -932,9 +932,9 @@ bool term_graph::makes_cycle(term *t) { void term_graph::refine_repr() { // invalidates cache m_term2app.reset(); - for (term *t : m_terms) { - if (!t->get_repr()->is_cgr()) refine_repr_class(t->get_repr()); - } + for (term *t : m_terms) + if (!t->get_repr()->is_cgr()) + refine_repr_class(t->get_repr()); } // returns true if tg ==> e = v where v is a value @@ -942,9 +942,11 @@ bool term_graph::has_val_in_class(expr *e) { term *r = get_term(e); if (!r) return false; auto is_val = [&](term *t) { return m.is_value(t->get_expr()); }; - if (is_val(r)) return true; + if (is_val(r)) + return true; for (term *it = &r->get_next(); it != r; it = &it->get_next()) - if (is_val(it)) return true; + if (is_val(it)) + return true; return false; } @@ -952,11 +954,14 @@ bool term_graph::has_val_in_class(expr *e) { // else return nullptr app *term_graph::get_const_in_class(expr *e) { term *r = get_term(e); - if (!r) return nullptr; + if (!r) + return nullptr; auto is_const = [](term *t) { return is_uninterp_const(t->get_expr()); }; - if (is_const(r)) return ::to_app(r->get_expr()); + if (is_const(r)) + return ::to_app(r->get_expr()); for (term *it = &r->get_next(); it != r; it = &it->get_next()) - if (is_const(it)) return ::to_app(it->get_expr()); + if (is_const(it)) + return ::to_app(it->get_expr()); return nullptr; } From d33d8ac07ac20dc70393b3dfbbc4368a8a8600fe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 10:55:03 -0700 Subject: [PATCH 056/428] revert setting arm on linux Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 595673647..74d585dae 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2664,7 +2664,7 @@ def mk_config(): LDFLAGS = '%s -static-libgcc -static-libstdc++' % LDFLAGS if sysname == 'Linux' and machine.startswith('armv7') or machine.startswith('armv8'): CXXFLAGS = '%s -fpic' % CXXFLAGS - if IS_ARCH_ARM64: + if IS_ARCH_ARM64 and IS_OSX: print("Setting arm64") CXXFLAGS = '%s -arch arm64' % CXXFLAGS LDFLAGS = '%s -arch arm64' % LDFLAGS From 09dd7688ce4339008c17ae32a7a5a7750d7fc9ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 11:19:41 -0700 Subject: [PATCH 057/428] fix build Signed-off-by: Nikolaj Bjorner --- src/qe/mbp/mbp_term_graph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 8c525d2f7..43fa514ef 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -825,7 +825,10 @@ bool term_graph::term_lt(term const &t1, term const &t2) { bool all_children_picked(term *t) { if (t->deg() == 0) return true; - return all_of(term::children(t), [](term* c) { return c->get_repr(); }); + for (term *c : term::children(t)) + if (!c->get_repr()) + return false; + return true; } // pick representatives for all terms in todo. Then, pick representatives for From ea95f8086f2de9d80ed2246a96943d0d3a846a55 Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Wed, 2 Aug 2023 11:24:32 -0700 Subject: [PATCH 058/428] try to instrument nightly with aarch compiler for arm64 Signed-off-by: NikolajBjorner --- scripts/mk_util.py | 4 ++-- scripts/nightly.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 74d585dae..ae1293d08 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -44,8 +44,8 @@ INSTALL_LIB_DIR=getenv("Z3_INSTALL_LIB_DIR", "lib") INSTALL_INCLUDE_DIR=getenv("Z3_INSTALL_INCLUDE_DIR", "include") INSTALL_PKGCONFIG_DIR=getenv("Z3_INSTALL_PKGCONFIG_DIR", os.path.join(INSTALL_LIB_DIR, 'pkgconfig')) -CXX_COMPILERS=['g++', 'clang++'] -C_COMPILERS=['gcc', 'clang'] +CXX_COMPILERS=['g++', 'clang++', 'aarch64-linux-gnu-g++'] +C_COMPILERS=['gcc', 'clang', 'aarch64-linux-gnu-gcc'] JAVAC=None JAR=None PYTHON_PACKAGE_DIR=sysconfig.get_path('purelib') diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index d3e0737b5..cb651d232 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -68,7 +68,7 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: CXX=aarch64-linux-gnu-g++ C=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. From 260cb337de066f2f0c909228fc84c6df48b442c6 Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Wed, 2 Aug 2023 11:25:16 -0700 Subject: [PATCH 059/428] try to instrument nightly with aarch compiler for arm64 Signed-off-by: NikolajBjorner --- scripts/mk_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index ae1293d08..74d585dae 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -44,8 +44,8 @@ INSTALL_LIB_DIR=getenv("Z3_INSTALL_LIB_DIR", "lib") INSTALL_INCLUDE_DIR=getenv("Z3_INSTALL_INCLUDE_DIR", "include") INSTALL_PKGCONFIG_DIR=getenv("Z3_INSTALL_PKGCONFIG_DIR", os.path.join(INSTALL_LIB_DIR, 'pkgconfig')) -CXX_COMPILERS=['g++', 'clang++', 'aarch64-linux-gnu-g++'] -C_COMPILERS=['gcc', 'clang', 'aarch64-linux-gnu-gcc'] +CXX_COMPILERS=['g++', 'clang++'] +C_COMPILERS=['gcc', 'clang'] JAVAC=None JAR=None PYTHON_PACKAGE_DIR=sysconfig.get_path('purelib') From 5b287bff09a08adcc698b245abca761ac37cf741 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 16:59:56 -0700 Subject: [PATCH 060/428] nits Signed-off-by: Nikolaj Bjorner --- src/qe/mbp/mbp_term_graph.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 43fa514ef..7af5e88a4 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -317,7 +317,7 @@ class term { term &get_root() const { return *m_root; } bool is_root() const { return m_root == this; } void set_root(term &r) { m_root = &r; } - term *get_repr() { return m_repr; } + term *get_repr() const { return m_repr; } bool is_repr() const { return m_repr == this; } void set_repr(term *t) { SASSERT(get_root().get_id() == t->get_root().get_id()); @@ -696,15 +696,15 @@ void term_graph::merge(term &t1, term &t2) { } expr *term_graph::mk_app_core(expr *e) { - if (is_app(e)) { - expr_ref_buffer kids(m); - app *a = ::to_app(e); - for (expr *arg : *a) { kids.push_back(mk_app(arg)); } - app *res = m.mk_app(a->get_decl(), a->get_num_args(), kids.data()); - m_pinned.push_back(res); - return res; - } - else { return e; } + if (!is_app(e)) + return e; + expr_ref_buffer kids(m); + app *a = ::to_app(e); + for (expr *arg : *a) + kids.push_back(mk_app(arg)); + app *res = m.mk_app(a->get_decl(), a->get_num_args(), kids.data()); + m_pinned.push_back(res); + return res; } expr_ref term_graph::mk_app(term &r) { From 8d48ff44c4b0cfa9cd14164c1e9b680627b668b2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 17:10:23 -0700 Subject: [PATCH 061/428] update nightly script Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index cb651d232..7345542bc 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -68,7 +68,10 @@ stages: pool: vmImage: "ubuntu-latest" steps: - - script: CXX=aarch64-linux-gnu-g++ C=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: sudo apt install gcc-arm-none-eabi -y + - script: sudo apt install gcc-arm-linux-gnueabihf -y + - script: sudo apt install gcc-aarch64-linux-gnu -y + - script: CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. From 0478ab14989d64ad651e015cacf2854533e33681 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Aug 2023 17:16:32 -0700 Subject: [PATCH 062/428] update nightly script Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 7345542bc..fe5bddd7c 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -68,6 +68,7 @@ stages: pool: vmImage: "ubuntu-latest" steps: + - script: sudo apt update - script: sudo apt install gcc-arm-none-eabi -y - script: sudo apt install gcc-arm-linux-gnueabihf -y - script: sudo apt install gcc-aarch64-linux-gnu -y From 7b36563196b3ead5f824f23676d14001158f5884 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Aug 2023 09:48:07 -0700 Subject: [PATCH 063/428] create insert-fresh and insert for indexed_uint_set to make use cases with non-fresh inserts easier Signed-off-by: Nikolaj Bjorner --- src/sat/sat_ddfw.cpp | 4 ++-- src/sat/sat_ddfw.h | 2 +- src/sat/sat_lookahead.cpp | 4 ++-- src/sat/sat_prob.cpp | 4 ++-- src/util/uint_set.h | 7 ++++++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index f15c241f2..bd7b0d26c 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -317,7 +317,7 @@ namespace sat { // cls becomes false: flip any variable in clause to receive reward w switch (ci.m_num_trues) { case 0: { - m_unsat.insert(cls_idx); + m_unsat.insert_fresh(cls_idx); clause const& c = get_clause(cls_idx); for (literal l : c) { inc_reward(l, w); @@ -406,7 +406,7 @@ namespace sat { inc_reward(lit, ci.m_weight); inc_make(lit); } - m_unsat.insert(i); + m_unsat.insert_fresh(i); break; case 1: dec_reward(to_literal(ci.m_trues), ci.m_weight); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index 988365285..ff86e9b8c 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -178,7 +178,7 @@ namespace sat { inline void inc_make(literal lit) { bool_var v = lit.var(); - if (make_count(v)++ == 0) m_unsat_vars.insert(v); + if (make_count(v)++ == 0) m_unsat_vars.insert_fresh(v); } inline void dec_make(literal lit) { diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index 2fa7ed040..2f3fc91b2 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -990,7 +990,7 @@ namespace sat { m_rating.push_back(0); m_vprefix.push_back(prefix()); if (!m_s.was_eliminated(v)) - m_freevars.insert(v); + m_freevars.insert_fresh(v); } void lookahead::init(bool learned) { @@ -1096,7 +1096,7 @@ namespace sat { literal l = m_trail[i]; set_undef(l); TRACE("sat", tout << "inserting free var v" << l.var() << "\n";); - m_freevars.insert(l.var()); + m_freevars.insert_fresh(l.var()); } m_num_tc1 = m_num_tc1_lim.back(); diff --git a/src/sat/sat_prob.cpp b/src/sat/sat_prob.cpp index 46a098ba8..824ec4fc6 100644 --- a/src/sat/sat_prob.cpp +++ b/src/sat/sat_prob.cpp @@ -80,7 +80,7 @@ namespace sat { ci.del(lit); switch (ci.m_num_trues) { case 0: - m_unsat.insert(cls_idx); + m_unsat.insert_fresh(cls_idx); dec_break(lit); break; case 1: @@ -184,7 +184,7 @@ namespace sat { } switch (ci.m_num_trues) { case 0: - m_unsat.insert(i); + m_unsat.insert_fresh(i); break; case 1: inc_break(to_literal(ci.m_trues)); diff --git a/src/util/uint_set.h b/src/util/uint_set.h index 6e64cc7ae..b04c67a07 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -318,7 +318,7 @@ public: m_size(0) {} - void insert(unsigned x) { + void insert_fresh(unsigned x) { SASSERT(!contains(x)); m_index.reserve(x + 1, UINT_MAX); m_elems.reserve(m_size + 1); @@ -327,6 +327,11 @@ public: m_size++; SASSERT(contains(x)); } + + void insert(unsigned x) { + if (!contains(x)) + insert_fresh(x); + } void remove(unsigned x) { SASSERT(contains(x)); From 4bfe9a895acc0a9781124f9eedeb956ec39a6dbe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Aug 2023 10:04:23 -0700 Subject: [PATCH 064/428] update nightly to pull arm Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index fe5bddd7c..f9944c35f 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -72,7 +72,8 @@ stages: - script: sudo apt install gcc-arm-none-eabi -y - script: sudo apt install gcc-arm-linux-gnueabihf -y - script: sudo apt install gcc-aarch64-linux-gnu -y - - script: CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: sudo apt install g++-aarch64-linux-gnu -y + - script: CXX=g++-aarch64-linux-gnu CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. From 3df6cd2c5f8f9aa8fdb29bc6ab97d6cceccd9948 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Aug 2023 10:26:12 -0700 Subject: [PATCH 065/428] update nightly to pull arm Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index f9944c35f..e4b22164f 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -73,7 +73,7 @@ stages: - script: sudo apt install gcc-arm-linux-gnueabihf -y - script: sudo apt install gcc-aarch64-linux-gnu -y - script: sudo apt install g++-aarch64-linux-gnu -y - - script: CXX=g++-aarch64-linux-gnu CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 + - script: CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. From 23da36126a3a2f201b29d0aa1d1ea7b35987df87 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Aug 2023 11:01:49 -0700 Subject: [PATCH 066/428] update nightly to pull arm Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index e4b22164f..154902d4c 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -74,8 +74,6 @@ stages: - script: sudo apt install gcc-aarch64-linux-gnu -y - script: sudo apt install g++-aarch64-linux-gnu -y - script: CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk --arch=arm64 - - script: git clone https://github.com/z3prover/z3test z3test - - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: From 4637339091df56eb6d61561b8a6fb847e18724e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 3 Aug 2023 15:51:29 -0700 Subject: [PATCH 067/428] update model validate to include arithmetic Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/proto_model.h | 3 +++ src/smt/smt_context.cpp | 10 +++------- src/smt/smt_theory.h | 3 ++- src/smt/theory_lra.cpp | 28 ++++++++++++++++++++++++++++ src/smt/theory_lra.h | 1 + src/smt/theory_seq.cpp | 2 +- src/smt/theory_seq.h | 2 +- 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/smt/proto_model/proto_model.h b/src/smt/proto_model/proto_model.h index f069e0a60..d12f56dee 100644 --- a/src/smt/proto_model/proto_model.h +++ b/src/smt/proto_model/proto_model.h @@ -63,6 +63,9 @@ public: void register_factory(value_factory * f) { m_factories.register_plugin(f); } bool eval(expr * e, expr_ref & result, bool model_completion = false); + bool are_equal(expr* a, expr* b) { return m_eval.are_equal(a, b); } + bool is_false(expr* e) { return m_eval.are_equal(e, m.mk_false()); } + expr_ref operator()(expr* e) { expr_ref result(e, m); eval(e, result, false); return result; } value_factory * get_factory(family_id fid); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 27494cb58..7895287ef 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3470,13 +3470,9 @@ namespace smt { } if (r == l_true && gparams::get_value("model_validate") == "true") { recfun::util u(m); - model_ref mdl; - get_model(mdl); - if (u.get_rec_funs().empty()) { - if (mdl.get()) { - for (theory* t : m_theory_set) { - t->validate_model(*mdl); - } + if (u.get_rec_funs().empty() && m_proto_model) { + for (theory* t : m_theory_set) { + t->validate_model(*m_proto_model); } } #if 0 diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index c715f215f..d0e73cc92 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -368,7 +368,8 @@ namespace smt { // // ---------------------------------------------------- - virtual void validate_model(model& mdl) {} + virtual void validate_model(proto_model& mdl) {} + // ---------------------------------------------------- // diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6ffefbdc6..99faa437d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3882,6 +3882,30 @@ public: } + void validate_model(proto_model& mdl) { + + rational r1, r2; + expr_ref res(m); + if (!m_model_is_initialized) + return; + for (unsigned v = 0; v < th.get_num_vars(); ++v) { + if (!is_registered_var(v)) + continue; + enode* n = get_enode(v); + if (!n) + continue; + if (!th.is_relevant_and_shared(n)) + continue; + rational r1 = get_value(v); + if (!mdl.eval(n->get_expr(), res, false)) + continue; + if (!a.is_numeral(res, r2)) + continue; + if (r1 != r2) + IF_VERBOSE(1, verbose_stream() << enode_pp(n, ctx()) << " evaluates to " << r2 << " but arith solver has " << r1 << "\n"); + } + } + }; theory_lra::theory_lra(context& ctx): @@ -4009,6 +4033,10 @@ void theory_lra::setup() { m_imp->setup(); } +void theory_lra::validate_model(proto_model& mdl) { + m_imp->validate_model(mdl); +} + } template class lp::lp_bound_propagator; template void lp::lar_solver::propagate_bounds_for_touched_rows(lp::lp_bound_propagator&); diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index b7d271079..4c2351c85 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -82,6 +82,7 @@ namespace smt { void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; + void validate_model(proto_model& mdl) override; bool get_value(enode* n, expr_ref& r) override; bool include_func_interp(func_decl* f) override; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 7b0955518..02c0c456b 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2160,7 +2160,7 @@ app* theory_seq::mk_value(app* e) { } -void theory_seq::validate_model(model& mdl) { +void theory_seq::validate_model(proto_model& mdl) { return; for (auto const& eq : m_eqs) { sort* srt = eq.ls[0]->get_sort(); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 49213dbd4..6ed4fc41f 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -407,7 +407,7 @@ namespace smt { void init_model(model_generator & mg) override; void finalize_model(model_generator & mg) override; void init_search_eh() override; - void validate_model(model& mdl) override; + void validate_model(proto_model& mdl) override; bool is_beta_redex(enode* p, enode* n) const override; void init_model(expr_ref_vector const& es); From f58b703ac5215092f99bb50fab2dd0c4aa03e48f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:01:27 -1000 Subject: [PATCH 068/428] u_set replaced by indexed_uint_set (#6841) * replace u_set by indexed_uint_set * replace u_set by indexed_uint_set * create insert-fresh and insert for indexed_uint_set to make use cases with non-fresh inserts easier Signed-off-by: Nikolaj Bjorner * update nightly to pull arm Signed-off-by: Nikolaj Bjorner * update nightly to pull arm Signed-off-by: Nikolaj Bjorner * fixing the build of lp_tst * update nightly to pull arm Signed-off-by: Nikolaj Bjorner * replace u_set by indexed_uint_set * replace u_set by indexed_uint_set * fixing the build of lp_tst * remove unnecessery call to contains() before insert to indexed_uint_set * formatting, no check for contains() in indexed_uint_set, always init m_touched_rows to nullptr --------- Signed-off-by: Nikolaj Bjorner Co-authored-by: Nikolaj Bjorner --- src/math/lp/horner.cpp | 4 +- src/math/lp/horner.h | 2 +- src/math/lp/int_solver.h | 2 +- src/math/lp/lar_solver.cpp | 22 ++---- src/math/lp/lar_solver.h | 15 ++-- src/math/lp/lp_core_solver_base.h | 4 +- src/math/lp/lp_primal_core_solver.h | 7 +- src/math/lp/monomial_bounds.h | 2 +- src/math/lp/nla_core.cpp | 12 ++- src/math/lp/nla_core.h | 17 ++--- src/math/lp/nla_grobner.cpp | 5 +- src/math/lp/nla_grobner.h | 4 +- src/math/lp/nra_solver.cpp | 10 +-- src/math/lp/random_updater.h | 4 +- src/math/lp/random_updater_def.h | 11 ++- src/math/lp/u_set.h | 114 ---------------------------- src/sat/smt/arith_solver.cpp | 3 +- src/sat/smt/arith_solver.h | 2 +- src/smt/theory_lra.cpp | 5 +- src/test/lp/lp.cpp | 12 ++- src/util/uint_set.h | 4 + 21 files changed, 69 insertions(+), 192 deletions(-) delete mode 100644 src/math/lp/u_set.h diff --git a/src/math/lp/horner.cpp b/src/math/lp/horner.cpp index 4d4ac4975..51e2f533e 100644 --- a/src/math/lp/horner.cpp +++ b/src/math/lp/horner.cpp @@ -76,7 +76,7 @@ bool horner::lemmas_on_expr(cross_nested& cn, nex_sum* e) { template bool horner::lemmas_on_row(const T& row) { SASSERT (row_is_interesting(row)); - c().clear_and_resize_active_var_set(); + c().clear_active_var_set(); u_dependency* dep = nullptr; create_sum_from_row(row, m_nex_creator, m_row_sum, dep); c().set_active_vars_weights(m_nex_creator); // without this call the comparisons will be incorrect @@ -110,7 +110,7 @@ bool horner::horner_lemmas() { for (auto & s : matrix.m_columns[j]) rows_to_check.insert(s.var()); } - c().clear_and_resize_active_var_set(); + c().clear_active_var_set(); svector rows; for (unsigned i : rows_to_check) { if (row_is_interesting(matrix.m_rows[i])) diff --git a/src/math/lp/horner.h b/src/math/lp/horner.h index 2b6fc3cd8..9d530acee 100644 --- a/src/math/lp/horner.h +++ b/src/math/lp/horner.h @@ -23,7 +23,7 @@ #include "math/lp/nla_intervals.h" #include "math/lp/nex.h" #include "math/lp/cross_nested.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" namespace nla { class core; diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 4c9f43ffe..b9d09081a 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -20,7 +20,7 @@ Revision History: #pragma once #include "math/lp/lp_settings.h" #include "math/lp/static_matrix.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" #include "math/lp/lar_term.h" #include "math/lp/lar_constraints.h" #include "math/lp/hnf_cutter.h" diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 4aecbf2cc..d5d2825bf 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -234,13 +234,13 @@ namespace lp { m_usage_in_terms.push(); } - void lar_solver::clean_popped_elements(unsigned n, u_set& set) { + void lar_solver::clean_popped_elements(unsigned n, indexed_uint_set& set) { vector to_remove; for (unsigned j : set) if (j >= n) to_remove.push_back(j); for (unsigned j : to_remove) - set.erase(j); + set.remove(j); } void lar_solver::clean_popped_elements_for_heap(unsigned n, lpvar_heap& heap) { @@ -361,7 +361,6 @@ namespace lp { void lar_solver::prepare_costs_for_r_solver(const lar_term& term) { TRACE("lar_solver", print_term(term, tout << "prepare: ") << "\n";); - m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); move_non_basic_columns_to_bounds(false); auto& rslv = m_mpq_lar_core_solver.m_r_solver; lp_assert(costs_are_zeros_for_r_solver()); @@ -619,7 +618,7 @@ namespace lp { } void lar_solver::add_touched_row(unsigned rid) { - if (m_settings.bound_propagation()) + if (m_settings.bound_propagation()) m_touched_rows.insert(rid); } @@ -710,9 +709,6 @@ namespace lp { void lar_solver::solve_with_core_solver() { m_mpq_lar_core_solver.prefix_r(); - if (costs_are_used()) { - m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); - } update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); @@ -1431,7 +1427,6 @@ namespace lp { void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { register_new_ext_var_index(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); - increase_by_one_columns_with_changed_bounds(); add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } @@ -1585,11 +1580,7 @@ namespace lp { void lar_solver::add_basic_var_to_core_fields() { m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); - increase_by_one_columns_with_changed_bounds(); - m_incorrect_columns.increase_size_by_one(); - m_touched_rows.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); - } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { @@ -1739,8 +1730,8 @@ namespace lp { 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;); } void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { - m_columns_with_changed_bounds.insert(j); - TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + m_columns_with_changed_bounds.insert(j); + TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); } void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, lconstraint_kind kind, @@ -2063,7 +2054,6 @@ namespace lp { void lar_solver::round_to_integer_solution() { - m_incorrect_columns.resize(column_count()); for (unsigned j = 0; j < column_count(); j++) { if (!column_is_int(j)) continue; if (column_corresponds_to_term(j)) continue; @@ -2085,7 +2075,7 @@ namespace lp { } if (!m_incorrect_columns.empty()) { fix_terms_with_rounded_columns(); - m_incorrect_columns.clear(); + m_incorrect_columns.reset(); } } diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index fc58d0a70..54abba579 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -85,14 +85,14 @@ class lar_solver : public column_namer { 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_touched_rows; + indexed_uint_set m_columns_with_changed_bounds; + indexed_uint_set m_touched_rows; unsigned_vector m_row_bounds_to_replay; - u_set m_basic_columns_with_changed_cost; + indexed_uint_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; + indexed_uint_set m_incorrect_columns; // copy of m_r_solver.inf_heap() unsigned_vector m_inf_index_copy; stacked_value m_term_count; @@ -132,8 +132,7 @@ class lar_solver : public column_namer { void add_basic_var_to_core_fields(); 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 clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } 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); @@ -175,7 +174,7 @@ class lar_solver : public column_namer { } static void clean_popped_elements_for_heap(unsigned n, lpvar_heap& set); - static void clean_popped_elements(unsigned n, u_set& set); + static void clean_popped_elements(unsigned n, indexed_uint_set& set); bool maximize_term_on_tableau(const lar_term& term, impq& term_max); bool costs_are_zeros_for_r_solver() const; @@ -349,7 +348,7 @@ class lar_solver : public column_namer { m_row_bounds_to_replay.push_back(i); } } - m_touched_rows.clear(); + m_touched_rows.reset(); } template diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 6db4776db..251199419 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -27,7 +27,7 @@ Revision History: #include "math/lp/static_matrix.h" #include "math/lp/permutation_matrix.h" #include "math/lp/column_namer.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" #include "util/heap.h" namespace lp { @@ -92,7 +92,7 @@ public: vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; // these rows are changed by adding to them a multiple of the pivot row - u_set* m_touched_rows; + indexed_uint_set* m_touched_rows = nullptr; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index d4130faae..c074da16a 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -21,7 +21,7 @@ Revision History: #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" #include "math/lp/static_matrix.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" #include "util/vector.h" #include #include @@ -46,7 +46,7 @@ namespace lp { vector m_costs_backup; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; - u_set m_left_basis_tableau; + indexed_uint_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; @@ -627,8 +627,7 @@ namespace lp { 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_tableau.reset(); m_left_basis_repeated = 0; } // stage1 constructor diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 236f29bc0..d82c4bebb 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -10,7 +10,7 @@ #include "math/lp/nla_common.h" #include "math/lp/nla_intervals.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" namespace nla { class core; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 3c1f9da57..86ddb05b7 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -828,14 +828,14 @@ void core::insert_to_refine(lpvar j) { void core::erase_from_to_refine(lpvar j) { TRACE("lar_solver", tout << "j=" << j << '\n';); - m_to_refine.erase(j); + if (m_to_refine.contains(j)) + m_to_refine.remove(j); } void core::init_to_refine() { TRACE("nla_solver_details", tout << "emons:" << pp_emons(*this, m_emons);); - m_to_refine.clear(); - m_to_refine.resize(m_lar_solver.number_of_vars()); + m_to_refine.reset(); unsigned r = random(), sz = m_emons.number_of_monics(); for (unsigned k = 0; k < sz; k++) { auto const & m = *(m_emons.begin() + (k + r)% sz); @@ -1407,8 +1407,12 @@ void core::patch_monomial(lpvar j) { } void core::patch_monomials_on_to_refine() { - auto to_refine = m_to_refine.index(); // the rest of the function might change m_to_refine, so have to copy + unsigned_vector to_refine; + for (unsigned j :m_to_refine) { + to_refine.push_back(j); + } + unsigned sz = to_refine.size(); unsigned start = random(); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 938bcbe83..51628b937 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -84,7 +84,7 @@ class core { reslimit& m_reslim; std::function m_relevant; vector * m_lemma_vec; - lp::u_set m_to_refine; + indexed_uint_set m_to_refine; tangents m_tangents; basics m_basics; order m_order; @@ -99,7 +99,7 @@ class core { grobner m_grobner; emonics m_emons; svector m_add_buffer; - mutable lp::u_set m_active_var_set; + mutable indexed_uint_set m_active_var_set; reslimit m_nra_lim; @@ -118,18 +118,15 @@ public: void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); - const lp::u_set& active_var_set () const { return m_active_var_set;} + const indexed_uint_set& active_var_set () const { return m_active_var_set;} bool active_var_set_contains(unsigned j) const { return m_active_var_set.contains(j); } - void insert_to_active_var_set(unsigned j) const { m_active_var_set.insert(j); } + void insert_to_active_var_set(unsigned j) const { + m_active_var_set.insert(j); + } - void clear_active_var_set() const { m_active_var_set.clear(); } + void clear_active_var_set() const { m_active_var_set.reset(); } - void clear_and_resize_active_var_set() const { - m_active_var_set.clear(); - m_active_var_set.resize(m_lar_solver.number_of_vars()); - } - unsigned get_var_weight(lpvar) const; reslimit& reslim() { return m_reslim; } diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 974c48d14..4c2f879cf 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -498,9 +498,8 @@ namespace nla { } void grobner::prepare_rows_and_active_vars() { - m_rows.clear(); - m_rows.resize(m_lar_solver.row_count()); - c().clear_and_resize_active_var_set(); + m_rows.reset(); + c().clear_active_var_set(); } diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index 902ad3a46..ed3aa77db 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -12,7 +12,7 @@ #include "math/lp/nla_intervals.h" #include "math/lp/nex.h" #include "math/lp/cross_nested.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" #include "math/grobner/pdd_solver.h" namespace nla { @@ -22,7 +22,7 @@ namespace nla { dd::pdd_manager m_pdd_manager; dd::solver m_solver; lp::lar_solver& m_lar_solver; - lp::u_set m_rows; + indexed_uint_set m_rows; lp::lp_settings& lp_settings(); diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 1f4e0b76a..377b318dd 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -9,7 +9,7 @@ #include "math/polynomial/polynomial.h" #include "math/polynomial/algebraic_numbers.h" #include "util/map.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" #include "math/lp/nla_core.h" @@ -23,7 +23,7 @@ struct solver::imp { reslimit& m_limit; params_ref m_params; u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables - lp::u_set m_term_set; + indexed_uint_set m_term_set; scoped_ptr m_nlsat; scoped_ptr m_zero; mutable variable_map_type m_variable_values; // current model @@ -55,7 +55,7 @@ struct solver::imp { m_zero = nullptr; m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); m_zero = alloc(scoped_anum, am()); - m_term_set.clear(); + m_term_set.reset(); m_lp2nl.reset(); vector core; @@ -182,7 +182,7 @@ struct solver::imp { m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); m_zero = alloc(scoped_anum, am()); m_lp2nl.reset(); - m_term_set.clear(); + m_term_set.reset(); for (auto const& eq : eqs) add_eq(eq); for (auto const& [v, w] : m_lp2nl) { @@ -283,8 +283,6 @@ struct solver::imp { r = m_nlsat->mk_var(is_int(v)); m_lp2nl.insert(v, r); if (!m_term_set.contains(v) && s.column_corresponds_to_term(v)) { - if (v >= m_term_set.data_size()) - m_term_set.resize(v + 1); m_term_set.insert(v); } } diff --git a/src/math/lp/random_updater.h b/src/math/lp/random_updater.h index d5cd4928c..f0f767750 100644 --- a/src/math/lp/random_updater.h +++ b/src/math/lp/random_updater.h @@ -25,14 +25,14 @@ Revision History: #include #include #include "math/lp/lp_settings.h" -#include "math/lp/u_set.h" +#include "util/uint_set.h" // see http://research.microsoft.com/projects/z3/smt07.pdf // The class searches for a feasible solution with as many different values of variables as it can find namespace lp { template struct numeric_pair; // forward definition class lar_solver; // forward definition class random_updater { - u_set m_var_set; + indexed_uint_set m_var_set; lar_solver & m_lar_solver; unsigned m_range; bool shift_var(unsigned j); diff --git a/src/math/lp/random_updater_def.h b/src/math/lp/random_updater_def.h index 7d167a4a0..861068106 100644 --- a/src/math/lp/random_updater_def.h +++ b/src/math/lp/random_updater_def.h @@ -32,7 +32,6 @@ random_updater::random_updater( const vector & column_indices) : m_lar_solver(lar_solver), m_range(100000) { - m_var_set.resize(m_lar_solver.number_of_vars()); for (unsigned j : column_indices) m_var_set.insert(j); TRACE("lar_solver_rand", tout << "size = " << m_var_set.size() << "\n";); @@ -45,7 +44,9 @@ bool random_updater::shift_var(unsigned j) { if (ret) { const auto & A = m_lar_solver.A_r(); for (const auto& c : A.m_columns[j]) { - m_var_set.erase(m_lar_solver.r_basis()[c.var()]); + unsigned k = m_lar_solver.r_basis()[c.var()]; + if (m_var_set.contains(k)) + m_var_set.remove(k); } } return ret; @@ -54,7 +55,11 @@ bool random_updater::shift_var(unsigned j) { void random_updater::update() { // VERIFY(m_lar_solver.check_feasible()); - auto columns = m_var_set.index(); // m_var_set is going to change during the loop + unsigned_vector columns; + // m_var_set is going to change during the loop, make a copy + for (unsigned j : m_var_set) { + columns.push_back(j); + } for (auto j : columns) { if (!m_var_set.contains(j)) { TRACE("lar_solver_rand", tout << "skipped " << j << "\n";); diff --git a/src/math/lp/u_set.h b/src/math/lp/u_set.h deleted file mode 100644 index ce59dccb7..000000000 --- a/src/math/lp/u_set.h +++ /dev/null @@ -1,114 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - -TBD use indexed_uint_set from src/util/uint_set.h, - ---*/ -#pragma once -#include "util/vector.h" -#include -namespace lp { -// serves at a set of non-negative integers smaller than the set size -class u_set { - svector m_data; - unsigned_vector m_index; - -public: - u_set(unsigned size): m_data(size, -1) {} - u_set() {} - u_set(u_set const& other): - m_data(other.m_data), - m_index(other.m_index) {} - - bool contains(unsigned j) const { - if (j >= m_data.size()) - return false; - return m_data[j] >= 0; - } - void insert(unsigned j) { - lp_assert(j < m_data.size()); - if (contains(j)) return; - m_data[j] = m_index.size(); - m_index.push_back(j); - } - void erase(unsigned j) { - if (!contains(j)) return; - unsigned pos_j = m_data[j]; - unsigned last_pos = m_index.size() - 1; - int last_j = m_index[last_pos]; - if (last_pos != pos_j) { - // move last to j spot - m_data[last_j] = pos_j; - m_index[pos_j] = last_j; - } - m_index.pop_back(); - m_data[j] = -1; - } - - int operator[](unsigned j) const { return m_index[j]; } - - void resize(unsigned size) { - if (size < data_size()) { - bool copy = false; - unsigned i = 0; - for (unsigned j : m_index) { - if (j < size) { - if (copy) { - m_data[j] = i; - m_index[i] = j; - } - i++; - } else { - copy = true; - } - } - m_index.shrink(i); - } - m_data.resize(size, -1); - } - - void increase_size_by_one() { - resize(m_data.size() + 1); - } - - unsigned data_size() const { return m_data.size(); } - unsigned size() const { return m_index.size();} - bool empty() const { return size() == 0; } - void clear() { - for (unsigned j : m_index) - m_data[j] = -1; - m_index.resize(0); - } - - std::ostream& display(std::ostream& out) const { - for (unsigned j : m_index) { - out << j << " "; - } - out << std::endl; - return out; - } - const unsigned * begin() const { return m_index.begin(); } - const unsigned * end() const { return m_index.end(); } - const unsigned_vector& index() { return m_index; } -}; - - -} - -inline std::ostream& operator<<(std::ostream& out, lp::u_set const& s) { - return s.display(out); -} diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 5784962a5..0e97c3503 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -847,8 +847,7 @@ namespace arith { if (m_nla) return; TRACE("arith", tout << s().scope_lvl() << "\n"; tout.flush();); - m_tmp_var_set.clear(); - m_tmp_var_set.resize(get_num_vars()); + m_tmp_var_set.reset(); m_model_eqs.reset(); svector vars; theory_var sz = static_cast(get_num_vars()); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index e5ba06ae6..20252ede9 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -225,7 +225,7 @@ namespace arith { svector > m_assume_eq_candidates; unsigned m_assume_eq_head = 0; - lp::u_set m_tmp_var_set; + indexed_uint_set m_tmp_var_set; unsigned m_num_conflicts = 0; lp_api::stats m_stats; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 99faa437d..79b6f79c1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -168,7 +168,7 @@ class theory_lra::imp { svector > m_assume_eq_candidates; unsigned m_assume_eq_head; - lp::u_set m_tmp_var_set; + indexed_uint_set m_tmp_var_set; unsigned m_num_conflicts; @@ -1493,8 +1493,7 @@ public: void random_update() { if (m_nla && m_nla->need_check()) return; - m_tmp_var_set.clear(); - m_tmp_var_set.resize(th.get_num_vars()); + m_tmp_var_set.reset(); m_model_eqs.reset(); svector vars; theory_var sz = static_cast(th.get_num_vars()); diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index a61b9339e..088a7dac0 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -50,7 +50,7 @@ #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 "util/uint_set.h" #include "test/lp/argument_parser.h" #include "test/lp/gomory_test.h" #include "test/lp/smt_reader.h" @@ -1072,20 +1072,18 @@ void test_bound_propagation() { } void test_int_set() { - u_set s(4); - s.insert(2); + indexed_uint_set s; s.insert(1); s.insert(2); lp_assert(s.contains(2)); lp_assert(s.size() == 2); - s.erase(2); - lp_assert(s.size() == 1); - s.erase(2); + s.remove(2); lp_assert(s.size() == 1); s.insert(3); s.insert(2); - s.clear(); + s.reset(); lp_assert(s.size() == 0); + std::cout << "done test_int_set\n"; } void test_rationals_no_numeric_pairs() { diff --git a/src/util/uint_set.h b/src/util/uint_set.h index b04c67a07..2106d7af8 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -350,6 +350,10 @@ public: SASSERT(index < m_size); return m_elems[index]; } + unsigned operator[](unsigned index) const { + SASSERT(index < m_size); + return m_elems[index]; + } bool contains(unsigned x) const { return x < m_index.size() && m_index[x] < m_size && m_elems[m_index[x]] == x; } void reset() { m_size = 0; } From b0055df4ab908ed32093a11cdd50175fa9c4a87f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Aug 2023 10:48:44 -0700 Subject: [PATCH 069/428] revert arithmetic final check to original order Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 98 ++++++++++++------------------------------ 1 file changed, 28 insertions(+), 70 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 79b6f79c1..4a090a798 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1652,83 +1652,41 @@ public: if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } - bool giveup = false; final_check_status st = FC_DONE; - m_final_check_idx = 0; // remove to experiment. - unsigned old_idx = m_final_check_idx; switch (is_sat) { case l_true: TRACE("arith", display(tout)); -#if 0 - m_dist.reset(); - m_dist.push(0, 1); - m_dist.push(1, 1); - m_dist.push(2, 1); - - for (auto idx : m_dist) { - if (!m.inc()) - return FC_GIVEUP; - - switch (idx) { - case 0: - if (assume_eqs()) - st = FC_CONTINUE; - break; - case 1: - st = check_nla(); - break; - case 2: - st = check_lia(); - break; - default: - UNREACHABLE(); - break; - } - switch (st) { - case FC_DONE: - break; - case FC_CONTINUE: - return st; - case FC_GIVEUP: - giveup = true; - break; - } - } - -#else - do { - if (!m.inc()) - return FC_GIVEUP; - - switch (m_final_check_idx) { - case 0: - if (assume_eqs()) - st = FC_CONTINUE; - break; - case 1: - st = check_lia(); - break; - case 2: - st = check_nla(); - break; - } - m_final_check_idx = (m_final_check_idx + 1) % 3; - switch (st) { - case FC_DONE: - break; - case FC_CONTINUE: - return st; - case FC_GIVEUP: - giveup = true; - break; - } + switch (check_lia()) { + case FC_DONE: + break; + case FC_CONTINUE: + return FC_CONTINUE; + case FC_GIVEUP: + TRACE("arith", tout << "check-lia giveup\n";); + if (ctx().get_fparams().m_arith_ignore_int) + st = FC_CONTINUE; + break; } - while (old_idx != m_final_check_idx); -#endif + + switch (check_nla()) { + case FC_DONE: + break; + case FC_CONTINUE: + return FC_CONTINUE; + case FC_GIVEUP: + TRACE("arith", tout << "check-nra giveup\n";); + verbose_stream() << "giveup nla\n"; + st = FC_GIVEUP; + break; + } + + if (assume_eqs()) { + ++m_stats.m_assume_eqs; + return FC_CONTINUE; + } + - if (giveup) - return FC_GIVEUP; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; From 84520d53eaa6a9463dca864d4fb0b3f107347727 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Aug 2023 11:33:39 -0700 Subject: [PATCH 070/428] remove out Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 4a090a798..afedc460a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1676,7 +1676,6 @@ public: return FC_CONTINUE; case FC_GIVEUP: TRACE("arith", tout << "check-nra giveup\n";); - verbose_stream() << "giveup nla\n"; st = FC_GIVEUP; break; } From dd0b0b47b827cffd506c602099574676d1f5d2a5 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Fri, 4 Aug 2023 15:18:16 -0700 Subject: [PATCH 071/428] fix #5925 (#6846) --- src/qe/mbp/mbp_term_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 7af5e88a4..83a1f3c58 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -770,7 +770,7 @@ void term_graph::mk_qe_lite_equalities(term &t, expr_ref_vector &out, it = &it->get_next()) { tout << *it << "\n"; };); DEBUG_CODE( for (term *it = &t.get_next(); it != &t; it = &it->get_next()) - SASSERT(!it->is_cgr() || + SASSERT(!it->is_cgr() || it->is_eq_or_neq() || contains_nc(mk_app_core(it->get_expr())));); return; } From 125787c45895cc52c3901498c0adef62a92bffbc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 7 Aug 2023 11:22:34 -0700 Subject: [PATCH 072/428] remove dead code --- src/math/lp/int_solver.cpp | 167 ------------------------------------- src/math/lp/int_solver.h | 5 +- src/math/lp/lar_solver.cpp | 1 - src/smt/theory_lra.cpp | 3 - 4 files changed, 1 insertion(+), 175 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 34666e040..b4d703218 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -176,174 +176,7 @@ namespace lp { return; } - 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(); - 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; - } - -void int_solver::patcher::patch_nbasic_column(unsigned j) { - impq & val = lrac.m_r_x[j]; - bool inf_l, inf_u; - impq l, u; - mpq m; - bool has_free = lia.get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m); - 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; - tout << ", "; - 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) { - TRACE("patch_int", tout << "patching with l: " << l << '\n';); - lra.set_value_for_nbasic_column(j, l); - } - else { - //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) { - u = impq(m_is_one ? floor(u) : m * floor(u / m)); - lra.set_value_for_nbasic_column(j, u); - TRACE("patch_int", tout << "patching with u: " << u << '\n';); - } - else { - lra.set_value_for_nbasic_column(j, impq(0)); - TRACE("patch_int", tout << "patching with 0\n";); - } - ++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) : lra(lar_slv), diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index b9d09081a..56237a5b8 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -52,15 +52,13 @@ class int_solver { patcher(int_solver& lia); 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(); + void remove_fixed_vars_from_base(); }; lar_solver& lra; @@ -134,7 +132,6 @@ public: bool all_columns_are_bounded() const; void find_feasible_solution(); lia_move hnf_cut(); - void patch_nbasic_column(unsigned j) { m_patcher.patch_nbasic_column(j); } int select_int_infeasible_var(); diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index d5d2825bf..8a4fc3707 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -534,7 +534,6 @@ namespace lp { return lp_status::FEASIBLE; // it should not happen } } - m_int_solver->patch_nbasic_column(j); if (!column_value_is_integer(j)) { term_max = prev_value; m_mpq_lar_core_solver.m_r_x = backup; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index afedc460a..87de2d9e2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1638,9 +1638,6 @@ public: return FC_DONE; return FC_GIVEUP; } - - unsigned m_final_check_idx = 0; - distribution m_dist { 0 }; final_check_status final_check_eh() { if (propagate_core()) From 0c98c755ba20fd8635abd733df79e200a9d7d82a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 3 Aug 2023 17:24:37 -1000 Subject: [PATCH 073/428] new equality propagation scheme, etc --- src/math/lp/lar_solver.cpp | 66 ++- src/math/lp/lar_solver.h | 31 +- src/math/lp/lp_bound_propagator.h | 678 +++++++------------------- src/math/lp/lp_core_solver_base.cpp | 2 +- src/math/lp/lp_core_solver_base.h | 2 +- src/math/lp/lp_core_solver_base_def.h | 32 +- 6 files changed, 269 insertions(+), 542 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 8a4fc3707..bf69ceb2b 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -477,9 +477,17 @@ namespace lp { } return false; } - - bool lar_solver::remove_from_basis(unsigned j) { - return m_mpq_lar_core_solver.m_r_solver.remove_from_basis(j); +// returns true iff the row of j has a non-fixed column different from j + bool lar_solver::remove_from_basis(unsigned j) { + lp_assert(is_base(j)); + unsigned i = row_of_basic_column(j); + for (const auto & c : A_r().m_rows[i]) { + if (j != c.var() && !is_fixed(c.var())) { + return m_mpq_lar_core_solver.m_r_solver.remove_from_basis_core(c.var(), j); + } + + } + return false; } lar_term lar_solver::get_term_to_maximize(unsigned j_or_term) const { @@ -621,6 +629,54 @@ namespace lp { m_touched_rows.insert(rid); } + void lar_solver::remove_fixed_vars_from_base() { + // this will allow to disable and restore the tracking of the touched rows + flet f(m_mpq_lar_core_solver.m_r_solver.m_touched_rows, nullptr); + unsigned num = A_r().column_count(); + unsigned_vector to_remove; + for (unsigned j : m_fixed_base_var_set) { + if (j >= num || !is_base(j) || !is_fixed(j)) { + to_remove.push_back(j); + continue; + } + + lp_assert(is_base(j) && is_fixed(j)); + auto const& r = basic2row(j); + for (auto const& c : r) { + unsigned j_entering = c.var(); + if (!is_fixed(j_entering)) { + pivot(j_entering, j); + to_remove.push_back(j); + lp_assert(is_base(j_entering)); + break; + } + } + } + for (unsigned j : to_remove) { + m_fixed_base_var_set.remove(j); + } + lp_assert(fixed_base_removed_correctly()); + } +#ifdef Z3DEBUG + bool lar_solver::fixed_base_removed_correctly() const { + for (unsigned i = 0; i < A_r().row_count(); i++) { + unsigned j = get_base_column_in_row(i); + if (column_is_fixed(j)) { + for (const auto & c : A_r().m_rows[i] ) { + if (!column_is_fixed(c.var())) { + TRACE("lar_solver", print_row(A_r().m_rows[i], tout) << "\n"; + for(const auto & c : A_r().m_rows[i]) { + print_column_info(c.var(), tout) << "\n"; + }); + return false; + } + } + } + } + return true; + } +#endif + bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } @@ -1726,6 +1782,10 @@ namespace lp { 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); + if (is_base(j) && column_is_fixed(j)) { + m_fixed_base_var_set.insert(j); + } + 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;); } void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 54abba579..a4a0e87b0 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -107,6 +107,8 @@ class lar_solver : public column_namer { map, default_eq> m_fixed_var_table_int; // maps values to non-integral fixed vars map, default_eq> m_fixed_var_table_real; + // the set of fixed variables which are also base variables + indexed_uint_set m_fixed_base_var_set; // end of fields ////////////////// methods //////////////////////////////// @@ -316,11 +318,14 @@ class lar_solver : public column_namer { void set_value_for_nbasic_column(unsigned j, const impq& new_val); + void remove_fixed_vars_from_base(); + inline unsigned get_base_column_in_row(unsigned row_index) const { return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index); } - - // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); } +#ifdef Z3DEBUG + bool fixed_base_removed_correctly() const; +#endif 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); @@ -328,26 +333,23 @@ class lar_solver : public column_namer { void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { - unsigned num_prop = 0; - for (unsigned i : m_touched_rows) { - num_prop += calculate_implied_bounds_for_row(i, bp); - if (settings().get_cancel_flag()) - return; - } - // these two loops should be run sequentially - // since the first loop might change column bounds - // and add fixed columns this way + remove_fixed_vars_from_base(); if (settings().propagate_eqs()) { bp.clear_for_eq(); for (unsigned i : m_touched_rows) { unsigned offset_eqs = stats().m_offset_eqs; - bp.cheap_eq_tree(i); + bp.cheap_eq_on_nbase(i); if (settings().get_cancel_flag()) return; if (stats().m_offset_eqs > offset_eqs) m_row_bounds_to_replay.push_back(i); } } + for (unsigned i : m_touched_rows) { + calculate_implied_bounds_for_row(i, bp); + if (settings().get_cancel_flag()) + return; + } m_touched_rows.reset(); } @@ -424,9 +426,10 @@ class lar_solver : public column_namer { bool try_to_patch(lpvar j, const mpq& val, const Blocker& is_blocked, const ChangeReport& change_report) { - if (is_base(j)) { + if (is_base(j)) { TRACE("nla_solver", get_int_solver()->display_row_info(tout, row_of_basic_column(j)) << "\n";); - remove_from_basis(j); + if (!remove_from_basis(j)) + return false; } impq ival(val); diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 6056444e8..e2c0d6c92 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -12,104 +12,7 @@ namespace lp { template class lp_bound_propagator { - class edge; // forward definition - // vertex represents a column - // 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 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) {} - 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; } - 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); - edge e = edge(this, child, row); - m_edges.push_back(e); - child->set_edge_from_parent(e); - child->m_level = m_level + 1; - } - const vector& edges() const { return m_edges; } - bool operator==(const vertex& o) const { - return m_column == o.m_column; - } - bool operator!=(const vertex& o) const { - return m_column != o.m_column; - } - }; - - class edge { - vertex* m_source; - vertex* m_target; - int m_row; - - 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; } - vertex* source() { return m_source; } - const vertex* target() const { return m_target; } - vertex* target() { return m_target; } - int row() const { return m_row; } - 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 { - 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 ? " -" : " +"); - 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; - // 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; - // 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; - // 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; - // x[m_root->column()] - m_pol[j].pol()*x[j] == const; - // to bind polarity and the vertex in the table - u_map m_pol; - // if m_pos.contains(j) then x[j] = x[m_root->column()] + o - uint_set m_pos; - + uint_set m_visited_rows; // 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; @@ -118,12 +21,14 @@ class lp_bound_propagator { vector m_ibounds; map, default_eq> m_val2fixed_row; - - bool is_fixed_row(unsigned r, unsigned& x) { + // works for rows of the form x + y + sum of fixed = 0 + map, default_eq> m_row2index_pos; + // works for rows of the form x - y + sum of fixed = 0 + map, default_eq> m_row2index_neg; + // returns true iff there is only one non-fixed column in the row + bool only_one_nfixed(unsigned r, unsigned& x) { x = UINT_MAX; - const auto& row = lp().get_row(r); - for (unsigned k = 0; k < row.size(); k++) { - const auto& c = row[k]; + for (const auto& c: lp().get_row(r)) { if (column_is_fixed(c.var())) continue; if (x != UINT_MAX) @@ -134,22 +39,27 @@ class lp_bound_propagator { } void try_add_equation_with_internal_fixed_tables(unsigned r1) { - SASSERT(m_fixed_vertex); unsigned v1, v2; - if (!is_fixed_row(r1, v1)) + if (!only_one_nfixed(r1, v1)) return; unsigned r2 = UINT_MAX; if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) { m_val2fixed_row.insert(val(v1), r1); return; } - if (!is_fixed_row(r2, v2) || val(v1) != val(v2) || is_int(v1) != is_int(v2)) { + if (!only_one_nfixed(r2, v2) || val(v1) != val(v2) || is_int(v1) != is_int(v2)) { m_val2fixed_row.insert(val(v1), r1); return; } if (v1 == v2) return; - +#if Z3DEBUG + lp_assert(val(v1) == val(v2)); + unsigned debv1, debv2; + lp_assert(only_one_nfixed(r1, debv1) && only_one_nfixed(r2, debv2)); + lp_assert(debv1 == v1 && debv2 == v2); + lp_assert(ival(v1).y == ival(v2).y); +#endif explanation ex; explain_fixed_in_row(r1, ex); explain_fixed_in_row(r2, ex); @@ -157,141 +67,12 @@ class lp_bound_propagator { add_eq_on_columns(ex, v1, v2, true); } - 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; - if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) { - 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 = "; 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;); - 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); - } - - void try_add_equation_with_val_table(const vertex* v) { - SASSERT(m_fixed_vertex); - unsigned v_j = v->column(); - const vertex* u = nullptr; - if (!m_vals_to_verts.find(val(v_j), u)) { - m_vals_to_verts.insert(val(v_j), v); - return; - } - 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); - explanation ex = get_explanation_from_path(path); - ex.add_expl(m_fixed_vertex_explanation); - add_eq_on_columns(ex, j, v_j, true); - } - - static bool not_set(unsigned j) { return j == UINT_MAX; } + static bool is_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; - int polarity; - TRACE("cheap_eq_det", print_row(tout, row_index);); - if (!is_tree_offset_row(row_index, x, y, polarity)) { - TRACE("cheap_eq_det", tout << "not an offset row\n";); - return; - } - 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 - 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); - if (v) - explore_under(v); - } - explore_under(m_root); - } - - 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; - const row_cell* x_cell = nullptr; - const row_cell* y_cell = nullptr; - 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())) - continue; - if (not_set(x)) { - if (c.coeff().is_one() || c.coeff().is_minus_one()) { - x = c.var(); - x_cell = &c; - } else - return false; - } else if (not_set(y)) { - if (c.coeff().is_one() || c.coeff().is_minus_one()) { - y = c.var(); - y_cell = &c; - } else - return false; - } else - return false; - } - if (is_set(x)) { - if (is_set(y)) - polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos() ? -1 : 1; - else - polarity = 1; - return true; - } - return false; - } - - 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)) { - 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); - } - } - void reset_cheap_eq_eh() { - if (!m_root) - return; - delete_tree(m_root); - m_root = nullptr; - set_fixed_vertex(nullptr); - m_fixed_vertex_explanation.clear(); - m_vals_to_verts.reset(); - m_vals_to_verts_neg.reset(); - m_pol.reset(); - m_vertices.reset(); + m_row2index_pos.reset(); + m_row2index_neg.reset(); } struct reset_cheap_eq { @@ -381,175 +162,24 @@ class lp_bound_propagator { } const mpq& val(unsigned j) const { - return lp().get_column_value(j).x; + return lp().get_column_value(j).x; // figure out why it is safe to return .x } - - const mpq& val(const vertex* v) const { - return val(v->column()); - } - - bool tree_contains_r(vertex* root, vertex* v) const { - if (*root == *v) - return true; - for (auto e : root->edges()) - if (tree_contains_r(e.target(), v)) - return true; - return false; - } - - // pol for polarity - int pol(const vertex* v) const { return pol(v->column()); } - int pol(unsigned j) const { return m_pol[j]; } - void set_polarity(const vertex* v, int p) { - SASSERT(p == 1 || p == -1); - unsigned j = v->column(); - SASSERT(!m_pol.contains(j)); - m_pol.insert(j, p); - } - - 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); - return; - } - if (prev_pol == polarity) - return; - // we have a path L between v and parent with p(L) = -1, that means we can - // create an equality of the form x + x = a, where x = v->column() = u->column() - vector path = connect_in_tree(v, v_parent); - 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());); - } - - 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); - m_vertices.insert(column, v); - SASSERT(!tree_contains(v)); - return v; + + const impq& ival(unsigned j) const { + return lp().get_column_value(j); // figure out why it is safe to return .x } 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; - if (!is_tree_offset_row(row_index, x, y, row_polarity)) { - 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 (!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* 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); - } - 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; - } 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) { - TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << "\n";); - const vertex* k; // the other vertex - if (table.find(val(v), k)) { - 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 { - TRACE("cheap_eq", tout << "no report\n";); - } - } 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 - 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(); - m_root = nullptr; - } - - std::ostream& print_edge(const edge& e, std::ostream& out) const { - out << e.source()->column() << "->" << e.target()->column() << "\n"; - return print_row(out, e.row()); - } - - std::ostream& print_path(const vector& path, std::ostream& out) const { - out << "path = \n"; - 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"); - - 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 { @@ -560,10 +190,12 @@ class lp_bound_propagator { } bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) { - SASSERT(j != k); + lp_assert(j != k && is_int(j) == is_int(k)); + lp_assert(ival(j) == ival(k)); + unsigned je = lp().column_to_reported_index(j); unsigned ke = lp().column_to_reported_index(k); - TRACE("cheap_eq", + TRACE("eq", tout << "reporting eq " << j << ", " << k << "\n"; tout << "reported idx " << je << ", " << ke << "\n"; print_expl(tout, exp); @@ -593,20 +225,26 @@ class lp_bound_propagator { return lp().column_is_int(j); } - explanation get_explanation_from_path(vector& path) const { - explanation ex; - 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); + TRACE("eq", tout << lp().get_row(row) << std::endl); for (const auto& c : lp().get_row(row)) if (lp().is_fixed(c.var())) explain_fixed_column(c.var(), ex); } + unsigned explain_fixed_in_row_and_get_base(unsigned row, explanation& ex) const { + unsigned base = UINT_MAX; + TRACE("eq", tout << lp().get_row(row) << std::endl); + for (const auto& c : lp().get_row(row)) { + if (lp().is_fixed(c.var())) { + explain_fixed_column(c.var(), ex); + } else if (lp().is_base(c.var())) { + base = c.var(); + } + } + return base; + } + void explain_fixed_column(unsigned j, explanation& ex) const { SASSERT(column_is_fixed(j)); constraint_index lc, uc; @@ -615,101 +253,156 @@ class lp_bound_propagator { if (lc != 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";); - 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";); - 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]); - } - 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()) - return false; - visited_verts.insert(v->column()); - for (auto e : v->edges()) - if (!tree_is_correct(e.target(), visited_verts)) +#ifdef Z3DEBUG + bool all_fixed_in_row(unsigned row) const { + for (const auto& c : lp().get_row(row)) + if (!lp().is_fixed(c.var())) return false; return true; } - std::ostream& print_tree(std::ostream& out, vertex* v) const { - print_vert(out, v); - out << "\nchildren :\n"; - for (auto c : v->edges()) { - out << "row = "; - print_row(out, c.row()); - print_tree(out, c.target()); + + // bounded by 2 + unsigned num_of_non_fixed_in_row(unsigned row_index) const { + unsigned n_of_nfixed = 0; + for (const auto& c : lp().get_row(row_index)) { + if (lp().column_is_fixed(c.var())) + continue; + n_of_nfixed++; + if (n_of_nfixed > 1) + return n_of_nfixed; } - return out; + + return n_of_nfixed; + } +#endif + // Let nf is the number of non-fixed columns in the row. + // Then the function returns min(nf, 3). + // if nf == 0, the row is of the form sum of fixed = 0 + // if nf == 1, the row is of the form x + sum of fixed = 0, where x is not fixed base + // if nf == 2, the row is of the form x + ay + sum of fixed = 0, x is a non fixed base and y is not fixed + // y_sign is set to a, if abs(a)= 1, and 0 otherwise + + unsigned extract_non_fixed(unsigned row_index, unsigned& x, unsigned& y, int& y_sign) const { + unsigned nf = 0; // number of non-fixed columns + y = UINT_MAX; + const auto& row = lp().get_row(row_index); + x = lp().get_base_column_in_row(row_index); + if (!column_is_fixed(x)) { + nf++; + } else { + lp_assert(all_fixed_in_row(row_index)); + return 0; + } + + for (const auto& c : row) { + unsigned j = c.var(); + if (j == x) continue; + if (column_is_fixed(j)) + continue; + if (++nf > 2) + return nf; + lp_assert(is_not_set(y)); + y = j; + if (c.coeff().is_one()) { + y_sign = 1; + } else if (c.coeff().is_minus_one()) { + y_sign = -1; + } else { + // y has a coefficient other than 1 or -1 + y_sign = 0; + return nf; // maybe be too low but we don't care + } + } + + return nf; } - void try_add_equation_with_fixed_tables(unsigned row_index, const vertex* v) { - 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()) + void try_add_equation_with_lp_fixed_tables(unsigned row_index, unsigned v_j) { + lp_assert(lp().get_base_column_in_row(row_index) == v_j); + lp_assert(num_of_non_fixed_in_row(row_index) == 1 || column_is_fixed(v_j)); + if (column_is_fixed(v_j)) { return; - const vertex* v = m_root; - try_add_equation_with_fixed_tables(row_index, v); - for (auto e : v->edges()) - try_add_equation_with_fixed_tables(row_index, e.target()); + } + unsigned j = null_lpvar; + if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) { + try_add_equation_with_internal_fixed_tables(row_index); + return; + } + TRACE("eq", + tout << "v_j = "; + lp().print_column_info(v_j, tout) << std::endl; + tout << "found j " << j << std::endl; lp().print_column_info(j, tout) << std::endl; + print_row(tout, row_index) << std::endl; + ); + explanation ex; + explain_fixed_in_row(row_index, ex); + explain_fixed_column(j, ex); + add_eq_on_columns(ex, j, v_j, true); } - void cheap_eq_tree(unsigned row_index) { + void cheap_eq_on_nbase(unsigned row_index) { reset_cheap_eq _reset(*this); - TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";); + TRACE("eq", tout << "row_index = " << row_index << "\n"; + print_row(tout, row_index) << "\n";); if (!check_insert(m_visited_rows, row_index)) return; - create_root(row_index); - if (!m_root) + unsigned x, y; + int y_sign; + unsigned nf = extract_non_fixed(row_index, x, y, y_sign); + if (nf == 0 || nf > 2) return; + if (nf == 1) { + lp_assert(is_not_set(y)); + try_add_equation_with_lp_fixed_tables(row_index, x); + return; + } + if (y_sign == 0) { + // the coefficient before y is not 1 or -1 + return; + } + lp_assert(y_sign == -1 || y_sign == 1); + lp_assert(lp().is_base(y) == false); + auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg; + table.insert(val(x), row_index); + TRACE("eq", tout << "y = " << y << "\n";); - TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); - SASSERT(tree_is_correct()); - handle_fixed_phase(row_index); + for (const column_cell& c : lp().get_column(y)) { + unsigned i = c.var(); // the running index of the row + if (i == row_index) + continue; + if (check_insert(m_visited_rows, i) == false) + continue; + unsigned y_nb; + nf = extract_non_fixed(i, x, y_nb, y_sign); + if (nf != 2 || y_sign == 0) + continue; - TRACE("cheap_eq", - tout << "done for row_index " << row_index << "\n"; - tout << "tree size = " << verts_size();); + lp_assert(y_nb == y); + lp_assert(y_sign == 1 || y_sign == -1); + auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg; + const auto& v = val(x); + unsigned found_i; + if (!table.find(v, found_i)) { + table.insert(v, i); + } else { + explanation ex; + unsigned base_of_found = lp().get_base_column_in_row(found_i); + if (is_int(x) != is_int(base_of_found) || ival(x).y != ival(base_of_found).y) + continue; + explain_fixed_in_row(found_i, ex); + explain_fixed_in_row(i, ex); + TRACE("eq", { + print_row(tout, i); + print_row(tout, found_i) << "\n"; + lp().print_column_info(base_of_found, tout); + lp().print_column_info(x, tout) << "\n"; + }); + add_eq_on_columns(ex, x, base_of_found, false); + } + } } 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]) { if (lp().column_is_fixed(c.var())) @@ -726,29 +419,6 @@ class lp_bound_propagator { return out; } - 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 - for (auto e : v->edges()) - r += subtree_size(e.target()); - return r; - } - - void delete_tree(vertex* v) { - for (auto p : v->edges()) - delete_tree(p.target()); - dealloc(v); - } - template bool check_insert(C& table, unsigned j) { if (table.contains(j)) diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 28f92d8e2..b14231d09 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -65,6 +65,6 @@ template bool lp::lp_core_solver_base::pivot_column_tableau(un template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_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); +template bool lp::lp_core_solver_base >::remove_from_basis_core(unsigned int, unsigned int); diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 251199419..e7570d5dd 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -338,7 +338,7 @@ public: } - bool remove_from_basis(unsigned j); + bool remove_from_basis_core(unsigned entering, unsigned leaving); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); void init_basic_part_of_basis_heading() { unsigned m = m_basis.size(); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 101f9dbea..613862dde 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -403,30 +403,24 @@ template void lp_core_solver_base::transpose_row transpose_basis(i, j); m_A.transpose_rows(i, j); } - -// j is the new basic column, j_basic - the leaving column -template bool lp_core_solver_base::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w) { - lp_assert(m_basis_heading[j] < 0); - lp_assert(m_basis_heading[j_basic] >= 0); - unsigned row_index = m_basis_heading[j_basic]; - // the tableau case - if (!pivot_column_tableau(j, row_index)) +// entering is the new base column, leaving - the column leaving the basis +template bool lp_core_solver_base::pivot_column_general(unsigned entering, unsigned leaving, indexed_vector & w) { + lp_assert(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[leaving] >= 0); + unsigned row_index = m_basis_heading[leaving]; + // the tableau case + if (pivot_column_tableau(entering, row_index)) + change_basis(entering, leaving); + else return false; - change_basis(j, j_basic); - return true; + + return true; } -template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { +template bool lp_core_solver_base::remove_from_basis_core(unsigned entering, unsigned leaving) { indexed_vector w(m_basis.size()); // the buffer - unsigned i = m_basis_heading[basic_j]; - for (auto &c : m_A.m_rows[i]) { - if (c.var() == basic_j) - continue; - if (pivot_column_general(c.var(), basic_j, w)) - return true; - } - return false; + return pivot_column_general(entering, leaving, w); } From c3a373e2255955cc1dec5feb30205a762c5df767 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 4 Aug 2023 09:20:36 -0700 Subject: [PATCH 074/428] format Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 13 +++++-------- src/math/lp/lp_core_solver_base_def.h | 16 +++++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index bf69ceb2b..852c47397 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -481,12 +481,9 @@ namespace lp { bool lar_solver::remove_from_basis(unsigned j) { lp_assert(is_base(j)); unsigned i = row_of_basic_column(j); - for (const auto & c : A_r().m_rows[i]) { - if (j != c.var() && !is_fixed(c.var())) { + for (const auto & c : A_r().m_rows[i]) + if (j != c.var() && !is_fixed(c.var())) return m_mpq_lar_core_solver.m_r_solver.remove_from_basis_core(c.var(), j); - } - - } return false; } @@ -630,7 +627,7 @@ namespace lp { } void lar_solver::remove_fixed_vars_from_base() { - // this will allow to disable and restore the tracking of the touched rows + // this will allow to disable and restore the tracking of the touched rows flet f(m_mpq_lar_core_solver.m_r_solver.m_touched_rows, nullptr); unsigned num = A_r().column_count(); unsigned_vector to_remove; @@ -1782,9 +1779,9 @@ namespace lp { 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); - if (is_base(j) && column_is_fixed(j)) { + + if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); - } 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;); } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 613862dde..0552b8e82 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -405,16 +405,14 @@ template void lp_core_solver_base::transpose_row } // entering is the new base column, leaving - the column leaving the basis template bool lp_core_solver_base::pivot_column_general(unsigned entering, unsigned leaving, indexed_vector & w) { - lp_assert(m_basis_heading[entering] < 0); - lp_assert(m_basis_heading[leaving] >= 0); - unsigned row_index = m_basis_heading[leaving]; - // the tableau case - if (pivot_column_tableau(entering, row_index)) - change_basis(entering, leaving); - else + lp_assert(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[leaving] >= 0); + unsigned row_index = m_basis_heading[leaving]; + // the tableau case + if (!pivot_column_tableau(entering, row_index)) return false; - - return true; + change_basis(entering, leaving); + return true; } From 0fbf8f92f51ce7f1df8027e4771b4757bbf0bbed Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 5 Aug 2023 09:31:55 -1000 Subject: [PATCH 075/428] delete remove_fixed_vars_from_base() from int_solver --- src/math/lp/int_solver.cpp | 29 +---------------------------- src/math/lp/int_solver.h | 3 +-- src/math/lp/lar_solver.h | 3 ++- 3 files changed, 4 insertions(+), 31 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index b4d703218..e03aa785e 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -18,22 +18,6 @@ namespace lp { 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; - } - } - } - } - - unsigned int_solver::patcher::count_non_int() { unsigned non_int = 0; for (auto j : lra.r_basis()) @@ -43,22 +27,12 @@ namespace lp { } lia_move int_solver::patcher::patch_basic_columns() { - remove_fixed_vars_from_base(); lia.settings().stats().m_patches++; + lra.remove_fixed_vars_from_base(); 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; @@ -177,7 +151,6 @@ namespace lp { } - int_solver::int_solver(lar_solver& lar_slv) : lra(lar_slv), lrac(lra.m_mpq_lar_core_solver), diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 56237a5b8..c6b1bb1d0 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -52,13 +52,12 @@ class int_solver { patcher(int_solver& lia); bool should_apply() const { return true; } lia_move operator()() { return patch_basic_columns(); } - 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: + bool patch_basic_column_on_row_cell(unsigned v, row_cell const& c); lia_move patch_basic_columns(); - void remove_fixed_vars_from_base(); }; lar_solver& lra; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index a4a0e87b0..ab61a32e9 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -333,8 +333,9 @@ class lar_solver : public column_namer { void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { - remove_fixed_vars_from_base(); if (settings().propagate_eqs()) { + if (settings().random_next() % 10 == 0) + remove_fixed_vars_from_base(); bp.clear_for_eq(); for (unsigned i : m_touched_rows) { unsigned offset_eqs = stats().m_offset_eqs; From 858eebca822b16d80dafcfdc376af2463b06143b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 5 Aug 2023 13:40:35 -1000 Subject: [PATCH 076/428] more efficient column_is_fixed --- src/math/lp/lar_core_solver.h | 4 +--- src/math/lp/lar_solver.h | 1 + src/math/lp/lp_bound_propagator.h | 11 +++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 5d445105f..0780d21af 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -215,9 +215,7 @@ public: } bool column_is_fixed(unsigned j) const { - return m_column_types()[j] == column_type::fixed || - ( m_column_types()[j] == column_type::boxed && - m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); + return m_column_types()[j] == column_type::fixed; } bool column_is_free(unsigned j) const { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index ab61a32e9..9faa40979 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -489,6 +489,7 @@ class lar_solver : public column_namer { 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 vector& get_column_types() const { return m_mpq_lar_core_solver.m_column_types(); } 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; diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index e2c0d6c92..f814bea73 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -25,6 +25,8 @@ class lp_bound_propagator { map, default_eq> m_row2index_pos; // works for rows of the form x - y + sum of fixed = 0 map, default_eq> m_row2index_neg; + + const vector* m_column_types; // returns true iff there is only one non-fixed column in the row bool only_one_nfixed(unsigned r, unsigned& x) { x = UINT_MAX; @@ -90,13 +92,14 @@ class lp_bound_propagator { m_improved_upper_bounds.clear(); m_improved_lower_bounds.clear(); m_ibounds.reset(); + m_column_types = &lp().get_column_types(); } 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); + return (*m_column_types)[j]; } const impq& get_lower_bound(unsigned j) const { @@ -117,7 +120,7 @@ class lp_bound_propagator { // require also the zero infinitesemal part bool column_is_fixed(lpvar j) const { - return lp().column_is_fixed(j) && get_lower_bound(j).y.is_zero(); + return (*m_column_types)[j] == column_type::fixed && 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) { @@ -265,7 +268,7 @@ class lp_bound_propagator { unsigned num_of_non_fixed_in_row(unsigned row_index) const { unsigned n_of_nfixed = 0; for (const auto& c : lp().get_row(row_index)) { - if (lp().column_is_fixed(c.var())) + if (column_is_fixed(c.var())) continue; n_of_nfixed++; if (n_of_nfixed > 1) @@ -370,7 +373,7 @@ class lp_bound_propagator { unsigned i = c.var(); // the running index of the row if (i == row_index) continue; - if (check_insert(m_visited_rows, i) == false) + if (!check_insert(m_visited_rows, i)) continue; unsigned y_nb; nf = extract_non_fixed(i, x, y_nb, y_sign); From a7966dc436c1404726583d2a723b8363facafd9a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 7 Aug 2023 13:05:16 -1000 Subject: [PATCH 077/428] remove an assert --- src/math/lp/lp_bound_propagator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index f814bea73..46670ad91 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -293,7 +293,7 @@ class lp_bound_propagator { if (!column_is_fixed(x)) { nf++; } else { - lp_assert(all_fixed_in_row(row_index)); + // we have a fixed base column, exiting return 0; } From a6ab0a7d49c964e4ca4002d59124e749e1a9152f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Aug 2023 13:50:49 -0700 Subject: [PATCH 078/428] formatting hygiene Signed-off-by: Nikolaj Bjorner --- src/math/lp/int_solver.cpp | 1048 +++++++++++++++++------------------- src/math/lp/int_solver.h | 3 +- 2 files changed, 506 insertions(+), 545 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index e03aa785e..0c83ea5bf 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -61,7 +61,6 @@ namespace lp { // + 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 @@ -69,10 +68,6 @@ namespace lp { 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 d = u * t * x1; @@ -103,13 +98,12 @@ namespace lp { if (!get_patching_deltas(r, a, delta_plus, delta_minus)) return false; - if (lia.random() % 2) { + if (lia.random() % 2) return try_patch_column(v, c.var(), delta_plus) || try_patch_column(v, c.var(), delta_minus); - } else { + else return try_patch_column(v, c.var(), delta_minus) || try_patch_column(v, c.var(), delta_plus); - } } bool int_solver::patcher::try_patch_column(unsigned v, unsigned j, mpq const& delta) { @@ -151,574 +145,542 @@ namespace lp { } -int_solver::int_solver(lar_solver& lar_slv) : - lra(lar_slv), - lrac(lra.m_mpq_lar_core_solver), - m_gcd(*this), - m_patcher(*this), - m_number_of_calls(0), - m_hnf_cutter(*this), - m_hnf_cut_period(settings().hnf_cut_period()) { - lra.set_int_solver(this); -} - -// this will allow to enable and disable tracking of the pivot rows -struct check_return_helper { - lar_solver& lra; - bool m_track_touched_rows; - check_return_helper(lar_solver& ls) : - lra(ls), - m_track_touched_rows(lra.touched_rows_are_tracked()) { - lra.track_touched_rows(false); + int_solver::int_solver(lar_solver& lar_slv) : + lra(lar_slv), + lrac(lra.m_mpq_lar_core_solver), + m_gcd(*this), + m_patcher(*this), + m_number_of_calls(0), + m_hnf_cutter(*this), + m_hnf_cut_period(settings().hnf_cut_period()) { + lra.set_int_solver(this); } - ~check_return_helper() { - lra.track_touched_rows(m_track_touched_rows); - } -}; -lia_move int_solver::check(lp::explanation * e) { - SASSERT(lra.ax_is_correct()); - if (!has_inf_int()) return lia_move::sat; - - m_t.clear(); - m_k.reset(); - m_ex = e; - m_ex->clear(); - m_upper = false; - lia_move r = lia_move::undef; - - if (m_gcd.should_apply()) r = m_gcd(); - - check_return_helper pc(lra); - - if (settings().get_cancel_flag()) - return lia_move::undef; - - ++m_number_of_calls; - if (r == lia_move::undef && m_patcher.should_apply()) r = m_patcher(); - if (r == lia_move::undef && should_find_cube()) r = int_cube(*this)(); - if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); - if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this)(); - if (r == lia_move::undef) r = int_branch(*this)(); - return r; -} - -std::ostream& int_solver::display_inf_rows(std::ostream& out) const { - unsigned num = lra.A_r().column_count(); - for (unsigned v = 0; v < num; v++) { - if (column_is_int(v) && !get_value(v).is_int()) { - display_column(out, v); + // this will allow to enable and disable tracking of the pivot rows + struct check_return_helper { + lar_solver& lra; + bool m_track_touched_rows; + check_return_helper(lar_solver& ls) : + lra(ls), + m_track_touched_rows(lra.touched_rows_are_tracked()) { + lra.track_touched_rows(false); } - } - - num = 0; - for (unsigned i = 0; i < lra.A_r().row_count(); i++) { - unsigned j = lrac.m_r_basis[i]; - if (column_is_int_inf(j)) { - num++; - lra.print_row(lra.A_r().m_rows[i], out); - out << "\n"; + ~check_return_helper() { + lra.track_touched_rows(m_track_touched_rows); } - } - out << "num of int infeasible: " << num << "\n"; - return out; -} - -bool int_solver::cut_indices_are_columns() const { - for (lar_term::ival p : m_t) { - if (p.column().index() >= lra.A_r().column_count()) - return false; - } - return true; -} - -bool int_solver::current_solution_is_inf_on_cut() const { - SASSERT(cut_indices_are_columns()); - const auto & x = lrac.m_r_x; - impq v = m_t.apply(x); - mpq sign = m_upper ? one_of_type() : -one_of_type(); - CTRACE("current_solution_is_inf_on_cut", v * sign <= impq(m_k) * sign, - tout << "m_upper = " << m_upper << std::endl; - tout << "v = " << v << ", k = " << m_k << std::endl; - ); - return v * sign > impq(m_k) * sign; -} - -bool int_solver::has_inf_int() const { - return lra.has_inf_int(); -} - -constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { - return lra.get_column_upper_bound_witness(j); -} - -constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { - return lra.get_column_lower_bound_witness(j); -} - -unsigned int_solver::row_of_basic_column(unsigned j) const { - return lra.row_of_basic_column(j); -} - -lp_settings& int_solver::settings() { - return lra.settings(); -} - -const lp_settings& int_solver::settings() const { - return lra.settings(); -} - -bool int_solver::column_is_int(column_index const& j) const { - return lra.column_is_int(j); -} - -bool int_solver::is_real(unsigned j) const { - return !column_is_int(j); -} - -bool int_solver::value_is_int(unsigned j) const { - return lra.column_value_is_int(j); -} - -unsigned int_solver::random() { - return settings().random_next(); -} - -const impq& int_solver::upper_bound(unsigned j) const { - return lra.column_upper_bound(j); -} - -const impq& int_solver::lower_bound(unsigned j) const { - return lra.column_lower_bound(j); -} - -bool int_solver::is_term(unsigned j) const { - return lra.column_corresponds_to_term(j); -} - -unsigned int_solver::column_count() const { - return lra.column_count(); -} - -bool int_solver::should_find_cube() { - return m_number_of_calls % settings().m_int_find_cube_period == 0; -} - - -bool int_solver::should_gomory_cut() { - return m_number_of_calls % settings().m_int_gomory_cut_period == 0; -} - -bool int_solver::should_hnf_cut() { - return settings().enable_hnf() && m_number_of_calls % m_hnf_cut_period == 0; -} - -lia_move int_solver::hnf_cut() { - lia_move r = m_hnf_cutter.make_hnf_cut(); - if (r == lia_move::undef) { - m_hnf_cut_period *= 2; - } - else { - m_hnf_cut_period = settings().hnf_cut_period(); - } - return r; -} - -bool int_solver::has_lower(unsigned j) const { - switch (lrac.m_column_types()[j]) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - return true; - default: - return false; - } -} - -bool int_solver::has_upper(unsigned j) const { - switch (lrac.m_column_types()[j]) { - case column_type::fixed: - case column_type::boxed: - case column_type::upper_bound: - return true; - default: - return false; - } -} - -static void set_lower(impq & l, bool & inf_l, impq const & v ) { - if (inf_l || v > l) { - l = v; - inf_l = false; - } -} - -static void set_upper(impq & u, bool & inf_u, impq const & v) { - if (inf_u || v < u) { - u = v; - inf_u = false; - } -} - -// 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 || is_fixed(j)) // basic or fixed var - return false; - - TRACE("random_update", display_column(tout, j) << ", is_int = " << column_is_int(j) << "\n";); - impq const & xj = get_value(j); - - inf_l = true; - inf_u = true; - l = u = zero_of_type(); - m = mpq(1); - - if (has_lower(j)) - set_lower(l, inf_l, lower_bound(j) - xj); - - if (has_upper(j)) - set_upper(u, inf_u, upper_bound(j) - xj); - - - const auto & A = lra.A_r(); - TRACE("random_update", tout << "m = " << m << "\n";); - - auto delta = [](mpq const& x, impq const& y, impq const& z) { - if (x.is_one()) - return y - z; - if (x.is_minus_one()) - return z - y; - return (y - z) / x; }; - for (auto c : A.column(j)) { - unsigned row_index = c.var(); - const mpq & a = c.coeff(); - 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() && xi.is_int()) - m = lcm(m, denominator(a)); - - if (!inf_l && !inf_u) { - if (l == u) - continue; - } + lia_move int_solver::check(lp::explanation * e) { + SASSERT(lra.ax_is_correct()); + if (!has_inf_int()) return lia_move::sat; - if (a.is_neg()) { - if (has_lower(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, lra.get_upper_bound(i))); - } - else { - if (has_upper(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, lra.get_lower_bound(i))); - } + m_t.clear(); + m_k.reset(); + m_ex = e; + m_ex->clear(); + m_upper = false; + lia_move r = lia_move::undef; + + if (m_gcd.should_apply()) r = m_gcd(); + + check_return_helper pc(lra); + + if (settings().get_cancel_flag()) + return lia_move::undef; + + ++m_number_of_calls; + if (r == lia_move::undef && m_patcher.should_apply()) r = m_patcher(); + if (r == lia_move::undef && should_find_cube()) r = int_cube(*this)(); + if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); + if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this)(); + if (r == lia_move::undef) r = int_branch(*this)(); + return r; } - l += xj; - u += xj; - - TRACE("freedom_interval", - tout << "freedom variable for:\n"; - tout << lra.get_variable_name(j); - tout << "["; - if (inf_l) tout << "-oo"; else tout << l; - tout << "; "; - if (inf_u) tout << "oo"; else tout << u; - tout << "]\n"; - tout << "val = " << get_value(j) << "\n"; - tout << "return " << (inf_l || inf_u || l <= u); - ); - return (inf_l || inf_u || l <= u); -} - - -bool int_solver::is_feasible() const { - lp_assert( - lrac.m_r_solver.calc_current_x_is_feasible_include_non_basis() == - lrac.m_r_solver.current_x_is_feasible()); - return lrac.m_r_solver.current_x_is_feasible(); -} - -const impq & int_solver::get_value(unsigned j) const { - return lrac.m_r_x[j]; -} - -std::ostream& int_solver::display_column(std::ostream & out, unsigned j) const { - return lrac.m_r_solver.print_column_info(j, out); -} - -bool int_solver::column_is_int_inf(unsigned j) const { - return column_is_int(j) && (!value_is_int(j)); -} - -bool int_solver::is_base(unsigned j) const { - return lrac.m_r_heading[j] >= 0; -} - -bool int_solver::is_boxed(unsigned j) const { - return lrac.m_column_types[j] == column_type::boxed; -} - -bool int_solver::is_fixed(unsigned j) const { - return lrac.m_column_types[j] == column_type::fixed; -} - -bool int_solver::is_free(unsigned j) const { - return lrac.m_column_types[j] == column_type::free_column; -} - -bool int_solver::at_bound(unsigned j) const { - auto & mpq_solver = lrac.m_r_solver; - switch (mpq_solver.m_column_types[j] ) { - case column_type::fixed: - case column_type::boxed: - return - mpq_solver.m_lower_bounds[j] == get_value(j) || - mpq_solver.m_upper_bounds[j] == get_value(j); - case column_type::lower_bound: - return mpq_solver.m_lower_bounds[j] == get_value(j); - case column_type::upper_bound: - return mpq_solver.m_upper_bounds[j] == get_value(j); - default: - return false; - } -} - -bool int_solver::at_lower(unsigned j) const { - auto & mpq_solver = lrac.m_r_solver; - switch (mpq_solver.m_column_types[j] ) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - return mpq_solver.m_lower_bounds[j] == get_value(j); - default: - return false; - } -} - -bool int_solver::at_upper(unsigned j) const { - auto & mpq_solver = lrac.m_r_solver; - switch (mpq_solver.m_column_types[j] ) { - case column_type::fixed: - case column_type::boxed: - case column_type::upper_bound: - return mpq_solver.m_upper_bounds[j] == get_value(j); - default: - return false; - } -} - -std::ostream & int_solver::display_row(std::ostream & out, lp::row_strip const & row) const { - 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()) { - impq val = get_value(c.var()) * c.coeff(); - if (!first && val.is_pos()) - out << "+"; - if (val.y.is_zero()) - out << val.x << " "; - else - out << val << " "; + std::ostream& int_solver::display_inf_rows(std::ostream& out) const { + unsigned num = lra.A_r().column_count(); + for (unsigned v = 0; v < num; v++) { + if (column_is_int(v) && !get_value(v).is_int()) { + display_column(out, v); } - first = false; - continue; } - if (c.coeff().is_one()) - { - if (!first) - out << "+"; - } - else if (c.coeff().is_minus_one()) - out << "-"; - else { - if (c.coeff().is_pos() && !first) - out << "+"; - if (c.coeff().is_big()) - out << " b*"; - else - out << c.coeff(); - } - out << rslv.column_name(c.var()) << " "; - first = false; - } - out << "\n"; - for (const auto &c : row) { - if (is_fixed(c.var())) - continue; - rslv.print_column_info(c.var(), out); - if (is_base(c.var())) - out << "j" << c.var() << " base\n"; - } - 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; - if (settings().get_cancel_flag()) - return false; - bool inf_l = false, inf_u = false; - impq l, u; - mpq m; - 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); - return true; - } - if (column_is_int(j)) { - if (!inf_l) { - l = impq(ceil(l)); - } - if (!inf_u) { - u = impq(floor(u)); + num = 0; + for (unsigned i = 0; i < lra.A_r().row_count(); i++) { + unsigned j = lrac.m_r_basis[i]; + if (column_is_int_inf(j)) { + num++; + lra.print_row(lra.A_r().m_rows[i], out); + out << "\n"; + } } + out << "num of int infeasible: " << num << "\n"; + return out; } - if (!inf_l && !inf_u && l >= u) - return false; - - if (inf_u) { - SASSERT(!inf_l); - impq new_val = x + m * impq(random() % (range + 1)); - lra.set_value_for_nbasic_column(j, new_val); + bool int_solver::cut_indices_are_columns() const { + for (lar_term::ival p : m_t) { + if (p.column().index() >= lra.A_r().column_count()) + return false; + } return true; } - if (inf_l) { - SASSERT(!inf_u); - impq new_val = x - m * impq(random() % (range + 1)); - lra.set_value_for_nbasic_column(j, new_val); - return true; + bool int_solver::current_solution_is_inf_on_cut() const { + SASSERT(cut_indices_are_columns()); + const auto & x = lrac.m_r_x; + impq v = m_t.apply(x); + mpq sign = m_upper ? one_of_type() : -one_of_type(); + CTRACE("current_solution_is_inf_on_cut", v * sign <= impq(m_k) * sign, + tout << "m_upper = " << m_upper << std::endl; + tout << "v = " << v << ", k = " << m_k << std::endl; + ); + return v * sign > impq(m_k) * sign; } - SASSERT(!inf_l && !inf_u); - // The shift has to be a multiple of m: let us look for s, such that the shift is m*s. - // We have new_val = x+m*s <= u, so m*s <= u-x and, finally, s <= floor((u- x)/m) = a - // The symmetric reasoning gives us s >= ceil((l-x)/m) = b - // We randomly pick s in the segment [b, a] - mpq a = floor((u - x) / m); - mpq b = ceil((l - x) / m); - mpq r = a - b; - if (!r.is_pos()) - return false; - TRACE("int_solver", tout << "a = " << a << ", b = " << b << ", r = " << r<< ", m = " << m << "\n";); - if (r < mpq(range)) - range = static_cast(r.get_uint64()); + bool int_solver::has_inf_int() const { + return lra.has_inf_int(); + } - mpq s = b + mpq(random() % (range + 1)); - impq new_val = x + m * impq(s); - TRACE("int_solver", tout << "new_val = " << new_val << "\n";); - SASSERT(l <= new_val && new_val <= u); - lra.set_value_for_nbasic_column(j, new_val); - return true; -} + constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { + return lra.get_column_upper_bound_witness(j); + } -// not used: -bool int_solver::non_basic_columns_are_at_bounds() const { - for (unsigned j : lrac.m_r_nbasis) { - auto & val = lrac.m_r_x[j]; + constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { + return lra.get_column_lower_bound_witness(j); + } + + unsigned int_solver::row_of_basic_column(unsigned j) const { + return lra.row_of_basic_column(j); + } + + lp_settings& int_solver::settings() { + return lra.settings(); + } + + const lp_settings& int_solver::settings() const { + return lra.settings(); + } + + bool int_solver::column_is_int(column_index const& j) const { + return lra.column_is_int(j); + } + + bool int_solver::is_real(unsigned j) const { + return !column_is_int(j); + } + + bool int_solver::value_is_int(unsigned j) const { + return lra.column_value_is_int(j); + } + + unsigned int_solver::random() { + return settings().random_next(); + } + + const impq& int_solver::upper_bound(unsigned j) const { + return lra.column_upper_bound(j); + } + + const impq& int_solver::lower_bound(unsigned j) const { + return lra.column_lower_bound(j); + } + + bool int_solver::is_term(unsigned j) const { + return lra.column_corresponds_to_term(j); + } + + unsigned int_solver::column_count() const { + return lra.column_count(); + } + + bool int_solver::should_find_cube() { + return m_number_of_calls % settings().m_int_find_cube_period == 0; + } + + + bool int_solver::should_gomory_cut() { + return m_number_of_calls % settings().m_int_gomory_cut_period == 0; + } + + bool int_solver::should_hnf_cut() { + return settings().enable_hnf() && m_number_of_calls % m_hnf_cut_period == 0; + } + + lia_move int_solver::hnf_cut() { + lia_move r = m_hnf_cutter.make_hnf_cut(); + if (r == lia_move::undef) + m_hnf_cut_period *= 2; + else + m_hnf_cut_period = settings().hnf_cut_period(); + return r; + } + + bool int_solver::has_lower(unsigned j) const { switch (lrac.m_column_types()[j]) { + case column_type::fixed: case column_type::boxed: - if (val != lrac.m_r_lower_bounds()[j] && val != lrac.m_r_upper_bounds()[j]) - return false; - break; case column_type::lower_bound: - if (val != lrac.m_r_lower_bounds()[j]) - return false; - break; - case column_type::upper_bound: - if (val != lrac.m_r_upper_bounds()[j]) - return false; - break; + return true; default: - if (column_is_int(j) && !val.is_int()) { - return false; - } + return false; } } - return true; -} -int int_solver::select_int_infeasible_var() { - int result = -1; - mpq range; - mpq new_range; - mpq small_value(1024); - unsigned n = 0; - lar_core_solver & lcs = lra.m_mpq_lar_core_solver; - unsigned prev_usage = 0; // to quiet down the compile - - enum state { small_box, is_small_value, any_value, not_found }; - state st = not_found; - - for (unsigned j : lra.r_basis()) { - if (!column_is_int_inf(j)) - continue; - 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) { - n = 0; - st = small_box; - } - if (n == 0 || new_range < range) { - result = j; - range = new_range; - n = 1; - } - else if (new_range == range && (random() % (++n) == 0)) { - result = j; - } - continue; + bool int_solver::has_upper(unsigned j) const { + switch (lrac.m_column_types()[j]) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return true; + default: + return false; } - if (st == small_box) - continue; - impq const& value = get_value(j); - if (abs(value.x) < small_value || - (has_upper(j) && small_value > upper_bound(j).x - value.x) || - (has_lower(j) && small_value > value.x - lower_bound(j).x)) { - if (st != is_small_value) { - n = 0; - st = is_small_value; - } - if (random() % (++n) == 0) - result = j; + } + + static void set_lower(impq & l, bool & inf_l, impq const & v ) { + if (inf_l || v > l) { + l = v; + inf_l = false; } - if (st == is_small_value) - continue; - SASSERT(st == not_found || st == any_value); - st = any_value; - if (n == 0 || usage > prev_usage) { - result = j; - prev_usage = usage; - n = 1; - } - else if (usage > 0 && usage == prev_usage && (random() % (++n) == 0)) - result = j; + } + + static void set_upper(impq & u, bool & inf_u, impq const & v) { + if (inf_u || v < u) { + u = v; + inf_u = false; + } + } + + // 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 || is_fixed(j)) // basic or fixed var + return false; + + TRACE("random_update", display_column(tout, j) << ", is_int = " << column_is_int(j) << "\n";); + impq const & xj = get_value(j); + + inf_l = true; + inf_u = true; + l = u = zero_of_type(); + m = mpq(1); + + if (has_lower(j)) + set_lower(l, inf_l, lower_bound(j) - xj); + + if (has_upper(j)) + set_upper(u, inf_u, upper_bound(j) - xj); + + + const auto & A = lra.A_r(); + TRACE("random_update", tout << "m = " << m << "\n";); + + auto delta = [](mpq const& x, impq const& y, impq const& z) { + if (x.is_one()) + return y - z; + if (x.is_minus_one()) + return z - y; + return (y - z) / x; + }; + + for (auto c : A.column(j)) { + unsigned row_index = c.var(); + const mpq & a = c.coeff(); + 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() && xi.is_int()) + m = lcm(m, denominator(a)); + + if (!inf_l && !inf_u && l == u) + continue; + + if (a.is_neg()) { + if (has_lower(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, lra.get_upper_bound(i))); + } + else { + if (has_upper(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, lra.get_lower_bound(i))); + } + } + + l += xj; + u += xj; + + TRACE("freedom_interval", + tout << "freedom variable for:\n"; + tout << lra.get_variable_name(j); + tout << "["; + if (inf_l) tout << "-oo"; else tout << l; + tout << "; "; + if (inf_u) tout << "oo"; else tout << u; + tout << "]\n"; + tout << "val = " << get_value(j) << "\n"; + tout << "return " << (inf_l || inf_u || l <= u); + ); + return (inf_l || inf_u || l <= u); + } + + + bool int_solver::is_feasible() const { + lp_assert( + lrac.m_r_solver.calc_current_x_is_feasible_include_non_basis() == + lrac.m_r_solver.current_x_is_feasible()); + return lrac.m_r_solver.current_x_is_feasible(); + } + + const impq & int_solver::get_value(unsigned j) const { + return lrac.m_r_x[j]; + } + + std::ostream& int_solver::display_column(std::ostream & out, unsigned j) const { + return lrac.m_r_solver.print_column_info(j, out); + } + + bool int_solver::column_is_int_inf(unsigned j) const { + return column_is_int(j) && (!value_is_int(j)); + } + + bool int_solver::is_base(unsigned j) const { + return lrac.m_r_heading[j] >= 0; + } + + bool int_solver::is_boxed(unsigned j) const { + return lrac.m_column_types[j] == column_type::boxed; + } + + bool int_solver::is_fixed(unsigned j) const { + return lrac.m_column_types[j] == column_type::fixed; + } + + bool int_solver::is_free(unsigned j) const { + return lrac.m_column_types[j] == column_type::free_column; + } + + bool int_solver::at_bound(unsigned j) const { + auto & mpq_solver = lrac.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + return + mpq_solver.m_lower_bounds[j] == get_value(j) || + mpq_solver.m_upper_bounds[j] == get_value(j); + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); + case column_type::upper_bound: + return mpq_solver.m_upper_bounds[j] == get_value(j); + default: + return false; + } + } + + bool int_solver::at_lower(unsigned j) const { + auto & mpq_solver = lrac.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); + default: + return false; + } + } + + bool int_solver::at_upper(unsigned j) const { + auto & mpq_solver = lrac.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return mpq_solver.m_upper_bounds[j] == get_value(j); + default: + return false; + } + } + + std::ostream & int_solver::display_row(std::ostream & out, lp::row_strip const & row) const { + 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()) { + impq val = get_value(c.var()) * c.coeff(); + if (!first && val.is_pos()) + out << "+"; + if (val.y.is_zero()) + out << val.x << " "; + else + out << val << " "; + } + first = false; + continue; + } + if (c.coeff().is_one()) { + if (!first) + out << "+"; + } + else if (c.coeff().is_minus_one()) + out << "-"; + else { + if (c.coeff().is_pos() && !first) + out << "+"; + if (c.coeff().is_big()) + out << " b*"; + else + out << c.coeff(); + } + out << rslv.column_name(c.var()) << " "; + first = false; + } + out << "\n"; + for (const auto &c : row) { + if (is_fixed(c.var())) + continue; + rslv.print_column_info(c.var(), out); + if (is_base(c.var())) + out << "j" << c.var() << " base\n"; + } + return out; } - return result; -} + 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; + if (settings().get_cancel_flag()) + return false; + bool inf_l = false, inf_u = false; + impq l, u; + mpq m; + 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); + return true; + } + if (column_is_int(j)) { + if (!inf_l) + l = impq(ceil(l)); + if (!inf_u) + u = impq(floor(u)); + } + if (!inf_l && !inf_u && l >= u) + return false; + + + if (inf_u) { + SASSERT(!inf_l); + impq new_val = x + m * impq(random() % (range + 1)); + lra.set_value_for_nbasic_column(j, new_val); + return true; + } + + if (inf_l) { + SASSERT(!inf_u); + impq new_val = x - m * impq(random() % (range + 1)); + lra.set_value_for_nbasic_column(j, new_val); + return true; + } + + SASSERT(!inf_l && !inf_u); + // The shift has to be a multiple of m: let us look for s, such that the shift is m*s. + // We have new_val = x+m*s <= u, so m*s <= u-x and, finally, s <= floor((u- x)/m) = a + // The symmetric reasoning gives us s >= ceil((l-x)/m) = b + // We randomly pick s in the segment [b, a] + mpq a = floor((u - x) / m); + mpq b = ceil((l - x) / m); + mpq r = a - b; + if (!r.is_pos()) + return false; + TRACE("int_solver", tout << "a = " << a << ", b = " << b << ", r = " << r<< ", m = " << m << "\n";); + if (r < mpq(range)) + range = static_cast(r.get_uint64()); + + mpq s = b + mpq(random() % (range + 1)); + impq new_val = x + m * impq(s); + TRACE("int_solver", tout << "new_val = " << new_val << "\n";); + SASSERT(l <= new_val && new_val <= u); + lra.set_value_for_nbasic_column(j, new_val); + return true; + } + + + int int_solver::select_int_infeasible_var() { + int result = -1; + mpq range; + mpq new_range; + mpq small_value(1024); + unsigned n = 0; + lar_core_solver & lcs = lra.m_mpq_lar_core_solver; + unsigned prev_usage = 0; // to quiet down the compile + + enum state { small_box, is_small_value, any_value, not_found }; + state st = not_found; + + for (unsigned j : lra.r_basis()) { + if (!column_is_int_inf(j)) + continue; + 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) { + n = 0; + st = small_box; + } + if (n == 0 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && (random() % (++n) == 0)) { + result = j; + } + continue; + } + if (st == small_box) + continue; + impq const& value = get_value(j); + if (abs(value.x) < small_value || + (has_upper(j) && small_value > upper_bound(j).x - value.x) || + (has_lower(j) && small_value > value.x - lower_bound(j).x)) { + if (st != is_small_value) { + n = 0; + st = is_small_value; + } + if (random() % (++n) == 0) + result = j; + } + if (st == is_small_value) + continue; + SASSERT(st == not_found || st == any_value); + st = any_value; + if (n == 0 || usage > prev_usage) { + result = j; + prev_usage = usage; + n = 1; + } + else if (usage > 0 && usage == prev_usage && (random() % (++n) == 0)) + result = j; + } + + return result; + } } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index c6b1bb1d0..c14fd0067 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -109,7 +109,6 @@ private: bool has_lower(unsigned j) const; bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; - bool non_basic_columns_are_at_bounds() const; bool cut_indices_are_columns() const; public: @@ -134,5 +133,5 @@ public: int select_int_infeasible_var(); - }; +}; } From a932e596eb9b1733ec953c9a8ee9d6cfb2f38195 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 10 Aug 2023 08:34:53 -1000 Subject: [PATCH 079/428] add a constructor from a variable to factor --- src/math/lp/factorization.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index b233894ad..60a8fb862 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -35,7 +35,8 @@ class factor { bool m_sign{ false }; public: factor(): factor(false) {} - factor(bool sign): m_sign(sign) {} + explicit factor(bool sign): m_sign(sign) {} + factor(lpvar var): m_var(var), m_type(factor_type::VAR), m_sign(false) {} explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t), m_sign(false) {} unsigned var() const { return m_var; } factor_type type() const { return m_type; } From eb817f779d363f7128f30b1dc3ccdf27551c374a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 11 Aug 2023 12:04:08 -1000 Subject: [PATCH 080/428] small change in factor to support TRACE --- src/math/lp/factorization.h | 3 +-- src/math/lp/nla_core.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index 60a8fb862..94c309473 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -35,8 +35,7 @@ class factor { bool m_sign{ false }; public: factor(): factor(false) {} - explicit factor(bool sign): m_sign(sign) {} - factor(lpvar var): m_var(var), m_type(factor_type::VAR), m_sign(false) {} + explicit factor(lpvar var): m_var(var), m_type(factor_type::VAR), m_sign(false) {} explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t), m_sign(false) {} unsigned var() const { return m_var; } factor_type type() const { return m_type; } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 51628b937..9baa1b026 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -232,6 +232,7 @@ public: std::ostream & print_factor(const factor& f, std::ostream& out) const; std::ostream & print_factor_with_vars(const factor& f, std::ostream& out) const; + std::ostream & print_factor_with_vars(lpvar j, std::ostream& out) const { return print_var(j, out); } std::ostream& print_monic(const monic& m, std::ostream& out) const; std::ostream& print_bfc(const factorization& m, std::ostream& out) const; std::ostream& print_monic_with_vars(unsigned i, std::ostream& out) const; From 41cac5f69eac6dbfbbdbbd4c399735218eb29a3a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 12 Aug 2023 20:34:15 -0700 Subject: [PATCH 081/428] remove output Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/sat_smt_solver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 36fc8e42f..ab0e71cc3 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -489,7 +489,6 @@ public: model_converter_ref get_model_converter() const override { const_cast(this)->convert_internalized(); - verbose_stream() << "get model converter " << (m_cached_mc.get() != nullptr) << "\n"; if (m_cached_mc) return m_cached_mc; if (is_internalized() && m_internalized_converted) { From 6366f8f6b27698d3b4d31c6a632300e8a1c4486c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Aug 2023 14:05:07 -0700 Subject: [PATCH 082/428] na Signed-off-by: Nikolaj Bjorner --- src/ast/for_each_expr.cpp | 6 +++--- src/ast/for_each_expr.h | 6 +++--- src/math/lp/lar_solver.cpp | 15 +++++---------- src/math/lp/lar_solver.h | 2 +- src/math/lp/var_register.h | 12 +++--------- src/util/util.h | 4 ++-- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 832c1d0bc..8feb217db 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -109,9 +109,9 @@ bool has_skolem_functions(expr * n) { subterms::subterms(expr_ref_vector const& es, bool include_bound, ptr_vector* esp, expr_mark* vp): m_include_bound(include_bound), m_es(es), m_esp(esp), m_vp(vp) {} subterms::subterms(expr_ref const& e, bool include_bound, ptr_vector* esp, expr_mark* vp) : m_include_bound(include_bound), m_es(e.m()), m_esp(esp), m_vp(vp) { if (e) m_es.push_back(e); } -subterms::iterator subterms::begin() { return iterator(* this, m_esp, m_vp, true); } -subterms::iterator subterms::end() { return iterator(*this, nullptr, nullptr, false); } -subterms::iterator::iterator(subterms& f, ptr_vector* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) { +subterms::iterator subterms::begin() const { return iterator(* this, m_esp, m_vp, true); } +subterms::iterator subterms::end() const { return iterator(*this, nullptr, nullptr, false); } +subterms::iterator::iterator(subterms const& f, ptr_vector* esp, expr_mark* vp, bool start): m_include_bound(f.m_include_bound), m_esp(esp), m_visitedp(vp) { if (!esp) m_esp = &m_es; else diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 0ba0dc992..77b01e939 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -186,7 +186,7 @@ public: expr_mark m_visited; expr_mark* m_visitedp = nullptr; public: - iterator(subterms& f, ptr_vector* esp, expr_mark* vp, bool start); + iterator(subterms const& f, ptr_vector* esp, expr_mark* vp, bool start); expr* operator*(); iterator operator++(int); iterator& operator++(); @@ -198,8 +198,8 @@ public: static subterms ground(expr_ref const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); } static subterms all(expr_ref_vector const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, true, esp, vp); } static subterms ground(expr_ref_vector const& e, ptr_vector* esp = nullptr, expr_mark* vp = nullptr) { return subterms(e, false, esp, vp); } - iterator begin(); - iterator end(); + iterator begin() const; + iterator end() const; }; class subterms_postorder { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 852c47397..ddfdb1437 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -258,7 +258,7 @@ namespace lp { m_crossed_bounds_column.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); - pop_tableau(); + pop_tableau(n); lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) { @@ -1326,15 +1326,15 @@ namespace lp { lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } - void lar_solver::pop_tableau() { + void lar_solver::pop_tableau(unsigned old_size) { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). // At this moment m_column_names is already popped - unsigned size = m_var_register.size(); - while (A_r().column_count() > size) + + while (A_r().column_count() > old_size) remove_last_column_from_tableau(); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); @@ -1544,12 +1544,7 @@ namespace lp { // terms bool lar_solver::all_vars_are_registered(const vector>& coeffs) { - for (const auto& p : coeffs) { - if (p.second >= m_var_register.size()) { - return false; - } - } - return true; + return all_of(coeffs, [&](const auto& p) { return p.second < m_var_register.size(); }); } void lar_solver::subst_known_terms(lar_term* t) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 9faa40979..7c1f9575b 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -225,7 +225,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 pop_tableau(unsigned old_size); 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; diff --git a/src/math/lp/var_register.h b/src/math/lp/var_register.h index 49767274d..3b326788b 100644 --- a/src/math/lp/var_register.h +++ b/src/math/lp/var_register.h @@ -72,9 +72,8 @@ public: svector vars() const { svector ret; - for (const auto& p : m_local_to_external) { + for (const auto& p : m_local_to_external) ret.push_back(p.external_j()); - } return ret; } @@ -126,11 +125,7 @@ public: } bool has_int_var() const { - for (const auto & vi : m_local_to_external) { - if (vi.is_integer()) - return true; - } - return false; + return any_of(m_local_to_external, [&](const auto& vi) { return vi.is_integer(); }); } bool local_is_int(unsigned j) const { @@ -138,9 +133,8 @@ public: } void shrink(unsigned shrunk_size) { - for (unsigned j = size(); j-- > shrunk_size;) { + for (unsigned j = size(); j-- > shrunk_size;) m_external_to_local.erase(m_local_to_external[j].external_j()); - } m_local_to_external.resize(shrunk_size); } diff --git a/src/util/util.h b/src/util/util.h index 6d4efb671..1e2310eb3 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -363,7 +363,7 @@ void set_fatal_error_handler(void (*pfn)(int error_code)); template -bool any_of(S& set, T const& p) { +bool any_of(S const& set, T const& p) { for (auto const& s : set) if (p(s)) return true; @@ -371,7 +371,7 @@ bool any_of(S& set, T const& p) { } template -bool all_of(S& set, T const& p) { +bool all_of(S const& set, T const& p) { for (auto const& s : set) if (!p(s)) return false; From 33c35b0c316cc9da3a3bb14e56d1cbb18d0961e8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Aug 2023 14:49:25 -0700 Subject: [PATCH 083/428] fix #6851 Signed-off-by: Nikolaj Bjorner --- src/math/lp/factorization.h | 11 +++++------ src/math/lp/nla_order_lemmas.cpp | 4 ++-- src/tactic/arith/purify_arith_tactic.cpp | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index 94c309473..04529d033 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -30,13 +30,12 @@ struct factorization_factory; enum class factor_type { VAR, MON }; class factor { - lpvar m_var{ UINT_MAX }; - factor_type m_type{ factor_type::VAR }; - bool m_sign{ false }; + lpvar m_var = UINT_MAX; + factor_type m_type = factor_type::VAR; + bool m_sign = false; public: - factor(): factor(false) {} - explicit factor(lpvar var): m_var(var), m_type(factor_type::VAR), m_sign(false) {} - explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t), m_sign(false) {} + factor() { } + explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t) {} unsigned var() const { return m_var; } factor_type type() const { return m_type; } void set(lpvar v, factor_type t) { m_var = v; m_type = t; } diff --git a/src/math/lp/nla_order_lemmas.cpp b/src/math/lp/nla_order_lemmas.cpp index 94ddc4d9b..2cf5ac004 100644 --- a/src/math/lp/nla_order_lemmas.cpp +++ b/src/math/lp/nla_order_lemmas.cpp @@ -116,7 +116,7 @@ void order::order_lemma_on_factor_binomial_rm(const monic& ac, bool k, const mon tout << "bd=" << pp_mon_with_vars(_(), bd) << "\n"; ); factor d(_().m_evars.find(ac.vars()[k]).var(), factor_type::VAR); - factor b(false); + factor b; if (c().divide(bd, d, b)) { order_lemma_on_binomial_ac_bd(ac, k, bd, b, d.var()); } @@ -192,7 +192,7 @@ bool order::order_lemma_on_ac_and_bc(const monic& rm_ac, tout << "rm_bd = " << pp_mon_with_vars(_(), rm_bd) << "\n"; tout << "ac_f[k] = "; c().print_factor_with_vars(ac_f[k], tout);); - factor b(false); + factor b; return c().divide(rm_bd, ac_f[k], b) && order_lemma_on_ac_and_bc_and_factors(rm_ac, ac_f[!k], ac_f[k], rm_bd, b); diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index db1986398..40ee967ba 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -444,7 +444,7 @@ struct purify_arith_proc { expr * x = args[0]; bool is_int = u().is_int(x); - expr * k = mk_fresh_var(is_int); + expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); @@ -454,7 +454,7 @@ struct purify_arith_proc { if (y.is_zero()) { expr* p0; if (is_int) { - if (!m_ipower0) m_ipower0 = mk_fresh_var(true); + if (!m_ipower0) m_ipower0 = mk_fresh_var(false); p0 = m_ipower0; } else { From b04e48f374e1e8b1bba3fef5f2a13c4bf2f25f53 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Aug 2023 15:06:39 -0700 Subject: [PATCH 084/428] fix #6850 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/pdecl.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index a64d0ede9..f343be94d 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -838,14 +838,18 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { } format * pp(smt2_pp_environment& env, pdecl_manager const & m) const override { + symbol s = m_decl->get_name(); + std::string name = s.str(); + if (is_smt2_quoted_symbol(s)) + name = mk_smt2_quoted_symbol(s); if (m_args.empty()) { - return mk_string(m.m(), m_decl->get_name().str()); + return mk_string(m.m(), name); } else { ptr_buffer b; for (auto arg : m_args) b.push_back(m.pp(env, arg)); - return mk_seq1(m.m(), b.begin(), b.end(), f2f(), m_decl->get_name().str()); + return mk_seq1(m.m(), b.begin(), b.end(), f2f(), name); } } }; @@ -874,12 +878,17 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { } format * pp(smt2_pp_environment& env, pdecl_manager const & m) const override { + symbol s = m_decl->get_name(); + std::string name = s.str(); + if (is_smt2_quoted_symbol(s)) + name = mk_smt2_quoted_symbol(s); + if (m_indices.empty()) { - return mk_string(m.m(), m_decl->get_name().str()); + return mk_string(m.m(), name); } else { ptr_buffer b; - b.push_back(mk_string(m.m(), m_decl->get_name().str())); + b.push_back(mk_string(m.m(), name)); for (auto idx : m_indices) b.push_back(mk_unsigned(m.m(), idx)); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), "_"); From 50717fb655eda6c4c297d2947adf79a7387e4f1b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Aug 2023 09:32:43 -0700 Subject: [PATCH 085/428] update pattern for glibc Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 5020792fc..7026b195e 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,7 +24,7 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'glibc-2.35' : ('so', 'linux-x64'), + 'x64-glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), From 1be692002d1da07a6e2b1a29bf1a23f38d94c0f4 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Wed, 16 Aug 2023 13:07:30 -0400 Subject: [PATCH 086/428] split on all ite terms. fix #6852 (#6859) --- src/qe/mbp/mbp_basic_tg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index ee83012a7..70d25d89b 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -43,6 +43,8 @@ struct mbp_basic_tg::impl { void mark_seen(expr *t) { m_seen.mark(t); } bool is_seen(expr *t) { return m_seen.is_marked(t); } + //Split on all ite terms, irrespective of whether + //they contain variables/are c-ground bool apply() { if (!m_use_mdl) return false; expr *term, *c, *th, *el; @@ -60,7 +62,6 @@ struct mbp_basic_tg::impl { SASSERT(!m.is_implies(term)); if (is_seen(term)) continue; - if (m_tg.is_cgr(term)) continue; if (m.is_ite(term, c, th, el)) { mark_seen(term); progress = true; From 51df7b75ce19d08a1e550be3ed9fed76d69f031e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 15:18:22 -0700 Subject: [PATCH 087/428] fix 6800 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_axioms.cpp | 2 ++ src/math/lp/int_solver.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 4d7da4d7f..67c1b757f 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -538,6 +538,8 @@ namespace seq { expr_ref t_eq_empty = mk_eq_empty(t); expr_ref xsy = mk_concat(x, s, y); + verbose_stream() << s << " " << t << "\n"; + // add_clause(~mk_eq(t, s), i_eq_0); add_clause(cnt, i_eq_m1); add_clause(~t_eq_empty, s_eq_empty, i_eq_m1); add_clause(~s_eq_empty, mk_eq(i, mk_len(t))); diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 0c83ea5bf..dbb5dbae2 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -31,7 +31,7 @@ namespace lp { lra.remove_fixed_vars_from_base(); lp_assert(lia.is_feasible()); for (unsigned j : lra.r_basis()) - if (!lra.get_value(j).is_int()) + if (!lra.get_value(j).is_int() && lra.column_is_int(j)) patch_basic_column(j); if (!lia.has_inf_int()) { lia.settings().stats().m_patches_success++; From 63ea8efcfbf0494759c45088b9c47215f37757ed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 15:20:12 -0700 Subject: [PATCH 088/428] remove output Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_axioms.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 67c1b757f..b0d3add0b 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -538,7 +538,6 @@ namespace seq { expr_ref t_eq_empty = mk_eq_empty(t); expr_ref xsy = mk_concat(x, s, y); - verbose_stream() << s << " " << t << "\n"; // add_clause(~mk_eq(t, s), i_eq_0); add_clause(cnt, i_eq_m1); add_clause(~t_eq_empty, s_eq_empty, i_eq_m1); From 252a30e727e8c5cc290eea8d6deeba63f16ce4c2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson <5377127+levnach@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:44:27 -0700 Subject: [PATCH 089/428] use param_ref in nla_solver (#6862) * use param_ref in nla_solver Signed-off-by: Lev Nachmanson * add parameters Signed-off-by: Nikolaj Bjorner * add parameters Signed-off-by: Nikolaj Bjorner * replace nla_setting by command line parameters * delete nla_setting.h --------- Signed-off-by: Lev Nachmanson Signed-off-by: Nikolaj Bjorner Co-authored-by: Nikolaj Bjorner --- package-lock.json | 27 ++-------------- src/math/lp/horner.cpp | 4 +-- src/math/lp/nla_common.cpp | 8 ++--- src/math/lp/nla_core.cpp | 11 ++++--- src/math/lp/nla_core.h | 15 ++++----- src/math/lp/nla_grobner.cpp | 36 ++++++++++++---------- src/math/lp/nla_grobner.h | 3 +- src/math/lp/nla_order_lemmas.cpp | 2 +- src/math/lp/nla_settings.h | 46 ---------------------------- src/math/lp/nla_solver.cpp | 7 ++--- src/math/lp/nla_solver.h | 4 +-- src/math/lp/nla_tangent_lemmas.cpp | 2 +- src/sat/smt/arith_internalize.cpp | 19 +----------- src/smt/params/smt_params_helper.pyg | 4 ++- src/smt/theory_lra.cpp | 19 +----------- src/test/lp/nla_solver_test.cpp | 24 +++++++-------- 16 files changed, 67 insertions(+), 164 deletions(-) delete mode 100644 src/math/lp/nla_settings.h diff --git a/package-lock.json b/package-lock.json index 4c22d5b85..ab471dfcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,6 @@ { + "name": "z3", + "lockfileVersion": 3, "requires": true, - "lockfileVersion": 1, - "dependencies": { - "async-mutex": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", - "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", - "requires": { - "tslib": "^2.3.1" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "z3-solver": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/z3-solver/-/z3-solver-4.9.0.tgz", - "integrity": "sha512-clSV0uyHsfrO84pSbHxoqvmd5HgSG4CoSJG2f8U65hBVylbV6p/0svctQWee9W2fWo0IsxHYRjxz2Z85GT0LAA==", - "requires": { - "async-mutex": "^0.3.2" - } - } - } + "packages": {} } diff --git a/src/math/lp/horner.cpp b/src/math/lp/horner.cpp index 51e2f533e..40f16d709 100644 --- a/src/math/lp/horner.cpp +++ b/src/math/lp/horner.cpp @@ -40,7 +40,7 @@ bool horner::row_has_monomial_to_refine(const T& row) const { template bool horner::row_is_interesting(const T& row) const { TRACE("nla_solver_details", c().print_row(row, tout);); - if (row.size() > c().m_nla_settings.horner_row_length_limit) { + if (row.size() > c().params().arith_nl_horner_row_length_limit()) { TRACE("nla_solver_details", tout << "disregard\n";); return false; } @@ -98,7 +98,7 @@ bool horner::lemmas_on_row(const T& row) { } bool horner::horner_lemmas() { - if (!c().m_nla_settings.run_horner) { + if (!c().params().arith_nl_horner()) { TRACE("nla_solver", tout << "not generating horner lemmas\n";); return false; } diff --git a/src/math/lp/nla_common.cpp b/src/math/lp/nla_common.cpp index 45898c613..00f2ff4ee 100644 --- a/src/math/lp/nla_common.cpp +++ b/src/math/lp/nla_common.cpp @@ -71,11 +71,11 @@ void common::add_deps_of_fixed(lpvar j, u_dependency*& dep) { // creates a nex expression for the coeff and var, nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_dependency*& dep) { SASSERT(!coeff.is_zero()); - if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(j)) { + if (c().params().arith_nl_horner_subs_fixed() == 1 && c().var_is_fixed(j)) { add_deps_of_fixed(j, dep); return cn.mk_scalar(coeff * c().m_lar_solver.column_lower_bound(j).x); } - if (c().m_nla_settings.horner_subs_fixed == 2 && c().var_is_fixed_to_zero(j)) { + if (c().params().arith_nl_horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(j)) { add_deps_of_fixed(j, dep); return cn.mk_scalar(rational(0)); } @@ -89,10 +89,10 @@ nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_depende mf *= coeff; u_dependency * initial_dep = dep; for (lpvar k : m.vars()) { - if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(k)) { + if (c().params().arith_nl_horner_subs_fixed() == 1 && c().var_is_fixed(k)) { add_deps_of_fixed(k, dep); mf *= c().m_lar_solver.column_lower_bound(k).x; - } else if (c().m_nla_settings.horner_subs_fixed == 2 && + } else if (c().params().arith_nl_horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(k)) { dep = initial_dep; add_deps_of_fixed(k, dep); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 86ddb05b7..54f955760 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -21,10 +21,11 @@ namespace nla { typedef lp::lar_term term; -core::core(lp::lar_solver& s, reslimit & lim) : +core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_evars(), m_lar_solver(s), m_reslim(lim), + m_params(p), m_tangents(this), m_basics(this), m_order(this), @@ -1428,7 +1429,7 @@ void core::patch_monomials_on_to_refine() { void core::patch_monomials() { m_cautious_patching = true; patch_monomials_on_to_refine(); - if (m_to_refine.size() == 0 || !m_nla_settings.expensive_patching) { + if (m_to_refine.size() == 0 || !params().arith_nl_expensive_patching()) { return; } NOT_IMPLEMENTED_YET(); @@ -1566,11 +1567,11 @@ lbool core::check(vector& l_vec) { check_weighted(3, checks); unsigned num_calls = lp_settings().stats().m_nla_calls; - if (!conflict_found() && m_nla_settings.run_nra && num_calls % 50 == 0 && num_calls > 500) + if (!conflict_found() && params().arith_nl_nra() && num_calls % 50 == 0 && num_calls > 500) ret = bounded_nlsat(); } - if (l_vec.empty() && !done() && m_nla_settings.run_nra && ret == l_undef) { + if (l_vec.empty() && !done() && params().arith_nl_nra() && ret == l_undef) { ret = m_nra.check(); m_stats.m_nra_calls++; } @@ -1590,7 +1591,7 @@ lbool core::check(vector& l_vec) { } bool core::should_run_bounded_nlsat() { - if (!m_nla_settings.run_nra) + if (!params().arith_nl_nra()) return false; if (m_nlsat_delay > m_nlsat_fails) ++m_nlsat_fails; diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 9baa1b026..78f46bb41 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -22,12 +22,12 @@ #include "math/lp/nla_powers.h" #include "math/lp/nla_divisions.h" #include "math/lp/emonics.h" -#include "math/lp/nla_settings.h" #include "math/lp/nex.h" #include "math/lp/horner.h" #include "math/lp/monomial_bounds.h" #include "math/lp/nla_intervals.h" #include "nlsat/nlsat_solver.h" +#include "smt/params/smt_params_helper.hpp" namespace nra { class solver; @@ -54,7 +54,6 @@ class core { friend struct tangents; friend class monotone; friend class powers; - friend struct nla_settings; friend class intervals; friend class horner; friend class solver; @@ -82,6 +81,7 @@ class core { lp::lar_solver& m_lar_solver; reslimit& m_reslim; + smt_params_helper m_params; std::function m_relevant; vector * m_lemma_vec; indexed_uint_set m_to_refine; @@ -93,8 +93,7 @@ class core { divisions m_divisions; intervals m_intervals; monomial_bounds m_monomial_bounds; - nla_settings m_nla_settings; - + horner m_horner; grobner m_grobner; emonics m_emons; @@ -113,7 +112,7 @@ class core { public: // constructor - core(lp::lar_solver& s, reslimit&); + core(lp::lar_solver& s, params_ref const& p, reslimit&); void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); @@ -165,13 +164,15 @@ public: lpvar var(const factor& f) const { return f.var(); } + smt_params_helper const & params() const { return m_params; } + // returns true if the combination of the Horner's schema and Grobner Basis should be called bool need_run_horner() const { - return m_nla_settings.run_horner && lp_settings().stats().m_nla_calls % m_nla_settings.horner_frequency == 0; + return params().arith_nl_horner() && lp_settings().stats().m_nla_calls % params().arith_nl_horner_frequency() == 0; } bool need_run_grobner() const { - return m_nla_settings.run_grobner && lp_settings().stats().m_nla_calls % m_nla_settings.grobner_frequency == 0; + return params().arith_nl_grobner() && lp_settings().stats().m_nla_calls % params().arith_nl_grobner_frequency() == 0; } void set_active_vars_weights(nex_creator&); diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 4c2f879cf..eefb07fac 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -24,8 +24,8 @@ namespace nla { common(c), m_pdd_manager(m_core.m_lar_solver.number_of_vars()), m_solver(m_core.m_reslim, m_pdd_manager), - m_lar_solver(m_core.m_lar_solver) - + m_lar_solver(m_core.m_lar_solver), + m_quota(m_core.params().arith_nl_gr_q()) {} lp::lp_settings& grobner::lp_settings() { @@ -33,8 +33,10 @@ namespace nla { } void grobner::operator()() { - unsigned& quota = c().m_nla_settings.grobner_quota; - if (quota == 1) + if (m_quota == 0) + m_quota = c().params().arith_nl_gr_q(); + + if (m_quota == 1) return; lp_settings().stats().m_grobner_calls++; @@ -59,12 +61,14 @@ namespace nla { } - if (quota > 1) - quota--; + if (m_quota > 0) + --m_quota; - IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << quota << "\n"); + IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << m_quota << "\n"); IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream())); + + #if 0 // diagnostics: did we miss something vector eqs; @@ -239,11 +243,11 @@ namespace nla { struct dd::solver::config cfg; cfg.m_max_steps = m_solver.equations().size(); - cfg.m_max_simplified = c().m_nla_settings.grobner_max_simplified; - cfg.m_eqs_growth = c().m_nla_settings.grobner_eqs_growth; - cfg.m_expr_size_growth = c().m_nla_settings.grobner_expr_size_growth; - cfg.m_expr_degree_growth = c().m_nla_settings.grobner_expr_degree_growth; - cfg.m_number_of_conflicts_to_report = c().m_nla_settings.grobner_number_of_conflicts_to_report; + cfg.m_max_simplified = c().params().arith_nl_grobner_max_simplified(); + cfg.m_eqs_growth = c().params().arith_nl_grobner_eqs_growth(); + cfg.m_expr_size_growth = c().params().arith_nl_grobner_expr_size_growth(); + cfg.m_expr_degree_growth = c().params().arith_nl_grobner_expr_degree_growth(); + cfg.m_number_of_conflicts_to_report = c().params().arith_nl_grobner_cnfl_to_report(); m_solver.set(cfg); m_solver.adjust_cfg(); m_pdd_manager.set_max_num_nodes(10000); // or something proportional to the number of initial nodes. @@ -348,9 +352,9 @@ namespace nla { unsigned k = m_lar_solver.get_base_column_in_row(row); if (m_lar_solver.column_is_free(k) && k != j) continue; - CTRACE("grobner", matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit, + CTRACE("grobner", matrix.m_rows[row].size() > c().params().arith_nl_grobner_row_length_limit(), tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";); - if (matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit) + if (matrix.m_rows[row].size() > c().params().arith_nl_horner_row_length_limit()) continue; for (auto& rc : matrix.m_rows[row]) add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q); @@ -373,12 +377,12 @@ namespace nla { while (!vars.empty()) { j = vars.back(); vars.pop_back(); - if (c().m_nla_settings.grobner_subs_fixed > 0 && c().var_is_fixed_to_zero(j)) { + if (c().params().arith_nl_grobner_subs_fixed() > 0 && c().var_is_fixed_to_zero(j)) { r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, zero_dep)); dep = zero_dep; return r; } - if (c().m_nla_settings.grobner_subs_fixed == 1 && c().var_is_fixed(j)) + if (c().params().arith_nl_grobner_subs_fixed() == 1 && c().var_is_fixed(j)) r *= val_of_fixed_var_with_deps(j, dep); else if (!c().is_monic_var(j)) r *= m_pdd_manager.mk_var(j); diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index ed3aa77db..f8e21d1a7 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -22,7 +22,8 @@ namespace nla { dd::pdd_manager m_pdd_manager; dd::solver m_solver; lp::lar_solver& m_lar_solver; - indexed_uint_set m_rows; + indexed_uint_set m_rows; + unsigned m_quota = 0; lp::lp_settings& lp_settings(); diff --git a/src/math/lp/nla_order_lemmas.cpp b/src/math/lp/nla_order_lemmas.cpp index 2cf5ac004..902e208fc 100644 --- a/src/math/lp/nla_order_lemmas.cpp +++ b/src/math/lp/nla_order_lemmas.cpp @@ -19,7 +19,7 @@ typedef lp::lar_term term; // a > b && c > 0 => ac > bc void order::order_lemma() { TRACE("nla_solver", ); - if (!c().m_nla_settings.run_order) { + if (!c().params().arith_nl_order()) { TRACE("nla_solver", tout << "not generating order lemmas\n";); return; } diff --git a/src/math/lp/nla_settings.h b/src/math/lp/nla_settings.h deleted file mode 100644 index ec11ea5b2..000000000 --- a/src/math/lp/nla_settings.h +++ /dev/null @@ -1,46 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Author: - - Lev Nachmanson (levnach) - ---*/ - -#pragma once -namespace nla { - struct nla_settings { - bool run_order = true; - bool run_tangents = true; - - // horner fields - bool run_horner = true; - unsigned horner_frequency = 4; - unsigned horner_row_length_limit = 10; - unsigned horner_subs_fixed = 2; - - - // grobner fields - bool run_grobner = true; - unsigned grobner_row_length_limit = 50; - unsigned grobner_subs_fixed = 1; - unsigned grobner_eqs_growth = 10; - unsigned grobner_tree_size_growth = 2; - unsigned grobner_expr_size_growth = 2; - unsigned grobner_expr_degree_growth = 2; - unsigned grobner_max_simplified = 10000; - unsigned grobner_number_of_conflicts_to_report = 1; - unsigned grobner_quota = 0; - unsigned grobner_frequency = 4; - - - // nra fields - bool run_nra = false; - - // expensive patching - bool expensive_patching = false; - - nla_settings() {} - - }; -} diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index bd0f1953c..0e6efd526 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -16,9 +16,6 @@ #include "math/polynomial/algebraic_numbers.h" namespace nla { - - nla_settings& solver::settings() { return m_core->m_nla_settings; } - void solver::add_monic(lpvar v, unsigned sz, lpvar const* vs) { m_core->add_monic(v, sz, vs); } @@ -57,8 +54,8 @@ namespace nla { m_core->pop(n); } - solver::solver(lp::lar_solver& s, reslimit& limit): - m_core(alloc(core, s, limit)) { + solver::solver(lp::lar_solver& s, params_ref const& p, reslimit& limit): + m_core(alloc(core, s, p, limit)) { } bool solver::influences_nl_var(lpvar j) const { diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index d04ff8e51..d61b0593b 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -13,7 +13,6 @@ Author: #include "util/params.h" #include "math/lp/lar_solver.h" #include "math/lp/monic.h" -#include "math/lp/nla_settings.h" #include "math/lp/nla_core.h" namespace nra { class solver; @@ -25,7 +24,7 @@ namespace nla { core* m_core; public: - solver(lp::lar_solver& s, reslimit& limit); + solver(lp::lar_solver& s, params_ref const& p, reslimit& limit); ~solver(); void add_monic(lpvar v, unsigned sz, lpvar const* vs); @@ -34,7 +33,6 @@ namespace nla { void add_bounded_division(lpvar q, lpvar x, lpvar y); void check_bounded_divisions(vector&); void set_relevant(std::function& is_relevant); - nla_settings& settings(); void push(); void pop(unsigned scopes); bool need_check(); diff --git a/src/math/lp/nla_tangent_lemmas.cpp b/src/math/lp/nla_tangent_lemmas.cpp index 4ba9eeccc..56ebbfacc 100644 --- a/src/math/lp/nla_tangent_lemmas.cpp +++ b/src/math/lp/nla_tangent_lemmas.cpp @@ -186,7 +186,7 @@ tangents::tangents(core * c) : common(c) {} void tangents::tangent_lemma() { factorization bf(nullptr); const monic* m = nullptr; - if (c().m_nla_settings.run_tangents && c().find_bfc_to_refine(m, bf)) { + if (c().params().arith_nl_tangents() && c().find_bfc_to_refine(m, bf)) { lpvar j = m->var(); tangent_imp tangent(point(val(bf[0]), val(bf[1])), c().val(j), *m, bf, *this); tangent(); diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 65ed5ac42..9a4399b96 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -61,29 +61,12 @@ namespace arith { void solver::ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), m.limit()); + m_nla = alloc(nla::solver, *m_solver.get(), s().params(), m.limit()); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); } smt_params_helper prms(s().params()); - m_nla->settings().run_order = prms.arith_nl_order(); - m_nla->settings().run_tangents = prms.arith_nl_tangents(); - m_nla->settings().run_horner = prms.arith_nl_horner(); - m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed(); - m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency(); - m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit(); - m_nla->settings().run_grobner = prms.arith_nl_grobner(); - m_nla->settings().run_nra = prms.arith_nl_nra(); - m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed(); - m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth(); - m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth(); - m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth(); - m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified(); - m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report(); - m_nla->settings().grobner_quota = prms.arith_nl_gr_q(); - m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency(); - m_nla->settings().expensive_patching = false; } } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 2bb3d4197..f059dccb8 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -58,7 +58,8 @@ def_module_params(module_name='smt', ('arith.solver', UINT, 6, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation, relevant only if smt.arith.solver=2'), ('arith.nl.nra', BOOL, True, 'call nra_solver when incremental linearization does not produce a lemma, this option is ignored when arith.nl=false, relevant only if smt.arith.solver=6'), - ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters, relevant only if smt.arith.solver=2'), + ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), + ('arith.nl.expensive_patching', BOOL, False, 'use the expensive of monomials'), ('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2'), ('arith.nl.order', BOOL, True, 'run order lemmas'), ('arith.nl.expp', BOOL, False, 'expensive patching'), @@ -67,6 +68,7 @@ def_module_params(module_name='smt', ('arith.nl.horner_subs_fixed', UINT, 2, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'), ('arith.nl.horner_frequency', UINT, 4, 'horner\'s call frequency'), ('arith.nl.horner_row_length_limit', UINT, 10, 'row is disregarded by the heuristic if its length is longer than the value'), + ('arith.nl.grobner_row_length_limit', UINT, 10, 'row is disregarded by the heuristic if its length is longer than the value'), ('arith.nl.grobner_frequency', UINT, 4, 'grobner\'s call frequency'), ('arith.nl.grobner', BOOL, True, 'run grobner\'s basis heuristic'), ('arith.nl.grobner_eqs_growth', UINT, 10, 'grobner\'s number of equalities growth '), diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 87de2d9e2..e134b13e2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -263,7 +263,7 @@ class theory_lra::imp { void ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), m.limit()); + m_nla = alloc(nla::solver, *m_solver.get(), ctx().get_params(), m.limit()); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); @@ -274,23 +274,6 @@ class theory_lra::imp { }; m_nla->set_relevant(is_relevant); smt_params_helper prms(ctx().get_params()); - m_nla->settings().run_order = prms.arith_nl_order(); - m_nla->settings().run_tangents = prms.arith_nl_tangents(); - m_nla->settings().run_horner = prms.arith_nl_horner(); - m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed(); - m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency(); - m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit(); - m_nla->settings().run_grobner = prms.arith_nl_grobner(); - m_nla->settings().run_nra = prms.arith_nl_nra(); - m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed(); - m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth(); - m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth(); - m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth(); - m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified(); - m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report(); - m_nla->settings().grobner_quota = prms.arith_nl_gr_q(); - m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency(); - m_nla->settings().expensive_patching = false; } } diff --git a/src/test/lp/nla_solver_test.cpp b/src/test/lp/nla_solver_test.cpp index 6e2e0336a..ce934e7ca 100644 --- a/src/test/lp/nla_solver_test.cpp +++ b/src/test/lp/nla_solver_test.cpp @@ -169,7 +169,7 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); v.clear(); @@ -246,7 +246,7 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); @@ -317,7 +317,7 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); create_abcde(nla, lp_a, @@ -379,7 +379,7 @@ void test_basic_lemma_for_mon_zero_from_monomial_to_factors() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial acd unsigned_vector vec; @@ -439,7 +439,7 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); create_abcde(nla, lp_a, @@ -514,7 +514,7 @@ void test_horner() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); vector v; v.push_back(a); v.push_back(b); nla.add_monic(lp_ab, v.size(), v.begin()); @@ -551,7 +551,7 @@ void test_basic_sign_lemma() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial bde vector vec; @@ -626,7 +626,7 @@ void test_order_lemma_params(bool var_equiv, int sign) { reslimit l; params_ref p; - solver nla(s,l); + solver nla(s,p,l); // create monomial ab vector vec; vec.push_back(lp_a); @@ -757,7 +757,7 @@ void test_monotone_lemma() { reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); @@ -814,7 +814,7 @@ void test_tangent_lemma_rat() { s_set_column_value_test(s, lp_ab, v); reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); @@ -841,7 +841,7 @@ void test_tangent_lemma_reg() { s_set_column_value_test(s, lp_ab, rational(11)); reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); @@ -885,7 +885,7 @@ void test_tangent_lemma_equiv() { s_set_column_value_test(s, lp_a, - s.get_column_value(lp_k)); reslimit l; params_ref p; - solver nla(s, l); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); From 73724f9cab4e12eda8404e675bd03d08a6a49a6d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 18:45:49 -0700 Subject: [PATCH 090/428] lines that go away Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_internalize.cpp | 1 - src/smt/theory_lra.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 9a4399b96..3174ad775 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -66,7 +66,6 @@ namespace arith { (void)_s; m_nla->push(); } - smt_params_helper prms(s().params()); } } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index e134b13e2..6dc14fb13 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -273,7 +273,6 @@ class theory_lra::imp { return ctx().is_relevant(th.get_enode(u)); }; m_nla->set_relevant(is_relevant); - smt_params_helper prms(ctx().get_params()); } } From 63f18a1d990d32f50cf95a1854a4ad15edc0ac42 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 18:47:05 -0700 Subject: [PATCH 091/428] #6822 - change to 2.31 for nuget packaging Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 7026b195e..cf7140e5e 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,7 +24,7 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'x64-glibc-2.35' : ('so', 'linux-x64'), + 'x64-glibc-2.31' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), From 8aa35f7fdb023950fd91c25c36edfcb990dce197 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 18:54:00 -0700 Subject: [PATCH 092/428] remove package lock Signed-off-by: Nikolaj Bjorner --- package-lock.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ab471dfcd..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "z3", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} From 59b56f2ce79bd4bc4ffe1c371cae7a1fd1af3ac6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 17 Aug 2023 19:13:50 -0700 Subject: [PATCH 093/428] update unit test to be compatible with C++ vs C exception semantics #6537 Signed-off-by: Nikolaj Bjorner --- src/test/smt2print_parse.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 765a78060..da4badf3e 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -64,26 +64,22 @@ void test_parseprint(char const* spec) { Z3_del_context(ctx); } +static bool is_error = false; +void setError(Z3_context c, Z3_error_code e) { + is_error = true; +} + void test_eval(Z3_context ctx, Z3_string spec, bool shouldFail) { std::cout << "spec:\n" << spec << "\n"; std::string resp; - bool failed = false; - try { - resp = Z3_eval_smtlib2_string(ctx, spec); - } - catch (std::runtime_error& e) { - resp = e.what(); - failed = true; - } - catch (...) { - resp = "unknown exception"; - failed = true; - } + is_error = false; + resp = Z3_eval_smtlib2_string(ctx, spec); - std::cout << "response:\n" << resp << "\n"; + if (!is_error) + std::cout << "response:\n" << resp << "\n"; - if (shouldFail != failed) { + if (shouldFail != is_error) { if (shouldFail) throw std::runtime_error("should have failed"); else @@ -91,9 +87,6 @@ void test_eval(Z3_context ctx, Z3_string spec, bool shouldFail) { } } -void throwError(Z3_context c, Z3_error_code e) { - throw std::runtime_error(Z3_get_error_msg(c, e)); -} void test_repeated_eval() { // Z3_eval_smtlib2_string reuses the parser and the scanner @@ -142,7 +135,7 @@ void test_repeated_eval() { "(pop)\n"; Z3_context ctx = Z3_mk_context(nullptr); - Z3_set_error_handler(ctx, throwError); + Z3_set_error_handler(ctx, setError); std::cout << "testing Z3_eval_smtlib2_string\n"; try { From a8c4384536019e438adb7a8df6d24e93d3431ab6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 18 Aug 2023 07:58:48 -0700 Subject: [PATCH 094/428] download 20.04 Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 154902d4c..f0a775d2d 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -255,6 +255,11 @@ stages: inputs: artifact: 'Ubuntu' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu 20.04 Build' + inputs: + artifact: 'Ubuntu-20.04' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu ARM64 Build' inputs: From 610313946d7d982236a381afdafedeeeaafca635 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 18 Aug 2023 12:36:14 -0700 Subject: [PATCH 095/428] split free vars in nla --- src/math/lp/lp_settings.h | 3 +- src/math/lp/nla_core.cpp | 97 ++++++++++++++++++++++++++++-------- src/math/lp/nla_core.h | 7 ++- src/math/lp/nla_solver.cpp | 4 +- src/math/lp/nla_solver.h | 2 +- src/sat/smt/arith_solver.cpp | 60 +++++++++++++--------- src/sat/smt/arith_solver.h | 3 ++ src/smt/theory_lra.cpp | 62 +++++++++++++---------- 8 files changed, 165 insertions(+), 73 deletions(-) diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 727bc3531..7ffffe5c5 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -115,6 +115,7 @@ struct statistics { unsigned m_hnf_cutter_calls; unsigned m_hnf_cuts; unsigned m_nla_calls; + unsigned m_nla_bounds; unsigned m_horner_calls; unsigned m_horner_conflicts; unsigned m_cross_nested_forms; @@ -144,7 +145,7 @@ struct statistics { st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-offset-eqs", m_offset_eqs); st.update("arith-fixed-eqs", m_fixed_eqs); - + st.update("arith-nla-bounds", m_nla_bounds); } }; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 54f955760..d06815410 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -811,6 +811,7 @@ void core::print_stats(std::ostream& out) { void core::clear() { m_lemma_vec->clear(); + m_literal_vec->clear(); } void core::init_search() { @@ -1504,11 +1505,62 @@ void core::check_bounded_divisions(vector& l_vec) { m_divisions.check_bounded_divisions(); } -lbool core::check(vector& l_vec) { +bool core::can_add_bound(unsigned j, u_map& bounds) { + unsigned count = 1; + if (bounds.find(j, count)) { + if (count >= 2) + return false; + ++count; + } + bounds.insert(j, count); + struct decrement : public trail { + u_map& bounds; + unsigned j; + decrement(u_map& bounds, unsigned j): + bounds(bounds), + j(j) + {} + void undo() override { + --bounds[j]; + } + }; + trail().push(decrement(bounds, j)); + return true; +} + +void core::add_bounds() { + unsigned r = random(), sz = m_to_refine.size(); + for (unsigned k = 0; k < sz; k++) { + lpvar i = m_to_refine[(k + r) % sz]; + auto const& m = m_emons[i]; + for (lpvar j : m.vars()) { + //m_lar_solver.print_column_info(j, verbose_stream() << "check variable " << j << " ") << "\n"; + if (var_is_free(j)) + m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); +#if 0 + else if (has_lower_bound(j) && can_add_bound(j, m_lower_bounds_added)) { + m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::LE, get_lower_bound(j))); + std::cout << "called lower\n"; + } + else if (has_upper_bound(j) && can_add_bound(j, m_upper_bounds_added)) { + m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::GE, get_upper_bound(j))); + std::cout << "called upper\n"; + } +#endif + else + continue; + ++lp_settings().stats().m_nla_bounds; + return; + } + } +} + +lbool core::check(vector& lits, vector& l_vec) { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); m_lar_solver.get_rid_of_inf_eps(); m_lemma_vec = &l_vec; + m_literal_vec = &lits; if (!(m_lar_solver.get_status() == lp::lp_status::OPTIMAL || m_lar_solver.get_status() == lp::lp_status::FEASIBLE)) { TRACE("nla_solver", tout << "unknown because of the m_lar_solver.m_status = " << m_lar_solver.get_status() << "\n";); @@ -1518,40 +1570,44 @@ lbool core::check(vector& l_vec) { init_to_refine(); patch_monomials(); set_use_nra_model(false); - if (m_to_refine.empty()) { return l_true; } + if (m_to_refine.empty()) + return l_true; init_search(); lbool ret = l_undef; bool run_grobner = need_run_grobner(); bool run_horner = need_run_horner(); bool run_bounded_nlsat = should_run_bounded_nlsat(); + bool run_bounds = params().arith_nl_branching(); - if (l_vec.empty() && !done()) + auto no_effect = [&]() { return !done() && l_vec.empty() && lits.empty(); }; + + if (no_effect()) m_monomial_bounds(); - if (l_vec.empty() && !done() && run_horner) - m_horner.horner_lemmas(); + { + std::function check1 = [&]() { if (no_effect() && run_horner) m_horner.horner_lemmas(); }; + std::function check2 = [&]() { if (no_effect() && run_grobner) m_grobner(); }; + std::function check3 = [&]() { if (no_effect() && run_bounds) add_bounds(); }; - if (l_vec.empty() && !done() && run_grobner) - m_grobner(); - - if (l_vec.empty() && !done()) + std::pair> checks[] = + { {1, check1}, + {1, check2}, + {1, check3} }; + check_weighted(3, checks); + if (!l_vec.empty() || !lits.empty()) + return l_false; + } + + if (no_effect()) m_basics.basic_lemma(true); - if (l_vec.empty() && !done()) + if (no_effect()) m_basics.basic_lemma(false); - if (l_vec.empty() && !done()) + if (no_effect()) m_divisions.check(); -#if 0 - if (l_vec.empty() && !done() && !run_horner) - m_horner.horner_lemmas(); - - if (l_vec.empty() && !done() && !run_grobner) - m_grobner(); -#endif - if (!conflict_found() && !done() && run_bounded_nlsat) ret = bounded_nlsat(); @@ -1635,8 +1691,9 @@ bool core::no_lemmas_hold() const { } lbool core::test_check(vector& l) { + vector lits; m_lar_solver.set_status(lp::lp_status::OPTIMAL); - return check(l); + return check(lits, l); } std::ostream& core::print_terms(std::ostream& out) const { diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 78f46bb41..530e08c8d 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -84,6 +84,7 @@ class core { smt_params_helper m_params; std::function m_relevant; vector * m_lemma_vec; + vector * m_literal_vec = nullptr; indexed_uint_set m_to_refine; tangents m_tangents; basics m_basics; @@ -110,6 +111,10 @@ class core { void check_weighted(unsigned sz, std::pair>* checks); + u_map m_lower_bounds_added, m_upper_bounds_added; + bool can_add_bound(unsigned j, u_map& bounds); + void add_bounds(); + public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&); @@ -380,7 +385,7 @@ public: bool conflict_found() const; - lbool check(vector& l_vec); + lbool check(vector& ineqs, vector& l_vec); lbool check_power(lpvar r, lpvar x, lpvar y, vector& l_vec); void check_bounded_divisions(vector&); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 0e6efd526..ccc7b6073 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -42,8 +42,8 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check(vector& l) { - return m_core->check(l); + lbool solver::check(vector& lits, vector& lemmas) { + return m_core->check(lits, lemmas); } void solver::push(){ diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index d61b0593b..c1ad5f32a 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -36,7 +36,7 @@ namespace nla { void push(); void pop(unsigned scopes); bool need_check(); - lbool check(vector&); + lbool check(vector& lits, vector&); lbool check_power(lpvar r, lpvar x, lpvar y, vector&); bool is_monic_var(lpvar) const; bool influences_nl_var(lpvar) const; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 0e97c3503..77f10c000 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1406,30 +1406,43 @@ namespace arith { m_lemma = l; //todo avoid the copy m_explanation = l.expl(); literal_vector core; - for (auto const& ineq : m_lemma.ineqs()) { - bool is_lower = true, pos = true, is_eq = false; - switch (ineq.cmp()) { - case lp::LE: is_lower = false; pos = false; break; - case lp::LT: is_lower = true; pos = true; break; - case lp::GE: is_lower = true; pos = false; break; - case lp::GT: is_lower = false; pos = true; break; - case lp::EQ: is_eq = true; pos = false; break; - case lp::NE: is_eq = true; pos = true; break; - default: UNREACHABLE(); - } - TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); - // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); - // then term is used instead of ineq.m_term - sat::literal lit; - if (is_eq) - lit = mk_eq(ineq.term(), ineq.rs()); - else - lit = ctx.expr2literal(mk_bound(ineq.term(), ineq.rs(), is_lower)); - core.push_back(pos ? lit : ~lit); - } + for (auto const& ineq : m_lemma.ineqs()) + core.push_back(mk_ineq_literal(ineq)); set_conflict_or_lemma(hint_type::nla_h, core, false); } + void solver::assume_literals() { + for (auto const& ineq : m_nla_literals) { + auto lit = mk_ineq_literal(ineq); + ctx.mark_relevant(lit); + s().set_phase(lit); + } + } + + sat::literal solver::mk_ineq_literal(nla::ineq const& ineq) { + bool is_lower = true, pos = true, is_eq = false; + switch (ineq.cmp()) { + case lp::LE: is_lower = false; pos = false; break; + case lp::LT: is_lower = true; pos = true; break; + case lp::GE: is_lower = true; pos = false; break; + case lp::GT: is_lower = false; pos = true; break; + case lp::EQ: is_eq = true; pos = false; break; + case lp::NE: is_eq = true; pos = true; break; + default: UNREACHABLE(); + } + TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); + // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); + // then term is used instead of ineq.m_term + sat::literal lit; + if (is_eq) + lit = mk_eq(ineq.term(), ineq.rs()); + else + lit = ctx.expr2literal(mk_bound(ineq.term(), ineq.rs(), is_lower)); + + return pos ? lit : ~lit; + } + + lbool solver::check_nla() { if (!m.inc()) { TRACE("arith", tout << "canceled\n";); @@ -1442,9 +1455,10 @@ namespace arith { return l_true; m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); switch (r) { - case l_false: + case l_false: + assume_literals(); for (const nla::lemma& l : m_nla_lemma_vector) false_case_of_check_nla(l); break; diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 20252ede9..87dfb1e57 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -249,6 +249,7 @@ namespace arith { // lemmas lp::explanation m_explanation; vector m_nla_lemma_vector; + vector m_nla_literals; literal_vector m_core, m_core2; vector m_coeffs; svector m_eqs; @@ -463,6 +464,8 @@ namespace arith { void set_evidence(lp::constraint_index idx); void assign(literal lit, literal_vector const& core, svector const& eqs, euf::th_proof_hint const* pma); + void assume_literals(); + sat::literal mk_ineq_literal(nla::ineq const& ineq); void false_case_of_check_nla(const nla::lemma& l); void dbg_finalize_model(model& mdl); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6dc14fb13..cf4262422 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1979,44 +1979,55 @@ public: } nla::lemma m_lemma; - + + literal mk_literal(nla::ineq const& ineq) { + bool is_lower = true, pos = true, is_eq = false; + switch (ineq.cmp()) { + case lp::LE: is_lower = false; pos = false; break; + case lp::LT: is_lower = true; pos = true; break; + case lp::GE: is_lower = true; pos = false; break; + case lp::GT: is_lower = false; pos = true; break; + case lp::EQ: is_eq = true; pos = false; break; + case lp::NE: is_eq = true; pos = true; break; + default: UNREACHABLE(); + } + TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); + app_ref atom(m); + // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); + // then term is used instead of ineq.m_term + if (is_eq) + atom = mk_eq(ineq.term(), ineq.rs()); + else + // create term >= 0 (or term <= 0) + atom = mk_bound(ineq.term(), ineq.rs(), is_lower); + return literal(ctx().get_bool_var(atom), pos); + } + void false_case_of_check_nla(const nla::lemma & l) { m_lemma = l; //todo avoid the copy m_explanation = l.expl(); literal_vector core; for (auto const& ineq : m_lemma.ineqs()) { - bool is_lower = true, pos = true, is_eq = false; - switch (ineq.cmp()) { - case lp::LE: is_lower = false; pos = false; break; - case lp::LT: is_lower = true; pos = true; break; - case lp::GE: is_lower = true; pos = false; break; - case lp::GT: is_lower = false; pos = true; break; - case lp::EQ: is_eq = true; pos = false; break; - case lp::NE: is_eq = true; pos = true; break; - default: UNREACHABLE(); - } - TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); - app_ref atom(m); - // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); - // then term is used instead of ineq.m_term - if (is_eq) { - atom = mk_eq(ineq.term(), ineq.rs()); - } - else { - // create term >= 0 (or term <= 0) - atom = mk_bound(ineq.term(), ineq.rs(), is_lower); - } - literal lit(ctx().get_bool_var(atom), pos); + auto lit = mk_literal(ineq); core.push_back(~lit); } set_conflict_or_lemma(core, false); } + + void assume_literal(nla::ineq const& i) { + auto lit = mk_literal(i); + ctx().mark_as_relevant(lit); + ctx().set_true_first_flag(lit.var()); + } final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); + switch (r) { - case l_false: + case l_false: + for (const nla::ineq& i : m_nla_literals) + assume_literal(i); for (const nla::lemma & l : m_nla_lemma_vector) false_case_of_check_nla(l); return FC_CONTINUE; @@ -3170,6 +3181,7 @@ public: lp::explanation m_explanation; vector m_nla_lemma_vector; + vector m_nla_literals; literal_vector m_core; svector m_eqs; vector m_params; From c3b344ec47af068a0704c4fff9b2c8189d08eb0c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 18 Aug 2023 16:51:58 -0700 Subject: [PATCH 096/428] fix #6865 Signed-off-by: Nikolaj Bjorner --- src/qe/mbp/mbp_basic_tg.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index 70d25d89b..d85215d17 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -47,36 +47,27 @@ struct mbp_basic_tg::impl { //they contain variables/are c-ground bool apply() { if (!m_use_mdl) return false; - expr *term, *c, *th, *el; + expr *c, *th, *el; expr_ref nterm(m); bool progress = false; TRACE("mbp_tg", tout << "Iterating over terms of tg";); // Not resetting terms because get_terms calls resize on terms m_tg.get_terms(terms, false); - for (unsigned i = 0; i < terms.size(); i++) { - term = terms.get(i); - // Unsupported operators - SASSERT(!m.is_and(term)); - SASSERT(!m.is_or(term)); - SASSERT(!m.is_distinct(term)); - SASSERT(!m.is_implies(term)); - - if (is_seen(term)) continue; + for (expr* term : terms) { + if (is_seen(term)) + continue; if (m.is_ite(term, c, th, el)) { mark_seen(term); progress = true; if (m_mdl.is_true(c)) { m_tg.add_lit(c); m_tg.add_eq(term, th); - } else { - if (m.is_not(c)) - nterm = to_app(c)->get_arg(0); - else - nterm = m.mk_not(c); + } + else { + nterm = mk_not(m, c); m_tg.add_lit(nterm); m_tg.add_eq(term, el); } - continue; } } return progress; From 5e3df9ee77c51ffba07e727f8ee680b9382f66e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 19 Aug 2023 17:44:09 -0700 Subject: [PATCH 097/428] Arith min max (#6864) * prepare for dependencies Signed-off-by: Nikolaj Bjorner * snapshot Signed-off-by: Nikolaj Bjorner * more refactoring Signed-off-by: Nikolaj Bjorner * more refactoring Signed-off-by: Nikolaj Bjorner * build Signed-off-by: Nikolaj Bjorner * pass in u_dependency_manager Signed-off-by: Nikolaj Bjorner * address NYIs Signed-off-by: Nikolaj Bjorner * more refactoring names Signed-off-by: Nikolaj Bjorner * eq_explanation update Signed-off-by: Nikolaj Bjorner * add outline of bounds improvement functionality Signed-off-by: Nikolaj Bjorner * fix unit tests Signed-off-by: Nikolaj Bjorner * remove unused structs Signed-off-by: Nikolaj Bjorner * more bounds Signed-off-by: Nikolaj Bjorner * more bounds Signed-off-by: Nikolaj Bjorner * convert more internals to use u_dependency instead of constraint_index Signed-off-by: Nikolaj Bjorner * convert more internals to use u_dependency instead of constraint_index Signed-off-by: Nikolaj Bjorner * remember to push/pop scopes Signed-off-by: Nikolaj Bjorner * use the main function for updating bounds Signed-off-by: Nikolaj Bjorner * na Signed-off-by: Nikolaj Bjorner * na Signed-off-by: Nikolaj Bjorner * remove reset of shared dep manager Signed-off-by: Nikolaj Bjorner * disable improve-bounds, add statistics Signed-off-by: Nikolaj Bjorner --------- Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/bound_simplifier.h | 3 +- src/math/grobner/pdd_solver.cpp | 5 +- src/math/grobner/pdd_solver.h | 5 +- src/math/interval/dep_intervals.h | 17 ++- src/math/lp/emonics.cpp | 7 +- src/math/lp/gomory.cpp | 20 +-- src/math/lp/hnf_cutter.cpp | 27 ++-- src/math/lp/hnf_cutter.h | 6 +- src/math/lp/horner.cpp | 2 +- src/math/lp/int_gcd_test.cpp | 7 +- src/math/lp/int_solver.cpp | 4 +- src/math/lp/int_solver.h | 4 +- src/math/lp/lar_constraints.h | 42 +++--- src/math/lp/lar_core_solver.h | 23 ++-- src/math/lp/lar_core_solver_def.h | 15 +-- src/math/lp/lar_solver.cpp | 163 ++++++++++++++-------- src/math/lp/lar_solver.h | 63 ++++++--- src/math/lp/lp_bound_propagator.h | 17 ++- src/math/lp/lp_primal_core_solver.h | 171 +++++++++++------------ src/math/lp/lp_types.h | 3 + src/math/lp/monomial_bounds.cpp | 10 +- src/math/lp/nla_common.cpp | 12 +- src/math/lp/nla_common.h | 15 --- src/math/lp/nla_core.cpp | 180 ++++++++++++++----------- src/math/lp/nla_core.h | 24 ++-- src/math/lp/nla_defs.h | 9 +- src/math/lp/nla_grobner.cpp | 41 +++--- src/math/lp/nla_grobner.h | 2 +- src/math/lp/nla_intervals.cpp | 38 +++--- src/math/lp/nla_intervals.h | 7 +- src/math/lp/nra_solver.cpp | 4 +- src/math/lp/ul_pair.h | 37 +++-- src/math/lp/var_eqs.h | 33 +++-- src/sat/sat_anf_simplifier.cpp | 3 +- src/sat/smt/arith_axioms.cpp | 6 +- src/sat/smt/arith_solver.cpp | 46 ++++--- src/sat/smt/arith_solver.h | 8 +- src/smt/theory_lra.cpp | 58 ++++---- src/test/pdd_solver.cpp | 7 +- src/util/dependency.h | 15 +-- 40 files changed, 630 insertions(+), 529 deletions(-) diff --git a/src/ast/simplifiers/bound_simplifier.h b/src/ast/simplifiers/bound_simplifier.h index 0e3fff239..9bd4b1908 100644 --- a/src/ast/simplifiers/bound_simplifier.h +++ b/src/ast/simplifiers/bound_simplifier.h @@ -37,6 +37,7 @@ class bound_simplifier : public dependent_expr_simplifier { unsynch_mpq_manager nm; small_object_allocator m_alloc; bound_propagator bp; + u_dependency_manager m_dep_manager; dep_intervals m_interval; ptr_vector m_var2expr; unsigned_vector m_expr2var; @@ -105,7 +106,7 @@ public: a(m), m_rewriter(m), bp(nm, m_alloc, p), - m_interval(m.limit()), + m_interval(m_dep_manager, m.limit()), m_trail(m), m_num_buffer(nm) { updt_params(p); diff --git a/src/math/grobner/pdd_solver.cpp b/src/math/grobner/pdd_solver.cpp index 63c5ad835..e689c78a9 100644 --- a/src/math/grobner/pdd_solver.cpp +++ b/src/math/grobner/pdd_solver.cpp @@ -59,9 +59,10 @@ namespace dd { */ - solver::solver(reslimit& lim, pdd_manager& m) : + solver::solver(reslimit& lim, u_dependency_manager& dm, pdd_manager& m) : m(m), - m_limit(lim) + m_limit(lim), + m_dep_manager(dm) {} solver::~solver() { diff --git a/src/math/grobner/pdd_solver.h b/src/math/grobner/pdd_solver.h index 40f8fdce2..bc20c21b4 100644 --- a/src/math/grobner/pdd_solver.h +++ b/src/math/grobner/pdd_solver.h @@ -112,6 +112,7 @@ private: pdd_manager& m; reslimit& m_limit; + u_dependency_manager& m_dep_manager; stats m_stats; config m_config; print_dep_t m_print_dep; @@ -119,12 +120,11 @@ private: equation_vector m_processed; equation_vector m_to_simplify; vector> m_subst; - mutable u_dependency_manager m_dep_manager; equation_vector m_all_eqs; equation* m_conflict = nullptr; bool m_too_complex; public: - solver(reslimit& lim, pdd_manager& m); + solver(reslimit& lim, u_dependency_manager& dm, pdd_manager& m); ~solver(); pdd_manager& get_manager() { return m; } @@ -144,7 +144,6 @@ public: void saturate(); equation_vector const& equations(); - u_dependency_manager& dep() const { return m_dep_manager; } void collect_statistics(statistics & st) const; std::ostream& display(std::ostream& out, const equation& eq) const; diff --git a/src/math/interval/dep_intervals.h b/src/math/interval/dep_intervals.h index d641a294d..f9768b6f4 100644 --- a/src/math/interval/dep_intervals.h +++ b/src/math/interval/dep_intervals.h @@ -27,6 +27,7 @@ #include "math/interval/interval.h" class dep_intervals { + public: enum with_deps_t { with_deps, without_deps }; @@ -142,8 +143,9 @@ private: public: typedef interval_manager::interval interval; + u_dependency_manager& m_dep_manager; mutable unsynch_mpq_manager m_num_manager; - mutable u_dependency_manager m_dep_manager; + im_config m_config; mutable interval_manager m_imanager; @@ -158,9 +160,10 @@ public: public: u_dependency_manager& dep_manager() { return m_dep_manager; } - dep_intervals(reslimit& lim) : - m_config(m_num_manager, m_dep_manager), - m_imanager(lim, im_config(m_num_manager, m_dep_manager)) + dep_intervals(u_dependency_manager& dm, reslimit& lim) : + m_dep_manager(dm), + m_config(m_num_manager, dm), + m_imanager(lim, im_config(m_num_manager, dm)) {} std::ostream& display(std::ostream& out, const interval& i) const; @@ -335,15 +338,17 @@ public: bool is_empty(interval const& a) const; void set_interval_for_scalar(interval&, const rational&); + template void linearize(u_dependency* dep, T& expl) const { vector v; m_dep_manager.linearize(dep, v); - for (unsigned ci: v) + for (auto ci: v) expl.push_back(ci); } - void reset() { m_dep_manager.reset(); } + + void reset() { } void del(interval& i) { m_imanager.del(i); } diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index bcdb81dd8..a8ac63689 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -517,11 +517,10 @@ bool emonics::invariant() const { TRACE("nla_solver_mons", display(tout);); // the variable index contains exactly the active monomials unsigned mons = 0; - for (lpvar v = 0; v < m_var2index.size(); v++) { - if (is_monic_var(v)) { + for (lpvar v = 0; v < m_var2index.size(); v++) + if (is_monic_var(v)) mons++; - } - } + if (m_monics.size() != mons) { TRACE("nla_solver_mons", tout << "missmatch of monic vars\n";); return false; diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2ecbc49ac..775025018 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -50,9 +50,13 @@ class create_cut { bool at_upper(unsigned j) const { return lia.at_upper(j); } const impq & lower_bound(unsigned j) const { return lia.lower_bound(j); } const impq & upper_bound(unsigned j) const { return lia.upper_bound(j); } - constraint_index column_lower_bound_constraint(unsigned j) const { return lia.column_lower_bound_constraint(j); } - constraint_index column_upper_bound_constraint(unsigned j) const { return lia.column_upper_bound_constraint(j); } + u_dependency* column_lower_bound_constraint(unsigned j) const { return lia.column_lower_bound_constraint(j); } + u_dependency* column_upper_bound_constraint(unsigned j) const { return lia.column_upper_bound_constraint(j); } bool column_is_fixed(unsigned j) const { return lia.lra.column_is_fixed(j); } + void push_explanation(u_dependency* d) { + for (auto ci : lia.lra.flatten(d)) + m_ex->push_back(ci); + } void int_case_in_gomory_cut(unsigned j) { lp_assert(is_int(j) && m_fj.is_pos()); @@ -67,7 +71,7 @@ class create_cut { new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f); lp_assert(new_a.is_pos()); m_k.addmul(new_a, lower_bound(j).x); - m_ex->push_back(column_lower_bound_constraint(j)); + push_explanation(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); @@ -75,7 +79,7 @@ class create_cut { new_a = - (m_fj <= m_f ? m_fj / m_f : ((1 - m_fj) / m_one_minus_f)); lp_assert(new_a.is_neg()); m_k.addmul(new_a, upper_bound(j).x); - m_ex->push_back(column_upper_bound_constraint(j)); + push_explanation(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); m_lcm_den = lcm(m_lcm_den, denominator(new_a)); @@ -99,7 +103,7 @@ class create_cut { new_a = - a / m_f; m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than // k += lower_bound(j).x * new_a; - m_ex->push_back(column_lower_bound_constraint(j)); + push_explanation(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); @@ -110,7 +114,7 @@ class create_cut { // the delta is positive works again m_one_minus_f new_a = a / m_one_minus_f; m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a; - m_ex->push_back(column_upper_bound_constraint(j)); + push_explanation(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); TRACE("gomory_cut_detail_real", tout << "add " << new_a << "*v" << j << ", k: " << m_k << "\n"; @@ -313,8 +317,8 @@ public: // use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T) try { if (lia.is_fixed(j)) { - m_ex->push_back(column_lower_bound_constraint(j)); - m_ex->push_back(column_upper_bound_constraint(j)); + push_explanation(column_lower_bound_constraint(j)); + push_explanation(column_upper_bound_constraint(j)); continue; } if (is_real(j)) diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index 3c4ea10ab..d688eeb63 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -39,7 +39,7 @@ namespace lp { m_overflow = false; } - void hnf_cutter::add_term(const lar_term* t, const mpq &rs, constraint_index ci, bool upper_bound) { + void hnf_cutter::add_term(const lar_term* t, const mpq &rs, u_dependency* ci, bool upper_bound) { m_terms.push_back(t); m_terms_upper.push_back(upper_bound); if (upper_bound) @@ -146,7 +146,7 @@ namespace lp { } #endif void hnf_cutter::shrink_explanation(const svector& basis_rows) { - svector new_expl; + ptr_vector new_expl; for (unsigned i : basis_rows) { new_expl.push_back(m_constraints_for_explanation[i]); } @@ -232,10 +232,10 @@ branch y_i >= ceil(y0_i) is impossible. void hnf_cutter::try_add_term_to_A_for_hnf(tv const &i) { mpq rs; const lar_term& t = lra.get_term(i); - constraint_index ci; + u_dependency* dep; bool upper_bound; - if (!is_full() && lra.get_equality_and_right_side_for_term_on_current_x(i, rs, ci, upper_bound)) { - add_term(&t, rs, ci, upper_bound); + if (!is_full() && lra.get_equality_and_right_side_for_term_on_current_x(i, rs, dep, upper_bound)) { + add_term(&t, rs, dep, upper_bound); } } @@ -259,8 +259,9 @@ branch y_i >= ceil(y0_i) is impossible. } lia.settings().stats().m_hnf_cutter_calls++; TRACE("hnf_cut", tout << "settings().stats().m_hnf_cutter_calls = " << lia.settings().stats().m_hnf_cutter_calls << "\n"; - for (unsigned i : constraints_for_explanation()) { - lra.constraints().display(tout, i); + for (u_dependency* d : constraints_for_explanation()) { + for (auto ci : lra.flatten(d)) + lra.constraints().display(tout, ci); } tout << lra.constraints(); ); @@ -277,16 +278,16 @@ branch y_i >= ceil(y0_i) is impossible. TRACE("hnf_cut", lra.print_term(lia.m_t, tout << "cut:"); tout << " <= " << lia.m_k << std::endl; - for (unsigned i : constraints_for_explanation()) { - lra.constraints().display(tout, i); - } + for (auto* dep : constraints_for_explanation()) + for (auto ci : lra.flatten(dep)) + lra.constraints().display(tout, ci); ); lp_assert(lia.current_solution_is_inf_on_cut()); lia.settings().stats().m_hnf_cuts++; lia.m_ex->clear(); - for (unsigned i : constraints_for_explanation()) { - lia.m_ex->push_back(i); - } + for (u_dependency* dep : constraints_for_explanation()) + for (auto ci : lia.lra.flatten(dep)) + lia.m_ex->push_back(ci); } return r; } diff --git a/src/math/lp/hnf_cutter.h b/src/math/lp/hnf_cutter.h index b3530ea29..74fb52327 100644 --- a/src/math/lp/hnf_cutter.h +++ b/src/math/lp/hnf_cutter.h @@ -34,7 +34,7 @@ class hnf_cutter { general_matrix m_A; vector m_terms; vector m_terms_upper; - svector m_constraints_for_explanation; + ptr_vector m_constraints_for_explanation; vector m_right_sides; mpq m_abs_max; bool m_overflow; @@ -55,13 +55,13 @@ private: unsigned terms_count() const { return m_terms.size(); } const mpq & abs_max() const { return m_abs_max; } const vector& terms() const { return m_terms; } - const svector& constraints_for_explanation() const { return m_constraints_for_explanation; } + const ptr_vector& constraints_for_explanation() const { return m_constraints_for_explanation; } const vector & right_sides() const { return m_right_sides; } bool is_full() const; void clear(); - void add_term(const lar_term* t, const mpq &rs, constraint_index ci, bool upper_bound); + void add_term(const lar_term* t, const mpq &rs, u_dependency* ci, bool upper_bound); void print(std::ostream & out); diff --git a/src/math/lp/horner.cpp b/src/math/lp/horner.cpp index 40f16d709..0cd62ecaf 100644 --- a/src/math/lp/horner.cpp +++ b/src/math/lp/horner.cpp @@ -103,7 +103,7 @@ bool horner::horner_lemmas() { return false; } c().lp_settings().stats().m_horner_calls++; - const auto& matrix = c().m_lar_solver.A_r(); + const auto& matrix = c().lra.A_r(); // choose only rows that depend on m_to_refine variables std::set rows_to_check; for (lpvar j : c().m_to_refine) { diff --git a/src/math/lp/int_gcd_test.cpp b/src/math/lp/int_gcd_test.cpp index 4801cc436..f3b1b6389 100644 --- a/src/math/lp/int_gcd_test.cpp +++ b/src/math/lp/int_gcd_test.cpp @@ -250,10 +250,9 @@ namespace lp { } void int_gcd_test::add_to_explanation_from_fixed_or_boxed_column(unsigned j) { - constraint_index lc, uc; - lra.get_bound_constraint_witnesses_for_column(j, lc, uc); - lia.m_ex->push_back(lc); - lia.m_ex->push_back(uc); + auto* deps = lra.get_bound_constraint_witnesses_for_column(j); + for (auto d : lra.flatten(deps)) + lia.m_ex->push_back(d); } bool int_gcd_test::accumulate_parity(const row_strip & row, unsigned least_idx) { diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index dbb5dbae2..0ab7d1e40 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -242,11 +242,11 @@ namespace lp { return lra.has_inf_int(); } - constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { + u_dependency* int_solver::column_upper_bound_constraint(unsigned j) const { return lra.get_column_upper_bound_witness(j); } - constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { + u_dependency* int_solver::column_lower_bound_constraint(unsigned j) const { return lra.get_column_lower_bound_witness(j); } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index c14fd0067..ab29ab7f4 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -113,8 +113,8 @@ private: public: std::ostream& display_column(std::ostream & out, unsigned j) const; - constraint_index column_upper_bound_constraint(unsigned j) const; - constraint_index column_lower_bound_constraint(unsigned j) const; + u_dependency* column_upper_bound_constraint(unsigned j) const; + u_dependency* column_lower_bound_constraint(unsigned j) const; bool current_solution_is_inf_on_cut() const; bool shift_var(unsigned j, unsigned range); diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index f8cffbe57..0a16353c1 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -1,21 +1,10 @@ /*++ Copyright (c) 2017 Microsoft Corporation -Module Name: - - - -Abstract: - - - Author: Lev Nachmanson (levnach) -Revision History: - - --*/ #pragma once @@ -53,15 +42,19 @@ class lar_base_constraint { mpq m_right_side; bool m_active; unsigned m_j; -public: + u_dependency* m_dep; + + public: virtual vector> coeffs() const = 0; - lar_base_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) :m_kind(kind), m_right_side(right_side), m_active(false), m_j(j) {} + lar_base_constraint(unsigned j, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : + m_kind(kind), m_right_side(right_side), m_active(false), m_j(j), m_dep(dep) {} virtual ~lar_base_constraint() = default; lconstraint_kind kind() const { return m_kind; } mpq const& rhs() const { return m_right_side; } unsigned column() const { return m_j; } + u_dependency* dep() const { return m_dep; } void activate() { m_active = true; } void deactivate() { m_active = false; } @@ -73,8 +66,8 @@ public: class lar_var_constraint: public lar_base_constraint { public: - lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : - lar_base_constraint(j, kind, right_side) {} + lar_var_constraint(unsigned j, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : + lar_base_constraint(j, kind, dep, right_side) {} vector> coeffs() const override { vector> ret; @@ -88,8 +81,8 @@ public: class lar_term_constraint: public lar_base_constraint { const lar_term * m_term; public: - lar_term_constraint(unsigned j, const lar_term *t, lconstraint_kind kind, const mpq& right_side) : - lar_base_constraint(j, kind, right_side), m_term(t) {} + lar_term_constraint(unsigned j, const lar_term* t, lconstraint_kind kind, u_dependency* dep, const mpq& right_side) : + lar_base_constraint(j, kind, dep, right_side), m_term(t) {} vector> coeffs() const override { return m_term->coeffs_as_vector(); } unsigned size() const override { return m_term->size();} @@ -98,10 +91,11 @@ public: class constraint_set { region m_region; column_namer& m_namer; + u_dependency_manager& m_dep_manager; vector m_constraints; stacked_value m_constraint_count; unsigned_vector m_active; - stacked_value m_active_lim; + stacked_value m_active_lim; constraint_index add(lar_base_constraint* c) { constraint_index ci = m_constraints.size(); @@ -137,8 +131,13 @@ class constraint_set { return out << "constraint " << T_to_string(ci) << " is not found" << std::endl; } + u_dependency* mk_dep() { + return m_dep_manager.mk_leaf(m_constraints.size()); + } + public: - constraint_set(column_namer& cn): + constraint_set(u_dependency_manager& d, column_namer& cn): + m_dep_manager(d), m_namer(cn) {} ~constraint_set() { @@ -169,11 +168,12 @@ public: } constraint_index add_var_constraint(var_index j, lconstraint_kind k, mpq const& rhs) { - return add(new (m_region) lar_var_constraint(j, k, rhs)); + return add(new (m_region) lar_var_constraint(j, k, mk_dep(), rhs)); } constraint_index add_term_constraint(unsigned j, const lar_term* t, lconstraint_kind k, mpq const& rhs) { - return add(new (m_region) lar_term_constraint(j, t, k, rhs)); + auto* dep = mk_dep(); + return add(new (m_region) lar_term_constraint(j, t, k, dep, rhs)); } // future behavior uses activation bit. diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 0780d21af..4a5cb20b1 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -21,9 +21,10 @@ class lar_core_solver { int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; - -public: stacked_value m_stacked_simplex_strategy; + +public: + stacked_vector m_column_types; // r - solver fields, for rational numbers vector> m_r_x; // the solution @@ -43,8 +44,6 @@ public: const column_namer & column_names ); - lp_settings & settings() { return m_r_solver.m_settings;} - const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } @@ -57,8 +56,7 @@ public: void fill_not_improvable_zero_sum_from_inf_row(); column_type get_column_type(unsigned j) { return m_column_types[j];} - - + void print_pivot_row(std::ostream & out, unsigned row_index) const { for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) @@ -68,9 +66,9 @@ public: out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl; - for (unsigned j : m_r_solver.m_pivot_row.m_index) { + for (unsigned j : m_r_solver.m_pivot_row.m_index) m_r_solver.print_column_bound_info(j, out); - } + m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } @@ -110,10 +108,7 @@ public: m_column_types.push(); // rational m_r_lower_bounds.push(); - m_r_upper_bounds.push(); - - - + m_r_upper_bounds.push(); } void pop(unsigned k) { @@ -127,11 +122,9 @@ public: m_r_solver.m_d.resize(m_r_A.column_count()); m_stacked_simplex_strategy.pop(k); - settings().set_simplex_strategy(m_stacked_simplex_strategy); + m_r_solver.m_settings.set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); } - - bool r_basis_is_OK() const { #ifdef Z3DEBUG diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 942c87b3b..b6743873f 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -88,19 +88,18 @@ void lar_core_solver::solve() { 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";); - return; + m_r_solver.set_status(lp_status::OPTIMAL); + TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); + return; } - ++settings().stats().m_need_to_solve_inf; + ++m_r_solver.m_settings.stats().m_need_to_solve_inf; lp_assert( r_basis_is_OK()); - - + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? m_r_solver.find_feasible_solution(); - else { + else m_r_solver.solve(); - } + lp_assert(r_basis_is_OK()); switch (m_r_solver.get_status()) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index ddfdb1437..6159c436e 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -26,7 +26,7 @@ namespace lp { m_mpq_lar_core_solver(m_settings, *this), m_var_register(false), m_term_register(true), - m_constraints(*this) {} + m_constraints(m_dependencies, *this) {} // start or ends tracking the rows that were changed by solve() void lar_solver::track_touched_rows(bool v) { @@ -218,8 +218,14 @@ namespace lp { // 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]; - evidence.add_pair(ul.upper_bound_witness(), numeric_traits::one()); - evidence.add_pair(ul.lower_bound_witness(), -numeric_traits::one()); + svector deps; + m_dependencies.linearize(ul.upper_bound_witness(), deps); + for (auto d : deps) + evidence.add_pair(d, numeric_traits::one()); + deps.reset(); + m_dependencies.linearize(ul.lower_bound_witness(), deps); + for (auto d : deps) + evidence.add_pair(d, -numeric_traits::one()); } void lar_solver::push() { @@ -232,6 +238,7 @@ namespace lp { m_term_count.push(); m_constraints.push(); m_usage_in_terms.push(); + m_dependencies.push_scope(); } void lar_solver::clean_popped_elements(unsigned n, indexed_uint_set& set) { @@ -297,6 +304,7 @@ namespace lp { lp_assert(sizes_are_correct()); lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); + m_dependencies.pop_scope(k); set_status(lp_status::UNKNOWN); } @@ -320,6 +328,39 @@ namespace lp { } } + bool lar_solver::improve_bound(lpvar j, bool improve_lower_bound) { + lar_term term = get_term_to_maximize(j); + if (improve_lower_bound) + term.negate(); + impq bound; + if (!maximize_term_on_tableau(term, bound)) + return false; + + return false; + // TODO + if (improve_lower_bound) { + if (column_has_lower_bound(j) && bound.x == column_lower_bound(j).x) + return false; + SASSERT(!column_has_lower_bound(j) || column_lower_bound(j).x < bound.x); + + // TODO - explain new lower bound. + // Seems the relevant information is in the "costs" that are used when + // setting the optimization objecive. The costs are cleared after a call so + // maybe have some way of extracting bound dependencies from the costs. + u_dependency* dep = nullptr; + update_column_type_and_bound(j, bound.y > 0 ? lconstraint_kind::GT : lconstraint_kind::GE, bound.x, dep); + } + else { + if (column_has_upper_bound(j) && bound.x == column_upper_bound(j).x) + return false; + SASSERT(!column_has_upper_bound(j) || column_upper_bound(j).x > bound.x); + // similar for upper bounds + u_dependency* dep = nullptr; + update_column_type_and_bound(j, bound.y < 0 ? lconstraint_kind::LT : lconstraint_kind::LE, bound.x, dep); + } + return true; + } + bool lar_solver::costs_are_zeros_for_r_solver() const { for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); @@ -349,7 +390,7 @@ namespace lp { jset.insert(rc.var()); } } - + for (unsigned j : jset) rslv.m_d[j] = zero_of_type(); @@ -577,15 +618,15 @@ namespace lp { } - void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { + void lar_solver::set_upper_bound_witness(var_index j, u_dependency* dep) { ul_pair ul = m_columns_to_ul_pairs[j]; - ul.upper_bound_witness() = ci; + ul.upper_bound_witness() = dep; m_columns_to_ul_pairs[j] = ul; } - void lar_solver::set_lower_bound_witness(var_index j, constraint_index ci) { + void lar_solver::set_lower_bound_witness(var_index j, u_dependency* dep) { ul_pair ul = m_columns_to_ul_pairs[j]; - ul.lower_bound_witness() = ci; + ul.lower_bound_witness() = dep; m_columns_to_ul_pairs[j] = ul; } @@ -920,7 +961,7 @@ namespace lp { return ret; } - bool lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { + bool lar_solver::has_lower_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const { if (var >= m_columns_to_ul_pairs.size()) { // TBD: bounds on terms could also be used, caller may have to track these. @@ -928,7 +969,7 @@ namespace lp { } const ul_pair& ul = m_columns_to_ul_pairs[var]; ci = ul.lower_bound_witness(); - if (ci != null_ci) { + if (ci != nullptr) { auto& p = m_mpq_lar_core_solver.m_r_lower_bounds()[var]; value = p.x; is_strict = p.y.is_pos(); @@ -939,7 +980,7 @@ namespace lp { } } - bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { + bool lar_solver::has_upper_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const { if (var >= m_columns_to_ul_pairs.size()) { // TBD: bounds on terms could also be used, caller may have to track these. @@ -947,7 +988,7 @@ namespace lp { } const ul_pair& ul = m_columns_to_ul_pairs[var]; ci = ul.upper_bound_witness(); - if (ci != null_ci) { + if (ci != nullptr) { auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; value = p.x; is_strict = p.y.is_neg(); @@ -1005,9 +1046,13 @@ namespace lp { int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; const ul_pair& ul = m_columns_to_ul_pairs[j]; - constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); - lp_assert(m_constraints.valid_index(bound_constr_i)); - exp.add_pair(bound_constr_i, coeff); + u_dependency* bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); + svector deps; + m_dependencies.linearize(bound_constr_i, deps); + for (auto d : deps) { + lp_assert(m_constraints.valid_index(d)); + exp.add_pair(d, coeff); + } } } @@ -1698,12 +1743,12 @@ namespace lp { void lar_solver::activate_check_on_equal(constraint_index ci, unsigned& equal_column) { auto const& c = m_constraints[ci]; - update_column_type_and_bound_check_on_equal(c.column(), c.kind(), c.rhs(), ci, equal_column); + update_column_type_and_bound_check_on_equal(c.column(), c.rhs(), ci, equal_column); } void lar_solver::activate(constraint_index ci) { auto const& c = m_constraints[ci]; - update_column_type_and_bound(c.column(), c.kind(), c.rhs(), ci); + update_column_type_and_bound(c.column(), c.rhs(), ci); } mpq lar_solver::adjust_bound_for_int(lpvar j, lconstraint_kind& k, const mpq& bound) { @@ -1765,31 +1810,37 @@ namespace lp { } void lar_solver::update_column_type_and_bound(unsigned j, - 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); - - if (is_base(j) && column_is_fixed(j)) - m_fixed_base_var_set.insert(j); - - 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;); + lconstraint_kind kind = m_constraints[constr_index].kind(); + u_dependency* dep = m_constraints[constr_index].dep(); + update_column_type_and_bound(j, kind, right_side, dep); } + + void lar_solver::update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + if (column_has_upper_bound(j)) + update_column_type_and_bound_with_ub(j, kind, right_side, dep); + else + update_column_type_and_bound_with_no_ub(j, kind, right_side, dep); + + if (is_base(j) && column_is_fixed(j)) + m_fixed_base_var_set.insert(j); + + 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;); + } + void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { m_columns_with_changed_bounds.insert(j); TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); } + void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, - lconstraint_kind kind, const mpq& right_side, constraint_index constr_index, unsigned& equal_to_j) { - update_column_type_and_bound(j, kind, right_side, constr_index); + update_column_type_and_bound(j, right_side, constr_index); equal_to_j = null_lpvar; if (column_is_fixed(j)) { register_in_fixed_var_table(j, equal_to_j); @@ -1852,28 +1903,28 @@ namespace lp { } - void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, unsigned constraint_index) { + void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { SASSERT(column_has_upper_bound(j)); if (column_has_lower_bound(j)) { - update_bound_with_ub_lb(j, kind, right_side, constraint_index); + update_bound_with_ub_lb(j, kind, right_side, dep); } else { - update_bound_with_ub_no_lb(j, kind, right_side, constraint_index); + update_bound_with_ub_no_lb(j, kind, right_side, dep); } } - void lar_solver::update_column_type_and_bound_with_no_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, unsigned constraint_index) { + void lar_solver::update_column_type_and_bound_with_no_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { SASSERT(!column_has_upper_bound(j)); if (column_has_lower_bound(j)) { - update_bound_with_no_ub_lb(j, kind, right_side, constraint_index); + update_bound_with_no_ub_lb(j, kind, right_side, dep); } else { - update_bound_with_no_ub_no_lb(j, kind, right_side, constraint_index); + update_bound_with_no_ub_no_lb(j, kind, right_side, dep); } } // clang-format on - void lar_solver::update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { + void lar_solver::update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { 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); @@ -1889,7 +1940,7 @@ namespace lp { } 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); + set_upper_bound_witness(j, dep); insert_to_columns_with_changed_bounds(j); break; } @@ -1904,7 +1955,7 @@ namespace lp { return; } m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - set_lower_bound_witness(j, ci); + set_lower_bound_witness(j, dep); 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; @@ -1914,8 +1965,8 @@ namespace lp { 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); + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; break; } @@ -1928,7 +1979,7 @@ namespace lp { } } // 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) { + void lar_solver::update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { 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); @@ -1942,7 +1993,7 @@ namespace lp { set_infeasible_column(j); } m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, ci); + set_upper_bound_witness(j, dep); 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; @@ -1955,7 +2006,7 @@ namespace lp { return; } m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - set_lower_bound_witness(j, ci); + set_lower_bound_witness(j, dep); insert_to_columns_with_changed_bounds(j); break; } @@ -1965,8 +2016,8 @@ namespace lp { set_infeasible_column(j); } - set_upper_bound_witness(j, ci); - set_lower_bound_witness(j, ci); + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); 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; @@ -1977,7 +2028,7 @@ namespace lp { } } // 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) { + void lar_solver::update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { 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); mpq y_of_bound(0); @@ -1989,7 +2040,7 @@ namespace lp { auto up = numeric_pair(right_side, y_of_bound); 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); + set_upper_bound_witness(j, dep); insert_to_columns_with_changed_bounds(j); } break; @@ -2002,7 +2053,7 @@ namespace lp { set_infeasible_column(j); } m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - set_lower_bound_witness(j, ci); + set_lower_bound_witness(j, dep); 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); @@ -2015,8 +2066,8 @@ namespace lp { set_infeasible_column(j); } - set_upper_bound_witness(j, ci); - set_lower_bound_witness(j, ci); + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); 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; @@ -2027,7 +2078,7 @@ namespace lp { } } // 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) { + void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { lp_assert(!column_has_lower_bound(j) && !column_has_upper_bound(j)); mpq y_of_bound(0); @@ -2037,7 +2088,7 @@ namespace lp { 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); + set_upper_bound_witness(j, dep); m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; } break; case GT: @@ -2045,14 +2096,14 @@ namespace lp { 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); + set_lower_bound_witness(j, dep); 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); + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); 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; @@ -2165,7 +2216,7 @@ namespace lp { return true; } - 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 { + bool lar_solver::get_equality_and_right_side_for_term_on_current_x(tv const& t, mpq& rs, u_dependency*& ci, bool& upper_bound) const { lp_assert(t.is_term()); unsigned j; bool is_int; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 7c1f9575b..4b60d7d62 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -47,6 +47,8 @@ namespace lp { class int_branch; class int_solver; + + class lar_solver : public column_namer { struct term_hasher { std::size_t operator()(const lar_term& t) const { @@ -88,6 +90,8 @@ class lar_solver : public column_namer { indexed_uint_set m_columns_with_changed_bounds; indexed_uint_set m_touched_rows; unsigned_vector m_row_bounds_to_replay; + u_dependency_manager m_dependencies; + svector m_tmp_dependencies; indexed_uint_set m_basic_columns_with_changed_cost; // these are basic columns with the value changed, so the corresponding row in the tableau @@ -136,14 +140,15 @@ class lar_solver : public column_namer { inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } 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); - 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, const mpq& right_side, constraint_index ci, unsigned&); + void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); + void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); 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); @@ -186,8 +191,8 @@ class lar_solver : public column_namer { 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 set_upper_bound_witness(var_index j, u_dependency* ci); + void set_lower_bound_witness(var_index j, u_dependency* ci); void substitute_terms_in_linear_expression(const vector>& left_side_with_terms, vector>& left_side) const; @@ -283,6 +288,8 @@ class lar_solver : public column_namer { lp_status maximize_term(unsigned j_or_term, impq& term_max); + bool improve_bound(lpvar j, bool is_lower); + inline core_solver_pretty_printer pp(std::ostream& out) const { return core_solver_pretty_printer(m_mpq_lar_core_solver.m_r_solver, out); } @@ -310,9 +317,10 @@ class lar_solver : public column_namer { int a_sign = is_pos(a) ? 1 : -1; int sign = j_sign * a_sign; 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); + auto* witness = sign > 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); + lp_assert(witness); + for (auto ci : flatten(witness)) + bp.consume(a, ci); } } @@ -460,7 +468,20 @@ class lar_solver : public column_namer { 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 { + svector const& flatten(u_dependency* d) { + m_tmp_dependencies.reset(); + m_dependencies.linearize(d, m_tmp_dependencies); + return m_tmp_dependencies; + } + + void push_explanation(u_dependency* d, explanation& ex) { + for (auto ci : flatten(d)) + ex.push_back(ci); + } + + u_dependency_manager& dep_manager() { return m_dependencies; } + + inline u_dependency* get_column_upper_bound_witness(unsigned j) const { if (tv::is_term(j)) { j = m_var_register.external_to_local(j); } @@ -474,8 +495,8 @@ class lar_solver : public column_namer { 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_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + bool has_lower_bound(var_index var, u_dependency*& ci, mpq& value, bool& is_strict) const; + bool has_upper_bound(var_index var, u_dependency*& 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; unsigned map_term_index_to_column_index(unsigned j) const; @@ -530,15 +551,15 @@ class lar_solver : public column_namer { 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 { + u_dependency* get_bound_constraint_witnesses_for_column(unsigned j) { const ul_pair& ul = m_columns_to_ul_pairs[j]; - lc = ul.lower_bound_witness(); - uc = ul.upper_bound_witness(); + return m_dependencies.mk_join(ul.lower_bound_witness(), ul.upper_bound_witness()); } inline constraint_set const& constraints() const { return m_constraints; } void push(); void pop(); - inline constraint_index get_column_lower_bound_witness(unsigned j) const { + + inline u_dependency* get_column_lower_bound_witness(unsigned j) const { if (tv::is_term(j)) { j = m_var_register.external_to_local(j); } @@ -601,7 +622,7 @@ class lar_solver : public column_namer { 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, u_dependency*& 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(); } diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 46670ad91..f676b0e1d 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -228,33 +228,32 @@ class lp_bound_propagator { return lp().column_is_int(j); } - void explain_fixed_in_row(unsigned row, explanation& ex) const { + void explain_fixed_in_row(unsigned row, explanation& ex) { TRACE("eq", tout << lp().get_row(row) << std::endl); for (const auto& c : lp().get_row(row)) if (lp().is_fixed(c.var())) explain_fixed_column(c.var(), ex); } - unsigned explain_fixed_in_row_and_get_base(unsigned row, explanation& ex) const { + unsigned explain_fixed_in_row_and_get_base(unsigned row, explanation& ex) { unsigned base = UINT_MAX; TRACE("eq", tout << lp().get_row(row) << std::endl); for (const auto& c : lp().get_row(row)) { if (lp().is_fixed(c.var())) { explain_fixed_column(c.var(), ex); - } else if (lp().is_base(c.var())) { + } + else if (lp().is_base(c.var())) { base = c.var(); } } return base; } - void explain_fixed_column(unsigned j, explanation& ex) const { + void explain_fixed_column(unsigned j, explanation& ex) { SASSERT(column_is_fixed(j)); - constraint_index lc, uc; - lp().get_bound_constraint_witnesses_for_column(j, lc, uc); - ex.push_back(lc); - if (lc != uc) - ex.push_back(uc); + auto* deps = lp().get_bound_constraint_witnesses_for_column(j); + for (auto ci : lp().flatten(deps)) + ex.push_back(ci); } #ifdef Z3DEBUG bool all_fixed_in_row(unsigned row) const { diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index c074da16a..9871ec691 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -138,95 +138,98 @@ namespace lp { return false; } -/** - * 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 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_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)) + /** + * 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 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; } - 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; - } - } + return r; } - if (choice == -1) { - m_inf_row_index_for_tableau = i; - return -1; + 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; + } + } + + 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(); } - 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); diff --git a/src/math/lp/lp_types.h b/src/math/lp/lp_types.h index ac046e1a1..c69dbe10b 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -22,6 +22,8 @@ Revision History: #include #include #include "util/debug.h" +#include "util/dependency.h" + namespace nla { class core; } @@ -36,6 +38,7 @@ typedef unsigned lpvar; const lpvar null_lpvar = UINT_MAX; const constraint_index null_ci = UINT_MAX; + class column_index { unsigned m_index; friend class lar_solver; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 1ed0956dc..a89d27a9b 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -149,22 +149,22 @@ namespace nla { } void monomial_bounds::var2interval(lpvar v, scoped_dep_interval& i) { - lp::constraint_index ci; + u_dependency* d = nullptr; rational bound; bool is_strict; - if (c().has_lower_bound(v, ci, bound, is_strict)) { + if (c().has_lower_bound(v, d, bound, is_strict)) { dep.set_lower_is_open(i, is_strict); dep.set_lower(i, bound); - dep.set_lower_dep(i, dep.mk_leaf(ci)); + dep.set_lower_dep(i, d); dep.set_lower_is_inf(i, false); } else { dep.set_lower_is_inf(i, true); } - if (c().has_upper_bound(v, ci, bound, is_strict)) { + if (c().has_upper_bound(v, d, bound, is_strict)) { dep.set_upper_is_open(i, is_strict); dep.set_upper(i, bound); - dep.set_upper_dep(i, dep.mk_leaf(ci)); + dep.set_upper_dep(i, d); dep.set_upper_is_inf(i, false); } else { diff --git a/src/math/lp/nla_common.cpp b/src/math/lp/nla_common.cpp index 00f2ff4ee..eceacbcb4 100644 --- a/src/math/lp/nla_common.cpp +++ b/src/math/lp/nla_common.cpp @@ -60,11 +60,9 @@ unsigned common::random() { } void common::add_deps_of_fixed(lpvar j, u_dependency*& dep) { - unsigned lc, uc; - auto& dep_manager = c().m_intervals.get_dep_intervals().dep_manager(); - c().m_lar_solver.get_bound_constraint_witnesses_for_column(j, lc, uc); - dep = dep_manager.mk_join(dep, dep_manager.mk_leaf(lc)); - dep = dep_manager.mk_join(dep, dep_manager.mk_leaf(uc)); + auto& dm = c().lra.dep_manager(); + auto* deps = c().lra.get_bound_constraint_witnesses_for_column(j); + dep = dm.mk_join(dep, deps); } @@ -73,7 +71,7 @@ nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_depende SASSERT(!coeff.is_zero()); if (c().params().arith_nl_horner_subs_fixed() == 1 && c().var_is_fixed(j)) { add_deps_of_fixed(j, dep); - return cn.mk_scalar(coeff * c().m_lar_solver.column_lower_bound(j).x); + return cn.mk_scalar(coeff * c().lra.column_lower_bound(j).x); } if (c().params().arith_nl_horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(j)) { add_deps_of_fixed(j, dep); @@ -91,7 +89,7 @@ nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_depende for (lpvar k : m.vars()) { if (c().params().arith_nl_horner_subs_fixed() == 1 && c().var_is_fixed(k)) { add_deps_of_fixed(k, dep); - mf *= c().m_lar_solver.column_lower_bound(k).x; + mf *= c().lra.column_lower_bound(k).x; } else if (c().params().arith_nl_horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(k)) { dep = initial_dep; diff --git a/src/math/lp/nla_common.h b/src/math/lp/nla_common.h index 1302c3909..731b4766e 100644 --- a/src/math/lp/nla_common.h +++ b/src/math/lp/nla_common.h @@ -80,21 +80,6 @@ struct common { bool check_monic(const monic&) const; unsigned random(); void add_deps_of_fixed(lpvar j, u_dependency*& dep); - class ci_value_manager { - public: - void inc_ref(lp::constraint_index const & v) { - } - - void dec_ref(lp::constraint_index const & v) { - } - }; - - struct u_dependency_config { - typedef ci_value_manager value_manager; - typedef region allocator; - static const bool ref_count = false; - typedef lp::constraint_index value; - }; nex* nexvar(const rational& coeff, lpvar j, nex_creator&, u_dependency*&); template diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 54f955760..49691d6b8 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -23,7 +23,7 @@ typedef lp::lar_term term; core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_evars(), - m_lar_solver(s), + lra(s), m_reslim(lim), m_params(p), m_tangents(this), @@ -69,7 +69,7 @@ lp::lar_term core::subs_terms_to_columns(const lp::lar_term& t) const { for (lp::lar_term::ival p : t) { lpvar j = p.column(); if (lp::tv::is_term(j)) - j = m_lar_solver.map_term_index_to_column_index(j); + j = lra.map_term_index_to_column_index(j); r.add_monomial(p.coeff(), j); } return r; @@ -133,7 +133,7 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { for (unsigned i = 0; i < sz; i++) { lpvar j = vs[i]; if (lp::tv::is_term(j)) - j = m_lar_solver.map_term_index_to_column_index(j); + j = lra.map_term_index_to_column_index(j); m_add_buffer[i] = j; } m_emons.add(v, m_add_buffer); @@ -154,7 +154,7 @@ void core::pop(unsigned n) { rational core::product_value(const monic& m) const { rational r(1); for (auto j : m.vars()) { - r *= m_lar_solver.get_column_value(j).x; + r *= lra.get_column_value(j).x; } return r; } @@ -167,10 +167,10 @@ 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 (lra.column_is_int(m.var()) && !lra.get_column_value(m.var()).is_int()) return true; - bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x; + bool ret = product_value(m) == lra.get_column_value(m.var()).x; CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';); return ret; } @@ -182,7 +182,7 @@ std::ostream& core::print_product(const T & m, std::ostream& out) const { for (lpvar v : m) { if (!first) out << "*"; else first = false; if (lp_settings().print_external_var_name()) - out << "(" << m_lar_solver.get_variable_name(v) << "=" << val(v) << ")"; + out << "(" << lra.get_variable_name(v) << "=" << val(v) << ")"; else out << "(j" << v << " = " << val(v) << ")"; @@ -228,7 +228,7 @@ std::ostream & core::print_factor_with_vars(const factor& f, std::ostream& out) std::ostream& core::print_monic(const monic& m, std::ostream& out) const { if (lp_settings().print_external_var_name()) - out << "([" << m.var() << "] = " << m_lar_solver.get_variable_name(m.var()) << " = " << val(m.var()) << " = "; + out << "([" << m.var() << "] = " << lra.get_variable_name(m.var()) << " = " << val(m.var()) << " = "; else out << "(j" << m.var() << " = " << val(m.var()) << " = "; print_product(m.vars(), out) << ")\n"; @@ -271,7 +271,7 @@ std::ostream& core::print_explanation(const lp::explanation& exp, std::ostream& unsigned i = 0; for (auto p : exp) { out << "(" << p.ci() << ")"; - m_lar_solver.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci()); + lra.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci()); if (++i < exp.size()) out << " "; } @@ -316,21 +316,20 @@ bool core::explain_lower_bound(const lp::lar_term& t, const rational& rs, lp::ex bool core::explain_coeff_lower_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const { const rational& a = p.coeff(); SASSERT(!a.is_zero()); - unsigned c; // the index for the lower or the upper bound if (a.is_pos()) { - unsigned c = m_lar_solver.get_column_lower_bound_witness(p.column()); - if (c + 1 == 0) + auto* dep = lra.get_column_lower_bound_witness(p.column()); + if (!dep) return false; - bound = a * m_lar_solver.get_lower_bound(p.column()).x; - e.push_back(c); + bound = a * lra.get_lower_bound(p.column()).x; + lra.push_explanation(dep, e); return true; } // a.is_neg() - c = m_lar_solver.get_column_upper_bound_witness(p.column()); - if (c + 1 == 0) + auto* dep = lra.get_column_upper_bound_witness(p.column()); + if (!dep) return false; - bound = a * m_lar_solver.get_upper_bound(p.column()).x; - e.push_back(c); + bound = a * lra.get_upper_bound(p.column()).x; + lra.push_explanation(dep, e); return true; } @@ -338,21 +337,20 @@ bool core::explain_coeff_upper_bound(const lp::lar_term::ival& p, rational& boun const rational& a = p.coeff(); lpvar j = p.column(); SASSERT(!a.is_zero()); - unsigned c; // the index for the lower or the upper bound if (a.is_neg()) { - unsigned c = m_lar_solver.get_column_lower_bound_witness(j); - if (c + 1 == 0) + auto *dep = lra.get_column_lower_bound_witness(j); + if (!dep) return false; - bound = a * m_lar_solver.get_lower_bound(j).x; - e.push_back(c); + bound = a * lra.get_lower_bound(j).x; + lra.push_explanation(dep, e); return true; } // a.is_pos() - c = m_lar_solver.get_column_upper_bound_witness(j); - if (c + 1 == 0) + auto* dep = lra.get_column_upper_bound_witness(j); + if (!dep) return false; - bound = a * m_lar_solver.get_upper_bound(j).x; - e.push_back(c); + bound = a * lra.get_upper_bound(j).x; + lra.push_explanation(dep, e); return true; } @@ -413,12 +411,12 @@ bool core::explain_by_equiv(const lp::lar_term& t, lp::explanation& e) const { return false; m_evars.explain(signed_var(i, false), signed_var(j, sign), e); - TRACE("nla_solver", tout << "explained :"; m_lar_solver.print_term_as_indices(t, tout);); + TRACE("nla_solver", tout << "explained :"; lra.print_term_as_indices(t, tout);); return true; } void core::mk_ineq_no_expl_check(new_lemma& lemma, lp::lar_term& t, llc cmp, const rational& rs) { - TRACE("nla_solver_details", m_lar_solver.print_term_as_indices(t, tout << "t = ");); + TRACE("nla_solver_details", lra.print_term_as_indices(t, tout << "t = ");); lemma |= ineq(cmp, t, rs); CTRACE("nla_solver", ineq_holds(ineq(cmp, t, rs)), print_ineq(ineq(cmp, t, rs), tout) << "\n";); SASSERT(!ineq_holds(ineq(cmp, t, rs))); @@ -485,18 +483,18 @@ int core::vars_sign(const svector& v) { } bool core::has_upper_bound(lpvar j) const { - return m_lar_solver.column_has_upper_bound(j); + return lra.column_has_upper_bound(j); } bool core::has_lower_bound(lpvar j) const { - return m_lar_solver.column_has_lower_bound(j); + return lra.column_has_lower_bound(j); } const rational& core::get_upper_bound(unsigned j) const { - return m_lar_solver.get_upper_bound(j).x; + return lra.get_upper_bound(j).x; } const rational& core::get_lower_bound(unsigned j) const { - return m_lar_solver.get_lower_bound(j).x; + return lra.get_lower_bound(j).x; } bool core::zero_is_an_inner_point_of_bounds(lpvar j) const { @@ -540,26 +538,26 @@ bool core::sign_contradiction(const monic& m) const { bool core::var_is_fixed_to_zero(lpvar j) const { return - m_lar_solver.column_is_fixed(j) && - m_lar_solver.get_lower_bound(j) == lp::zero_of_type(); + lra.column_is_fixed(j) && + lra.get_lower_bound(j) == lp::zero_of_type(); } bool core::var_is_fixed_to_val(lpvar j, const rational& v) const { return - m_lar_solver.column_is_fixed(j) && - m_lar_solver.get_lower_bound(j) == lp::impq(v); + lra.column_is_fixed(j) && + lra.get_lower_bound(j) == lp::impq(v); } bool core::var_is_fixed(lpvar j) const { - return m_lar_solver.column_is_fixed(j); + return lra.column_is_fixed(j); } bool core::var_is_free(lpvar j) const { - return m_lar_solver.column_is_free(j); + return lra.column_is_free(j); } std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const { - m_lar_solver.print_term_as_indices(in.term(), out); + lra.print_term_as_indices(in.term(), out); out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs(); return out; } @@ -569,14 +567,14 @@ std::ostream & core::print_var(lpvar j, std::ostream & out) const { print_monic(m_emons[j], out); } - m_lar_solver.print_column_info(j, out); + lra.print_column_info(j, out); signed_var jr = m_evars.find(j); out << "root="; if (jr.sign()) { out << "-"; } - out << m_lar_solver.get_variable_name(jr.var()) << "\n"; + out << lra.get_variable_name(jr.var()) << "\n"; return out; } @@ -643,11 +641,11 @@ void core::trace_print_monic_and_factorization(const monic& rm, const factorizat bool core::var_has_positive_lower_bound(lpvar j) const { - return m_lar_solver.column_has_lower_bound(j) && m_lar_solver.get_lower_bound(j) > lp::zero_of_type(); + return lra.column_has_lower_bound(j) && lra.get_lower_bound(j) > lp::zero_of_type(); } bool core::var_has_negative_upper_bound(lpvar j) const { - return m_lar_solver.column_has_upper_bound(j) && m_lar_solver.get_upper_bound(j) < lp::zero_of_type(); + return lra.column_has_upper_bound(j) && lra.get_upper_bound(j) < lp::zero_of_type(); } bool core::var_is_separated_from_zero(lpvar j) const { @@ -684,10 +682,10 @@ template bool core::mon_has_zero(const unsigned_vector& product lp::lp_settings& core::lp_settings() { - return m_lar_solver.settings(); + return lra.settings(); } const lp::lp_settings& core::lp_settings() const { - return m_lar_solver.settings(); + return lra.settings(); } unsigned core::random() { return lp_settings().random_next(); } @@ -695,7 +693,7 @@ unsigned core::random() { return lp_settings().random_next(); } // we look for octagon constraints here, with a left part +-x +- y void core::collect_equivs() { - const lp::lar_solver& s = m_lar_solver; + const lp::lar_solver& s = lra; for (unsigned i = 0; i < s.terms().size(); i++) { if (!s.term_is_used_as_row(i)) @@ -737,7 +735,7 @@ bool core::is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar & return true; } -void core::add_equivalence_maybe(const lp::lar_term *t, lpci c0, lpci c1) { +void core::add_equivalence_maybe(const lp::lar_term* t, u_dependency* c0, u_dependency* c1) { bool sign; lpvar i, j; if (!is_octagon_term(*t, sign, i, j)) @@ -866,7 +864,7 @@ std::unordered_set core::collect_vars(const lemma& l) const { } } for (auto p : l.expl()) { - const auto& c = m_lar_solver.constraints()[p.ci()]; + const auto& c = lra.constraints()[p.ci()]; for (const auto& r : c.coeffs()) { insert_j(r.second); } @@ -1125,8 +1123,8 @@ new_lemma& new_lemma::explain_equiv(lpvar a, lpvar b) { new_lemma& new_lemma::explain_var_separated_from_zero(lpvar j) { SASSERT(c.var_is_separated_from_zero(j)); - if (c.m_lar_solver.column_has_upper_bound(j) && - (c.m_lar_solver.get_upper_bound(j)< lp::zero_of_type())) + if (c.lra.column_has_upper_bound(j) && + (c.lra.get_upper_bound(j)< lp::zero_of_type())) explain_existing_upper_bound(j); else explain_existing_lower_bound(j); @@ -1136,7 +1134,7 @@ new_lemma& new_lemma::explain_var_separated_from_zero(lpvar j) { new_lemma& new_lemma::explain_existing_lower_bound(lpvar j) { SASSERT(c.has_lower_bound(j)); lp::explanation ex; - ex.push_back(c.m_lar_solver.get_column_lower_bound_witness(j)); + c.lra.push_explanation(c.lra.get_column_lower_bound_witness(j), ex); *this &= ex; TRACE("nla_solver", tout << j << ": " << *this << "\n";); return *this; @@ -1145,7 +1143,7 @@ new_lemma& new_lemma::explain_existing_lower_bound(lpvar j) { new_lemma& new_lemma::explain_existing_upper_bound(lpvar j) { SASSERT(c.has_upper_bound(j)); lp::explanation ex; - ex.push_back(c.m_lar_solver.get_column_upper_bound_witness(j)); + c.lra.push_explanation(c.lra.get_column_upper_bound_witness(j), ex); *this &= ex; return *this; } @@ -1155,7 +1153,7 @@ std::ostream& new_lemma::display(std::ostream & out) const { for (auto p : lemma.expl()) { out << "(" << p.ci() << ") "; - c.m_lar_solver.constraints().display(out, [this](lpvar j) { return c.var_str(j);}, p.ci()); + c.lra.constraints().display(out, [this](lpvar j) { return c.var_str(j);}, p.ci()); } out << " ==> "; if (lemma.ineqs().empty()) { @@ -1303,7 +1301,7 @@ bool core::has_real(const monic& m) const { bool core::is_patch_blocked(lpvar u, const lp::impq& ival) const { TRACE("nla_solver", tout << "u = " << u << '\n';); if (m_cautious_patching && - (!m_lar_solver.inside_bounds(u, ival) || (var_is_int(u) && ival.is_int() == false))) { + (!lra.inside_bounds(u, ival) || (var_is_int(u) && ival.is_int() == false))) { TRACE("nla_solver", tout << "u = " << u << " blocked, for feas or integr\n";); return true; // block } @@ -1334,7 +1332,7 @@ bool core::is_patch_blocked(lpvar u, const lp::impq& ival) const { bool core::try_to_patch(const rational& v) { auto is_blocked = [this](lpvar u, const lp::impq& iv) { return is_patch_blocked(u, iv); }; auto change_report = [this](lpvar u) { update_to_refine_of_var(u); }; - return m_lar_solver.try_to_patch(m_patched_var, v, is_blocked, change_report); + return lra.try_to_patch(m_patched_var, v, is_blocked, change_report); } bool in_power(const svector& vs, unsigned l) { @@ -1343,7 +1341,7 @@ bool in_power(const svector& vs, unsigned l) { } bool core::to_refine_is_correct() const { - for (unsigned j = 0; j < m_lar_solver.number_of_vars(); j++) { + for (unsigned j = 0; j < lra.number_of_vars(); j++) { if (!is_monic_var(j)) continue; bool valid = check_monic(emons()[j]); if (valid == m_to_refine.contains(j)) { @@ -1435,17 +1433,17 @@ void core::patch_monomials() { NOT_IMPLEMENTED_YET(); m_cautious_patching = false; patch_monomials_on_to_refine(); - m_lar_solver.push(); + lra.push(); save_tableau(); constrain_nl_in_tableau(); if (solve_tableau() && integrality_holds()) { - m_lar_solver.pop(1); + lra.pop(1); } else { - m_lar_solver.pop(); + lra.pop(); restore_tableau(); - m_lar_solver.clear_inf_heap(); + lra.clear_inf_heap(); } - SASSERT(m_lar_solver.ax_is_correct()); + SASSERT(lra.ax_is_correct()); } void core::constrain_nl_in_tableau() { @@ -1507,11 +1505,11 @@ void core::check_bounded_divisions(vector& l_vec) { lbool core::check(vector& l_vec) { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); - m_lar_solver.get_rid_of_inf_eps(); + lra.get_rid_of_inf_eps(); m_lemma_vec = &l_vec; - if (!(m_lar_solver.get_status() == lp::lp_status::OPTIMAL || - m_lar_solver.get_status() == lp::lp_status::FEASIBLE)) { - TRACE("nla_solver", tout << "unknown because of the m_lar_solver.m_status = " << m_lar_solver.get_status() << "\n";); + if (!(lra.get_status() == lp::lp_status::OPTIMAL || + lra.get_status() == lp::lp_status::FEASIBLE)) { + TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";); return l_undef; } @@ -1528,6 +1526,9 @@ lbool core::check(vector& l_vec) { if (l_vec.empty() && !done()) m_monomial_bounds(); + + if (l_vec.empty() && !done() && improve_bounds()) + return l_false; if (l_vec.empty() && !done() && run_horner) m_horner.horner_lemmas(); @@ -1635,21 +1636,21 @@ bool core::no_lemmas_hold() const { } lbool core::test_check(vector& l) { - m_lar_solver.set_status(lp::lp_status::OPTIMAL); + lra.set_status(lp::lp_status::OPTIMAL); return check(l); } std::ostream& core::print_terms(std::ostream& out) const { - for (unsigned i = 0; i< m_lar_solver.terms().size(); i++) { + for (unsigned i = 0; i< lra.terms().size(); i++) { unsigned ext = lp::tv::mask_term(i); - if (!m_lar_solver.var_is_registered(ext)) { + if (!lra.var_is_registered(ext)) { out << "term is not registered\n"; continue; } - const lp::lar_term & t = *m_lar_solver.terms()[i]; + const lp::lar_term & t = *lra.terms()[i]; out << "term:"; print_term(t, out) << std::endl; - lpvar j = m_lar_solver.external_to_local(ext); + lpvar j = lra.external_to_local(ext); print_var(j, out); } return out; @@ -1677,7 +1678,7 @@ std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const std::unordered_set core::get_vars_of_expr_with_opening_terms(const nex *e ) { auto ret = get_vars_of_expr(e); - auto & ls = m_lar_solver; + auto & ls = lra; svector added; for (auto j : ret) { added.push_back(j); @@ -1685,7 +1686,7 @@ std::unordered_set core::get_vars_of_expr_with_opening_terms(const nex *e for (unsigned i = 0; i < added.size(); ++i) { lpvar j = added[i]; if (ls.column_corresponds_to_term(j)) { - const auto& t = m_lar_solver.get_term(lp::tv::raw(ls.local_to_external(j))); + const auto& t = lra.get_term(lp::tv::raw(ls.local_to_external(j))); for (auto p : t) { if (ret.find(p.column()) == ret.end()) { added.push_back(p.column()); @@ -1705,7 +1706,7 @@ bool core::is_nl_var(lpvar j) const { unsigned core::get_var_weight(lpvar j) const { unsigned k; - switch (m_lar_solver.get_column_type(j)) { + switch (lra.get_column_type(j)) { case lp::column_type::fixed: k = 0; @@ -1734,7 +1735,7 @@ unsigned core::get_var_weight(lpvar j) const { void core::set_active_vars_weights(nex_creator& nc) { - nc.set_number_of_vars(m_lar_solver.column_count()); + nc.set_number_of_vars(lra.column_count()); for (lpvar j : active_var_set()) nc.set_var_weight(j, get_var_weight(j)); } @@ -1744,8 +1745,8 @@ bool core::influences_nl_var(lpvar j) const { j = lp::tv::unmask_term(j); if (is_nl_var(j)) return true; - for (const auto & c : m_lar_solver.A_r().m_columns[j]) { - lpvar basic_in_row = m_lar_solver.r_basis()[c.var()]; + for (const auto & c : lra.A_r().m_columns[j]) { + lpvar basic_in_row = lra.r_basis()[c.var()]; if (is_nl_var(basic_in_row)) return true; } @@ -1762,9 +1763,32 @@ void core::set_use_nra_model(bool m) { void core::collect_statistics(::statistics & st) { st.update("arith-nla-explanations", m_stats.m_nla_explanations); st.update("arith-nla-lemmas", m_stats.m_nla_lemmas); - st.update("arith-nra-calls", m_stats.m_nra_calls); + st.update("arith-nra-calls", m_stats.m_nra_calls); + st.update("arith-bounds-improvements", m_stats.m_bounds_improvements); } +bool core::improve_bounds() { + return false; + + uint_set seen; + bool bounds_improved = false; + auto insert = [&](lpvar v) { + if (seen.contains(v)) + return; + seen.insert(v); + if (lra.improve_bound(v, false)) + bounds_improved = true, m_stats.m_bounds_improvements++; + if (lra.improve_bound(v, true)) + bounds_improved = true, m_stats.m_bounds_improvements++; + }; + for (auto & m : m_emons) { + insert(m.var()); + for (auto v : m.vars()) + insert(v); + } + return bounds_improved; +} + } // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 78f46bb41..0bd4a5814 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -64,6 +64,7 @@ class core { unsigned m_nla_explanations; unsigned m_nla_lemmas; unsigned m_nra_calls; + unsigned m_bounds_improvements; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); @@ -79,7 +80,7 @@ class core { var_eqs m_evars; - lp::lar_solver& m_lar_solver; + lp::lar_solver& lra; reslimit& m_reslim; smt_params_helper m_params; std::function m_relevant; @@ -110,6 +111,9 @@ class core { void check_weighted(unsigned sz, std::pair>* checks); + // try to improve bounds for variables in monomials. + bool improve_bounds(); + public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&); @@ -142,13 +146,13 @@ public: bool ineq_holds(const ineq& n) const; bool lemma_holds(const lemma& l) const; bool is_monic_var(lpvar j) const { return m_emons.is_monic_var(j); } - const rational& val(lpvar j) const { return m_lar_solver.get_column_value(j).x; } + const rational& val(lpvar j) const { return lra.get_column_value(j).x; } - const rational& var_val(const monic& m) const { return m_lar_solver.get_column_value(m.var()).x; } + const rational& var_val(const monic& m) const { return lra.get_column_value(m.var()).x; } rational mul_val(const monic& m) const { rational r(1); - for (lpvar v : m.vars()) r *= m_lar_solver.get_column_value(v).x; + for (lpvar v : m.vars()) r *= lra.get_column_value(v).x; return r; } @@ -287,16 +291,16 @@ public: } const rational& get_upper_bound(unsigned j) const; const rational& get_lower_bound(unsigned j) const; - bool has_lower_bound(lp::var_index var, lp::constraint_index& ci, lp::mpq& value, bool& is_strict) const { - return m_lar_solver.has_lower_bound(var, ci, value, is_strict); + bool has_lower_bound(lp::var_index var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { + return lra.has_lower_bound(var, ci, value, is_strict); } - bool has_upper_bound(lp::var_index var, lp::constraint_index& ci, lp::mpq& value, bool& is_strict) const { - return m_lar_solver.has_upper_bound(var, ci, value, is_strict); + bool has_upper_bound(lp::var_index var, u_dependency*& ci, lp::mpq& value, bool& is_strict) const { + return lra.has_upper_bound(var, ci, value, is_strict); } bool zero_is_an_inner_point_of_bounds(lpvar j) const; - bool var_is_int(lpvar j) const { return m_lar_solver.column_is_int(j); } + bool var_is_int(lpvar j) const { return lra.column_is_int(j); } int rat_sign(const monic& m) const; inline int rat_sign(lpvar j) const { return nla::rat_sign(val(j)); } @@ -338,7 +342,7 @@ public: bool is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar &j) const; - void add_equivalence_maybe(const lp::lar_term *t, lpci c0, lpci c1); + void add_equivalence_maybe(const lp::lar_term* t, u_dependency* c0, u_dependency* c1); void init_vars_equivalence(); diff --git a/src/math/lp/nla_defs.h b/src/math/lp/nla_defs.h index df9158b42..e9807eaeb 100644 --- a/src/math/lp/nla_defs.h +++ b/src/math/lp/nla_defs.h @@ -14,11 +14,10 @@ #include "math/lp/explanation.h" namespace nla { -typedef lp::constraint_index lpci; -typedef lp::lconstraint_kind llc; -typedef lp::constraint_index lpci; -typedef lp::explanation expl_set; -typedef lp::var_index lpvar; + typedef lp::constraint_index lpci; + typedef lp::lconstraint_kind llc; + typedef lp::explanation expl_set; + typedef lp::var_index lpvar; struct from_index_dummy{}; class signed_var { diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index eefb07fac..f2fe8d9b2 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -22,9 +22,9 @@ namespace nla { grobner::grobner(core* c): common(c), - m_pdd_manager(m_core.m_lar_solver.number_of_vars()), - m_solver(m_core.m_reslim, m_pdd_manager), - m_lar_solver(m_core.m_lar_solver), + m_pdd_manager(m_core.lra.number_of_vars()), + m_solver(m_core.m_reslim, m_core.lra.dep_manager(), m_pdd_manager), + lra(m_core.lra), m_quota(m_core.params().arith_nl_gr_q()) {} @@ -211,12 +211,12 @@ namespace nla { TRACE("grobner", tout << "base vars: "; for (lpvar j : c().active_var_set()) - if (m_lar_solver.is_base(j)) + if (lra.is_base(j)) tout << "j" << j << " "; tout << "\n"); for (lpvar j : c().active_var_set()) { - if (m_lar_solver.is_base(j)) - add_row(m_lar_solver.basic2row(j)); + if (lra.is_base(j)) + add_row(lra.basic2row(j)); if (c().is_monic_var(j) && c().var_is_fixed(j)) add_fixed_monic(j); @@ -267,12 +267,12 @@ namespace nla { } } - for (unsigned j = 0; j < m_lar_solver.number_of_vars(); ++j) { - if (m_lar_solver.column_has_lower_bound(j) || m_lar_solver.column_has_upper_bound(j)) { + for (unsigned j = 0; j < lra.number_of_vars(); ++j) { + if (lra.column_has_lower_bound(j) || lra.column_has_upper_bound(j)) { out << j << ": ["; - if (m_lar_solver.column_has_lower_bound(j)) out << m_lar_solver.get_lower_bound(j); + if (lra.column_has_lower_bound(j)) out << lra.get_lower_bound(j); out << ".."; - if (m_lar_solver.column_has_upper_bound(j)) out << m_lar_solver.get_upper_bound(j); + if (lra.column_has_upper_bound(j)) out << lra.get_upper_bound(j); out << "]\n"; } } @@ -343,14 +343,14 @@ namespace nla { if (c().var_is_fixed(j)) return; - const auto& matrix = m_lar_solver.A_r(); + const auto& matrix = lra.A_r(); for (auto & s : matrix.m_columns[j]) { unsigned row = s.var(); if (m_rows.contains(row)) continue; m_rows.insert(row); - unsigned k = m_lar_solver.get_base_column_in_row(row); - if (m_lar_solver.column_is_free(k) && k != j) + unsigned k = lra.get_base_column_in_row(row); + if (lra.column_is_free(k) && k != j) continue; CTRACE("grobner", matrix.m_rows[row].size() > c().params().arith_nl_grobner_row_length_limit(), tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";); @@ -362,11 +362,10 @@ namespace nla { } const rational& grobner::val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep) { - unsigned lc, uc; - m_lar_solver.get_bound_constraint_witnesses_for_column(j, lc, uc); - dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(lc)); - dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(uc)); - return m_lar_solver.column_lower_bound(j).x; + auto* d = lra.get_bound_constraint_witnesses_for_column(j); + if (d) + dep = c().m_intervals.mk_join(dep, d); + return lra.column_lower_bound(j).x; } dd::pdd grobner::pdd_expr(const rational& coeff, lpvar j, u_dependency*& dep) { @@ -415,7 +414,7 @@ namespace nla { SASSERT(r.hi().is_val()); v = r.var(); rational val = r.hi().val(); - switch (m_lar_solver.get_column_type(v)) { + switch (lra.get_column_type(v)) { case lp::column_type::lower_bound: if (val > 0) num_lo++, lo = v, lc = val; else num_hi++, hi = v, hc = val; break; @@ -508,7 +507,7 @@ namespace nla { void grobner::display_matrix_of_m_rows(std::ostream & out) const { - const auto& matrix = m_lar_solver.A_r(); + const auto& matrix = lra.A_r(); out << m_rows.size() << " rows" << "\n"; out << "the matrix\n"; for (const auto & r : matrix.m_rows) @@ -516,7 +515,7 @@ namespace nla { } void grobner::set_level2var() { - unsigned n = m_lar_solver.column_count(); + unsigned n = lra.column_count(); unsigned_vector sorted_vars(n), weighted_vars(n); for (unsigned j = 0; j < n; j++) { sorted_vars[j] = j; diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index f8e21d1a7..c7d41413c 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -21,7 +21,7 @@ namespace nla { class grobner : common { dd::pdd_manager m_pdd_manager; dd::solver m_solver; - lp::lar_solver& m_lar_solver; + lp::lar_solver& lra; indexed_uint_set m_rows; unsigned m_quota = 0; diff --git a/src/math/lp/nla_intervals.cpp b/src/math/lp/nla_intervals.cpp index 4ffbcb7e3..791251fee 100644 --- a/src/math/lp/nla_intervals.cpp +++ b/src/math/lp/nla_intervals.cpp @@ -4,6 +4,11 @@ #include "util/mpq.h" namespace nla { + +intervals::intervals(core* c, reslimit& lim): + m_dep_intervals(c->lra.dep_manager(), lim), + m_core(c) {} + typedef enum dep_intervals::with_deps_t e_with_deps; const nex* intervals::get_inf_interval_child(const nex_sum& e) const { @@ -173,7 +178,7 @@ lp::lar_term intervals::expression_to_normalized_term(const nex_sum* e, rational // where m_terms[k] corresponds to the returned lpvar lpvar intervals::find_term_column(const lp::lar_term & norm_t, rational& a) const { std::pair a_j; - if (m_core->m_lar_solver.fetch_normalized_term_column(norm_t, a_j)) { + if (m_core->lra.fetch_normalized_term_column(norm_t, a_j)) { a /= a_j.first; return a_j.second; } @@ -206,19 +211,10 @@ void intervals::set_zero_interval_deps_for_mult(interval& a) { a.m_upper_dep = a.m_lower_dep; } -u_dependency *intervals::mk_dep(lp::constraint_index ci) { - return m_dep_intervals.mk_leaf(ci); -} - -u_dependency *intervals::mk_dep(const lp::explanation& expl) { +u_dependency* intervals::mk_dep(const lp::explanation& expl) { u_dependency * r = nullptr; - for (auto p : expl) { - if (r == nullptr) { - r = m_dep_intervals.mk_leaf(p.ci()); - } else { - r = m_dep_intervals.mk_join(r, m_dep_intervals.mk_leaf(p.ci())); - } - } + for (auto p : expl) + r = m_dep_intervals.mk_join(r, m_dep_intervals.mk_leaf(p.ci())); return r; } @@ -249,25 +245,25 @@ std::ostream& intervals::display(std::ostream& out, const interval& i) const { template void intervals::set_var_interval(lpvar v, interval& b) { TRACE("nla_intervals_details", m_core->print_var(v, tout) << "\n";); - lp::constraint_index ci; + u_dependency* dep = nullptr; rational val; bool is_strict; - if (ls().has_lower_bound(v, ci, val, is_strict)) { + if (ls().has_lower_bound(v, dep, val, is_strict)) { m_dep_intervals.set_lower(b, val); m_dep_intervals.set_lower_is_open(b, is_strict); m_dep_intervals.set_lower_is_inf(b, false); - if (wd == e_with_deps::with_deps) b.m_lower_dep = mk_dep(ci); + if (wd == e_with_deps::with_deps) b.m_lower_dep = dep; } else { m_dep_intervals.set_lower_is_open(b, true); m_dep_intervals.set_lower_is_inf(b, true); if (wd == e_with_deps::with_deps) b.m_lower_dep = nullptr; } - if (ls().has_upper_bound(v, ci, val, is_strict)) { + if (ls().has_upper_bound(v, dep, val, is_strict)) { m_dep_intervals.set_upper(b, val); m_dep_intervals.set_upper_is_open(b, is_strict); m_dep_intervals.set_upper_is_inf(b, false); - if (wd == e_with_deps::with_deps) b.m_upper_dep = mk_dep(ci); + if (wd == e_with_deps::with_deps) b.m_upper_dep = dep; } else { m_dep_intervals.set_upper_is_open(b, true); @@ -303,7 +299,7 @@ bool intervals::interval_from_term(const nex& e, scoped_dep_interval& i) { m_dep_intervals.set(i, bi); TRACE("nla_intervals", - m_core->m_lar_solver.print_column_info(j, tout) << "\n"; + m_core->lra.print_column_info(j, tout) << "\n"; tout << "a=" << a << ", b=" << b << "\n"; tout << e << ", interval = "; display(tout, i);); return true; @@ -476,9 +472,9 @@ bool intervals::interval_of_expr(const nex* e, unsigned p, scoped_dep_interval& } -lp::lar_solver& intervals::ls() { return m_core->m_lar_solver; } +lp::lar_solver& intervals::ls() { return m_core->lra; } -const lp::lar_solver& intervals::ls() const { return m_core->m_lar_solver; } +const lp::lar_solver& intervals::ls() const { return m_core->lra; } } // end of nla namespace diff --git a/src/math/lp/nla_intervals.h b/src/math/lp/nla_intervals.h index 0545e9933..514049aca 100644 --- a/src/math/lp/nla_intervals.h +++ b/src/math/lp/nla_intervals.h @@ -25,16 +25,13 @@ class intervals { public: typedef dep_intervals::interval interval; private: - u_dependency* mk_dep(lp::constraint_index ci); u_dependency* mk_dep(lp::explanation const&); lp::lar_solver& ls(); const lp::lar_solver& ls() const; public: - intervals(core* c, reslimit& lim) : - m_dep_intervals(lim), - m_core(c) - {} + intervals(core* c, reslimit& lim); + dep_intervals& get_dep_intervals() { return m_dep_intervals; } u_dependency* mk_join(u_dependency* a, u_dependency* b) { return m_dep_intervals.mk_join(a, b); } u_dependency* mk_leaf(lp::constraint_index ci) { return m_dep_intervals.mk_leaf(ci); } diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 377b318dd..dc4681559 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -186,7 +186,7 @@ struct solver::imp { for (auto const& eq : eqs) add_eq(eq); for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.m_lar_solver; + auto& ls = m_nla_core.lra; if (ls.column_has_lower_bound(v)) add_lb(ls.get_lower_bound(v), w); if (ls.column_has_upper_bound(v)) @@ -209,7 +209,7 @@ struct solver::imp { IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n"; m_nlsat->display(verbose_stream()); for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.m_lar_solver; + auto& ls = m_nla_core.lra; if (ls.column_has_lower_bound(v)) verbose_stream() << w << " >= " << ls.get_lower_bound(v) << "\n"; if (ls.column_has_upper_bound(v)) diff --git a/src/math/lp/ul_pair.h b/src/math/lp/ul_pair.h index abfb4483b..37fd6e9ed 100644 --- a/src/math/lp/ul_pair.h +++ b/src/math/lp/ul_pair.h @@ -1,25 +1,19 @@ /*++ Copyright (c) 2017 Microsoft Corporation -Module Name: - - - Abstract: - + justifications for upper or lower bounds Author: Lev Nachmanson (levnach) -Revision History: - - --*/ #pragma once #include "util/vector.h" +#include "util/dependency.h" #include #include #include @@ -48,14 +42,20 @@ inline bool compare(const std::pair & a, const std::pair cs) { + eq_justification(std::initializer_list cs) { int i = 0; - for (lpci c: cs) { + for (auto c: cs) { m_cs[i++] = c; } for (; i < 4; i++) { - m_cs[i] = -1; + m_cs[i] = nullptr; } } - void explain(lp::explanation& e) const { - for (lpci c : m_cs) - if (c + 1 != 0) // c != -1 - e.push_back(c); + u_dependency* const* begin() const { return m_cs; } + u_dependency* const* end() const { + unsigned i = 0; + for (; i < 4 && m_cs[i]; ++i) + ; + return m_cs + i; } }; @@ -202,7 +204,7 @@ public: } for (eq_justification const& j : m_justtrail) { - j.explain(e); + explain_eq(j, e); } m_stats.m_num_explains += m_justtrail.size(); m_stats.m_num_explain_calls++; @@ -216,6 +218,17 @@ public: // IF_VERBOSE(2, verbose_stream() << (double)m_stats.m_num_explains / m_stats.m_num_explain_calls << "\n"); } + void explain_eq(eq_justification const& eq, lp::explanation& e) const { + u_dependency_manager dm; + unsigned_vector deps; + for (auto* dep : eq) { + deps.reset(); + dm.linearize(dep, deps); + for (auto ci : deps) + e.push_back(ci); + } + } + void explain_bfs(signed_var v1, signed_var v2, lp::explanation& e) const { SASSERT(find(v1) == find(v2)); if (v1 == v2) { @@ -249,7 +262,7 @@ public: } while (head != 0) { - m_justtrail[head].explain(e); + explain_eq(m_justtrail[head], e); head = m_todo[head].m_index; ++m_stats.m_num_explains; } diff --git a/src/sat/sat_anf_simplifier.cpp b/src/sat/sat_anf_simplifier.cpp index 6d28d9f98..5ed45171c 100644 --- a/src/sat/sat_anf_simplifier.cpp +++ b/src/sat/sat_anf_simplifier.cpp @@ -42,7 +42,8 @@ namespace sat { void anf_simplifier::operator()() { dd::pdd_manager m(20, dd::pdd_manager::semantics::mod2_e); - pdd_solver solver(s.rlimit(), m); + u_dependency_manager dm; + pdd_solver solver(s.rlimit(), dm, m); report _report(*this); configure_solver(solver); clauses2anf(solver); diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index c2a96177d..d17154361 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -524,7 +524,7 @@ namespace arith { return all_divs_valid; } - void solver::fixed_var_eh(theory_var v, lp::constraint_index ci1, lp::constraint_index ci2, rational const& bound) { + void solver::fixed_var_eh(theory_var v, u_dependency* dep, rational const& bound) { theory_var w = euf::null_theory_var; enode* x = var2enode(v); if (bound.is_zero()) @@ -540,8 +540,8 @@ namespace arith { return; reset_evidence(); m_explanation.clear(); - consume(rational::one(), ci1); - consume(rational::one(), ci2); + for (auto ci : lp().flatten(dep)) + consume(rational::one(), ci); ++m_stats.m_fixed_eqs; auto* hint = explain_implied_eq(m_explanation, x, y); auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y, hint); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 0e97c3503..bda3bebcd 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -402,12 +402,13 @@ namespace arith { } void solver::propagate_eqs(lp::tv t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { - lp::constraint_index ci2; - if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), ci2, value)) { - fixed_var_eh(b.get_var(), ci1, ci2, value); + u_dependency* dep; + auto& dm = lp().dep_manager(); + if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), dep, value)) { + fixed_var_eh(b.get_var(), dm.mk_join(dm.mk_leaf(ci1), dep), value); } - else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), ci2, value)) { - fixed_var_eh(b.get_var(), ci1, ci2, value); + else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), dep, value)) { + fixed_var_eh(b.get_var(), dm.mk_join(dm.mk_leaf(ci1), dep), value); } } @@ -433,11 +434,12 @@ namespace arith { // m_solver already tracks bounds on proper variables, but not on terms. bool is_strict = false; rational b; + u_dependency* dep = nullptr; if (is_lower) { - return lp().has_lower_bound(tv.id(), ci, b, is_strict) && !is_strict && b == v; + return lp().has_lower_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; } else { - return lp().has_upper_bound(tv.id(), ci, b, is_strict) && !is_strict && b == v; + return lp().has_upper_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; } } } @@ -692,7 +694,7 @@ namespace arith { void solver::report_equality_of_fixed_vars(unsigned vi1, unsigned vi2) { rational bound; - lp::constraint_index ci1, ci2, ci3, ci4; + u_dependency* ci1 = nullptr, *ci2 = nullptr, *ci3 = nullptr, *ci4 = nullptr; theory_var v1 = lp().local_to_external(vi1); theory_var v2 = lp().local_to_external(vi2); TRACE("arith", tout << "fixed: " << mk_pp(var2expr(v1), m) << " " << mk_pp(var2expr(v2), m) << "\n";); @@ -714,10 +716,10 @@ namespace arith { ++m_stats.m_fixed_eqs; reset_evidence(); m_explanation.clear(); - consume(rational::one(), ci1); - consume(rational::one(), ci2); - consume(rational::one(), ci3); - consume(rational::one(), ci4); + auto& dm = lp().dep_manager(); + auto* d = dm.mk_join(dm.mk_join(ci1, ci2), dm.mk_join(ci3, ci4)); + for (auto ci : lp().flatten(d)) + consume(rational::one(), ci); enode* x = var2enode(v1); enode* y = var2enode(v2); auto* ex = explain_implied_eq(m_explanation, x, y); @@ -729,26 +731,28 @@ namespace arith { return x == y || var2enode(x)->get_root() == var2enode(y)->get_root(); } - bool solver::has_upper_bound(lpvar vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } + bool solver::has_upper_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } - bool solver::has_lower_bound(lpvar vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } + bool solver::has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } - bool solver::has_bound(lpvar vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { + bool solver::has_bound(lpvar vi, u_dependency*& dep, rational const& bound, bool is_lower) { if (lp::tv::is_term(vi)) { theory_var v = lp().local_to_external(vi); rational val; TRACE("arith", tout << lp().get_variable_name(vi) << " " << v << "\n";); if (v != euf::null_theory_var && a.is_numeral(var2expr(v), val) && bound == val) { - ci = UINT_MAX; + dep = nullptr; return bound == val; } auto& vec = is_lower ? m_lower_terms : m_upper_terms; lpvar ti = lp::tv::unmask_term(vi); if (vec.size() > ti) { - constraint_bound& b = vec[ti]; - ci = b.first; - return ci != UINT_MAX && bound == b.second; + auto& [ci, coeff] = vec[ti]; + if (ci == UINT_MAX) + return false; + dep = lp().dep_manager().mk_leaf(ci); + return bound == coeff; } else { return false; @@ -758,10 +762,10 @@ namespace arith { bool is_strict = false; rational b; if (is_lower) { - return lp().has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + return lp().has_lower_bound(vi, dep, b, is_strict) && b == bound && !is_strict; } else { - return lp().has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + return lp().has_upper_bound(vi, dep, b, is_strict) && b == bound && !is_strict; } } } diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 20252ede9..1b6f58782 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -359,7 +359,7 @@ namespace arith { void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r); api_bound* mk_var_bound(sat::literal lit, theory_var v, lp_api::bound_kind bk, rational const& bound); lp::lconstraint_kind bound2constraint_kind(bool is_int, lp_api::bound_kind bk, bool is_true); - void fixed_var_eh(theory_var v1, lp::constraint_index ci1, lp::constraint_index ci2, rational const& bound); + void fixed_var_eh(theory_var v1, u_dependency* dep, rational const& bound); bool set_upper_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); } bool set_lower_bound(lp::tv t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); } bool set_bound(lp::tv tv, lp::constraint_index ci, rational const& v, bool is_lower); @@ -412,9 +412,9 @@ namespace arith { nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const; - bool has_bound(lpvar vi, lp::constraint_index& ci, rational const& bound, bool is_lower); - bool has_lower_bound(lpvar vi, lp::constraint_index& ci, rational const& bound); - bool has_upper_bound(lpvar vi, lp::constraint_index& ci, rational const& bound); + bool has_bound(lpvar vi, u_dependency*& ci, rational const& bound, bool is_lower); + bool has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound); + bool has_upper_bound(lpvar vi, u_dependency*& ci, rational const& bound); /* * Facility to put a small box around integer variables used in branch and bounds. diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6dc14fb13..08fa39864 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2863,7 +2863,7 @@ public: lp::lar_term const& term = lp().get_term(ti); for (auto const mono : term) { auto wi = lp().column2tv(mono.column()); - lp::constraint_index ci; + u_dependency* ci = nullptr; rational value; bool is_strict; if (wi.is_term()) { @@ -2966,12 +2966,13 @@ public: vector m_upper_terms; void propagate_eqs(lp::tv t, lp::constraint_index ci1, lp::lconstraint_kind k, api_bound& b, rational const& value) { - lp::constraint_index ci2; + u_dependency* ci2 = nullptr; + auto pair = [&]() { return lp().dep_manager().mk_join(lp().dep_manager().mk_leaf(ci1), ci2); }; if (k == lp::GE && set_lower_bound(t, ci1, value) && has_upper_bound(t.index(), ci2, value)) { - fixed_var_eh(b.get_var(), t, ci1, ci2, value); + fixed_var_eh(b.get_var(), t, pair(), value); } else if (k == lp::LE && set_upper_bound(t, ci1, value) && has_lower_bound(t.index(), ci2, value)) { - fixed_var_eh(b.get_var(), t, ci1, ci2, value); + fixed_var_eh(b.get_var(), t, pair(), value); } } @@ -3012,11 +3013,12 @@ public: // m_solver already tracks bounds on proper variables, but not on terms. bool is_strict = false; rational b; + u_dependency* dep = nullptr; if (is_lower) { - return lp().has_lower_bound(tv.id(), ci, b, is_strict) && !is_strict && b == v; + return lp().has_lower_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; } else { - return lp().has_upper_bound(tv.id(), ci, b, is_strict) && !is_strict && b == v; + return lp().has_upper_bound(tv.id(), dep, b, is_strict) && !is_strict && b == v; } } } @@ -3024,35 +3026,37 @@ public: bool var_has_bound(lpvar vi, bool is_lower) { bool is_strict = false; rational b; - lp::constraint_index ci; + u_dependency* dep; if (is_lower) { - return lp().has_lower_bound(vi, ci, b, is_strict); + return lp().has_lower_bound(vi, dep, b, is_strict); } else { - return lp().has_upper_bound(vi, ci, b, is_strict); + return lp().has_upper_bound(vi, dep, b, is_strict); } } - bool has_upper_bound(lpvar vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } + bool has_upper_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } - bool has_lower_bound(lpvar vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } + bool has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } - bool has_bound(lpvar vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { + bool has_bound(lpvar vi, u_dependency*& dep, rational const& bound, bool is_lower) { if (lp::tv::is_term(vi)) { theory_var v = lp().local_to_external(vi); rational val; TRACE("arith", tout << lp().get_variable_name(vi) << " " << v << "\n";); if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { - ci = UINT_MAX; + dep = nullptr; return bound == val; } auto& vec = is_lower ? m_lower_terms : m_upper_terms; lpvar ti = lp::tv::unmask_term(vi); if (vec.size() > ti) { - constraint_bound& b = vec[ti]; - ci = b.first; - return ci != UINT_MAX && bound == b.second; + auto const& [ci, coeff] = vec[ti]; + if (ci == UINT_MAX) + return false; + dep = lp().dep_manager().mk_leaf(ci); + return bound == coeff; } else { return false; @@ -3062,10 +3066,10 @@ public: bool is_strict = false; rational b; if (is_lower) { - return lp().has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + return lp().has_lower_bound(vi, dep, b, is_strict) && b == bound && !is_strict; } else { - return lp().has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + return lp().has_upper_bound(vi, dep, b, is_strict) && b == bound && !is_strict; } } } @@ -3078,7 +3082,7 @@ public: void report_equality_of_fixed_vars(unsigned vi1, unsigned vi2) { rational bound(0); - lp::constraint_index ci1, ci2, ci3, ci4; + u_dependency* ci1 = nullptr, *ci2 = nullptr, *ci3 = nullptr, *ci4 = nullptr; theory_var v1 = lp().local_to_external(vi1); theory_var v2 = lp().local_to_external(vi2); TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << "\n";); @@ -3130,7 +3134,7 @@ public: ctx().assign_eq(x, y, eq_justification(js)); } - void fixed_var_eh(theory_var v, lp::tv t, lp::constraint_index ci1, lp::constraint_index ci2, rational const& bound) { + void fixed_var_eh(theory_var v, lp::tv t, u_dependency* dep, rational const& bound) { theory_var w = null_theory_var; enode* x = get_enode(v); if (m_value2var.find(bound, w)) @@ -3147,8 +3151,7 @@ public: if (x->get_root() == y->get_root()) return; reset_evidence(); - set_evidence(ci1, m_core, m_eqs); - set_evidence(ci2, m_core, m_eqs); + set_evidence(dep, m_core, m_eqs); ++m_stats.m_fixed_eqs; assign_eq(v, w); } @@ -3182,6 +3185,11 @@ public: // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is + void set_evidence(u_dependency* dep, literal_vector& core, svector& eqs) { + for (auto ci : lp().flatten(dep)) + set_evidence(ci, core, eqs); + } + void set_evidence(lp::constraint_index idx, literal_vector& core, svector& eqs) { if (idx == UINT_MAX) { return; @@ -3402,7 +3410,7 @@ public: if (!is_registered_var(v)) return false; lpvar vi = get_lpvar(v); - lp::constraint_index ci; + u_dependency* ci; return lp().has_lower_bound(vi, ci, val, is_strict); } @@ -3421,8 +3429,8 @@ public: if (!is_registered_var(v)) return false; lpvar vi = get_lpvar(v); - lp::constraint_index ci; - return lp().has_upper_bound(vi, ci, val, is_strict); + u_dependency* dep = nullptr; + return lp().has_upper_bound(vi, dep, val, is_strict); } diff --git a/src/test/pdd_solver.cpp b/src/test/pdd_solver.cpp index a86504266..33b0abf77 100644 --- a/src/test/pdd_solver.cpp +++ b/src/test/pdd_solver.cpp @@ -20,13 +20,15 @@ namespace dd { } void test1() { pdd_manager m(4); + u_dependency_manager dm; reslimit lim; pdd v0 = m.mk_var(0); pdd v1 = m.mk_var(1); pdd v2 = m.mk_var(2); pdd v3 = m.mk_var(3); - solver gb(lim, m); + + solver gb(lim, dm, m); gb.add(v1*v2 + v1*v3); gb.add(v1 - 1); gb.display(std::cout); @@ -198,10 +200,11 @@ namespace dd { void test_simplify(expr_ref_vector& fmls, bool use_mod2) { ast_manager& m = fmls.get_manager(); unsigned_vector id2var; + u_dependency_manager dm; collect_id2var(id2var, fmls); pdd_manager p(id2var.size(), use_mod2 ? pdd_manager::mod2_e : pdd_manager::zero_one_vars_e); - solver g(m.limit(), p); + solver g(m.limit(), dm, p); for (expr* e : subterms::ground(fmls)) { add_def(id2var, to_app(e), m, p, g); diff --git a/src/util/dependency.h b/src/util/dependency.h index 7ccba716a..57057460c 100644 --- a/src/util/dependency.h +++ b/src/util/dependency.h @@ -69,7 +69,7 @@ private: value_manager & m_vmanager; allocator & m_allocator; - ptr_vector m_todo; + mutable ptr_vector m_todo; void inc_ref(value const & v) { if (C::ref_count) @@ -106,12 +106,9 @@ private: } } - void unmark_todo() { - typename ptr_vector::iterator it = m_todo.begin(); - typename ptr_vector::iterator end = m_todo.end(); - for (; it != end; ++it) { - (*it)->unmark(); - } + void unmark_todo() const { + for (auto* d : m_todo) + d->unmark(); m_todo.reset(); } @@ -193,7 +190,7 @@ public: return false; } - void linearize(dependency * d, vector & vs) { + void linearize(dependency * d, vector & vs) const { if (d) { m_todo.reset(); d->mark(); @@ -300,7 +297,7 @@ public: return m_dep_manager.contains(d, v); } - void linearize(dependency * d, vector & vs) { + void linearize(dependency * d, vector & vs) const { return m_dep_manager.linearize(d, vs); } From 5d33805c8b1f377bfccabe2cc2d56ca35c54ed40 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 10:07:56 +0100 Subject: [PATCH 098/428] optimize ~relevancy_propagator_imp() so it just dec refs the exprs in the trail It avoid doing all the funky watch stuff One extreme Alive2 test case goes from 40s to 28s :) --- src/smt/smt_relevancy.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_relevancy.cpp b/src/smt/smt_relevancy.cpp index 649d75737..ce6de3ec3 100644 --- a/src/smt/smt_relevancy.cpp +++ b/src/smt/smt_relevancy.cpp @@ -123,7 +123,7 @@ namespace smt { } struct relevancy_propagator_imp : public relevancy_propagator { - unsigned m_qhead; + unsigned m_qhead = 0; expr_ref_vector m_relevant_exprs; uint_set m_is_relevant; typedef list relevancy_ehs; @@ -144,14 +144,18 @@ namespace smt { unsigned m_trail_lim; }; svector m_scopes; - bool m_propagating; + bool m_propagating = false; relevancy_propagator_imp(context & ctx): - relevancy_propagator(ctx), m_qhead(0), m_relevant_exprs(ctx.get_manager()), - m_propagating(false) {} + relevancy_propagator(ctx), m_relevant_exprs(ctx.get_manager()) {} ~relevancy_propagator_imp() override { - undo_trail(0); + ast_manager & m = get_manager(); + unsigned i = m_trail.size(); + while (i != 0) { + --i; + m.dec_ref(m_trail[i].get_node()); + } } relevancy_ehs * get_handlers(expr * n) { From 3b546b2348fec5a19f708746ee268bc701d00023 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 10:34:28 +0100 Subject: [PATCH 099/428] smt_context: we can't assert that the resource limits were exceeded on cancel_exception It happens sometimes that e.g. the internalizer goes above the soft memory limit But since it's only by a small amount, when the exception propagates back to the context, some stuff has been freed already and we are not longer above the memory threshold Just delete these asserts --- src/smt/smt_context.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 7895287ef..64c7bbf2e 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3565,7 +3565,6 @@ namespace smt { try { internalize_assertions(); } catch (cancel_exception&) { - VERIFY(resource_limits_exceeded()); return l_undef; } expr_ref_vector theory_assumptions(m); @@ -3637,7 +3636,6 @@ namespace smt { TRACE("unsat_core_bug", tout << asms << '\n';); init_assumptions(asms); } catch (cancel_exception&) { - VERIFY(resource_limits_exceeded()); return l_undef; } TRACE("before_search", display(tout);); @@ -3664,7 +3662,6 @@ namespace smt { for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; init_assumptions(asms); } catch (cancel_exception&) { - VERIFY(resource_limits_exceeded()); return l_undef; } for (auto const& clause : clauses) init_clause(clause); From 28884b398c26291f867ae3ef08128b2709db72d5 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 12:57:47 +0100 Subject: [PATCH 100/428] remove unneeded virtual destructor (optimization) --- src/smt/smt_relevancy.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index f64b7d059..0fae815f9 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -29,7 +29,6 @@ namespace smt { void mark_as_relevant(relevancy_propagator & rp, expr * n); void mark_args_as_relevant(relevancy_propagator & rp, app * n); public: - virtual ~relevancy_eh() = default; /** \brief This method is invoked when n is marked as relevant. */ @@ -41,7 +40,7 @@ namespace smt { /** \brief Fallback for the two previous methods. */ - virtual void operator()(relevancy_propagator & rp) {} + virtual void operator()(relevancy_propagator & rp) = 0; }; class simple_relevancy_eh : public relevancy_eh { From c469c6e1d5a2188859f33faaa4a78c349bf8119a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 13:39:15 +0100 Subject: [PATCH 101/428] attempt to fix clang buildbots --- src/smt/smt_relevancy.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index 0fae815f9..c32eabc5d 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -24,6 +24,9 @@ namespace smt { class context; class relevancy_propagator; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" + class relevancy_eh { protected: void mark_as_relevant(relevancy_propagator & rp, expr * n); @@ -42,6 +45,7 @@ namespace smt { */ virtual void operator()(relevancy_propagator & rp) = 0; }; +#pragma clang diagnostic pop class simple_relevancy_eh : public relevancy_eh { expr * m_target; From 8210aafb69715e637cdad04e92638f791b27df2a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 14:09:04 +0100 Subject: [PATCH 102/428] ast compare_nodes: fail faster when comparing quantifier expressions --- src/ast/ast.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index e2e01bbee..0004aaa42 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -479,15 +479,15 @@ bool compare_nodes(ast const * n1, ast const * n2) { return q1->get_kind() == q2->get_kind() && q1->get_num_decls() == q2->get_num_decls() && + q1->get_expr() == q2->get_expr() && + q1->get_weight() == q2->get_weight() && + q1->get_num_patterns() == q2->get_num_patterns() && compare_arrays(q1->get_decl_sorts(), q2->get_decl_sorts(), q1->get_num_decls()) && compare_arrays(q1->get_decl_names(), q2->get_decl_names(), q1->get_num_decls()) && - q1->get_expr() == q2->get_expr() && - q1->get_weight() == q2->get_weight() && - q1->get_num_patterns() == q2->get_num_patterns() && ((q1->get_qid().is_numerical() && q2->get_qid().is_numerical()) || (q1->get_qid() == q2->get_qid())) && compare_arrays(q1->get_patterns(), From a694d2755737bdde9a7cec79e7090b08acb04deb Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 14:20:20 +0100 Subject: [PATCH 103/428] revert removal of virtual destructor of relevancy_eh since clang doesnt play along --- src/smt/smt_relevancy.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index c32eabc5d..8dea2842f 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -24,14 +24,12 @@ namespace smt { class context; class relevancy_propagator; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-virtual-dtor" - class relevancy_eh { protected: void mark_as_relevant(relevancy_propagator & rp, expr * n); void mark_args_as_relevant(relevancy_propagator & rp, app * n); public: + virtual ~relevancy_eh() = default; /** \brief This method is invoked when n is marked as relevant. */ @@ -45,7 +43,6 @@ namespace smt { */ virtual void operator()(relevancy_propagator & rp) = 0; }; -#pragma clang diagnostic pop class simple_relevancy_eh : public relevancy_eh { expr * m_target; From 57c667e355a906c9bfc129e4a0fa5c979e423017 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 15:16:47 +0100 Subject: [PATCH 104/428] remove unused code --- src/ast/ast.cpp | 16 ---------------- src/ast/ast.h | 5 ----- 2 files changed, 21 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 0004aaa42..56a89d346 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -542,22 +542,6 @@ inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_v } } } -unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); -} -unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); -} -unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); -} -unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); -} -unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); -} - unsigned get_node_hash(ast const * n) { unsigned a, b, c; diff --git a/src/ast/ast.h b/src/ast/ast.h index d7a715cd1..b4b3b1b2f 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -970,11 +970,6 @@ inline quantifier const * to_quantifier(ast const * n) { SASSERT(is_quantifier(n unsigned get_node_hash(ast const * n); bool compare_nodes(ast const * n1, ast const * n2); unsigned get_node_size(ast const * n); -unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init); -unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init); -unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init); -unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init); -unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init); // This is the internal comparison functor for hash-consing AST nodes. struct ast_eq_proc { From 9b672bc5cc71121045def5a1a3f7eff0136a8393 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 20 Aug 2023 10:10:48 -0700 Subject: [PATCH 105/428] remove tracking of bounds --- src/math/lp/nla_core.cpp | 43 ++++------------------------------------ src/math/lp/nla_core.h | 4 ---- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index d06815410..a255bbbce 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1504,51 +1504,16 @@ void core::check_bounded_divisions(vector& l_vec) { m_lemma_vec = &l_vec; m_divisions.check_bounded_divisions(); } - -bool core::can_add_bound(unsigned j, u_map& bounds) { - unsigned count = 1; - if (bounds.find(j, count)) { - if (count >= 2) - return false; - ++count; - } - bounds.insert(j, count); - struct decrement : public trail { - u_map& bounds; - unsigned j; - decrement(u_map& bounds, unsigned j): - bounds(bounds), - j(j) - {} - void undo() override { - --bounds[j]; - } - }; - trail().push(decrement(bounds, j)); - return true; -} - +// looking for a free variable inside of a monic to split void core::add_bounds() { unsigned r = random(), sz = m_to_refine.size(); for (unsigned k = 0; k < sz; k++) { lpvar i = m_to_refine[(k + r) % sz]; auto const& m = m_emons[i]; for (lpvar j : m.vars()) { - //m_lar_solver.print_column_info(j, verbose_stream() << "check variable " << j << " ") << "\n"; - if (var_is_free(j)) - m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); -#if 0 - else if (has_lower_bound(j) && can_add_bound(j, m_lower_bounds_added)) { - m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::LE, get_lower_bound(j))); - std::cout << "called lower\n"; - } - else if (has_upper_bound(j) && can_add_bound(j, m_upper_bounds_added)) { - m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::GE, get_upper_bound(j))); - std::cout << "called upper\n"; - } -#endif - else - continue; + if (!var_is_free(j)) continue; + // split the free variable (j <= 0, or j > 0), and return + m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); ++lp_settings().stats().m_nla_bounds; return; } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 530e08c8d..6dd15c2c3 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -110,11 +110,7 @@ class core { monic const* m_patched_monic = nullptr; void check_weighted(unsigned sz, std::pair>* checks); - - u_map m_lower_bounds_added, m_upper_bounds_added; - bool can_add_bound(unsigned j, u_map& bounds); void add_bounds(); - public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&); From dda0c8ff4200faa6a441855716b47ec7f93e026e Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 20 Aug 2023 22:28:57 +0100 Subject: [PATCH 106/428] array theory: use expr_ref for mk_default() so it doesnt leak if internalize throws like on timeout/memout --- src/smt/theory_array_full.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 079c2f62e..345663b6b 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -546,7 +546,7 @@ namespace smt { expr_ref def2(m.mk_app(f, args2.size(), args2.data()), m); ctx.get_rewriter()(def2); - expr* def1 = mk_default(map); + expr_ref def1(mk_default(map), m); ctx.internalize(def1, false); ctx.internalize(def2, false); return try_assign_eq(def1, def2); @@ -561,7 +561,7 @@ namespace smt { SASSERT(is_const(cnst)); TRACE("array", tout << mk_bounded_pp(cnst->get_expr(), m) << "\n";); expr* val = cnst->get_arg(0)->get_expr(); - expr* def = mk_default(cnst->get_expr()); + expr_ref def(mk_default(cnst->get_expr()), m); ctx.internalize(def, false); return try_assign_eq(val, def); } @@ -598,7 +598,7 @@ namespace smt { return false; m_stats.m_num_default_lambda_axiom++; expr* e = arr->get_expr(); - expr* def = mk_default(e); + expr_ref def(mk_default(e), m); quantifier* lam = m.is_lambda_def(arr->get_decl()); TRACE("array", tout << mk_pp(lam, m) << "\n" << mk_pp(e, m) << "\n"); expr_ref_vector args(m); From 37ddaaef69db52097917581f9f7698027d3e536b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 20 Aug 2023 15:30:57 -0700 Subject: [PATCH 107/428] make destructors virtual Signed-off-by: Nikolaj Bjorner --- src/smt/arith_eq_adapter.cpp | 2 ++ src/smt/smt_relevancy.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp index b77a38927..f251d01ae 100644 --- a/src/smt/arith_eq_adapter.cpp +++ b/src/smt/arith_eq_adapter.cpp @@ -67,6 +67,8 @@ namespace smt { m_ge(ge) { } + ~arith_eq_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override { if (!rp.is_relevant(m_n1)) return; diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index 8dea2842f..f6e3c4659 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -48,6 +48,7 @@ namespace smt { expr * m_target; public: simple_relevancy_eh(expr * t):m_target(t) {} + ~simple_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; @@ -60,6 +61,7 @@ namespace smt { expr * m_target; public: pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {} + ~pair_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; From b8d8553c4155aa5cdf225d6704964c58089ef19d Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Sun, 20 Aug 2023 18:36:22 -0400 Subject: [PATCH 108/428] support or, and, implies, distinct in mbp_basic (#6867) --- src/qe/mbp/mbp_arrays_tg.cpp | 1 - src/qe/mbp/mbp_basic_tg.cpp | 92 ++++++++++++++++++++++++++++++++---- src/qe/mbp/mbp_dt_tg.cpp | 1 - src/qe/mbp/mbp_qel.cpp | 2 +- 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index 8f127819d..d3ed1a9eb 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -278,7 +278,6 @@ struct mbp_array_tg::impl { m_tg.get_terms(terms, false); for (unsigned i = 0; i < terms.size(); i++) { term = terms.get(i); - SASSERT(!m.is_distinct(term)); if (m_seen.is_marked(term)) continue; if (m_tg.is_cgr(term)) continue; TRACE("mbp_tg", tout << "processing " << expr_ref(term, m);); diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index d85215d17..5eca04151 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -43,32 +43,106 @@ struct mbp_basic_tg::impl { void mark_seen(expr *t) { m_seen.mark(t); } bool is_seen(expr *t) { return m_seen.is_marked(t); } - //Split on all ite terms, irrespective of whether - //they contain variables/are c-ground + // Split on all ite terms, irrespective of whether + // they contain variables/are c-ground bool apply() { - if (!m_use_mdl) return false; + std::function should_split, is_true, is_false; + if (!m_use_mdl) { + should_split = [&](expr *t) { return m_tg.has_val_in_class(t); }; + is_true = [&](expr *t) { + return m_tg.has_val_in_class(t) && m_mdl.is_true(t); + }; + is_false = [&](expr *t) { + return m_tg.has_val_in_class(t) && m_mdl.is_false(t); + }; + } else { + should_split = [](expr *t) { return true; }; + is_true = [&](expr *t) { return m_mdl.is_true(t); }; + is_false = [&](expr *t) { return m_mdl.is_false(t); }; + } + expr *c, *th, *el; expr_ref nterm(m); bool progress = false; TRACE("mbp_tg", tout << "Iterating over terms of tg";); // Not resetting terms because get_terms calls resize on terms m_tg.get_terms(terms, false); - for (expr* term : terms) { - if (is_seen(term)) - continue; - if (m.is_ite(term, c, th, el)) { + for (expr *term : terms) { + if (is_seen(term)) continue; + if (m.is_ite(term, c, th, el) && should_split(c)) { mark_seen(term); progress = true; if (m_mdl.is_true(c)) { m_tg.add_lit(c); m_tg.add_eq(term, th); - } - else { + } else { nterm = mk_not(m, c); m_tg.add_lit(nterm); m_tg.add_eq(term, el); } } + if (m.is_implies(term, c, th)) { + if (is_true(th) || is_false(c)) { + mark_seen(term); + progress = true; + if (is_true(th)) + m_tg.add_lit(th); + else if (is_false(c)) + m_tg.add_lit(c); + m_tg.add_eq(term, m.mk_true()); + } else if (is_true(c) && is_false(th)) { + mark_seen(term); + progress = true; + m_tg.add_eq(term, m.mk_false()); + } + } + if (m.is_or(term) || m.is_and(term)) { + bool is_or = m.is_or(term); + app *c = to_app(term); + bool t = is_or ? any_of(*c, is_true) : all_of(*c, is_true); + bool f = is_or ? all_of(*c, is_false) : all_of(*c, is_false); + if (t || f) { + mark_seen(term); + progress = true; + m_tg.add_eq(term, t ? m.mk_true() : m.mk_false()); + if (f) { + for (auto a : *c) { + if (is_false(a)) { + m_tg.add_lit(mk_not(m, a)); + if (!is_or) break; + } + } + } else { + for (auto a : *c) { + if (is_true(a)) { + m_tg.add_lit(a); + if (is_or) break; + } + } + } + } + } + if (m_use_mdl && m.is_distinct(term)) { + mark_seen(term); + progress = true; + bool eq = false; + app *c = to_app(term); + for (auto a1 : *c) { + for (auto a2 : *c) { + if (a1 == a2) continue; + if (m_mdl.are_equal(a1, a2)) { + m_tg.add_eq(a1, a2); + eq = true; + break; + } else + m_tg.add_deq(a1, a2); + } + } + if (eq) + m_tg.add_eq(term, m.mk_false()); + else + m_tg.add_eq(term, m.mk_true()); + } } return progress; } diff --git a/src/qe/mbp/mbp_dt_tg.cpp b/src/qe/mbp/mbp_dt_tg.cpp index b3b24617d..626e8a0e4 100644 --- a/src/qe/mbp/mbp_dt_tg.cpp +++ b/src/qe/mbp/mbp_dt_tg.cpp @@ -158,7 +158,6 @@ struct mbp_dt_tg::impl { m_tg.get_terms(terms, false); for (unsigned i = 0; i < terms.size(); i++) { term = terms.get(i); - SASSERT(!m.is_distinct(term)); if (is_seen(term)) continue; if (m_tg.is_cgr(term)) continue; if (is_app(term) && diff --git a/src/qe/mbp/mbp_qel.cpp b/src/qe/mbp/mbp_qel.cpp index d50a8c1f6..11b651268 100644 --- a/src/qe/mbp/mbp_qel.cpp +++ b/src/qe/mbp/mbp_qel.cpp @@ -178,7 +178,7 @@ class mbp_qel::impl { std::function non_core = [&](expr *e) { if (is_app(e) && is_partial_eq(to_app(e))) return true; - if (m.is_ite(e)) return true; + if (m.is_ite(e) || m.is_or(e) || m.is_implies(e) || m.is_and(e) || m.is_distinct(e)) return true; return red_vars.is_marked(e); }; From 79aa317af441c9ab4e4a28973f68570e7f5dc9d3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 Aug 2023 09:19:06 -0700 Subject: [PATCH 109/428] remove if-def inside cpp file that should not be there #6869 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/elim_bounds.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp index 27c34463d..b6b98e0cb 100644 --- a/src/ast/rewriter/elim_bounds.cpp +++ b/src/ast/rewriter/elim_bounds.cpp @@ -17,8 +17,6 @@ Revision History: --*/ -#ifndef ELIM_BOUNDS_H_ -#define ELIM_BOUNDS_H_ #include "ast/used_vars.h" #include "util/obj_hashtable.h" @@ -201,4 +199,3 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, return true; } -#endif /* ELIM_BOUNDS_H_ */ From 61a7c6b28da9bdaed63b2f779043562c0aa878d7 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 21 Aug 2023 17:11:55 -0700 Subject: [PATCH 110/428] init m_literal_vec Signed-off-by: Lev Nachmanson --- 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 1990d60fd..1b34272df 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1523,7 +1523,7 @@ lbool core::check(vector& lits, vector& l_vec) { TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); m_lemma_vec = &l_vec; - + m_literal_vec = &lits; if (!(lra.get_status() == lp::lp_status::OPTIMAL || lra.get_status() == lp::lp_status::FEASIBLE)) { TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";); From 818b1129a54eb4980efa28b9cff95681687b7b08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 Aug 2023 09:04:08 -0700 Subject: [PATCH 111/428] fix regression in sign of literals from new solver --- src/sat/smt/arith_solver.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 6d3768176..c06b8fafb 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1411,7 +1411,7 @@ namespace arith { m_explanation = l.expl(); literal_vector core; for (auto const& ineq : m_lemma.ineqs()) - core.push_back(mk_ineq_literal(ineq)); + core.push_back(~mk_ineq_literal(ineq)); set_conflict_or_lemma(hint_type::nla_h, core, false); } @@ -1424,17 +1424,17 @@ namespace arith { } sat::literal solver::mk_ineq_literal(nla::ineq const& ineq) { - bool is_lower = true, pos = true, is_eq = false; + bool is_lower = true, sign = true, is_eq = false; switch (ineq.cmp()) { - case lp::LE: is_lower = false; pos = false; break; - case lp::LT: is_lower = true; pos = true; break; - case lp::GE: is_lower = true; pos = false; break; - case lp::GT: is_lower = false; pos = true; break; - case lp::EQ: is_eq = true; pos = false; break; - case lp::NE: is_eq = true; pos = true; break; + case lp::LE: is_lower = false; sign = false; break; + case lp::LT: is_lower = true; sign = true; break; + case lp::GE: is_lower = true; sign = false; break; + case lp::GT: is_lower = false; sign = true; break; + case lp::EQ: is_eq = true; sign = false; break; + case lp::NE: is_eq = true; sign = true; break; default: UNREACHABLE(); } - TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); + TRACE("arith", tout << "is_lower: " << is_lower << " sign " << sign << "\n";); // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); // then term is used instead of ineq.m_term sat::literal lit; @@ -1443,7 +1443,7 @@ namespace arith { else lit = ctx.expr2literal(mk_bound(ineq.term(), ineq.rs(), is_lower)); - return pos ? lit : ~lit; + return sign ? ~lit : lit; } From e8929041b840ce7d04df8ce101d408042d2a4384 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 Aug 2023 09:29:12 -0700 Subject: [PATCH 112/428] fixing calls, signs --- src/math/lp/lar_solver.cpp | 3 ++- src/math/lp/nla_core.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 6159c436e..1bd55b9bb 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -333,12 +333,13 @@ namespace lp { if (improve_lower_bound) term.negate(); impq bound; - if (!maximize_term_on_tableau(term, bound)) + if (!maximize_term_on_corrected_r_solver(term, bound)) return false; return false; // TODO if (improve_lower_bound) { + bound.neg(); if (column_has_lower_bound(j) && bound.x == column_lower_bound(j).x) return false; SASSERT(!column_has_lower_bound(j) || column_lower_bound(j).x < bound.x); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 1b34272df..61afa8859 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1548,7 +1548,7 @@ lbool core::check(vector& lits, vector& l_vec) { if (no_effect()) m_monomial_bounds(); - if (l_vec.empty() && !done() && improve_bounds()) + if (no_effect() && improve_bounds()) return l_false; { From 63467f9dfa9bff8f0323b7d81ab8dddaee2143b5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Aug 2023 17:14:35 -0700 Subject: [PATCH 113/428] fix #6876 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/th_rewriter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 18b335ecb..ee8b79be5 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -76,6 +76,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { bool m_ignore_patterns_on_ground_qbody = true; bool m_rewrite_patterns = true; bool m_enable_der = true; + bool m_nested_der = false; ast_manager & m() const { return m_b_rw.m(); } @@ -92,6 +93,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_ignore_patterns_on_ground_qbody = p.ignore_patterns_on_ground_qbody(); m_rewrite_patterns = p.rewrite_patterns(); m_enable_der = p.enable_der(); + m_nested_der = _p.get_bool("nested_der", false); } void updt_params(params_ref const & p) { @@ -843,8 +845,11 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result = r; } - if (der_change) { + if (der_change && !m_nested_der) { th_rewriter rw(m()); + params_ref p; + p.set_bool("nested_der", true); + rw.updt_params(p); rw(result, r, p2); if (m().proofs_enabled() && result.get() != r.get()) result_pr = m().mk_transitivity(result_pr, p2); From 5ba06f4e28b892f34b6b05924d9ae7b96b29e0b6 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Sat, 26 Aug 2023 23:53:15 -0400 Subject: [PATCH 114/428] print deq in lits2pure. fix #6877 (#6878) --- src/qe/mbp/mbp_term_graph.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 83a1f3c58..190acecdf 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -1177,7 +1177,7 @@ class term_graph::projector { } void lits2pure(expr_ref_vector &res) { - expr *e1 = nullptr, *e2 = nullptr, *p1 = nullptr, *p2 = nullptr; + expr *e1 = nullptr, *e2 = nullptr, *e = nullptr, *p1 = nullptr, *p2 = nullptr; for (auto *lit : m_tg.m_lits) { if (m.is_eq(lit, e1, e2)) { if (find_app(e1, p1) && find_app(e2, p2)) { @@ -1186,6 +1186,13 @@ class term_graph::projector { else TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); } + else if (m.is_not(lit, e) && m.is_eq(e, e1, e2)) { + if (find_app(e1, p1) && find_app(e2, p2)) { + res.push_back(mk_neq(m, p1, p2)); + } + else + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } else if (m.is_distinct(lit)) { ptr_buffer diff; for (expr *arg : *to_app(lit)) From 09f911d8a84cd91988e5b96b69485b2a9a2edba3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Aug 2023 11:31:32 -0700 Subject: [PATCH 115/428] include rewriter for recursive functions in model-evaluator fixes bug reported by Tahina and Nick, @tahina-pro --- src/model/model_evaluator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 125380930..28b726100 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -32,6 +32,7 @@ Revision History: #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/recfun_rewriter.h" #include "model/model_smt2_pp.h" #include "model/model.h" #include "model/model_evaluator_params.hpp" @@ -53,6 +54,7 @@ struct evaluator_cfg : public default_rewriter_cfg { pb_rewriter m_pb_rw; fpa_rewriter m_f_rw; seq_rewriter m_seq_rw; + recfun_rewriter m_rec_rw; array_util m_ar; arith_util m_au; fpa_util m_fpau; @@ -80,6 +82,7 @@ struct evaluator_cfg : public default_rewriter_cfg { m_pb_rw(m), m_f_rw(m), m_seq_rw(m), + m_rec_rw(m), m_ar(m), m_au(m), m_fpau(m), @@ -283,6 +286,8 @@ struct evaluator_cfg : public default_rewriter_cfg { st = m_f_rw.mk_app_core(f, num, args, result); else if (fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_app_core(f, num, args, result); + else if (fid == m_rec_rw.get_fid()) + st = m_rec_rw.mk_app_core(f, num, args, result); else if (fid == m.get_label_family_id() && num == 1) { result = args[0]; st = BR_DONE; From 00593609c5d5125998a0efd6786f597295c7bc14 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 30 Aug 2023 12:50:29 +0100 Subject: [PATCH 116/428] minor code simplification --- src/ast/ast.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 56a89d346..6ea293b57 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -202,8 +202,7 @@ unsigned decl_info::hash() const { bool decl_info::operator==(decl_info const & info) const { return m_family_id == info.m_family_id && m_kind == info.m_kind && - m_parameters.size() == info.m_parameters.size() && - compare_arrays(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size()); + m_parameters == info.m_parameters; } std::ostream & operator<<(std::ostream & out, decl_info const & info) { @@ -449,7 +448,7 @@ bool compare_nodes(ast const * n1, ast const * n2) { if (to_sort(n1)->get_info() != nullptr && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) { return false; } - return to_sort(n1)->get_name() == to_sort(n2)->get_name(); + return to_sort(n1)->get_name() == to_sort(n2)->get_name(); case AST_FUNC_DECL: if ((to_func_decl(n1)->get_info() == nullptr) != (to_func_decl(n2)->get_info() == nullptr)) { return false; From 38b131386d877ed54863f85471e7859af4060ee0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 30 Aug 2023 17:21:38 -0700 Subject: [PATCH 117/428] add stubs for monomial unit propagation --- src/math/lp/lar_solver.h | 1 - src/math/lp/nla_core.cpp | 46 ++++++++++++++++++++++++++++++++--- src/math/lp/nla_core.h | 10 ++++++++ src/math/lp/nla_divisions.cpp | 23 +++++++++++++----- src/math/lp/nla_solver.cpp | 4 +++ src/math/lp/nla_solver.h | 1 + src/smt/theory_lra.cpp | 9 +++++++ 7 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 4b60d7d62..fc696d4b2 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -646,7 +646,6 @@ class lar_solver : public column_namer { 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_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 { var_index local_j; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 61afa8859..76241786d 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1574,10 +1574,10 @@ lbool core::check(vector& lits, vector& l_vec) { if (no_effect()) m_divisions.check(); - if (!conflict_found() && !done() && run_bounded_nlsat) + if (no_effect() && run_bounded_nlsat) ret = bounded_nlsat(); - if (l_vec.empty() && !done() && ret == l_undef) { + if (no_effect() && ret == l_undef) { std::function check1 = [&]() { m_order.order_lemma(); }; std::function check2 = [&]() { m_monotone.monotonicity_lemma(); }; std::function check3 = [&]() { m_tangents.tangent_lemma(); }; @@ -1593,7 +1593,7 @@ lbool core::check(vector& lits, vector& l_vec) { ret = bounded_nlsat(); } - if (l_vec.empty() && !done() && params().arith_nl_nra() && ret == l_undef) { + if (no_effect() && params().arith_nl_nra() && ret == l_undef) { ret = m_nra.check(); m_stats.m_nra_calls++; } @@ -1811,6 +1811,46 @@ bool core::improve_bounds() { return bounds_improved; } +void core::propagate(vector& lemmas) { + // disable for now + return; + + // propagate linear monomials + lemmas.reset(); + for (auto const& m : m_emons) + if (propagate(m, lemmas)) + break; +} + +bool core::propagate(monic const& m, vector& lemmas) { + m_propagated.reserve(m.var() + 1, false); + if (m_propagated[m.var()]) + return false; + + if (!is_linear(m)) + return false; + + trail().push(set_bitvector_trail(m_propagated, m.var())); + + mpq k = fixed_var_product(m); + // todo + + // return true if m_emons changes (so the iterator is invalid) + return false; +} + +bool core::is_linear(monic const& m) { + // todo + return false; +} + + +mpq core::fixed_var_product(monic const& m) { + // todo + return mpq(0); +} + + } // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index e2a66c321..47e992c32 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -59,6 +59,7 @@ class core { friend class solver; friend class monomial_bounds; friend class nra::solver; + friend class divisions; struct stats { unsigned m_nla_explanations; @@ -390,6 +391,8 @@ public: void check_bounded_divisions(vector&); bool no_lemmas_hold() const; + + void propagate(vector& lemmas); lbool test_check(vector& l); lpvar map_to_root(lpvar) const; @@ -432,6 +435,13 @@ private: void restore_tableau(); void save_tableau(); bool integrality_holds(); + + // monomial propagation + bool_vector m_propagated; + bool propagate(monic const& m, vector& lemmas); + bool is_linear(monic const& m); + mpq fixed_var_product(monic const& m); + }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index cbb30d9d9..7d52cd0ba 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -19,19 +19,30 @@ Description: namespace nla { void divisions::add_idivision(lpvar q, lpvar x, lpvar y) { + auto& lra = m_core.lra; if (x == null_lpvar || y == null_lpvar || q == null_lpvar) return; - if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) - return; + if (lp::tv::is_term(x)) + x = lra.map_term_index_to_column_index(x); + if (lp::tv::is_term(y)) + y = lra.map_term_index_to_column_index(y); + if (lp::tv::is_term(q)) + q = lra.map_term_index_to_column_index(q); m_idivisions.push_back({q, x, y}); m_core.trail().push(push_back_vector(m_idivisions)); } void divisions::add_rdivision(lpvar q, lpvar x, lpvar y) { + auto& lra = m_core.lra; if (x == null_lpvar || y == null_lpvar || q == null_lpvar) return; - if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(q)) - return; + if (lp::tv::is_term(x)) + x = lra.map_term_index_to_column_index(x); + if (lp::tv::is_term(y)) + y = lra.map_term_index_to_column_index(y); + if (lp::tv::is_term(q)) + q = lra.map_term_index_to_column_index(q); + m_rdivisions.push_back({ q, x, y }); m_core.trail().push(push_back_vector(m_rdivisions)); } @@ -52,7 +63,7 @@ namespace nla { // y2 <= y1 < 0 & x1 <= x2 <= 0 => x1/y1 >= x2/y2 void divisions::check() { - core& c = m_core; + core& c = m_core; if (c.use_nra_model()) return; @@ -132,7 +143,7 @@ namespace nla { auto x2val = c.val(x2); auto y2val = c.val(y2); auto q2val = c.val(q2); - if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, q2, q2val)) + if (monotonicity(x, xval, y, yval, r, rval, x2, x2val, y2, y2val, q2, q2val)) return; } } diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index ccc7b6073..b7197ff2f 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -45,6 +45,10 @@ namespace nla { lbool solver::check(vector& lits, vector& lemmas) { return m_core->check(lits, lemmas); } + + void solver::propagate(vector& lemmas) { + m_core->propagate(lemmas); + } void solver::push(){ m_core->push(); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index c1ad5f32a..7a8a97b3f 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -37,6 +37,7 @@ namespace nla { void pop(unsigned scopes); bool need_check(); lbool check(vector& lits, vector&); + void propagate(vector& lemmas); lbool check_power(lpvar r, lpvar x, lpvar y, vector&); bool is_monic_var(lpvar) const; bool influences_nl_var(lpvar) const; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 9e9e2d730..41426e0d6 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2152,11 +2152,20 @@ public: propagate_bounds_with_lp_solver(); break; case l_undef: + propagate_nla(); break; } return true; } + void propagate_nla() { + if (!m_nla) + return; + m_nla->propagate(m_nla_lemma_vector); + for (nla::lemma const& l : m_nla_lemma_vector) + false_case_of_check_nla(l); + } + bool should_propagate() const { return bound_prop_mode::BP_NONE != propagation_mode(); } From c2cbe72b6496be6ac1b875bb6a61af6051af5cbb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 31 Aug 2023 11:49:05 -0700 Subject: [PATCH 118/428] sketch linear monomial propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 67 +++++++++++++++++++++++++++++++--------- src/math/lp/nla_core.h | 5 +-- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 76241786d..67c48cb8a 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1817,39 +1817,76 @@ void core::propagate(vector& lemmas) { // propagate linear monomials lemmas.reset(); + m_lemma_vec = &lemmas; for (auto const& m : m_emons) - if (propagate(m, lemmas)) - break; + propagate(m, lemmas); } -bool core::propagate(monic const& m, vector& lemmas) { +void core::propagate(monic const& m, vector& lemmas) { m_propagated.reserve(m.var() + 1, false); if (m_propagated[m.var()]) - return false; - + return; + if (!is_linear(m)) - return false; + return; trail().push(set_bitvector_trail(m_propagated, m.var())); - mpq k = fixed_var_product(m); - // todo + rational k = fixed_var_product(m); + + new_lemma lemma(*this, "fixed-values"); + if (k == 0) { + for (auto v : m) { + if (var_is_fixed(v) && val(v).is_zero()) { + lemma.explain_fixed(v); + break; + } + } + lemma |= ineq(m.var(), lp::lconstraint_kind::EQ, 0); + } + else { + for (auto v : m) + if (var_is_fixed(v)) + lemma.explain_fixed(v); + + lpvar w = non_fixed_var(m); + SASSERT(w != null_lpvar); + + lp::lar_term term; + term.add_monomial(mpq(-1), m.var()); + term.add_monomial(k, w); + lemma |= ineq(term, lp::lconstraint_kind::EQ, 0); + } - // return true if m_emons changes (so the iterator is invalid) - return false; } bool core::is_linear(monic const& m) { - // todo - return false; + unsigned non_fixed = 0; + for (lpvar v : m) { + if (!var_is_fixed(v)) + ++non_fixed; + else if (val(v).is_zero()) + return true; + } + return non_fixed <= 1; } -mpq core::fixed_var_product(monic const& m) { - // todo - return mpq(0); +rational core::fixed_var_product(monic const& m) { + rational r(1); + for (lpvar v : m) { + if (var_is_fixed(v)) + r *= lra.get_column_value(v).x; + } + return r; } +lpvar core::non_fixed_var(monic const& m) { + for (lpvar v : m) + if (!var_is_fixed(v)) + return v; + return null_lpvar; +} } // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 47e992c32..297ad1641 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -438,9 +438,10 @@ private: // monomial propagation bool_vector m_propagated; - bool propagate(monic const& m, vector& lemmas); + void propagate(monic const& m, vector& lemmas); bool is_linear(monic const& m); - mpq fixed_var_product(monic const& m); + rational fixed_var_product(monic const& m); + lpvar non_fixed_var(monic const& m); }; // end of core From ff3268e636e01a492825ac17d4db3b5622f4f2e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 31 Aug 2023 14:32:05 -0700 Subject: [PATCH 119/428] move unit propagation into monomial_bounds Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 74 +++++++++++++++++++++++++++++++- src/math/lp/monomial_bounds.h | 13 +++++- src/math/lp/nla_core.cpp | 75 +++------------------------------ src/math/lp/nla_core.h | 6 --- 4 files changed, 90 insertions(+), 78 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index a89d27a9b..84af29d05 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -17,13 +17,14 @@ namespace nla { common(c), dep(c->m_intervals.get_dep_intervals()) {} - void monomial_bounds::operator()() { + void monomial_bounds::propagate() { for (lpvar v : c().m_to_refine) { monic const& m = c().emons()[v]; propagate(m); } } + bool monomial_bounds::is_too_big(mpq const& q) const { return rational(q).bitsize() > 256; } @@ -257,5 +258,76 @@ namespace nla { } } + void monomial_bounds::unit_propagate() { + for (auto const& m : c().m_emons) + unit_propagate(m); + } + + void monomial_bounds::unit_propagate(monic const& m) { + m_propagated.reserve(m.var() + 1, false); + if (m_propagated[m.var()]) + return; + + if (!is_linear(m)) + return; + + c().trail().push(set_bitvector_trail(m_propagated, m.var())); + + rational k = fixed_var_product(m); + + new_lemma lemma(c(), "fixed-values"); + if (k == 0) { + for (auto v : m) { + if (c().var_is_fixed(v) && c().val(v).is_zero()) { + lemma.explain_fixed(v); + break; + } + } + lemma |= ineq(m.var(), lp::lconstraint_kind::EQ, 0); + } + else { + for (auto v : m) + if (c().var_is_fixed(v)) + lemma.explain_fixed(v); + + lpvar w = non_fixed_var(m); + SASSERT(w != null_lpvar); + + lp::lar_term term; + term.add_monomial(-m.rat_sign(), m.var()); + term.add_monomial(k, w); + lemma |= ineq(term, lp::lconstraint_kind::EQ, 0); + } + + } + + bool monomial_bounds::is_linear(monic const& m) { + unsigned non_fixed = 0; + for (lpvar v : m) { + if (!c().var_is_fixed(v)) + ++non_fixed; + else if (c().val(v).is_zero()) + return true; + } + return non_fixed <= 1; + } + + + rational monomial_bounds::fixed_var_product(monic const& m) { + rational r(1); + for (lpvar v : m) { + if (c().var_is_fixed(v)) + r *= c().lra.get_column_value(v).x; + } + return r; + } + + lpvar monomial_bounds::non_fixed_var(monic const& m) { + for (lpvar v : m) + if (!c().var_is_fixed(v)) + return v; + return null_lpvar; + } + } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index d82c4bebb..c728d1a4c 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -16,6 +16,8 @@ namespace nla { class core; class monomial_bounds : common { dep_intervals& dep; + + void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; bool propagate_down(monic const& m, lpvar u); @@ -27,8 +29,17 @@ namespace nla { void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const; bool is_free(lpvar v) const; bool is_zero(lpvar v) const; + + // monomial propagation + bool_vector m_propagated; + void unit_propagate(monic const& m); + bool is_linear(monic const& m); + rational fixed_var_product(monic const& m); + lpvar non_fixed_var(monic const& m); + public: monomial_bounds(core* core); - void operator()(); + void propagate(); + void unit_propagate(); }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 67c48cb8a..3ab89e012 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1546,7 +1546,7 @@ lbool core::check(vector& lits, vector& l_vec) { auto no_effect = [&]() { return !done() && l_vec.empty() && lits.empty(); }; if (no_effect()) - m_monomial_bounds(); + m_monomial_bounds.propagate(); if (no_effect() && improve_bounds()) return l_false; @@ -1814,79 +1814,14 @@ bool core::improve_bounds() { void core::propagate(vector& lemmas) { // disable for now return; - - // propagate linear monomials + // propagate linear monomials lemmas.reset(); m_lemma_vec = &lemmas; - for (auto const& m : m_emons) - propagate(m, lemmas); + + m_monomial_bounds.unit_propagate(); + } -void core::propagate(monic const& m, vector& lemmas) { - m_propagated.reserve(m.var() + 1, false); - if (m_propagated[m.var()]) - return; - - if (!is_linear(m)) - return; - - trail().push(set_bitvector_trail(m_propagated, m.var())); - - rational k = fixed_var_product(m); - - new_lemma lemma(*this, "fixed-values"); - if (k == 0) { - for (auto v : m) { - if (var_is_fixed(v) && val(v).is_zero()) { - lemma.explain_fixed(v); - break; - } - } - lemma |= ineq(m.var(), lp::lconstraint_kind::EQ, 0); - } - else { - for (auto v : m) - if (var_is_fixed(v)) - lemma.explain_fixed(v); - - lpvar w = non_fixed_var(m); - SASSERT(w != null_lpvar); - - lp::lar_term term; - term.add_monomial(mpq(-1), m.var()); - term.add_monomial(k, w); - lemma |= ineq(term, lp::lconstraint_kind::EQ, 0); - } - -} - -bool core::is_linear(monic const& m) { - unsigned non_fixed = 0; - for (lpvar v : m) { - if (!var_is_fixed(v)) - ++non_fixed; - else if (val(v).is_zero()) - return true; - } - return non_fixed <= 1; -} - - -rational core::fixed_var_product(monic const& m) { - rational r(1); - for (lpvar v : m) { - if (var_is_fixed(v)) - r *= lra.get_column_value(v).x; - } - return r; -} - -lpvar core::non_fixed_var(monic const& m) { - for (lpvar v : m) - if (!var_is_fixed(v)) - return v; - return null_lpvar; -} } // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 297ad1641..9c96f2fbf 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -436,12 +436,6 @@ private: void save_tableau(); bool integrality_holds(); - // monomial propagation - bool_vector m_propagated; - void propagate(monic const& m, vector& lemmas); - bool is_linear(monic const& m); - rational fixed_var_product(monic const& m); - lpvar non_fixed_var(monic const& m); }; // end of core From 5509b468e95626e195732cc0dcda4d89402a550b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 31 Aug 2023 17:35:41 -0700 Subject: [PATCH 120/428] handle monomial_bounds::unit_propagate() --- src/math/lp/monomial_bounds.cpp | 16 +++++++++------- src/math/lp/nla_core.cpp | 4 +--- src/smt/theory_lra.cpp | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 84af29d05..28d287477 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -283,7 +283,7 @@ namespace nla { break; } } - lemma |= ineq(m.var(), lp::lconstraint_kind::EQ, 0); + lemma |= ineq(m.var(), lp::lconstraint_kind::NE, 0); } else { for (auto v : m) @@ -291,12 +291,14 @@ namespace nla { lemma.explain_fixed(v); lpvar w = non_fixed_var(m); - SASSERT(w != null_lpvar); - - lp::lar_term term; - term.add_monomial(-m.rat_sign(), m.var()); - term.add_monomial(k, w); - lemma |= ineq(term, lp::lconstraint_kind::EQ, 0); + if (w != null_lpvar) { + lp::lar_term term; + term.add_var(m.var()); + term.add_monomial(-k, w); + lemma |= ineq(term, lp::lconstraint_kind::NE, 0); + } else { + lemma |= ineq(m.var(), lp::lconstraint_kind::NE, k); + } } } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 3ab89e012..0e87f8a1d 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1812,9 +1812,7 @@ bool core::improve_bounds() { } void core::propagate(vector& lemmas) { - // disable for now - return; - // propagate linear monomials + // propagate linear monomials, those that have all, or all but one, variables fixed lemmas.reset(); m_lemma_vec = &lemmas; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 41426e0d6..31882ea11 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2150,9 +2150,10 @@ public: case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); + propagate_nla(); break; case l_undef: - propagate_nla(); + UNREACHABLE(); break; } return true; From d3258e7084a17539baf523866cc6271333727c02 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 1 Sep 2023 11:18:03 -0700 Subject: [PATCH 121/428] propagate lineal monomial --- src/math/lp/monomial_bounds.cpp | 6 +++--- src/math/lp/nla_core.cpp | 11 +++++++++++ src/math/lp/nla_types.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 28d287477..e21891ee4 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -283,7 +283,7 @@ namespace nla { break; } } - lemma |= ineq(m.var(), lp::lconstraint_kind::NE, 0); + lemma += ineq(m.var(), lp::lconstraint_kind::EQ, 0); } else { for (auto v : m) @@ -295,9 +295,9 @@ namespace nla { lp::lar_term term; term.add_var(m.var()); term.add_monomial(-k, w); - lemma |= ineq(term, lp::lconstraint_kind::NE, 0); + lemma += ineq(term, lp::lconstraint_kind::EQ, 0); } else { - lemma |= ineq(m.var(), lp::lconstraint_kind::NE, k); + lemma += ineq(m.var(), lp::lconstraint_kind::EQ, k); } } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 0e87f8a1d..622475cf4 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1056,6 +1056,17 @@ new_lemma& new_lemma::operator|=(ineq const& ineq) { } return *this; } + +// Contrary to new_lemma::operator|=, this method does not assert that the model does not satisfy the ineq. +// If ineq holds then it is a nop. +new_lemma& new_lemma::operator+=(ineq const& ineq) { + if (c.ineq_holds(ineq)) return *this; + + if (!c.explain_ineq(*this, ineq.term(), ineq.cmp(), ineq.rs())) { + current().push_back(ineq); + } + return *this; +} new_lemma::~new_lemma() { diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index 8169266cc..186c2e902 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -83,6 +83,7 @@ namespace nla { new_lemma& operator&=(const factorization& f); new_lemma& operator&=(lpvar j); new_lemma& operator|=(ineq const& i); + new_lemma& operator+=(ineq const& i); new_lemma& explain_fixed(lpvar j); new_lemma& explain_equiv(lpvar u, lpvar v); new_lemma& explain_var_separated_from_zero(lpvar j); From 318d7d7564425ed003d6a342f8c77a0885b838ee Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 1 Sep 2023 11:32:26 -0700 Subject: [PATCH 122/428] fixes a bug --- src/math/lp/nla_core.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 622475cf4..4d4fa6cbe 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1058,10 +1058,7 @@ new_lemma& new_lemma::operator|=(ineq const& ineq) { } // Contrary to new_lemma::operator|=, this method does not assert that the model does not satisfy the ineq. -// If ineq holds then it is a nop. new_lemma& new_lemma::operator+=(ineq const& ineq) { - if (c.ineq_holds(ineq)) return *this; - if (!c.explain_ineq(*this, ineq.term(), ineq.cmp(), ineq.rs())) { current().push_back(ineq); } From c2e73a6aaee3ec5a3cd5758046c14fedefc97e95 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Sep 2023 15:19:28 -0700 Subject: [PATCH 123/428] logging pre-processing Signed-off-by: Nikolaj Bjorner --- src/solver/simplifier_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index d70d232e4..0c4c2a7b6 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -110,12 +110,13 @@ class simplifier_solver : public solver { expr_ref_vector orig_assumptions(assumptions); m_core_replace.reset(); if (qhead < m_fmls.size() || !assumptions.empty()) { - TRACE("solver", tout << "qhead " << qhead << "\n"); m_preprocess_state.replay(qhead, assumptions); m_preprocess_state.freeze(assumptions); m_preprocess.reduce(); if (!m.inc()) return; + TRACE("solver", tout << "qhead " << qhead << "\n"; + m_preprocess_state.display(tout)); m_preprocess_state.advance_qhead(); for (unsigned i = 0; i < assumptions.size(); ++i) m_core_replace.insert(assumptions.get(i), orig_assumptions.get(i)); From 99239068baec84c16acb231182341ff1633e46ea Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Sep 2023 15:21:49 -0700 Subject: [PATCH 124/428] some template instantiations #6869 Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/elim_term_ite.cpp | 2 ++ src/ast/rewriter/rewriter.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/ast/normal_forms/elim_term_ite.cpp b/src/ast/normal_forms/elim_term_ite.cpp index 3376e9dda..077f66d1f 100644 --- a/src/ast/normal_forms/elim_term_ite.cpp +++ b/src/ast/normal_forms/elim_term_ite.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "ast/normal_forms/elim_term_ite.h" #include "ast/ast_smt2_pp.h" +#include "ast/rewriter/rewriter_def.h" br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr) { if (!m.is_term_ite(f)) { @@ -38,3 +39,4 @@ br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* return BR_DONE; } +template class rewriter_tpl; diff --git a/src/ast/rewriter/rewriter.cpp b/src/ast/rewriter/rewriter.cpp index 3b25b9409..51c4764df 100644 --- a/src/ast/rewriter/rewriter.cpp +++ b/src/ast/rewriter/rewriter.cpp @@ -17,6 +17,8 @@ Notes: --*/ #include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/push_app_ite.h" +#include "ast/rewriter/elim_bounds.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" @@ -417,3 +419,6 @@ void inv_var_shifter::process_var(var * v) { } template class rewriter_tpl; +template class rewriter_tpl; +template class rewriter_tpl; +template class rewriter_tpl; From 4d9af7848dcbb51293ba3002875b3e63c4c5733b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Sep 2023 15:27:37 -0700 Subject: [PATCH 125/428] add parameter to disable pattern inference #6884 Signed-off-by: Nikolaj Bjorner --- src/ast/pattern/pattern_inference.cpp | 9 +++++---- src/params/pattern_inference_params.cpp | 2 ++ src/params/pattern_inference_params.h | 11 +++++------ src/params/pattern_inference_params_helper.pyg | 1 + 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 4391d8bf8..7c7576c84 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -624,9 +624,11 @@ bool pattern_inference_cfg::reduce_quantifier( proof_ref & result_pr) { TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); - if (!is_forall(q)) { + if (!m_params.m_pi_enabled) + return false; + + if (!is_forall(q)) return false; - } int weight = q->get_weight(); @@ -653,9 +655,8 @@ bool pattern_inference_cfg::reduce_quantifier( } } - if (q->get_num_patterns() > 0) { + if (q->get_num_patterns() > 0) return false; - } if (m_params.m_pi_nopat_weight >= 0) weight = m_params.m_pi_nopat_weight; diff --git a/src/params/pattern_inference_params.cpp b/src/params/pattern_inference_params.cpp index aac574c6e..0e548c896 100644 --- a/src/params/pattern_inference_params.cpp +++ b/src/params/pattern_inference_params.cpp @@ -21,6 +21,7 @@ Revision History: void pattern_inference_params::updt_params(params_ref const & _p) { pattern_inference_params_helper p(_p); + m_pi_enabled = p.enabled(); 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(); @@ -35,6 +36,7 @@ void pattern_inference_params::updt_params(params_ref const & _p) { #define DISPLAY_PARAM(X) out << #X"=" << X << '\n'; void pattern_inference_params::display(std::ostream & out) const { + DISPLAY_PARAM(m_pi_enabled); DISPLAY_PARAM(m_pi_max_multi_patterns); DISPLAY_PARAM(m_pi_block_loop_patterns); DISPLAY_PARAM(m_pi_decompose_patterns); diff --git a/src/params/pattern_inference_params.h b/src/params/pattern_inference_params.h index b037411ec..e558a6a7b 100644 --- a/src/params/pattern_inference_params.h +++ b/src/params/pattern_inference_params.h @@ -27,7 +27,8 @@ enum arith_pattern_inference_kind { }; struct pattern_inference_params { - unsigned m_pi_max_multi_patterns; + bool m_pi_enabled = true; + unsigned m_pi_max_multi_patterns = 1; bool m_pi_block_loop_patterns; bool m_pi_decompose_patterns; arith_pattern_inference_kind m_pi_arith; @@ -35,13 +36,11 @@ struct pattern_inference_params { unsigned m_pi_arith_weight; unsigned m_pi_non_nested_arith_weight; bool m_pi_pull_quantifiers; - int m_pi_nopat_weight; - bool m_pi_avoid_skolems; + int m_pi_nopat_weight = -1; + bool m_pi_avoid_skolems = true; bool m_pi_warnings; - pattern_inference_params(params_ref const & p = params_ref()): - m_pi_nopat_weight(-1), - m_pi_avoid_skolems(true) { + pattern_inference_params(params_ref const & p = params_ref()) { updt_params(p); } diff --git a/src/params/pattern_inference_params_helper.pyg b/src/params/pattern_inference_params_helper.pyg index cb1f02877..80d36e3ec 100644 --- a/src/params/pattern_inference_params_helper.pyg +++ b/src/params/pattern_inference_params_helper.pyg @@ -7,6 +7,7 @@ def_module_params(class_name='pattern_inference_params_helper', ('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'), + ('enabled', BOOL, True, 'enable a heuristic to infer patterns, when they are not provided'), ('arith_weight', UINT, 5, 'default weight for quantifiers where the only available pattern has nested arithmetic terms'), ('non_nested_arith_weight', UINT, 10, 'default weight for quantifiers where the only available pattern has non nested arithmetic terms'), ('pull_quantifiers', BOOL, True, 'pull nested quantifiers, if no pattern was found'), From 3aea4ebf42dd2b26261657e9a4823f91f9b3e736 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:58:47 -0700 Subject: [PATCH 126/428] Bump actions/checkout from 3 to 4 (#6888) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/android-build.yml | 2 +- .github/workflows/coverage.yml | 2 +- .github/workflows/cross-build.yml | 2 +- .github/workflows/docker-image.yml | 2 +- .github/workflows/msvc-static-build.yml | 2 +- .github/workflows/wasm-release.yml | 2 +- .github/workflows/wasm.yml | 2 +- .github/workflows/wip.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 019eb18b1..8a6a32c6b 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure CMake and build run: | diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 366a2224e..2ed02aab1 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,7 +21,7 @@ jobs: COV_DETAILS_PATH: ${{github.workspace}}/cov-details steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup run: | diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml index 83fae7a5d..8745215d2 100644 --- a/.github/workflows/cross-build.yml +++ b/.github/workflows/cross-build.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install cross build tools run: apt update && apt install -y ninja-build cmake python3 g++-11-${{ matrix.arch }}-linux-gnu diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 301ee2c87..583e36034 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Check out the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Log in to GitHub Docker registry uses: docker/login-action@v2 diff --git a/.github/workflows/msvc-static-build.yml b/.github/workflows/msvc-static-build.yml index 2db222161..b329d5abc 100644 --- a/.github/workflows/msvc-static-build.yml +++ b/.github/workflows/msvc-static-build.yml @@ -14,7 +14,7 @@ jobs: BUILD_TYPE: Release steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build run: | diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index de15a242c..defcd8fea 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v3 diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index e8ac095e5..9e321947e 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v3 diff --git a/.github/workflows/wip.yml b/.github/workflows/wip.yml index ffea6225c..5ed29a457 100644 --- a/.github/workflows/wip.yml +++ b/.github/workflows/wip.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} From 41f59cb1ed22a344b2b16271a7414c5be168f7d8 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 5 Sep 2023 18:49:59 -0700 Subject: [PATCH 127/428] propagate monomial is nla --- src/math/lp/int_solver.cpp | 2 +- src/math/lp/lar_solver.cpp | 139 ++++++++++++++++++------------ src/math/lp/lar_solver.h | 12 +-- src/math/lp/lp_core_solver_base.h | 4 +- src/math/lp/monomial_bounds.cpp | 132 ++++++++++++++++++++-------- src/math/lp/monomial_bounds.h | 6 +- src/math/lp/nla_core.cpp | 10 ++- src/math/lp/nla_core.h | 2 +- src/smt/theory_arith_aux.h | 5 +- src/smt/theory_arith_nl.h | 3 + src/smt/theory_lra.cpp | 10 ++- 11 files changed, 210 insertions(+), 115 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 0ab7d1e40..bd1ac5313 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -31,7 +31,7 @@ namespace lp { lra.remove_fixed_vars_from_base(); lp_assert(lia.is_feasible()); for (unsigned j : lra.r_basis()) - if (!lra.get_value(j).is_int() && lra.column_is_int(j)) + if (!lra.get_value(j).is_int() && lra.column_is_int(j)&& !lia.is_fixed(j)) patch_basic_column(j); if (!lia.has_inf_int()) { lia.settings().stats().m_patches_success++; diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 1bd55b9bb..5f82516ef 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -22,7 +22,8 @@ namespace lp { } lar_solver::lar_solver() : - m_crossed_bounds_column(-1), + m_crossed_bounds_column(null_lpvar), + m_crossed_bounds_deps(nullptr), m_mpq_lar_core_solver(m_settings, *this), m_var_register(false), m_term_register(true), @@ -213,17 +214,10 @@ 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(!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]; svector deps; - m_dependencies.linearize(ul.upper_bound_witness(), deps); - for (auto d : deps) - evidence.add_pair(d, numeric_traits::one()); - deps.reset(); - m_dependencies.linearize(ul.lower_bound_witness(), deps); + SASSERT(m_crossed_bounds_deps != nullptr); + m_dependencies.linearize(m_crossed_bounds_deps, deps); for (auto d : deps) evidence.add_pair(d, -numeric_traits::one()); } @@ -232,7 +226,8 @@ namespace lp { m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); m_columns_to_ul_pairs.push(); - m_crossed_bounds_column.push(); + m_crossed_bounds_column = null_lpvar; + m_crossed_bounds_deps = nullptr; m_mpq_lar_core_solver.push(); m_term_count = m_terms.size(); m_term_count.push(); @@ -262,7 +257,8 @@ namespace lp { void lar_solver::pop(unsigned k) { TRACE("lar_solver", tout << "k = " << k << std::endl;); - m_crossed_bounds_column.pop(k); + m_crossed_bounds_column = null_lpvar; + m_crossed_bounds_deps = nullptr; unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); pop_tableau(n); @@ -1021,7 +1017,7 @@ namespace lp { void lar_solver::get_infeasibility_explanation(explanation& exp) const { exp.clear(); - if (m_crossed_bounds_column != -1) { + if (m_crossed_bounds_column != null_lpvar) { fill_explanation_from_crossed_bounds_column(exp); return; } @@ -1828,7 +1824,6 @@ namespace lp { if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); - 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;); } @@ -1924,7 +1919,6 @@ namespace lp { } } - // clang-format on void lar_solver::update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { 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 || @@ -1937,12 +1931,14 @@ namespace lp { 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); + set_infeasible_column_and_witness(j, true, dep); + } + else { + 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, dep); + insert_to_columns_with_changed_bounds(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, dep); - insert_to_columns_with_changed_bounds(j); break; } case GT: @@ -1950,25 +1946,33 @@ namespace lp { 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); + set_infeasible_column_and_witness(j, false, dep); + } else { + 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, dep); + 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); } - 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, dep); - 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: { 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); + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]){ + set_infeasible_column_and_witness(j, false, dep); + } + else if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + set_infeasible_column_and_witness(j, true, dep); + } + else { + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; + insert_to_columns_with_changed_bounds(j); } - set_upper_bound_witness(j, dep); - set_lower_bound_witness(j, dep); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = m_mpq_lar_core_solver.m_r_lower_bounds[j] = v; break; } @@ -1979,7 +1983,7 @@ namespace lp { 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, u_dependency* dep) { 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); @@ -1991,12 +1995,14 @@ namespace lp { 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); + set_infeasible_column_and_witness(j, true, dep); + } + else { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, dep); + 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); } - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, dep); - 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: @@ -2014,13 +2020,15 @@ namespace lp { 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_infeasible_column_and_witness(j, true, dep); + } + else { + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); + 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; + insert_to_columns_with_changed_bounds(j); } - - set_upper_bound_witness(j, dep); - set_lower_bound_witness(j, dep); - 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; } @@ -2051,26 +2059,29 @@ namespace lp { { 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); + set_infeasible_column_and_witness(j, false, dep); + } + else { + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_bound_witness(j, dep); + 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); } - m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; - set_lower_bound_witness(j, dep); - 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: { auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - set_infeasible_column(j); + set_infeasible_column_and_witness(j, false, dep); + } + else { + set_upper_bound_witness(j, dep); + set_lower_bound_witness(j, dep); + 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; + insert_to_columns_with_changed_bounds(j); } - - set_upper_bound_witness(j, dep); - set_lower_bound_witness(j, dep); - 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; } @@ -2345,7 +2356,21 @@ namespace lp { return false; return true; } + // If lower_bound is true than the new asserted upper bound is less than the existing lower bound. + // Otherwise the new asserted lower bound is is greater than the existing upper bound. + // dep is the reason for the new bound + void lar_solver::set_infeasible_column_and_witness(unsigned j, bool lower_bound, u_dependency* dep) { + bool was_feas = column_is_feasible(j); + bool in_heap = m_mpq_lar_core_solver.m_r_solver.inf_heap().contains(j); + SASSERT(m_crossed_bounds_deps == nullptr && m_crossed_bounds_deps == nullptr); + set_status(lp_status::INFEASIBLE); + m_crossed_bounds_column = j; + const auto& ul = this->m_columns_to_ul_pairs()[j]; + u_dependency* bdep = lower_bound? ul.lower_bound_witness() : ul.upper_bound_witness(); + SASSERT(bdep != nullptr); + m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); + } } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index fc696d4b2..a689d6665 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -78,7 +78,8 @@ class lar_solver : public column_namer { 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; + lpvar m_crossed_bounds_column; + u_dependency* m_crossed_bounds_deps; lar_core_solver m_mpq_lar_core_solver; int_solver* m_int_solver = nullptr; bool m_need_register_terms = false; @@ -139,10 +140,14 @@ class lar_solver : public column_namer { bool compare_values(impq const& lhs, lconstraint_kind k, const mpq& rhs); inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } + public: void insert_to_columns_with_changed_bounds(unsigned j); + private: void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); + public: void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + private: void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); @@ -152,10 +157,7 @@ class lar_solver : public column_namer { 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); - inline void set_infeasible_column(unsigned j) { - set_status(lp_status::INFEASIBLE); - m_crossed_bounds_column = j; - } + void set_infeasible_column_and_witness(unsigned j, bool lower_bound, u_dependency* dep); 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); unsigned row_of_basic_column(unsigned) const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index e7570d5dd..d7d1666d0 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -540,9 +540,9 @@ public: } 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";); + TRACE("lar_solver_feas", tout << "del = " << del << ", was x[" << j << "] = " << m_x[j] << "\n";); m_x[j] += del; - TRACE("lar_solver_feas_bug", tout << "became x[" << j << "] = " << m_x[j] << "\n";); + TRACE("lar_solver_feas", tout << "became x[" << j << "] = " << m_x[j] << "\n";); track_column_feasibility(j); } diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index e21891ee4..9dd4ffa90 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -258,51 +258,113 @@ namespace nla { } } - void monomial_bounds::unit_propagate() { - for (auto const& m : c().m_emons) - unit_propagate(m); + bool monomial_bounds::unit_propagate() { + unsigned sz = 0; + vector monics_vars; + for (auto const& m : c().m_emons) { + monics_vars.push_back(m.var()); + sz++; + } + unsigned l = this->random(); + for (unsigned i = 0; i < sz; ++i) { + lpvar v = monics_vars[(i + l) % sz]; + if (!unit_propagate(c().m_emons[v])) { + return false; + } + } + + return true; } - void monomial_bounds::unit_propagate(monic const& m) { +// returns false if and only if there is a conflict + bool monomial_bounds::unit_propagate(monic const& m) { m_propagated.reserve(m.var() + 1, false); if (m_propagated[m.var()]) - return; + return true; if (!is_linear(m)) - return; + return true; c().trail().push(set_bitvector_trail(m_propagated, m.var())); - - rational k = fixed_var_product(m); - - new_lemma lemma(c(), "fixed-values"); - if (k == 0) { - for (auto v : m) { - if (c().var_is_fixed(v) && c().val(v).is_zero()) { - lemma.explain_fixed(v); + lpvar zero_fixed = null_lpvar, non_fixed = null_lpvar; + // find a zero fixed variable and a non-fixed variable + for (lpvar v : m) { + if (c().var_is_fixed(v)) { + if (c().val(v).is_zero()) { + zero_fixed = v; break; } } - lemma += ineq(m.var(), lp::lconstraint_kind::EQ, 0); - } - else { - for (auto v : m) - if (c().var_is_fixed(v)) - lemma.explain_fixed(v); - - lpvar w = non_fixed_var(m); - if (w != null_lpvar) { - lp::lar_term term; - term.add_var(m.var()); - term.add_monomial(-k, w); - lemma += ineq(term, lp::lconstraint_kind::EQ, 0); - } else { - lemma += ineq(m.var(), lp::lconstraint_kind::EQ, k); + else { + non_fixed = v; } } - + + if (zero_fixed != null_lpvar) { + // the m.var() has to have a zero value + u_dependency* d = this->dep.mk_join(c().lra.get_column_lower_bound_witness(zero_fixed), + c().lra.get_column_upper_bound_witness(zero_fixed)); + + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, mpq(0), d); + } else if (non_fixed != null_lpvar) { + u_dependency* d = nullptr; + rational k(1); + for (auto v : m) + if (v != non_fixed) { + d = this->dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); + d = this->dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); + k *= c().val(v); + } + SASSERT(k.is_pos() || k.is_neg()); + // we have m = k* non_fixed: m.var() getting the bounds witnesses of non_fixed + if (k.is_pos()) { + d = c().lra.get_column_upper_bound_witness(non_fixed); + if (d) { + const auto& b = c().lra.get_column_value(non_fixed); + bool strict = b.y.is_neg(); + c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::LT : lp::lconstraint_kind::LE, k * b.x, d); + } + d = c().lra.get_column_lower_bound_witness(non_fixed); + if (d) { + const auto& b = c().lra.get_column_value(non_fixed); + bool strict = b.y.is_pos(); + c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::GT : lp::lconstraint_kind::GE, k * b.x, d); + } + + } else { + d = c().lra.get_column_upper_bound_witness(non_fixed); + if (d) { + const auto& b = c().lra.get_column_value(non_fixed); + bool strict = b.y.is_neg(); + c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::GT : lp::lconstraint_kind::GE, k * b.x, d); + } + d = c().lra.get_column_lower_bound_witness(non_fixed); + if (d) { + const auto& b = c().lra.get_column_value(non_fixed); + bool strict = b.y.is_pos(); + c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::LT : lp::lconstraint_kind::LE, k * b.x, d); + } + } + } else { + SASSERT(non_fixed == null_lpvar && zero_fixed == null_lpvar); + rational k(1); + u_dependency* d = nullptr; + for (auto v : m) { + SASSERT(c().var_is_fixed(v)); + d = this->dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); + d = this->dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); + k *= c().val(v); + } + SASSERT(k.is_pos() || k.is_neg()); + // we have m = k: m.var() getting the bounds witnesses of all fixed variables + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, d); + } + if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { + TRACE("nla_solver", tout << "conflict in unit_propagate\n";); + return false; + } + return true; } - bool monomial_bounds::is_linear(monic const& m) { unsigned non_fixed = 0; for (lpvar v : m) { @@ -324,12 +386,6 @@ namespace nla { return r; } - lpvar monomial_bounds::non_fixed_var(monic const& m) { - for (lpvar v : m) - if (!c().var_is_fixed(v)) - return v; - return null_lpvar; - } - + } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index c728d1a4c..41aa48f6c 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -32,14 +32,12 @@ namespace nla { // monomial propagation bool_vector m_propagated; - void unit_propagate(monic const& m); + bool unit_propagate(monic const& m); bool is_linear(monic const& m); rational fixed_var_product(monic const& m); - lpvar non_fixed_var(monic const& m); - public: monomial_bounds(core* core); void propagate(); - void unit_propagate(); + bool unit_propagate(); }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4d4fa6cbe..08a768ee5 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1818,14 +1818,18 @@ bool core::improve_bounds() { } return bounds_improved; } - -void core::propagate(vector& lemmas) { + // returns false if and only if makes lp_solver inconsistent +bool core::propagate(vector& lemmas) { // propagate linear monomials, those that have all, or all but one, variables fixed lemmas.reset(); m_lemma_vec = &lemmas; m_monomial_bounds.unit_propagate(); - + if (lra.get_status() == lp::lp_status::INFEASIBLE) { + TRACE("nla_solver", tout << "propagation found infeasibility\n";); + return false; + } + return true; } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 9c96f2fbf..340b94430 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -392,7 +392,7 @@ public: bool no_lemmas_hold() const; - void propagate(vector& lemmas); + bool propagate(vector& lemmas); lbool test_check(vector& l); lpvar map_to_root(lpvar) const; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 470ea5f7b..ae224e3ec 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -1532,11 +1532,12 @@ namespace smt { bool max, bool maintain_integrality, bool& has_shared) { + return UNBOUNDED; + m_stats.m_max_min++; unsigned best_efforts = 0; bool inc = false; - - + SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index f44516cad..77e2b25f3 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -511,6 +511,7 @@ bool theory_arith::propagate_nl_downward(expr * n, var_power_pair const& p) */ template bool theory_arith::propagate_nl_bounds(expr * m) { + return false; TRACE("non_linear", tout << "propagate several bounds using:\n"; display_monomial(tout, m); tout << "\n";); bool result = propagate_nl_upward(m); buffer vp; @@ -530,6 +531,7 @@ bool theory_arith::propagate_nl_bounds(expr * m) { */ template bool theory_arith::propagate_nl_bounds() { + return false; m_dep_manager.reset(); bool propagated = false; for (unsigned i = 0; i < m_nl_monomials.size(); i++) { @@ -1632,6 +1634,7 @@ bool theory_arith::is_cross_nested_consistent(row const & r) { */ template bool theory_arith::is_cross_nested_consistent(svector const & nl_cluster) { + return true; for (theory_var v : nl_cluster) { if (!is_base(v)) continue; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 31882ea11..29a46db19 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2163,8 +2163,14 @@ public: if (!m_nla) return; m_nla->propagate(m_nla_lemma_vector); - for (nla::lemma const& l : m_nla_lemma_vector) - false_case_of_check_nla(l); + if (lp().get_status() == lp::lp_status::INFEASIBLE) { + TRACE("arith", tout << "propagation conflict\n";); + get_infeasibility_explanation_and_set_conflict(); + } + else { + for (nla::lemma const& l : m_nla_lemma_vector) + false_case_of_check_nla(l); + } } bool should_propagate() const { From 288e66de590c654051172d30e768f7a181ee6629 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 6 Sep 2023 09:27:30 -0700 Subject: [PATCH 128/428] restore m_crossed* and create lemmas Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 2 -- src/math/lp/lar_solver.h | 9 +++++- src/math/lp/monomial_bounds.cpp | 49 ++++++++++++++------------------- src/math/lp/monomial_bounds.h | 5 ++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 5f82516ef..8480025f2 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -2361,8 +2361,6 @@ namespace lp { // dep is the reason for the new bound void lar_solver::set_infeasible_column_and_witness(unsigned j, bool lower_bound, u_dependency* dep) { - bool was_feas = column_is_feasible(j); - bool in_heap = m_mpq_lar_core_solver.m_r_solver.inf_heap().contains(j); SASSERT(m_crossed_bounds_deps == nullptr && m_crossed_bounds_deps == nullptr); set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index a689d6665..03d20c793 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -79,7 +79,7 @@ class lar_solver : public column_namer { stacked_value m_simplex_strategy; // such can be found at the initialization step: u < l lpvar m_crossed_bounds_column; - u_dependency* m_crossed_bounds_deps; + u_dependency* m_crossed_bounds_deps; lar_core_solver m_mpq_lar_core_solver; int_solver* m_int_solver = nullptr; bool m_need_register_terms = false; @@ -142,6 +142,13 @@ class lar_solver : public column_namer { inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } public: void insert_to_columns_with_changed_bounds(unsigned j); + const u_dependency* crossed_bounds_deps() const { return m_crossed_bounds_deps;} + u_dependency*& crossed_bounds_deps() { return m_crossed_bounds_deps;} + + lpvar crossed_bounds_column() const { return m_crossed_bounds_column; } + lpvar& crossed_bounds_column() { return m_crossed_bounds_column; } + + private: void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 9dd4ffa90..8061444c8 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -258,32 +258,30 @@ namespace nla { } } - bool monomial_bounds::unit_propagate() { - unsigned sz = 0; - vector monics_vars; - for (auto const& m : c().m_emons) { - monics_vars.push_back(m.var()); - sz++; - } - unsigned l = this->random(); - for (unsigned i = 0; i < sz; ++i) { - lpvar v = monics_vars[(i + l) % sz]; - if (!unit_propagate(c().m_emons[v])) { - return false; - } - } - - return true; + void monomial_bounds::unit_propagate() { + for (auto const& m : c().m_emons) + unit_propagate(m); } -// returns false if and only if there is a conflict - bool monomial_bounds::unit_propagate(monic const& m) { + void monomial_bounds::check_for_conflict() { + if (c().lra.crossed_bounds_deps() != nullptr) { + new_lemma lemma(c(), "fixed-values"); + lp::explanation ex; + c().lra.fill_explanation_from_crossed_bounds_column(ex); + lemma &= ex; + c().lra.crossed_bounds_deps() = nullptr; + c().lra.crossed_bounds_column() = null_lpvar; + c().lra.set_status(lp::lp_status::OPTIMAL); + } + } + + void monomial_bounds::unit_propagate(monic const& m) { m_propagated.reserve(m.var() + 1, false); if (m_propagated[m.var()]) - return true; + return; if (!is_linear(m)) - return true; + return; c().trail().push(set_bitvector_trail(m_propagated, m.var())); lpvar zero_fixed = null_lpvar, non_fixed = null_lpvar; @@ -303,8 +301,7 @@ namespace nla { if (zero_fixed != null_lpvar) { // the m.var() has to have a zero value u_dependency* d = this->dep.mk_join(c().lra.get_column_lower_bound_witness(zero_fixed), - c().lra.get_column_upper_bound_witness(zero_fixed)); - + c().lra.get_column_upper_bound_witness(zero_fixed)); c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, mpq(0), d); } else if (non_fixed != null_lpvar) { u_dependency* d = nullptr; @@ -330,7 +327,6 @@ namespace nla { bool strict = b.y.is_pos(); c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::GT : lp::lconstraint_kind::GE, k * b.x, d); } - } else { d = c().lra.get_column_upper_bound_witness(non_fixed); if (d) { @@ -358,12 +354,9 @@ namespace nla { SASSERT(k.is_pos() || k.is_neg()); // we have m = k: m.var() getting the bounds witnesses of all fixed variables c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, d); - } - if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { - TRACE("nla_solver", tout << "conflict in unit_propagate\n";); - return false; } - return true; + check_for_conflict(); + SASSERT (c().lra.get_status() != lp::lp_status::INFEASIBLE); } bool monomial_bounds::is_linear(monic const& m) { unsigned non_fixed = 0; diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 41aa48f6c..9faf0e470 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -32,12 +32,13 @@ namespace nla { // monomial propagation bool_vector m_propagated; - bool unit_propagate(monic const& m); + void unit_propagate(monic const& m); bool is_linear(monic const& m); rational fixed_var_product(monic const& m); public: monomial_bounds(core* core); void propagate(); - bool unit_propagate(); + void unit_propagate(); + void check_for_conflict(); }; } From 47b64e689cc26a79ff88e46465dce9dc6df054ff Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 7 Sep 2023 11:33:14 -0700 Subject: [PATCH 129/428] restore the lemma scheme Signed-off-by: Lev Nachmanson --- src/math/lp/int_solver.cpp | 2 +- src/math/lp/monomial_bounds.cpp | 113 +++++++++----------------------- src/math/lp/monomial_bounds.h | 3 +- src/math/lp/nla_core.cpp | 10 +-- src/math/lp/nla_core.h | 2 +- 5 files changed, 39 insertions(+), 91 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index bd1ac5313..1964262f3 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -31,7 +31,7 @@ namespace lp { lra.remove_fixed_vars_from_base(); lp_assert(lia.is_feasible()); for (unsigned j : lra.r_basis()) - if (!lra.get_value(j).is_int() && lra.column_is_int(j)&& !lia.is_fixed(j)) + if (!lra.get_value(j).is_int() && lra.column_is_int(j) && !lia.is_fixed(j)) patch_basic_column(j); if (!lia.has_inf_int()) { lia.settings().stats().m_patches_success++; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 8061444c8..e21891ee4 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -258,23 +258,11 @@ namespace nla { } } - void monomial_bounds::unit_propagate() { - for (auto const& m : c().m_emons) + void monomial_bounds::unit_propagate() { + for (auto const& m : c().m_emons) unit_propagate(m); } - void monomial_bounds::check_for_conflict() { - if (c().lra.crossed_bounds_deps() != nullptr) { - new_lemma lemma(c(), "fixed-values"); - lp::explanation ex; - c().lra.fill_explanation_from_crossed_bounds_column(ex); - lemma &= ex; - c().lra.crossed_bounds_deps() = nullptr; - c().lra.crossed_bounds_column() = null_lpvar; - c().lra.set_status(lp::lp_status::OPTIMAL); - } - } - void monomial_bounds::unit_propagate(monic const& m) { m_propagated.reserve(m.var() + 1, false); if (m_propagated[m.var()]) @@ -284,80 +272,37 @@ namespace nla { return; c().trail().push(set_bitvector_trail(m_propagated, m.var())); - lpvar zero_fixed = null_lpvar, non_fixed = null_lpvar; - // find a zero fixed variable and a non-fixed variable - for (lpvar v : m) { - if (c().var_is_fixed(v)) { - if (c().val(v).is_zero()) { - zero_fixed = v; + + rational k = fixed_var_product(m); + + new_lemma lemma(c(), "fixed-values"); + if (k == 0) { + for (auto v : m) { + if (c().var_is_fixed(v) && c().val(v).is_zero()) { + lemma.explain_fixed(v); break; } } - else { - non_fixed = v; - } + lemma += ineq(m.var(), lp::lconstraint_kind::EQ, 0); } - - if (zero_fixed != null_lpvar) { - // the m.var() has to have a zero value - u_dependency* d = this->dep.mk_join(c().lra.get_column_lower_bound_witness(zero_fixed), - c().lra.get_column_upper_bound_witness(zero_fixed)); - c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, mpq(0), d); - } else if (non_fixed != null_lpvar) { - u_dependency* d = nullptr; - rational k(1); - for (auto v : m) - if (v != non_fixed) { - d = this->dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); - d = this->dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); - k *= c().val(v); - } - SASSERT(k.is_pos() || k.is_neg()); - // we have m = k* non_fixed: m.var() getting the bounds witnesses of non_fixed - if (k.is_pos()) { - d = c().lra.get_column_upper_bound_witness(non_fixed); - if (d) { - const auto& b = c().lra.get_column_value(non_fixed); - bool strict = b.y.is_neg(); - c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::LT : lp::lconstraint_kind::LE, k * b.x, d); - } - d = c().lra.get_column_lower_bound_witness(non_fixed); - if (d) { - const auto& b = c().lra.get_column_value(non_fixed); - bool strict = b.y.is_pos(); - c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::GT : lp::lconstraint_kind::GE, k * b.x, d); - } + else { + for (auto v : m) + if (c().var_is_fixed(v)) + lemma.explain_fixed(v); + + lpvar w = non_fixed_var(m); + if (w != null_lpvar) { + lp::lar_term term; + term.add_var(m.var()); + term.add_monomial(-k, w); + lemma += ineq(term, lp::lconstraint_kind::EQ, 0); } else { - d = c().lra.get_column_upper_bound_witness(non_fixed); - if (d) { - const auto& b = c().lra.get_column_value(non_fixed); - bool strict = b.y.is_neg(); - c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::GT : lp::lconstraint_kind::GE, k * b.x, d); - } - d = c().lra.get_column_lower_bound_witness(non_fixed); - if (d) { - const auto& b = c().lra.get_column_value(non_fixed); - bool strict = b.y.is_pos(); - c().lra.update_column_type_and_bound(m.var(), strict ? lp::lconstraint_kind::LT : lp::lconstraint_kind::LE, k * b.x, d); - } + lemma += ineq(m.var(), lp::lconstraint_kind::EQ, k); } - } else { - SASSERT(non_fixed == null_lpvar && zero_fixed == null_lpvar); - rational k(1); - u_dependency* d = nullptr; - for (auto v : m) { - SASSERT(c().var_is_fixed(v)); - d = this->dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); - d = this->dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); - k *= c().val(v); - } - SASSERT(k.is_pos() || k.is_neg()); - // we have m = k: m.var() getting the bounds witnesses of all fixed variables - c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, d); } - check_for_conflict(); - SASSERT (c().lra.get_status() != lp::lp_status::INFEASIBLE); + } + bool monomial_bounds::is_linear(monic const& m) { unsigned non_fixed = 0; for (lpvar v : m) { @@ -379,6 +324,12 @@ namespace nla { return r; } - + lpvar monomial_bounds::non_fixed_var(monic const& m) { + for (lpvar v : m) + if (!c().var_is_fixed(v)) + return v; + return null_lpvar; + } + } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 9faf0e470..c728d1a4c 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -35,10 +35,11 @@ namespace nla { void unit_propagate(monic const& m); bool is_linear(monic const& m); rational fixed_var_product(monic const& m); + lpvar non_fixed_var(monic const& m); + public: monomial_bounds(core* core); void propagate(); void unit_propagate(); - void check_for_conflict(); }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 08a768ee5..4d4fa6cbe 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1818,18 +1818,14 @@ bool core::improve_bounds() { } return bounds_improved; } - // returns false if and only if makes lp_solver inconsistent -bool core::propagate(vector& lemmas) { + +void core::propagate(vector& lemmas) { // propagate linear monomials, those that have all, or all but one, variables fixed lemmas.reset(); m_lemma_vec = &lemmas; m_monomial_bounds.unit_propagate(); - if (lra.get_status() == lp::lp_status::INFEASIBLE) { - TRACE("nla_solver", tout << "propagation found infeasibility\n";); - return false; - } - return true; + } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 340b94430..9c96f2fbf 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -392,7 +392,7 @@ public: bool no_lemmas_hold() const; - bool propagate(vector& lemmas); + void propagate(vector& lemmas); lbool test_check(vector& l); lpvar map_to_root(lpvar) const; From c43b99daae35e86926df9cf4b0e8cb0e8a3c1a30 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 7 Sep 2023 11:57:20 -0700 Subject: [PATCH 130/428] renaming Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 18 +++++++++--------- src/math/lp/lar_solver.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 8480025f2..bf38a9269 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1931,7 +1931,7 @@ namespace lp { 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_and_witness(j, true, dep); + set_crossed_bounds_column_and_deps(j, true, dep); } else { if (up >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) return; @@ -1946,7 +1946,7 @@ namespace lp { 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_and_witness(j, false, dep); + set_crossed_bounds_column_and_deps(j, false, dep); } else { if (low < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { return; @@ -1962,10 +1962,10 @@ namespace lp { case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]){ - set_infeasible_column_and_witness(j, false, dep); + set_crossed_bounds_column_and_deps(j, false, dep); } else if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { - set_infeasible_column_and_witness(j, true, dep); + set_crossed_bounds_column_and_deps(j, true, dep); } else { set_upper_bound_witness(j, dep); @@ -1995,7 +1995,7 @@ namespace lp { 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_and_witness(j, true, dep); + set_crossed_bounds_column_and_deps(j, true, dep); } else { m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; @@ -2020,7 +2020,7 @@ namespace lp { 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_and_witness(j, true, dep); + set_crossed_bounds_column_and_deps(j, true, dep); } else { set_upper_bound_witness(j, dep); @@ -2059,7 +2059,7 @@ namespace lp { { auto low = numeric_pair(right_side, y_of_bound); if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - set_infeasible_column_and_witness(j, false, dep); + set_crossed_bounds_column_and_deps(j, false, dep); } else { m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; @@ -2073,7 +2073,7 @@ namespace lp { { auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - set_infeasible_column_and_witness(j, false, dep); + set_crossed_bounds_column_and_deps(j, false, dep); } else { set_upper_bound_witness(j, dep); @@ -2360,7 +2360,7 @@ namespace lp { // Otherwise the new asserted lower bound is is greater than the existing upper bound. // dep is the reason for the new bound - void lar_solver::set_infeasible_column_and_witness(unsigned j, bool lower_bound, u_dependency* dep) { + void lar_solver::set_crossed_bounds_column_and_deps(unsigned j, bool lower_bound, u_dependency* dep) { SASSERT(m_crossed_bounds_deps == nullptr && m_crossed_bounds_deps == nullptr); set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 03d20c793..4b8cd0c1b 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -164,7 +164,7 @@ class lar_solver : public column_namer { 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); - void set_infeasible_column_and_witness(unsigned j, bool lower_bound, u_dependency* dep); + void set_crossed_bounds_column_and_deps(unsigned j, bool lower_bound, u_dependency* dep); 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); unsigned row_of_basic_column(unsigned) const; From c050af922f9858bcca9070b8ac1c9e8e4fb2349c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 7 Sep 2023 15:59:20 -0700 Subject: [PATCH 131/428] fixing the bugs --- src/math/lp/lar_solver.cpp | 2 ++ src/math/lp/lar_solver.h | 1 + src/math/lp/monomial_bounds.cpp | 4 +-- src/math/lp/nla_core.cpp | 52 ++++++++++++++++++++------------- src/math/lp/nla_core.h | 3 +- src/smt/theory_arith_aux.h | 2 -- 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index bf38a9269..08ff3b0d4 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -789,6 +789,8 @@ namespace lp { void lar_solver::detect_rows_with_changed_bounds() { for (auto j : m_columns_with_changed_bounds) detect_rows_with_changed_bounds_for_column(j); + if (m_find_monics_with_changed_bounds_func) + m_find_monics_with_changed_bounds_func(m_columns_with_changed_bounds); } void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 4b8cd0c1b..acab137b5 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -670,6 +670,7 @@ class lar_solver : public column_namer { return 0; return m_usage_in_terms[j]; } + std::function m_find_monics_with_changed_bounds_func = nullptr; friend int_solver; friend int_branch; }; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index e21891ee4..77e01c2db 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -259,8 +259,8 @@ namespace nla { } void monomial_bounds::unit_propagate() { - for (auto const& m : c().m_emons) - unit_propagate(m); + for (lpvar v : c().m_monics_with_changed_bounds) + unit_propagate(c().emons()[v]); } void monomial_bounds::unit_propagate(monic const& m) { diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4d4fa6cbe..cc2f0f1b5 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -21,28 +21,40 @@ namespace nla { typedef lp::lar_term term; -core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : - m_evars(), - lra(s), - m_reslim(lim), - m_params(p), - m_tangents(this), - m_basics(this), - m_order(this), - m_monotone(this), - m_powers(*this), - m_divisions(*this), - m_intervals(this, lim), - m_monomial_bounds(this), - m_horner(this), - m_grobner(this), - m_emons(m_evars), - m_use_nra_model(false), - m_nra(s, m_nra_lim, *this) -{ +core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim) : m_evars(), + lra(s), + m_reslim(lim), + m_params(p), + m_tangents(this), + m_basics(this), + m_order(this), + m_monotone(this), + m_powers(*this), + m_divisions(*this), + m_intervals(this, lim), + m_monomial_bounds(this), + m_horner(this), + m_grobner(this), + m_emons(m_evars), + m_use_nra_model(false), + m_nra(s, m_nra_lim, *this) { m_nlsat_delay = lp_settings().nlsat_delay(); + lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { + m_monics_with_changed_bounds.clear(); + for (const auto& m : m_emons) { + if (columns_with_changed_bounds.contains(m.var())) { + m_monics_with_changed_bounds.push_back(m.var()); + continue; + } + for (lpvar j : m.vars()) { + if (columns_with_changed_bounds.contains(j)) { + m_monics_with_changed_bounds.push_back(m.var()); + break; + } + } + } }; } - + bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const { switch(cmp) { case llc::LE: return ls <= rs; diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 9c96f2fbf..8dee571ae 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -87,7 +87,8 @@ class core { std::function m_relevant; vector * m_lemma_vec; vector * m_literal_vec = nullptr; - indexed_uint_set m_to_refine; + indexed_uint_set m_to_refine; + vector m_monics_with_changed_bounds; tangents m_tangents; basics m_basics; order m_order; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index ae224e3ec..dd9ba2dfe 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -1532,8 +1532,6 @@ namespace smt { bool max, bool maintain_integrality, bool& has_shared) { - return UNBOUNDED; - m_stats.m_max_min++; unsigned best_efforts = 0; bool inc = false; From 0a444f357a82f37db5b089bcf90cd91473838795 Mon Sep 17 00:00:00 2001 From: Sijmen Date: Mon, 11 Sep 2023 21:58:03 +0200 Subject: [PATCH 132/428] Slightly improve Z3_LIBRARY_PATH error message (#6895) --- scripts/update_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 5f8db1932..c68597ea2 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1887,10 +1887,10 @@ if _lib is None: print(" - to the custom Z3_LIB_DIRS Python-builtin before importing the z3 module, e.g. via") if sys.version < '3': print(" import __builtin__") - print(" __builtin__.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) + print(" __builtin__.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] \# directory containing libz3.%s" % _ext) else: print(" import builtins") - print(" builtins.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) + print(" builtins.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] \# directory containing libz3.%s" % _ext) print(_failures) raise Z3Exception("libz3.%s not found." % _ext) From e718bb64737fac5c4b365919530fca77104560aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:09:01 +0100 Subject: [PATCH 133/428] Bump docker/build-push-action from 4.1.1 to 4.2.1 (#6896) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.1.1 to 4.2.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4.1.1...v4.2.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 583e36034..bf8138bec 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.1.1 + uses: docker/build-push-action@v4.2.1 with: context: . push: true From c309d522831b4b62812399b6571624899e1557cd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 13 Sep 2023 08:12:00 -0700 Subject: [PATCH 134/428] runs a simple test --- src/math/lp/bound_analyzer_on_row.h | 31 ++++++- src/math/lp/implied_bound.h | 31 +++---- src/math/lp/lar_solver.h | 56 ++++++++---- src/math/lp/lp_bound_propagator.h | 132 ++++++++++++++++++++++++++-- src/math/lp/monomial_bounds.cpp | 74 +++++++--------- src/math/lp/monomial_bounds.h | 6 +- src/math/lp/nla_core.cpp | 8 +- src/math/lp/nla_core.h | 4 +- src/math/lp/nla_solver.h | 3 +- src/sat/smt/arith_solver.cpp | 2 +- src/smt/theory_lra.cpp | 56 ++++++++---- 11 files changed, 291 insertions(+), 112 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 0008a0ee9..35e3bb6ed 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -26,6 +26,8 @@ Revision History: #include "math/lp/test_bound_analyzer.h" namespace lp { + + template // C plays a role of a container, B - lp_bound_propagator class bound_analyzer_on_row { const C& m_row; @@ -301,8 +303,12 @@ private: // */ // } - void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ - m_bp.try_add_bound(u, j, is_lower_bound, coeff_before_j_is_pos, m_row_index, strict); + + void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ + lar_solver* lar = & this->m_bp.lp(); + unsigned row_index = this->m_row_index; + auto explain = [lar, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index]() { return explain_bound_on_var_on_coeff(lar, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; + m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain ); } void advance_u(unsigned j) { @@ -335,6 +341,27 @@ private: break; } } + static u_dependency* explain_bound_on_var_on_coeff(lar_solver* lar, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { + int bound_sign = (is_lower_bound ? 1 : -1); + int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; + + if (tv::is_term(bound_j)) + bound_j = lar->map_term_index_to_column_index(bound_j); + u_dependency* ret = nullptr; + for (auto const& r : lar->get_row(row_index)) { + unsigned j = r.var(); + if (j == bound_j) + continue; + mpq const& a = r.coeff(); + int a_sign = is_pos(a) ? 1 : -1; + int sign = j_sign * a_sign; + u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j); + ret = lar->join_deps(ret, witness); + } + return ret; + } }; + + } diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 9435edcdc..2e7fcbbcb 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -21,37 +21,34 @@ Revision History: #include "math/lp/lp_settings.h" #include "math/lp/lar_constraints.h" namespace lp { -struct implied_bound { +class implied_bound { + public: mpq m_bound; unsigned m_j; // the column for which the bound has been found bool m_is_lower_bound; - bool m_coeff_before_j_is_pos; - unsigned m_row_or_term_index; - bool m_strict; + bool m_strict; + private: + std::function m_explain_bound = nullptr; + public: + u_dependency* explain() const { return m_explain_bound(); } + void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) k = static_cast(k / 2); return k; } - bool operator==(const implied_bound & o) const { - return m_j == o.m_j && m_is_lower_bound == o.m_is_lower_bound && m_bound == o.m_bound && - m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos && - m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict; - } implied_bound(){} implied_bound(const mpq & a, unsigned j, - bool lower_bound, - bool coeff_before_j_is_pos, - unsigned row_or_term_index, - bool strict): + bool is_lower_bound, + bool is_strict, + std::function get_dep): m_bound(a), m_j(j), - m_is_lower_bound(lower_bound), - m_coeff_before_j_is_pos(coeff_before_j_is_pos), - m_row_or_term_index(row_or_term_index), - m_strict(strict) { + m_is_lower_bound(is_lower_bound), + m_strict(is_strict), + m_explain_bound(get_dep) { } }; } diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index acab137b5..7d16c7b63 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -311,26 +311,34 @@ class lar_solver : public column_namer { template 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)) - bound_j = m_var_register.external_to_local(bound_j); + u_dependency* dep = ib.explain(); + for (auto ci : flatten(dep)) + bp.consume(mpq(1), ci); // TODO: flatten should provid the coefficients + /* + if (ib.m_is_monic) { + NOT_IMPLEMENTED_YET(); + } else { + 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)) + 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) - 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]; - auto* witness = sign > 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); - lp_assert(witness); - for (auto ci : flatten(witness)) - bp.consume(a, ci); - } + for (auto const& r : get_row(i)) { + unsigned j = r.var(); + 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]; + auto* witness = sign > 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); + lp_assert(witness); + for (auto ci : flatten(witness)) + bp.consume(a, ci); + } + }*/ } void set_value_for_nbasic_column(unsigned j, const impq& new_val); @@ -564,6 +572,16 @@ class lar_solver : public column_namer { const ul_pair& ul = m_columns_to_ul_pairs[j]; return m_dependencies.mk_join(ul.lower_bound_witness(), ul.upper_bound_witness()); } + template + u_dependency* get_bound_constraint_witnesses_for_columns(const T& collection) { + u_dependency* dep = nullptr; + for (auto j : collection) { + u_dependency* d = get_bound_constraint_witnesses_for_column(j); + dep = m_dependencies.mk_join(dep, d); + } + return dep; + } + u_dependency* join_deps(u_dependency* a, u_dependency *b) { return m_dependencies.mk_join(a, b); } inline constraint_set const& constraints() const { return m_constraints; } void push(); void pop(); diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index f676b0e1d..7ee647200 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -40,6 +40,7 @@ class lp_bound_propagator { return x != UINT_MAX; } + void try_add_equation_with_internal_fixed_tables(unsigned r1) { unsigned v1, v2; if (!only_one_nfixed(r1, v1)) @@ -94,6 +95,115 @@ class lp_bound_propagator { m_ibounds.reset(); m_column_types = &lp().get_column_types(); } + + bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) { + zero_var = non_fixed = null_lpvar; + unsigned n_of_non_fixed = 0; + bool big_bound = false; + for (lpvar v : m) { + if (!this->column_is_fixed(v)) { + n_of_non_fixed++; + non_fixed = v; + continue; + } + const auto & b = get_lower_bound(v).x; + if (b.is_zero()) { + zero_var = v; + return true; + } + if (b.is_big()) { + big_bound |= true; + } + } + return n_of_non_fixed <= 1 && !big_bound; + } + + void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { + add_lower_bound_monic(monic_var, mpq(0), false, [&](){return lp().get_bound_constraint_witnesses_for_column(zero_var);}); + add_upper_bound_monic(monic_var, mpq(0), false, [&](){return lp().get_bound_constraint_witnesses_for_column(zero_var);}); + } + + void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { + unsigned k; + if (!try_get_value(m_improved_lower_bounds, monic_var, k)) { + m_improved_lower_bounds[monic_var] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(v, monic_var, true, is_strict, explain_dep)); + } else { + auto& found_bound = m_ibounds[k]; + if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { + found_bound = implied_bound(v, monic_var, true, is_strict, explain_dep); + TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + } + } + } + + void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { + unsigned k; + if (!try_get_value(m_improved_upper_bounds, monic_var, k)) { + m_improved_upper_bounds[monic_var] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(bound_val, monic_var, false, is_strict, explain_bound)); + } else { + auto& found_bound = m_ibounds[k]; + if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { + found_bound = implied_bound(bound_val, monic_var, false, is_strict, explain_bound); + TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + } + } + } + + void propagate_monic(lpvar monic_var, const svector& vars) { + lpvar non_fixed, zero_var; + if (!is_linear(vars, zero_var, non_fixed)) { + return; + } + + if (zero_var != null_lpvar) { + add_bounds_for_zero_var(monic_var, zero_var); + } else { + if (non_fixed != null_lpvar && get_column_type(non_fixed) == column_type::free_column) return; + rational k = rational(1); + for (auto v : vars) + if (v != non_fixed) { + k *= lp().get_column_value(v).x; + if (k.is_big()) return; + } + u_dependency* dep; + lp::mpq bound_value; + bool is_strict; + if (non_fixed != null_lpvar) { + if (this->lp().has_lower_bound(non_fixed, dep, bound_value, is_strict)) { + if (k.is_pos()) + add_lower_bound_monic(monic_var, k * bound_value, is_strict, [&]() { return dep; }); + else + add_upper_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + } + if (this->lp().has_upper_bound(non_fixed, dep, bound_value, is_strict)) { + if (k.is_neg()) + add_lower_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + else + add_upper_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + } + + if (this->lp().has_lower_bound(monic_var, dep, bound_value, is_strict)) { + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + else + add_upper_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + } + + if (this->lp().has_upper_bound(monic_var, dep, bound_value, is_strict)) { + if (k.is_neg()) + add_lower_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + else + add_upper_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + } + + } else { // all variables are fixed + add_lower_bound_monic(monic_var, k, false, [&](){return lp().get_bound_constraint_witnesses_for_columns(vars);}); + add_upper_bound_monic(monic_var, k, false, [&](){return lp().get_bound_constraint_witnesses_for_columns(vars);}); + } + } + } const lar_solver& lp() const { return m_imp.lp(); } lar_solver& lp() { return m_imp.lp(); } @@ -123,7 +233,7 @@ class lp_bound_propagator { return (*m_column_types)[j] == column_type::fixed && 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) { + void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { j = m_imp.lp().column_to_reported_index(j); lconstraint_kind kind = is_low ? GE : LE; @@ -137,25 +247,29 @@ class lp_bound_propagator { if (try_get_value(m_improved_lower_bounds, j, 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);); + found_bound.m_bound = v; + found_bound.m_strict = strict; + found_bound.set_explain(explain_bound); + TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } } 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);); + m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); + TRACE("add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, 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);); + found_bound.m_bound = v; + found_bound.m_strict = strict; + found_bound.set_explain(explain_bound); + TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } } 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);); + m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); + TRACE("add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); } } } diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 77e01c2db..ad2b20c07 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -261,6 +261,7 @@ namespace nla { void monomial_bounds::unit_propagate() { for (lpvar v : c().m_monics_with_changed_bounds) unit_propagate(c().emons()[v]); + c().m_monics_with_changed_bounds.clear(); } void monomial_bounds::unit_propagate(monic const& m) { @@ -268,68 +269,61 @@ namespace nla { if (m_propagated[m.var()]) return; - if (!is_linear(m)) + lpvar non_fixed = null_lpvar, zero_var = null_lpvar; + if (!is_linear(m, zero_var, non_fixed)) return; c().trail().push(set_bitvector_trail(m_propagated, m.var())); - rational k = fixed_var_product(m); - new_lemma lemma(c(), "fixed-values"); - if (k == 0) { - for (auto v : m) { - if (c().var_is_fixed(v) && c().val(v).is_zero()) { - lemma.explain_fixed(v); - break; - } - } + if (zero_var != null_lpvar) { + new_lemma lemma(c(), "fixed-values"); + lemma.explain_fixed(zero_var); lemma += ineq(m.var(), lp::lconstraint_kind::EQ, 0); } else { + rational k = rational(1); for (auto v : m) - if (c().var_is_fixed(v)) + if (v != non_fixed) { + k *= c().lra.get_column_value(v).x; + if (k.is_big()) return; + } + + new_lemma lemma(c(), "fixed-values"); + + for (auto v : m) + if (v != non_fixed) lemma.explain_fixed(v); - lpvar w = non_fixed_var(m); - if (w != null_lpvar) { + if (non_fixed != null_lpvar) { lp::lar_term term; term.add_var(m.var()); - term.add_monomial(-k, w); + term.add_monomial(-k, non_fixed); lemma += ineq(term, lp::lconstraint_kind::EQ, 0); } else { lemma += ineq(m.var(), lp::lconstraint_kind::EQ, k); } } - } - - bool monomial_bounds::is_linear(monic const& m) { - unsigned non_fixed = 0; + // returns true iff (all variables are fixed, + // or all but one variable are fixed) and the bounds are not big, + // or at least one variable is fixed to zero. + bool monomial_bounds::is_linear(monic const& m, lpvar& zero_var, lpvar& non_fixed) { + zero_var = non_fixed = null_lpvar; + unsigned n_of_non_fixed = 0; + bool big_bound = false; for (lpvar v : m) { - if (!c().var_is_fixed(v)) - ++non_fixed; - else if (c().val(v).is_zero()) + if (!c().var_is_fixed(v)) { + n_of_non_fixed++; + non_fixed = v; + } else if (c().var_is_fixed_to_zero(v)) { + zero_var = v; return true; + } else if (c().fixed_var_has_big_bound(v)) { + big_bound |= true; + } } - return non_fixed <= 1; + return n_of_non_fixed <= 1 && !big_bound; } - - - rational monomial_bounds::fixed_var_product(monic const& m) { - rational r(1); - for (lpvar v : m) { - if (c().var_is_fixed(v)) - r *= c().lra.get_column_value(v).x; - } - return r; - } - - lpvar monomial_bounds::non_fixed_var(monic const& m) { - for (lpvar v : m) - if (!c().var_is_fixed(v)) - return v; - return null_lpvar; - } - } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index c728d1a4c..15ab6b992 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -33,10 +33,8 @@ namespace nla { // monomial propagation bool_vector m_propagated; void unit_propagate(monic const& m); - bool is_linear(monic const& m); - rational fixed_var_product(monic const& m); - lpvar non_fixed_var(monic const& m); - + bool is_linear(monic const& m, lpvar& zero_var, lpvar& non_fixed); + public: monomial_bounds(core* core); void propagate(); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index cc2f0f1b5..20003f947 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -40,7 +40,6 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim) : m_evars(), m_nra(s, m_nra_lim, *this) { m_nlsat_delay = lp_settings().nlsat_delay(); lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { - m_monics_with_changed_bounds.clear(); for (const auto& m : m_emons) { if (columns_with_changed_bounds.contains(m.var())) { m_monics_with_changed_bounds.push_back(m.var()); @@ -553,6 +552,13 @@ bool core::var_is_fixed_to_zero(lpvar j) const { lra.column_is_fixed(j) && lra.get_lower_bound(j) == lp::zero_of_type(); } + +bool core::fixed_var_has_big_bound(lpvar j) const { + SASSERT(lra.column_is_fixed(j)); + const auto& b = lra.get_lower_bound(j); + return b.x.is_big() || b.y.is_big(); +} + bool core::var_is_fixed_to_val(lpvar j, const rational& v) const { return lra.column_is_fixed(j) && diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 8dee571ae..3b888f8ef 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -120,7 +120,8 @@ class core { public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&); - + const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; } + void reset_monics_with_changed_bounds() { m_monics_with_changed_bounds.reset(); } void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); @@ -310,6 +311,7 @@ public: bool sign_contradiction(const monic& m) const; bool var_is_fixed_to_zero(lpvar j) const; + bool fixed_var_has_big_bound(lpvar j) const; bool var_is_fixed_to_val(lpvar j, const rational& v) const; bool var_is_fixed(lpvar j) const; diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 7a8a97b3f..07bf095a6 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -26,7 +26,8 @@ namespace nla { solver(lp::lar_solver& s, params_ref const& p, reslimit& limit); ~solver(); - + const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } + void reset_monics_with_changed_bounds() { m_core->reset_monics_with_changed_bounds(); } void add_monic(lpvar v, unsigned sz, lpvar const* vs); void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index c06b8fafb..e4bec83cc 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - lp().explain_implied_bound(be, m_bp); + be.explain(); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 29a46db19..1a022e087 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2150,7 +2150,7 @@ public: case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); - propagate_nla(); + propagate_bounds_with_nlp(); break; case l_undef: UNREACHABLE(); @@ -2185,33 +2185,55 @@ public: set_evidence(j, m_core, m_eqs); m_explanation.add_pair(j, v); } - - void propagate_bounds_with_lp_solver() { - if (!should_propagate()) - return; - - m_bp.init(); - lp().propagate_bounds_for_touched_rows(m_bp); - - if (!m.inc()) - return; + void finish_bound_propagation() { if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); // verbose_stream() << "unsat\n"; - } - else { - unsigned count = 0, prop = 0; - for (auto& ib : m_bp.ibounds()) { + } else { + for (auto &ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - ++prop; - count += propagate_lp_solver_bound(ib); + propagate_lp_solver_bound(ib); } } } + void propagate_bounds_with_lp_solver() { + if (!should_propagate()) + return; + m_bp.init(); + lp().propagate_bounds_for_touched_rows(m_bp); + + if (m.inc()) + finish_bound_propagation(); + } + + void calculate_implied_bounds_for_monic(lpvar monic_var, const svector& vars) { + m_bp.propagate_monic(monic_var, vars); + } + + void propagate_bounds_for_touched_monomials() { + for (unsigned v : m_nla->monics_with_changed_bounds()) { + calculate_implied_bounds_for_monic(v, m_nla->get_core().emons()[v].vars()); + } + m_nla->reset_monics_with_changed_bounds(); + } + + void propagate_bounds_with_nlp() { + if (!m_nla) + return; + if (is_infeasible() || !should_propagate()) + return; + + m_bp.init(); + propagate_bounds_for_touched_monomials(); + + if (m.inc()) + finish_bound_propagation(); + } + bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { theory_var v = lp().local_to_external(vi); if (v == null_theory_var) From cbad61ba2e157b86abaf03dfb4db0d3c912f5988 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 13 Sep 2023 14:27:34 -0700 Subject: [PATCH 135/428] propagate monics with lp_bound_propagator --- src/math/lp/bound_analyzer_on_row.h | 43 ++++------------- src/math/lp/implied_bound.h | 16 ++++-- src/math/lp/lar_solver.h | 2 +- src/math/lp/lp_bound_propagator.h | 75 ++++++++++++++++++++--------- src/sat/smt/arith_solver.cpp | 2 +- 5 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 35e3bb6ed..5af864220 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -93,39 +93,17 @@ private: } bool bound_is_available(unsigned j, bool lower_bound) { - return (lower_bound && lower_bound_is_available(j)) || - (!lower_bound && upper_bound_is_available(j)); - } - - bool upper_bound_is_available(unsigned j) const { - switch (m_bp.get_column_type(j)) { - case column_type::fixed: - case column_type::boxed: - case column_type::upper_bound: - return true; - default: - return false; - } - } - - bool lower_bound_is_available(unsigned j) const { - switch (m_bp.get_column_type(j)) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - return true; - default: - return false; - } + return (lower_bound && m_bp.lower_bound_is_available(j)) || + (!lower_bound && m_bp.upper_bound_is_available(j)); } const impq & ub(unsigned j) const { - lp_assert(upper_bound_is_available(j)); + lp_assert(m_bp.upper_bound_is_available(j)); return m_bp.get_upper_bound(j); } const impq & lb(unsigned j) const { - lp_assert(lower_bound_is_available(j)); + lp_assert(m_bp.lower_bound_is_available(j)); return m_bp.get_lower_bound(j); } @@ -305,9 +283,8 @@ private: void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ - lar_solver* lar = & this->m_bp.lp(); unsigned row_index = this->m_row_index; - auto explain = [lar, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index]() { return explain_bound_on_var_on_coeff(lar, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; + auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index](lar_solver& s) { return explain_bound_on_var_on_coeff(s, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain ); } @@ -341,22 +318,22 @@ private: break; } } - static u_dependency* explain_bound_on_var_on_coeff(lar_solver* lar, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { + static u_dependency* explain_bound_on_var_on_coeff(lar_solver& lar, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; if (tv::is_term(bound_j)) - bound_j = lar->map_term_index_to_column_index(bound_j); + bound_j = lar.map_term_index_to_column_index(bound_j); u_dependency* ret = nullptr; - for (auto const& r : lar->get_row(row_index)) { + for (auto const& r : lar.get_row(row_index)) { unsigned j = r.var(); if (j == bound_j) continue; mpq const& a = r.coeff(); int a_sign = is_pos(a) ? 1 : -1; int sign = j_sign * a_sign; - u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j); - ret = lar->join_deps(ret, witness); + u_dependency* witness = sign > 0 ? lar.get_column_upper_bound_witness(j) : lar.get_column_lower_bound_witness(j); + ret = lar.join_deps(ret, witness); } return ret; } diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 2e7fcbbcb..0df1f4f60 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -20,18 +20,24 @@ Revision History: #pragma once #include "math/lp/lp_settings.h" #include "math/lp/lar_constraints.h" +#include "math/lp/lar_solver.h" namespace lp { class implied_bound { public: mpq m_bound; - unsigned m_j; // the column for which the bound has been found + // It is either the column for which the bound has been found, or, + // in the case the column was created as + // the slack variable to a term, it is the term index. + // It is the same index that was returned by lar_solver::add_var(), or + // by lar_solver::add_term() + unsigned m_j; bool m_is_lower_bound; bool m_strict; private: - std::function m_explain_bound = nullptr; + std::function m_explain_bound = nullptr; public: - u_dependency* explain() const { return m_explain_bound(); } - void set_explain(std::function f) { m_explain_bound = f; } + u_dependency* explain(lar_solver& s) const { return m_explain_bound(s); } + void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) @@ -43,7 +49,7 @@ class implied_bound { unsigned j, bool is_lower_bound, bool is_strict, - std::function get_dep): + std::function get_dep): m_bound(a), m_j(j), m_is_lower_bound(is_lower_bound), diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 7d16c7b63..50e9de7a3 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -311,7 +311,7 @@ class lar_solver : public column_namer { template void explain_implied_bound(const implied_bound& ib, lp_bound_propagator& bp) { - u_dependency* dep = ib.explain(); + u_dependency* dep = ib.explain(*this); for (auto ci : flatten(dep)) bp.consume(mpq(1), ci); // TODO: flatten should provid the coefficients /* diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 7ee647200..c67e99651 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -39,8 +39,28 @@ class lp_bound_propagator { } return x != UINT_MAX; } - - +public: + bool upper_bound_is_available(unsigned j) const { + switch (get_column_type(j)) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return true; + default: + return false; + } + } + bool lower_bound_is_available(unsigned j) const { + switch (get_column_type(j)) { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return true; + default: + return false; + } + } +private: void try_add_equation_with_internal_fixed_tables(unsigned r1) { unsigned v1, v2; if (!only_one_nfixed(r1, v1)) @@ -119,11 +139,11 @@ class lp_bound_propagator { } void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - add_lower_bound_monic(monic_var, mpq(0), false, [&](){return lp().get_bound_constraint_witnesses_for_column(zero_var);}); - add_upper_bound_monic(monic_var, mpq(0), false, [&](){return lp().get_bound_constraint_witnesses_for_column(zero_var);}); + add_lower_bound_monic(monic_var, mpq(0), false, [zero_var](lar_solver& s){return s.get_bound_constraint_witnesses_for_column(zero_var);}); + add_upper_bound_monic(monic_var, mpq(0), false, [zero_var](lar_solver& s){return s.get_bound_constraint_witnesses_for_column(zero_var);}); } - void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { + void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { unsigned k; if (!try_get_value(m_improved_lower_bounds, monic_var, k)) { m_improved_lower_bounds[monic_var] = m_ibounds.size(); @@ -137,7 +157,7 @@ class lp_bound_propagator { } } - void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { + void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { unsigned k; if (!try_get_value(m_improved_upper_bounds, monic_var, k)) { m_improved_upper_bounds[monic_var] = m_ibounds.size(); @@ -160,47 +180,54 @@ class lp_bound_propagator { if (zero_var != null_lpvar) { add_bounds_for_zero_var(monic_var, zero_var); } else { - if (non_fixed != null_lpvar && get_column_type(non_fixed) == column_type::free_column) return; rational k = rational(1); for (auto v : vars) if (v != non_fixed) { k *= lp().get_column_value(v).x; if (k.is_big()) return; } - u_dependency* dep; - lp::mpq bound_value; + lp::impq bound_value; bool is_strict; if (non_fixed != null_lpvar) { - if (this->lp().has_lower_bound(non_fixed, dep, bound_value, is_strict)) { + if (lower_bound_is_available(non_fixed)) { + bound_value = lp().column_lower_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value, is_strict, [&]() { return dep; }); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , [non_fixed](lar_solver& s) { return s.get_column_lower_bound_witness(non_fixed); }); else - add_upper_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_lower_bound_witness(non_fixed);}); } - if (this->lp().has_upper_bound(non_fixed, dep, bound_value, is_strict)) { + if (upper_bound_is_available(non_fixed)) { + bound_value = lp().column_upper_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_upper_bound_witness(non_fixed);}); else - add_upper_bound_monic(monic_var, k * bound_value, is_strict, [&]() {return dep;}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_upper_bound_witness(non_fixed);}); } - if (this->lp().has_lower_bound(monic_var, dep, bound_value, is_strict)) { + if (lower_bound_is_available(monic_var)) { + bound_value = lp().column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver& s) {return s.get_column_lower_bound_witness(monic_var);}); else - add_upper_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver & s) {return s.get_column_lower_bound_witness(monic_var);}); } - if (this->lp().has_upper_bound(monic_var, dep, bound_value, is_strict)) { + if (upper_bound_is_available(monic_var)) { + bound_value = lp().column_upper_bound(monic_var); + is_strict = !bound_value.y.is_zero(); if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver& s) {return s.get_column_upper_bound_witness(monic_var);}); else - add_upper_bound_monic(non_fixed, bound_value / k, is_strict, [&]() {return dep;}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver & s) {return s.get_column_upper_bound_witness(monic_var);}); } + } else { // all variables are fixed - add_lower_bound_monic(monic_var, k, false, [&](){return lp().get_bound_constraint_witnesses_for_columns(vars);}); - add_upper_bound_monic(monic_var, k, false, [&](){return lp().get_bound_constraint_witnesses_for_columns(vars);}); + add_lower_bound_monic(monic_var, k, false, [vars](lar_solver& s){return s.get_bound_constraint_witnesses_for_columns(vars);}); + add_upper_bound_monic(monic_var, k, false, [vars](lar_solver& s){return s.get_bound_constraint_witnesses_for_columns(vars);}); } } } @@ -233,7 +260,7 @@ class lp_bound_propagator { return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero(); } - void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { + void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { j = m_imp.lp().column_to_reported_index(j); lconstraint_kind kind = is_low ? GE : LE; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index e4bec83cc..346fbb7a9 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - be.explain(); + be.explain(lp()); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); From 50d76a2fe3373bf840fd2b1ec727bf1aab6ee44c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Sep 2023 16:03:56 -0700 Subject: [PATCH 136/428] fix #6894 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_context_eqs.cpp | 67 +++++++++++++++++++---- src/ast/simplifiers/solve_context_eqs.h | 4 +- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index b56802caf..31696dc54 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -243,8 +243,8 @@ namespace euf { void solve_context_eqs::collect_nested_equalities(dependent_expr const& df, expr_mark& visited, dep_eq_vector& eqs) { - svector> todo; - todo.push_back({ false, 0, df.fml()}); + svector> todo; + todo.push_back({ false, 0, df.fml(), 0}); // even depth is conjunctive context, odd is disjunctive // when alternating between conjunctive and disjunctive context, increment depth. @@ -255,37 +255,84 @@ namespace euf { return (0 == depth % 2) ? depth : depth + 1; }; - while (!todo.empty()) { - auto [s, depth, f] = todo.back(); - todo.pop_back(); + for (unsigned i = 0; i < todo.size(); ++i) { + auto [s, depth, f, p] = todo[i]; if (visited.is_marked(f)) continue; visited.mark(f, true); if (s && m.is_and(f)) { for (auto* arg : *to_app(f)) - todo.push_back({ s, inc_or(depth), arg }); + todo.push_back({ s, inc_or(depth), arg, i }); } else if (!s && m.is_or(f)) { for (auto* arg : *to_app(f)) - todo.push_back({ s, inc_or(depth), arg }); + todo.push_back({ s, inc_or(depth), arg, i }); } if (!s && m.is_and(f)) { for (auto* arg : *to_app(f)) - todo.push_back({ s, inc_and(depth), arg }); + todo.push_back({ s, inc_and(depth), arg, i }); } else if (s && m.is_or(f)) { for (auto* arg : *to_app(f)) - todo.push_back({ s, inc_and(depth), arg }); + todo.push_back({ s, inc_and(depth), arg, i }); } else if (m.is_not(f, f)) - todo.push_back({ !s, depth, f }); + todo.push_back({ !s, depth, f, i }); else if (!s && 1 <= depth) { + unsigned sz = eqs.size(); for (extract_eq* ex : m_solve_eqs.m_extract_plugins) { ex->set_allow_booleans(false); ex->get_eqs(dependent_expr(m, f, nullptr, df.dep()), eqs); ex->set_allow_booleans(true); } + // prune eqs for solutions that are not safe in df.fml() + for (; sz < eqs.size(); ++sz) { + if (!is_safe_var(eqs[sz].var, i, df.fml(), todo)) { + eqs[sz] = eqs.back(); + --sz; + eqs.pop_back(); + } + } } } } + + bool solve_context_eqs::is_safe_var(expr* x, unsigned i, expr* f, svector> const& todo) { + m_contains_v.reset(); + m_todo.push_back(f); + mark_occurs(m_todo, x, m_contains_v); + SASSERT(m_todo.empty()); + + auto is_parent = [&](unsigned p, unsigned i) { + while (p != i && i != 0) { + auto [_s,_depth, _f, _p] = todo[i]; + i = _p; + } + return p == i; + }; + + // retrieve oldest parent of i whose sign is false + unsigned pi = i; + while (pi != 0) { + auto [s, depth, f, p] = todo[pi]; + if (s) + break; + pi = p; + } + + // determine if j and j have common conjunctive parent + // for every j in todo. + for (unsigned j = 0; j < todo.size(); ++j) { + auto [s, depth, f, p] = todo[j]; + if (i == j || !m_contains_v.is_marked(f)) + continue; + if (is_parent(j, i)) // j is a parent if i + continue; + if (is_parent(pi, j)) // pi is a parent of j + continue; + return false; + } + return true; + } + } diff --git a/src/ast/simplifiers/solve_context_eqs.h b/src/ast/simplifiers/solve_context_eqs.h index 8332d3a73..a11a1043b 100644 --- a/src/ast/simplifiers/solve_context_eqs.h +++ b/src/ast/simplifiers/solve_context_eqs.h @@ -45,7 +45,9 @@ namespace euf { bool contains_conjunctively(expr* f, bool sign, expr* e, signed_expressions& conjuncts); bool is_conjunction(bool sign, expr* f) const; - void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); + void collect_nested_equalities(dependent_expr const& f, expr_mark& visited, dep_eq_vector& eqs); + + bool is_safe_var(expr* x, unsigned i, expr* f, svector> const& todo); public: From b87a91379ce6ac5d71dc5b46805e0df77b90219c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 14 Sep 2023 17:10:53 -0700 Subject: [PATCH 137/428] fix #6894 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifiers/solve_context_eqs.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/simplifiers/solve_context_eqs.cpp b/src/ast/simplifiers/solve_context_eqs.cpp index 31696dc54..c58786f0e 100644 --- a/src/ast/simplifiers/solve_context_eqs.cpp +++ b/src/ast/simplifiers/solve_context_eqs.cpp @@ -311,11 +311,12 @@ namespace euf { return p == i; }; - // retrieve oldest parent of i whose sign is false + // retrieve oldest parent of i within the same alternation of and unsigned pi = i; + auto [_s, _depth, _f, _p] = todo[i]; while (pi != 0) { auto [s, depth, f, p] = todo[pi]; - if (s) + if (depth != _depth) break; pi = p; } From b3673d491e45e3319e155979630f9b74a1cccf11 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 14 Sep 2023 19:20:47 -0700 Subject: [PATCH 138/428] fix the build for gcc --- src/math/lp/bound_analyzer_on_row.h | 7 ++-- src/math/lp/implied_bound.h | 9 ++--- src/math/lp/lar_constraints.h | 4 +- src/math/lp/lar_solver.h | 2 +- src/math/lp/lp_bound_propagator.h | 58 ++++++++++++++--------------- src/sat/smt/arith_solver.cpp | 2 +- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 5af864220..fa29d6201 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -284,8 +284,8 @@ private: void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ unsigned row_index = this->m_row_index; - auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index](lar_solver& s) { return explain_bound_on_var_on_coeff(s, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; - m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain ); + auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index](int * s) { return explain_bound_on_var_on_coeff((B*)s, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; + m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain); } void advance_u(unsigned j) { @@ -318,7 +318,8 @@ private: break; } } - static u_dependency* explain_bound_on_var_on_coeff(lar_solver& lar, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { + static u_dependency* explain_bound_on_var_on_coeff(B* bp, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { + auto& lar = bp->lp(); int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 0df1f4f60..04f55f867 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -20,7 +20,6 @@ Revision History: #pragma once #include "math/lp/lp_settings.h" #include "math/lp/lar_constraints.h" -#include "math/lp/lar_solver.h" namespace lp { class implied_bound { public: @@ -34,10 +33,10 @@ class implied_bound { bool m_is_lower_bound; bool m_strict; private: - std::function m_explain_bound = nullptr; + std::function m_explain_bound = nullptr; public: - u_dependency* explain(lar_solver& s) const { return m_explain_bound(s); } - void set_explain(std::function f) { m_explain_bound = f; } + u_dependency* explain(int * s) const { return m_explain_bound(s); } + void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) @@ -49,7 +48,7 @@ class implied_bound { unsigned j, bool is_lower_bound, bool is_strict, - std::function get_dep): + std::function get_dep): m_bound(a), m_j(j), m_is_lower_bound(is_lower_bound), diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 0a16353c1..b5e679e7e 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -137,8 +137,8 @@ class constraint_set { public: constraint_set(u_dependency_manager& d, column_namer& cn): - m_dep_manager(d), - m_namer(cn) {} + m_namer(cn), + m_dep_manager(d) {} ~constraint_set() { for (auto* c : m_constraints) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 50e9de7a3..e2023aeaa 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -311,7 +311,7 @@ class lar_solver : public column_namer { template void explain_implied_bound(const implied_bound& ib, lp_bound_propagator& bp) { - u_dependency* dep = ib.explain(*this); + u_dependency* dep = ib.explain((int*)&bp); for (auto ci : flatten(dep)) bp.consume(mpq(1), ci); // TODO: flatten should provid the coefficients /* diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index c67e99651..d48d9b50c 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -40,6 +40,8 @@ class lp_bound_propagator { return x != UINT_MAX; } public: + const lar_solver& lp() const { return m_imp.lp(); } + lar_solver& lp() { return m_imp.lp(); } bool upper_bound_is_available(unsigned j) const { switch (get_column_type(j)) { case column_type::fixed: @@ -138,12 +140,13 @@ private: return n_of_non_fixed <= 1 && !big_bound; } + void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - add_lower_bound_monic(monic_var, mpq(0), false, [zero_var](lar_solver& s){return s.get_bound_constraint_witnesses_for_column(zero_var);}); - add_upper_bound_monic(monic_var, mpq(0), false, [zero_var](lar_solver& s){return s.get_bound_constraint_witnesses_for_column(zero_var);}); + add_lower_bound_monic(monic_var, mpq(0), false, [zero_var](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var);}); + add_upper_bound_monic(monic_var, mpq(0), false, [zero_var](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var);}); } - void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { + void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { unsigned k; if (!try_get_value(m_improved_lower_bounds, monic_var, k)) { m_improved_lower_bounds[monic_var] = m_ibounds.size(); @@ -152,12 +155,12 @@ private: auto& found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(v, monic_var, true, is_strict, explain_dep); - TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } } - void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { + void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { unsigned k; if (!try_get_value(m_improved_upper_bounds, monic_var, k)) { m_improved_upper_bounds[monic_var] = m_ibounds.size(); @@ -166,7 +169,7 @@ private: auto& found_bound = m_ibounds[k]; if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(bound_val, monic_var, false, is_strict, explain_bound); - TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } } @@ -193,66 +196,63 @@ private: bound_value = lp().column_lower_bound(non_fixed); is_strict = !bound_value.y.is_zero(); if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , [non_fixed](lar_solver& s) { return s.get_column_lower_bound_witness(non_fixed); }); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , [non_fixed](int* s) { return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(non_fixed); }); else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_lower_bound_witness(non_fixed);}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(non_fixed);}); } if (upper_bound_is_available(non_fixed)) { bound_value = lp().column_upper_bound(non_fixed); is_strict = !bound_value.y.is_zero(); if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_upper_bound_witness(non_fixed);}); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(non_fixed);}); else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](lar_solver& s) {return s.get_column_upper_bound_witness(non_fixed);}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(non_fixed);}); } if (lower_bound_is_available(monic_var)) { bound_value = lp().column_lower_bound(monic_var); is_strict = !bound_value.y.is_zero(); if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver& s) {return s.get_column_lower_bound_witness(monic_var);}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(monic_var);}); else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver & s) {return s.get_column_lower_bound_witness(monic_var);}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(monic_var);}); } if (upper_bound_is_available(monic_var)) { bound_value = lp().column_upper_bound(monic_var); is_strict = !bound_value.y.is_zero(); if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver& s) {return s.get_column_upper_bound_witness(monic_var);}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(monic_var);}); else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](lar_solver & s) {return s.get_column_upper_bound_witness(monic_var);}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(monic_var);}); } } else { // all variables are fixed - add_lower_bound_monic(monic_var, k, false, [vars](lar_solver& s){return s.get_bound_constraint_witnesses_for_columns(vars);}); - add_upper_bound_monic(monic_var, k, false, [vars](lar_solver& s){return s.get_bound_constraint_witnesses_for_columns(vars);}); + add_lower_bound_monic(monic_var, k, false, [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}); + add_upper_bound_monic(monic_var, k, false, [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}); } } } - 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_column_types)[j]; } const impq& get_lower_bound(unsigned j) const { - return m_imp.lp().get_lower_bound(j); + return lp().get_lower_bound(j); } const mpq& get_lower_bound_rational(unsigned j) const { - return m_imp.lp().get_lower_bound(j).x; + return lp().get_lower_bound(j).x; } const impq& get_upper_bound(unsigned j) const { - return m_imp.lp().get_upper_bound(j); + return lp().get_upper_bound(j); } const mpq& get_upper_bound_rational(unsigned j) const { - return m_imp.lp().get_upper_bound(j).x; + return lp().get_upper_bound(j).x; } // require also the zero infinitesemal part @@ -260,8 +260,8 @@ private: return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero(); } - void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { - j = m_imp.lp().column_to_reported_index(j); + void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { + j = lp().column_to_reported_index(j); lconstraint_kind kind = is_low ? GE : LE; if (strict) @@ -277,12 +277,12 @@ private: found_bound.m_bound = v; found_bound.m_strict = strict; found_bound.set_explain(explain_bound); - TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { m_improved_lower_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); - TRACE("add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); + TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, k)) { @@ -291,12 +291,12 @@ private: found_bound.m_bound = v; found_bound.m_strict = strict; found_bound.set_explain(explain_bound); - TRACE("add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); + TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { m_improved_upper_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); - TRACE("add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); + TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 346fbb7a9..d2e8e5b39 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - be.explain(lp()); + be.explain((int*)&m_bp); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); From a55aa1a6489bb1db7442e0ceb7523169b4891a6d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 14 Sep 2023 19:29:48 -0700 Subject: [PATCH 139/428] add a comment --- src/math/lp/implied_bound.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 04f55f867..e6d2bb898 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -35,6 +35,7 @@ class implied_bound { private: std::function m_explain_bound = nullptr; public: + // s is expected to be the pointer to lp_bound_propagator. u_dependency* explain(int * s) const { return m_explain_bound(s); } void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { From 762ade2a798e127a96f939eb08052853f7f8ff1c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 15 Sep 2023 06:15:22 -0700 Subject: [PATCH 140/428] check m_unassigned_bounds in bound_is_interesting --- src/smt/theory_lra.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1a022e087..d88d54a69 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2239,6 +2239,9 @@ public: if (v == null_theory_var) return false; + if (m_unassigned_bounds[v] == 0) + return false; + if (should_refine_bounds()) return true; From 4cfba9787b5639cd0d2092071465d4d113693f4d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 15 Sep 2023 17:41:10 -0700 Subject: [PATCH 141/428] debug lp_bound_propagator --- src/math/lp/lp_bound_propagator.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index d48d9b50c..1c9dff84f 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -146,29 +146,31 @@ private: add_upper_bound_monic(monic_var, mpq(0), false, [zero_var](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var);}); } - void add_lower_bound_monic(lpvar monic_var, const mpq& v, bool is_strict, std::function explain_dep) { + void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { unsigned k; - if (!try_get_value(m_improved_lower_bounds, monic_var, k)) { - m_improved_lower_bounds[monic_var] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(v, monic_var, true, is_strict, explain_dep)); + j = lp().column_to_reported_index(j); + if (!try_get_value(m_improved_lower_bounds, j, k)) { + m_improved_lower_bounds[j] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); } else { auto& found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = implied_bound(v, monic_var, true, is_strict, explain_dep); + found_bound = implied_bound(v, j, true, is_strict, explain_dep); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } } - void add_upper_bound_monic(lpvar monic_var, const mpq& bound_val, bool is_strict, std::function explain_bound) { + void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { + j = lp().column_to_reported_index(j); unsigned k; - if (!try_get_value(m_improved_upper_bounds, monic_var, k)) { - m_improved_upper_bounds[monic_var] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(bound_val, monic_var, false, is_strict, explain_bound)); + if (!try_get_value(m_improved_upper_bounds, j, k)) { + m_improved_upper_bounds[j] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); } else { auto& found_bound = m_ibounds[k]; if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = implied_bound(bound_val, monic_var, false, is_strict, explain_bound); + found_bound = implied_bound(bound_val, j, false, is_strict, explain_bound); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } From b621c9fa1c53151529d030b33820b4f35114aa10 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 15 Sep 2023 17:42:18 -0700 Subject: [PATCH 142/428] remove an extrac check in bound_is_interesting --- src/smt/theory_lra.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d88d54a69..1a022e087 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2239,9 +2239,6 @@ public: if (v == null_theory_var) return false; - if (m_unassigned_bounds[v] == 0) - return false; - if (should_refine_bounds()) return true; From c240f62ca86cc21a3d1d50a631d46c51019ec605 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 15 Sep 2023 17:44:10 -0700 Subject: [PATCH 143/428] is_linear does not check for is_big --- src/math/lp/lp_bound_propagator.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 1c9dff84f..61eeae325 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -121,7 +121,6 @@ private: bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) { zero_var = non_fixed = null_lpvar; unsigned n_of_non_fixed = 0; - bool big_bound = false; for (lpvar v : m) { if (!this->column_is_fixed(v)) { n_of_non_fixed++; @@ -133,11 +132,9 @@ private: zero_var = v; return true; } - if (b.is_big()) { - big_bound |= true; - } + } - return n_of_non_fixed <= 1 && !big_bound; + return n_of_non_fixed <= 1; } From 77e56b0a6953ff1e03063c600b9f5f447ffcfe1c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 16 Sep 2023 13:54:14 -0700 Subject: [PATCH 144/428] debug --- src/math/lp/bound_analyzer_on_row.h | 1 + src/math/lp/implied_bound.h | 2 +- src/math/lp/lar_solver.h | 4 ++-- src/math/lp/lp_bound_propagator.h | 10 +++++++--- src/sat/smt/arith_solver.cpp | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index fa29d6201..642ed9d1c 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -319,6 +319,7 @@ private: } } static u_dependency* explain_bound_on_var_on_coeff(B* bp, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { + TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << ", row_index = " << row_index << "\n";); auto& lar = bp->lp(); int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index e6d2bb898..66b3453d2 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -36,7 +36,7 @@ class implied_bound { std::function m_explain_bound = nullptr; public: // s is expected to be the pointer to lp_bound_propagator. - u_dependency* explain(int * s) const { return m_explain_bound(s); } + u_dependency* explain_implied(int * s) const { return m_explain_bound(s); } void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index e2023aeaa..9af24d567 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -311,9 +311,9 @@ class lar_solver : public column_namer { template void explain_implied_bound(const implied_bound& ib, lp_bound_propagator& bp) { - u_dependency* dep = ib.explain((int*)&bp); + u_dependency* dep = ib.explain_implied((int*)&bp); for (auto ci : flatten(dep)) - bp.consume(mpq(1), ci); // TODO: flatten should provid the coefficients + bp.consume(mpq(1), ci); // TODO: flatten should provide the coefficients /* if (ib.m_is_monic) { NOT_IMPLEMENTED_YET(); diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 61eeae325..c7b0d1f3f 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -137,14 +137,18 @@ private: return n_of_non_fixed <= 1; } - void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - add_lower_bound_monic(monic_var, mpq(0), false, [zero_var](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var);}); - add_upper_bound_monic(monic_var, mpq(0), false, [zero_var](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var);}); + auto lambda = [zero_var](int* s) { + return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var); + }; + TRACE("add_bound", lp().print_column_info(zero_var, tout) << std::endl;); + add_lower_bound_monic(monic_var, mpq(0), false, lambda); + add_upper_bound_monic(monic_var, mpq(0), false, lambda); } void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { unsigned k; + TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); j = lp().column_to_reported_index(j); if (!try_get_value(m_improved_lower_bounds, j, k)) { m_improved_lower_bounds[j] = m_ibounds.size(); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index d2e8e5b39..65329f9c4 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - be.explain((int*)&m_bp); + be.explain_implied((int*)&m_bp); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); From 7353d7fb4dd2730281ee1d1f9edd30f29c4795f8 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 17 Sep 2023 06:48:12 -0700 Subject: [PATCH 145/428] fix dep calculations in lp_bound_propagator --- src/math/lp/bound_analyzer_on_row.h | 3 +- src/math/lp/lp_bound_propagator.h | 61 ++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 642ed9d1c..4bad3c0bc 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -324,8 +324,7 @@ private: int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; - if (tv::is_term(bound_j)) - bound_j = lar.map_term_index_to_column_index(bound_j); + SASSERT(!tv::is_term(bound_j)); u_dependency* ret = nullptr; for (auto const& r : lar.get_row(row_index)) { unsigned j = r.var(); diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index c7b0d1f3f..e104459a3 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -198,42 +198,83 @@ private: if (lower_bound_is_available(non_fixed)) { bound_value = lp().column_lower_bound(non_fixed); is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , [non_fixed](int* s) { return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(non_fixed); }); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , lambda); else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(non_fixed);}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); } if (upper_bound_is_available(non_fixed)) { bound_value = lp().column_upper_bound(non_fixed); is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(non_fixed);}); + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, [non_fixed](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(non_fixed);}); + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); } if (lower_bound_is_available(monic_var)) { + auto lambda = [vars, monic_var, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; bound_value = lp().column_lower_bound(monic_var); is_strict = !bound_value.y.is_zero(); if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(monic_var);}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_lower_bound_witness(monic_var);}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); } if (upper_bound_is_available(monic_var)) { bound_value = lp().column_upper_bound(monic_var); is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, monic_var, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_upper_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(monic_var);}); + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, [monic_var](int* s) {return ((lp_bound_propagator*)s)->lp().get_column_upper_bound_witness(monic_var);}); + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); } } else { // all variables are fixed - add_lower_bound_monic(monic_var, k, false, [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}); - add_upper_bound_monic(monic_var, k, false, [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}); + auto lambda = [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}; + add_lower_bound_monic(monic_var, k, false, lambda); + add_upper_bound_monic(monic_var, k, false, lambda); } } } From 30b743d7b3278f7b5d2720240f950eb0d1fae266 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 17 Sep 2023 10:45:54 -0700 Subject: [PATCH 146/428] refactor propagat_monic --- src/math/lp/lp_bound_propagator.h | 183 ++++++++++++++++-------------- 1 file changed, 96 insertions(+), 87 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index e104459a3..13e6bbb9b 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -178,105 +178,114 @@ private: } void propagate_monic(lpvar monic_var, const svector& vars) { - lpvar non_fixed, zero_var; - if (!is_linear(vars, zero_var, non_fixed)) { + lpvar non_fixed, zero_var; + if (!is_linear(vars, zero_var, non_fixed)) { return; - } + } - if (zero_var != null_lpvar) { + if (zero_var != null_lpvar) { add_bounds_for_zero_var(monic_var, zero_var); - } else { + } else { rational k = rational(1); for (auto v : vars) if (v != non_fixed) { k *= lp().get_column_value(v).x; if (k.is_big()) return; } - lp::impq bound_value; - bool is_strict; + if (non_fixed != null_lpvar) { - if (lower_bound_is_available(non_fixed)) { - bound_value = lp().column_lower_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_lower_bound_witness(non_fixed); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict , lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - if (upper_bound_is_available(non_fixed)) { - bound_value = lp().column_upper_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - bound_value = lp().column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - - if (upper_bound_is_available(monic_var)) { - bound_value = lp().column_upper_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_upper_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - - + propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); } else { // all variables are fixed - auto lambda = [vars](int* s){return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars);}; - add_lower_bound_monic(monic_var, k, false, lambda); - add_upper_bound_monic(monic_var, k, false, lambda); + propagate_monic_with_all_fixed(monic_var, vars, k); } - } + } + } + + void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, rational k) { + lp::impq bound_value; + bool is_strict; + + if (lower_bound_is_available(non_fixed)) { + bound_value = lp().column_lower_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + if (k.is_pos()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (upper_bound_is_available(non_fixed)) { + bound_value = lp().column_upper_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (lower_bound_is_available(monic_var)) { + auto lambda = [vars, monic_var, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + bound_value = lp().column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } + + if (upper_bound_is_available(monic_var)) { + bound_value = lp().column_upper_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, monic_var, non_fixed](int* s) { + auto& l = ((lp_bound_propagator*)s)->lp(); + u_dependency* dep = l.get_column_upper_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } + } + + void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, rational k) { + auto lambda = [vars](int* s) { return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars); }; + add_lower_bound_monic(monic_var, k, false, lambda); + add_upper_bound_monic(monic_var, k, false, lambda); } column_type get_column_type(unsigned j) const { From 66f6a0327f5149d7fa7bf3fc42415e4a5c5f3d75 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 17 Sep 2023 11:00:48 -0700 Subject: [PATCH 147/428] change type of m_ibounds to std::vector --- src/math/lp/lp_bound_propagator.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 13e6bbb9b..764ec4a52 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -18,7 +18,7 @@ class lp_bound_propagator { std::unordered_map m_improved_upper_bounds; T& m_imp; - vector m_ibounds; + std::vector m_ibounds; map, default_eq> m_val2fixed_row; // works for rows of the form x + y + sum of fixed = 0 @@ -109,12 +109,12 @@ private: public: lp_bound_propagator(T& imp) : m_imp(imp) {} - const vector& ibounds() const { return m_ibounds; } + const std::vector& ibounds() const { return m_ibounds; } void init() { m_improved_upper_bounds.clear(); m_improved_lower_bounds.clear(); - m_ibounds.reset(); + m_ibounds.clear(); m_column_types = &lp().get_column_types(); } @@ -151,7 +151,7 @@ private: TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); j = lp().column_to_reported_index(j); if (!try_get_value(m_improved_lower_bounds, j, k)) { - m_improved_lower_bounds[j] = m_ibounds.size(); + m_improved_lower_bounds[j] = static_cast(m_ibounds.size()); m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); } else { auto& found_bound = m_ibounds[k]; @@ -166,7 +166,7 @@ private: j = lp().column_to_reported_index(j); unsigned k; if (!try_get_value(m_improved_upper_bounds, j, k)) { - m_improved_upper_bounds[j] = m_ibounds.size(); + m_improved_upper_bounds[j] = static_cast(m_ibounds.size()); m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); } else { auto& found_bound = m_ibounds[k]; @@ -333,7 +333,7 @@ private: TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { - m_improved_lower_bounds[j] = m_ibounds.size(); + m_improved_lower_bounds[j] = static_cast(m_ibounds.size()); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } @@ -347,7 +347,7 @@ private: TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { - m_improved_upper_bounds[j] = m_ibounds.size(); + m_improved_upper_bounds[j] = static_cast(m_ibounds.size()); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } From 10095a30b7b2d9a5a61f0815e96a8162fb8d8288 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 17 Sep 2023 12:25:11 -0700 Subject: [PATCH 148/428] add an include file --- src/math/lp/lp_bound_propagator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 764ec4a52..bbddd6357 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -6,9 +6,9 @@ */ #pragma once #include - #include "math/lp/lp_settings.h" #include "util/uint_set.h" +#include "math/lp/implied_bound.h" namespace lp { template class lp_bound_propagator { From af8d1923928bfc8e30e0c3d43258216ab163d03e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 17 Sep 2023 13:14:36 -0700 Subject: [PATCH 149/428] add an include --- src/math/lp/lp_bound_propagator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index bbddd6357..2c5c654c2 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -9,6 +9,7 @@ #include "math/lp/lp_settings.h" #include "util/uint_set.h" #include "math/lp/implied_bound.h" +#include namespace lp { template class lp_bound_propagator { From ff33fa355a33aeff4c559f63232325979f395b71 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 18 Sep 2023 09:44:37 +0100 Subject: [PATCH 150/428] fix debug single-thread build --- src/api/api_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 2b7a4ce43..53f53347e 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -157,6 +157,9 @@ namespace api { flush_objects(); for (auto& kv : m_allocated_objects) { api::object* val = kv.m_value; +#ifdef SINGLE_THREAD +# define m_concurrent_dec_ref false +#endif DEBUG_CODE(if (!m_concurrent_dec_ref) warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name());); dealloc(val); } From b1c52c0b16906f4d835261b672c80905b396456a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 18 Sep 2023 10:16:19 +0100 Subject: [PATCH 151/428] don't crash when a function doesn't have a model when converting a solver to string --- src/ast/converters/model_converter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ast/converters/model_converter.cpp b/src/ast/converters/model_converter.cpp index 716970cba..d053394ca 100644 --- a/src/ast/converters/model_converter.cpp +++ b/src/ast/converters/model_converter.cpp @@ -24,7 +24,8 @@ Notes: * Add or overwrite value in model. */ void model_converter::display_add(std::ostream& out, smt2_pp_environment& env, ast_manager& m, func_decl* f, expr* e) { - VERIFY(e); + if (!e) + return; VERIFY(f->get_range() == e->get_sort()); ast_smt2_pp_rev(out, f, e, env, params_ref(), 0, "model-add") << "\n"; } From 858477f3e3dcec52409846b95e291e5612dc41b8 Mon Sep 17 00:00:00 2001 From: John Fleisher Date: Mon, 18 Sep 2023 12:03:56 -0400 Subject: [PATCH 152/428] Add c++ flags for vulcan assembly compliance (#6906) --- scripts/mk_util.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 74d585dae..3d3996792 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2519,19 +2519,19 @@ def mk_config(): 'SLINK_FLAGS=/nologo /LDd\n' % static_opt) if VS_X64: config.write( - 'CXXFLAGS=/c %s /W3 /WX- /Od /Oy- /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /Gm- /RTC1 %s %s\n' % (CXXFLAGS, extra_opt, static_opt)) + 'CXXFLAGS=/c %s /Zi /W3 /WX- /Od /Oy- /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /Gm- /RTC1 %s %s\n' % (CXXFLAGS, extra_opt, static_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' - 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt)) + 'LINK_EXTRA_FLAGS=/link /PROFILE /DEBUG:full /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' + 'SLINK_EXTRA_FLAGS=/link /PROFILE /DEBUG:full /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt)) elif VS_ARM: print("ARM on VS is unsupported") exit(1) else: config.write( - 'CXXFLAGS=/c %s /W3 /WX- /Od /Oy- /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /Gm- /RTC1 /arch:SSE2 %s %s\n' % (CXXFLAGS, extra_opt, static_opt)) + 'CXXFLAGS=/c %s /Zi /W3 /WX- /Od /Oy- /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /Gm- /RTC1 /arch:SSE2 %s %s\n' % (CXXFLAGS, extra_opt, static_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' - 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt)) + 'LINK_EXTRA_FLAGS=/link /PROFILE /DEBUG:full /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' + 'SLINK_EXTRA_FLAGS=/link /PROFILE /DEBUG:full /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt)) else: # Windows Release mode LTCG=' /LTCG' if SLOW_OPTIMIZE else '' @@ -2544,19 +2544,19 @@ def mk_config(): extra_opt = '%s /D _TRACE ' % extra_opt if VS_X64: config.write( - 'CXXFLAGS=/c%s %s /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D NDEBUG /D _LIB /D UNICODE /Gm- /GF /Gy /TP %s %s\n' % (GL, CXXFLAGS, extra_opt, static_opt)) + 'CXXFLAGS=/c%s %s /Zi /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D NDEBUG /D _LIB /D UNICODE /Gm- /GF /Gy /TP %s %s\n' % (GL, CXXFLAGS, extra_opt, static_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:CONSOLE /STACK:8388608 %s\n' - 'SLINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:WINDOWS /STACK:8388608 %s\n' % (LTCG, link_extra_opt, LTCG, link_extra_opt)) + 'LINK_EXTRA_FLAGS=/link%s /PROFILE /DEBUG:full /profile /MACHINE:X64 /SUBSYSTEM:CONSOLE /STACK:8388608 %s\n' + 'SLINK_EXTRA_FLAGS=/link%s /PROFILE /DEBUG:full /profile /MACHINE:X64 /SUBSYSTEM:WINDOWS /STACK:8388608 %s\n' % (LTCG, link_extra_opt, LTCG, link_extra_opt)) elif VS_ARM: print("ARM on VS is unsupported") exit(1) else: config.write( - 'CXXFLAGS=/c%s %s /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D NDEBUG /D _CONSOLE /D ASYNC_COMMANDS /Gm- /arch:SSE2 %s %s\n' % (GL, CXXFLAGS, extra_opt, static_opt)) + 'CXXFLAGS=/c%s %s /Zi /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D NDEBUG /D _CONSOLE /D ASYNC_COMMANDS /Gm- /arch:SSE2 %s %s\n' % (GL, CXXFLAGS, extra_opt, static_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' - 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (LTCG, link_extra_opt, LTCG, maybe_disable_dynamic_base, link_extra_opt)) + 'LINK_EXTRA_FLAGS=/link%s /PROFILE /DEBUG:full /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' + 'SLINK_EXTRA_FLAGS=/link%s /PROFILE /DEBUG:full /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (LTCG, link_extra_opt, LTCG, maybe_disable_dynamic_base, link_extra_opt)) config.write('CFLAGS=$(CXXFLAGS)\n') From 31c91e167425989476829d36fe3f9329cadb84d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Sep 2023 12:52:26 -0700 Subject: [PATCH 153/428] #6902 add parse check for identifiers used for datatype declarations. --- src/parsers/smt2/smt2parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 5e22d9d29..98323816a 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -963,6 +963,7 @@ namespace smt2 { unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); symbol dt_name = curr_id(); + check_identifier("unexpected token used as datatype name"); next(); m_dt_name2idx.reset(); From 643512613ab696b021892907a4ed0432f020a513 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Sep 2023 12:52:50 -0700 Subject: [PATCH 154/428] simplify last_index function --- src/ast/rewriter/seq_rewriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 24df51845..90ba5bb3a 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1716,6 +1716,10 @@ br_status seq_rewriter::mk_seq_last_index(expr* a, expr* b, expr_ref& result) { result = m_autil.mk_numeral(rational(idx), true); return BR_DONE; } + if (a == b) { + result = m_autil.mk_int(0); + return BR_DONE; + } return BR_FAILED; } From cf63e7589887a4e2b6ed02b8bd8088b923a6c7c9 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 18 Sep 2023 13:25:24 -0700 Subject: [PATCH 155/428] using structures from util in lp_bound_propagator Signed-off-by: Lev Nachmanson --- src/math/lp/lp_bound_propagator.h | 66 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 2c5c654c2..01fe42dff 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -12,14 +12,26 @@ #include namespace lp { template +struct my_allocator { + using value_type = T; + + T* allocate(std::size_t n) { + return static_cast(memory::allocate(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t n) { + memory::deallocate(p); + } +}; +template class lp_bound_propagator { uint_set m_visited_rows; // 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; + u_map m_improved_lower_bounds; + u_map m_improved_upper_bounds; T& m_imp; - std::vector m_ibounds; + std::vector> m_ibounds; map, default_eq> m_val2fixed_row; // works for rows of the form x + y + sum of fixed = 0 @@ -110,11 +122,11 @@ private: public: lp_bound_propagator(T& imp) : m_imp(imp) {} - const std::vector& ibounds() const { return m_ibounds; } + const std::vector>& ibounds() const { return m_ibounds; } void init() { - m_improved_upper_bounds.clear(); - m_improved_lower_bounds.clear(); + m_improved_upper_bounds.reset(); + m_improved_lower_bounds.reset(); m_ibounds.clear(); m_column_types = &lp().get_column_types(); } @@ -148,14 +160,14 @@ private: } void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { - unsigned k; TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); j = lp().column_to_reported_index(j); - if (!try_get_value(m_improved_lower_bounds, j, k)) { - m_improved_lower_bounds[j] = static_cast(m_ibounds.size()); + auto *e = m_improved_lower_bounds.find_core(j); + if (!e) { + m_improved_lower_bounds.insert(j,static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); } else { - auto& found_bound = m_ibounds[k]; + auto& found_bound = m_ibounds[e->get_data().m_value]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(v, j, true, is_strict, explain_dep); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); @@ -165,12 +177,12 @@ private: void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { j = lp().column_to_reported_index(j); - unsigned k; - if (!try_get_value(m_improved_upper_bounds, j, k)) { - m_improved_upper_bounds[j] = static_cast(m_ibounds.size()); + auto *e = m_improved_upper_bounds.find_core(j); + if (!e) { + m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); - } else { - auto& found_bound = m_ibounds[k]; + } else { + auto& found_bound = m_ibounds[e->get_data().m_value]; if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(bound_val, j, false, is_strict, explain_bound); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); @@ -202,7 +214,7 @@ private: } } - void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, rational k) { + void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { lp::impq bound_value; bool is_strict; @@ -283,7 +295,7 @@ private: } } - void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, rational k) { + void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { auto lambda = [vars](int* s) { return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars); }; add_lower_bound_monic(monic_var, k, false, lambda); add_upper_bound_monic(monic_var, k, false, lambda); @@ -323,10 +335,10 @@ private: if (!m_imp.bound_is_interesting(j, kind, v)) return; - 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 *e = m_improved_lower_bounds.find_core(j); + if (e) { + auto& found_bound = m_ibounds[e->get_data().m_value]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { found_bound.m_bound = v; found_bound.m_strict = strict; @@ -334,13 +346,14 @@ private: TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { - m_improved_lower_bounds[j] = static_cast(m_ibounds.size()); + m_improved_lower_bounds.insert(j, static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case - if (try_get_value(m_improved_upper_bounds, j, k)) { - auto& found_bound = m_ibounds[k]; + auto *e = m_improved_upper_bounds.find_core(j); + if (e) { + auto& found_bound = m_ibounds[e->get_data().m_value]; if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { found_bound.m_bound = v; found_bound.m_strict = strict; @@ -348,7 +361,7 @@ private: TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); } } else { - m_improved_upper_bounds[j] = static_cast(m_ibounds.size()); + m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } @@ -578,11 +591,12 @@ private: lp_assert(y_sign == 1 || y_sign == -1); auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg; const auto& v = val(x); - unsigned found_i; - if (!table.find(v, found_i)) { + auto * e = table.find_core(v); + if (!e) { table.insert(v, i); } else { explanation ex; + unsigned found_i = e->get_data().m_value; unsigned base_of_found = lp().get_base_column_in_row(found_i); if (is_int(x) != is_int(base_of_found) || ival(x).y != ival(base_of_found).y) continue; From 3b78c6318cc803dc397467bdf43694fef34b1885 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:48:16 -0700 Subject: [PATCH 156/428] Bump docker/build-push-action from 4.2.1 to 5.0.0 (#6909) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.2.1 to 5.0.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4.2.1...v5.0.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index bf8138bec..41e47c7de 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.2.1 + uses: docker/build-push-action@v5.0.0 with: context: . push: true From 17a38b7ae0bff5010ff4cf7c69ac94bdc4b916a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:48:35 -0700 Subject: [PATCH 157/428] Bump docker/metadata-action from 4 to 5 (#6910) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 41e47c7de..67c728b0f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -29,7 +29,7 @@ jobs: # ------- - name: Extract metadata (tags, labels) for Bare Z3 Docker Image id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ghcr.io/z3prover/z3 flavor: | From 345d6ec8a5ec7bfb48ca5b2f20c736440a322c39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:49:00 -0700 Subject: [PATCH 158/428] Bump docker/login-action from 2 to 3 (#6911) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 67c728b0f..82e0c4c0b 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Log in to GitHub Docker registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.DOCKER_USERNAME }} From c5cfd62e0a0858f3c37df5e9f325593983402960 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 19 Sep 2023 10:56:09 -0700 Subject: [PATCH 159/428] remove dead code related to nla unit propagation Signed-off-by: Lev Nachmanson --- src/math/lp/lp_bound_propagator.h | 30 +++++++++---------- src/math/lp/monomial_bounds.cpp | 48 ------------------------------- src/math/lp/monomial_bounds.h | 4 +-- src/math/lp/nla_core.cpp | 11 ------- src/math/lp/nla_solver.cpp | 4 --- src/smt/theory_lra.cpp | 14 --------- 6 files changed, 16 insertions(+), 95 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 01fe42dff..6a01d4ace 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -162,12 +162,12 @@ private: void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); j = lp().column_to_reported_index(j); - auto *e = m_improved_lower_bounds.find_core(j); - if (!e) { + unsigned k; + if (!m_improved_lower_bounds.find(j, k)) { m_improved_lower_bounds.insert(j,static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); } else { - auto& found_bound = m_ibounds[e->get_data().m_value]; + auto& found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(v, j, true, is_strict, explain_dep); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); @@ -177,12 +177,12 @@ private: void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { j = lp().column_to_reported_index(j); - auto *e = m_improved_upper_bounds.find_core(j); - if (!e) { + unsigned k; + if (!m_improved_upper_bounds.find(j, k)) { m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); } else { - auto& found_bound = m_ibounds[e->get_data().m_value]; + auto& found_bound = m_ibounds[k]; if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(bound_val, j, false, is_strict, explain_bound); TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); @@ -336,9 +336,9 @@ private: if (!m_imp.bound_is_interesting(j, kind, v)) return; if (is_low) { - auto *e = m_improved_lower_bounds.find_core(j); - if (e) { - auto& found_bound = m_ibounds[e->get_data().m_value]; + unsigned k; + if (m_improved_lower_bounds.find(j, 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.m_bound = v; found_bound.m_strict = strict; @@ -351,9 +351,9 @@ private: TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case - auto *e = m_improved_upper_bounds.find_core(j); - if (e) { - auto& found_bound = m_ibounds[e->get_data().m_value]; + unsigned k; + if (m_improved_upper_bounds.find(j, 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.m_bound = v; found_bound.m_strict = strict; @@ -591,12 +591,12 @@ private: lp_assert(y_sign == 1 || y_sign == -1); auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg; const auto& v = val(x); - auto * e = table.find_core(v); - if (!e) { + unsigned found_i;; + + if (!table.find(v, found_i)) { table.insert(v, i); } else { explanation ex; - unsigned found_i = e->get_data().m_value; unsigned base_of_found = lp().get_base_column_in_row(found_i); if (is_int(x) != is_int(base_of_found) || ival(x).y != ival(base_of_found).y) continue; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index ad2b20c07..7d2dc5ce6 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -24,7 +24,6 @@ namespace nla { } } - bool monomial_bounds::is_too_big(mpq const& q) const { return rational(q).bitsize() > 256; } @@ -258,53 +257,6 @@ namespace nla { } } - void monomial_bounds::unit_propagate() { - for (lpvar v : c().m_monics_with_changed_bounds) - unit_propagate(c().emons()[v]); - c().m_monics_with_changed_bounds.clear(); - } - - void monomial_bounds::unit_propagate(monic const& m) { - m_propagated.reserve(m.var() + 1, false); - if (m_propagated[m.var()]) - return; - - lpvar non_fixed = null_lpvar, zero_var = null_lpvar; - if (!is_linear(m, zero_var, non_fixed)) - return; - - c().trail().push(set_bitvector_trail(m_propagated, m.var())); - - - if (zero_var != null_lpvar) { - new_lemma lemma(c(), "fixed-values"); - lemma.explain_fixed(zero_var); - lemma += ineq(m.var(), lp::lconstraint_kind::EQ, 0); - } - else { - rational k = rational(1); - for (auto v : m) - if (v != non_fixed) { - k *= c().lra.get_column_value(v).x; - if (k.is_big()) return; - } - - new_lemma lemma(c(), "fixed-values"); - - for (auto v : m) - if (v != non_fixed) - lemma.explain_fixed(v); - - if (non_fixed != null_lpvar) { - lp::lar_term term; - term.add_var(m.var()); - term.add_monomial(-k, non_fixed); - lemma += ineq(term, lp::lconstraint_kind::EQ, 0); - } else { - lemma += ineq(m.var(), lp::lconstraint_kind::EQ, k); - } - } - } // returns true iff (all variables are fixed, // or all but one variable are fixed) and the bounds are not big, // or at least one variable is fixed to zero. diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 15ab6b992..6253af744 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -20,7 +20,6 @@ namespace nla { void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; - bool propagate_down(monic const& m, lpvar u); bool propagate_value(dep_interval& range, lpvar v); bool propagate_value(dep_interval& range, lpvar v, unsigned power); void compute_product(unsigned start, monic const& m, scoped_dep_interval& i); @@ -32,12 +31,11 @@ namespace nla { // monomial propagation bool_vector m_propagated; - void unit_propagate(monic const& m); bool is_linear(monic const& m, lpvar& zero_var, lpvar& non_fixed); public: monomial_bounds(core* core); void propagate(); - void unit_propagate(); + }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 20003f947..63dd29d9b 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1836,17 +1836,6 @@ bool core::improve_bounds() { } return bounds_improved; } - -void core::propagate(vector& lemmas) { - // propagate linear monomials, those that have all, or all but one, variables fixed - lemmas.reset(); - m_lemma_vec = &lemmas; - - m_monomial_bounds.unit_propagate(); - -} - - } // end of nla diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index b7197ff2f..8be918f01 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -46,10 +46,6 @@ namespace nla { return m_core->check(lits, lemmas); } - void solver::propagate(vector& lemmas) { - m_core->propagate(lemmas); - } - void solver::push(){ m_core->push(); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1a022e087..fa73788e6 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2159,20 +2159,6 @@ public: return true; } - void propagate_nla() { - if (!m_nla) - return; - m_nla->propagate(m_nla_lemma_vector); - if (lp().get_status() == lp::lp_status::INFEASIBLE) { - TRACE("arith", tout << "propagation conflict\n";); - get_infeasibility_explanation_and_set_conflict(); - } - else { - for (nla::lemma const& l : m_nla_lemma_vector) - false_case_of_check_nla(l); - } - } - bool should_propagate() const { return bound_prop_mode::BP_NONE != propagation_mode(); } From 85db8163fa6a9e9c99c15899a306d59e20be9b6c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2023 13:57:28 -0700 Subject: [PATCH 160/428] move allocator to memory_manager and std_vector to vector Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_bound_propagator.h | 23 ++++++----------------- src/math/lp/nla_monotone_lemmas.h | 2 +- src/util/memory_manager.h | 13 +++++++++++++ src/util/vector.h | 3 +++ 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 6a01d4ace..d035d9bcb 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -9,29 +9,18 @@ #include "math/lp/lp_settings.h" #include "util/uint_set.h" #include "math/lp/implied_bound.h" -#include +#include "util/vector.h" namespace lp { -template -struct my_allocator { - using value_type = T; - - T* allocate(std::size_t n) { - return static_cast(memory::allocate(n * sizeof(T))); - } - - void deallocate(T* p, std::size_t n) { - memory::deallocate(p); - } -}; + template class lp_bound_propagator { - uint_set m_visited_rows; + uint_set m_visited_rows; // these maps map a column index to the corresponding index in ibounds u_map m_improved_lower_bounds; u_map m_improved_upper_bounds; T& m_imp; - std::vector> m_ibounds; + std_vector m_ibounds; map, default_eq> m_val2fixed_row; // works for rows of the form x + y + sum of fixed = 0 @@ -119,10 +108,10 @@ private: ~reset_cheap_eq() { p.reset_cheap_eq_eh(); } }; - public: +public: lp_bound_propagator(T& imp) : m_imp(imp) {} - const std::vector>& ibounds() const { return m_ibounds; } + const std_vector& ibounds() const { return m_ibounds; } void init() { m_improved_upper_bounds.reset(); diff --git a/src/math/lp/nla_monotone_lemmas.h b/src/math/lp/nla_monotone_lemmas.h index d13f588e8..2cb646777 100644 --- a/src/math/lp/nla_monotone_lemmas.h +++ b/src/math/lp/nla_monotone_lemmas.h @@ -16,7 +16,7 @@ private: void monotonicity_lemma(monic const& m); void monotonicity_lemma_gt(const monic& m); void monotonicity_lemma_lt(const monic& m); - std::vector get_sorted_key(const monic& rm) const; + // std_vector get_sorted_key(const monic& rm) const; vector> get_sorted_key_with_rvars(const monic& a) const; }; } diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 7dab520df..053816449 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -128,6 +128,19 @@ void dealloc_svect(T * ptr) { memory::deallocate(ptr); } +template +struct std_allocator { + using value_type = T; + + T* allocate(std::size_t n) { + return static_cast(memory::allocate(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t n) { + memory::deallocate(p); + } +}; + struct mem_stat { }; diff --git a/src/util/vector.h b/src/util/vector.h index 1cb25a8c4..9ca9a1103 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -33,6 +33,7 @@ Revision History: #include "util/memory_manager.h" #include "util/hash.h" #include "util/z3_exception.h" +#include // disable warning for constant 'if' expressions. // these are used heavily in templates. @@ -40,6 +41,8 @@ Revision History: #pragma warning(disable:4127) #endif +template +class std_vector : public std::vector> {}; #if 0 From 4d742001ab4d871e2929224d4f7649bd20d20667 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2023 14:36:21 -0700 Subject: [PATCH 161/428] formatting of else Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index fa73788e6..9357b7a65 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2176,7 +2176,8 @@ public: if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); // verbose_stream() << "unsat\n"; - } else { + } + else { for (auto &ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) From fba5de3a25afc762e3c060c660b55bb334a675f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2023 14:42:55 -0700 Subject: [PATCH 162/428] remove unused code Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_monotone_lemmas.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/math/lp/nla_monotone_lemmas.h b/src/math/lp/nla_monotone_lemmas.h index d13f588e8..fb9c469a8 100644 --- a/src/math/lp/nla_monotone_lemmas.h +++ b/src/math/lp/nla_monotone_lemmas.h @@ -7,16 +7,14 @@ --*/ #pragma once namespace nla { -class core; -class monotone : common { -public: - monotone(core *core); - void monotonicity_lemma(); -private: - void monotonicity_lemma(monic const& m); - void monotonicity_lemma_gt(const monic& m); - void monotonicity_lemma_lt(const monic& m); - std::vector get_sorted_key(const monic& rm) const; - vector> get_sorted_key_with_rvars(const monic& a) const; -}; + class core; + class monotone : common { + public: + monotone(core *core); + void monotonicity_lemma(); + private: + void monotonicity_lemma(monic const& m); + void monotonicity_lemma_gt(const monic& m); + void monotonicity_lemma_lt(const monic& m); + }; } From f07553ed3a2ca03c69b0a60b13671a579b7313b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2023 15:18:38 -0700 Subject: [PATCH 163/428] formatting updates Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_bound_propagator.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index d035d9bcb..8af04c793 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -181,13 +181,12 @@ public: void propagate_monic(lpvar monic_var, const svector& vars) { lpvar non_fixed, zero_var; - if (!is_linear(vars, zero_var, non_fixed)) { - return; - } + if (!is_linear(vars, zero_var, non_fixed)) + return; - if (zero_var != null_lpvar) { + if (zero_var != null_lpvar) add_bounds_for_zero_var(monic_var, zero_var); - } else { + else { rational k = rational(1); for (auto v : vars) if (v != non_fixed) { @@ -195,19 +194,18 @@ public: if (k.is_big()) return; } - if (non_fixed != null_lpvar) { + if (non_fixed != null_lpvar) propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); - } else { // all variables are fixed + else // all variables are fixed propagate_monic_with_all_fixed(monic_var, vars, k); - } } } void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { - lp::impq bound_value; - bool is_strict; + lp::impq bound_value; + bool is_strict; - if (lower_bound_is_available(non_fixed)) { + if (lower_bound_is_available(non_fixed)) { bound_value = lp().column_lower_bound(non_fixed); is_strict = !bound_value.y.is_zero(); auto lambda = [vars, non_fixed](int* s) { From d77c91b1aa1531703208d100f12c211c36b4c45c Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 19 Sep 2023 15:46:59 -0700 Subject: [PATCH 164/428] trying to get else on a new line with clang-formatter --- src/math/lp/.clang-format | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/math/lp/.clang-format b/src/math/lp/.clang-format index f5c8a41b2..d7f8d6171 100644 --- a/src/math/lp/.clang-format +++ b/src/math/lp/.clang-format @@ -1,4 +1,5 @@ BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 0 -NamespaceIndentation: All \ No newline at end of file +NamespaceIndentation: All +BreakBeforeBraces: Stroustrup \ No newline at end of file From 9648793206a6ddd6554af14080c0d1493e8d5c82 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 20 Sep 2023 14:11:38 -0700 Subject: [PATCH 165/428] add features to std_allocator --- src/util/memory_manager.h | 10 ++++++++++ src/util/vector.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 053816449..a50d03ac3 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -131,6 +131,10 @@ void dealloc_svect(T * ptr) { template struct std_allocator { using value_type = T; + // the constructors must be proveded according to cpp docs + std_allocator() = default; + template constexpr std_allocator(const std_allocator&) noexcept {} + T* allocate(std::size_t n) { return static_cast(memory::allocate(n * sizeof(T))); @@ -141,6 +145,12 @@ struct std_allocator { } }; +// the comparison operators must be proveded according to cpp docs +template +bool operator==(const std_allocator&, const std_allocator&) { return true; } +template +bool operator!=(const std_allocator&, const std_allocator&) { return false; } + struct mem_stat { }; diff --git a/src/util/vector.h b/src/util/vector.h index 9ca9a1103..55b52d745 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -42,7 +42,7 @@ Revision History: #endif template -class std_vector : public std::vector> {}; +using std_vector = std::vector>; #if 0 From 24512d5ec2b595c68fff96e2929f22683db83a5d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 20 Sep 2023 14:12:36 -0700 Subject: [PATCH 166/428] typo --- src/util/memory_manager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index a50d03ac3..af56c4507 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -131,7 +131,7 @@ void dealloc_svect(T * ptr) { template struct std_allocator { using value_type = T; - // the constructors must be proveded according to cpp docs + // the constructors must be provided according to cpp docs std_allocator() = default; template constexpr std_allocator(const std_allocator&) noexcept {} @@ -145,7 +145,7 @@ struct std_allocator { } }; -// the comparison operators must be proveded according to cpp docs +// the comparison operators must be provided according to cpp docs template bool operator==(const std_allocator&, const std_allocator&) { return true; } template From 7a74b099baa40326f5f954f3e42af1f517c5d3e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Sep 2023 15:04:24 -0700 Subject: [PATCH 167/428] remove experimental code Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_nl.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 77e2b25f3..f44516cad 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -511,7 +511,6 @@ bool theory_arith::propagate_nl_downward(expr * n, var_power_pair const& p) */ template bool theory_arith::propagate_nl_bounds(expr * m) { - return false; TRACE("non_linear", tout << "propagate several bounds using:\n"; display_monomial(tout, m); tout << "\n";); bool result = propagate_nl_upward(m); buffer vp; @@ -531,7 +530,6 @@ bool theory_arith::propagate_nl_bounds(expr * m) { */ template bool theory_arith::propagate_nl_bounds() { - return false; m_dep_manager.reset(); bool propagated = false; for (unsigned i = 0; i < m_nl_monomials.size(); i++) { @@ -1634,7 +1632,6 @@ bool theory_arith::is_cross_nested_consistent(row const & r) { */ template bool theory_arith::is_cross_nested_consistent(svector const & nl_cluster) { - return true; for (theory_var v : nl_cluster) { if (!is_base(v)) continue; From 615aad77795d7aaa35c9afb335f3c26563923c8a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Sep 2023 15:18:37 -0700 Subject: [PATCH 168/428] get rid of int*, use lambda scoping Signed-off-by: Nikolaj Bjorner --- src/math/lp/bound_analyzer_on_row.h | 4 +- src/math/lp/implied_bound.h | 12 ++-- src/math/lp/lar_solver.h | 2 +- src/math/lp/lp_bound_propagator.h | 91 ++++++++++++++--------------- src/sat/smt/arith_solver.cpp | 2 +- 5 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 4bad3c0bc..074b6e464 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -284,7 +284,9 @@ private: void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ unsigned row_index = this->m_row_index; - auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index](int * s) { return explain_bound_on_var_on_coeff((B*)s, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); }; + auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index,this]() { + return explain_bound_on_var_on_coeff((B*)&m_bp, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); + }; m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain); } diff --git a/src/math/lp/implied_bound.h b/src/math/lp/implied_bound.h index 66b3453d2..195ec0359 100644 --- a/src/math/lp/implied_bound.h +++ b/src/math/lp/implied_bound.h @@ -31,13 +31,13 @@ class implied_bound { // by lar_solver::add_term() unsigned m_j; bool m_is_lower_bound; - bool m_strict; + bool m_strict; private: - std::function m_explain_bound = nullptr; + std::function m_explain_bound = nullptr; public: // s is expected to be the pointer to lp_bound_propagator. - u_dependency* explain_implied(int * s) const { return m_explain_bound(s); } - void set_explain(std::function f) { m_explain_bound = f; } + u_dependency* explain_implied() const { return m_explain_bound(); } + void set_explain(std::function f) { m_explain_bound = f; } lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) @@ -48,8 +48,8 @@ class implied_bound { implied_bound(const mpq & a, unsigned j, bool is_lower_bound, - bool is_strict, - std::function get_dep): + bool is_strict, + std::function get_dep): m_bound(a), m_j(j), m_is_lower_bound(is_lower_bound), diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 9af24d567..9f878e363 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -311,7 +311,7 @@ class lar_solver : public column_namer { template void explain_implied_bound(const implied_bound& ib, lp_bound_propagator& bp) { - u_dependency* dep = ib.explain_implied((int*)&bp); + u_dependency* dep = ib.explain_implied(); for (auto ci : flatten(dep)) bp.consume(mpq(1), ci); // TODO: flatten should provide the coefficients /* diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 8af04c793..e912d4f23 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -140,22 +140,24 @@ public: } void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - auto lambda = [zero_var](int* s) { - return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_column(zero_var); + auto& lps = lp(); + auto lambda = [zero_var,&lps]() { + return lps.get_bound_constraint_witnesses_for_column(zero_var); }; TRACE("add_bound", lp().print_column_info(zero_var, tout) << std::endl;); add_lower_bound_monic(monic_var, mpq(0), false, lambda); add_upper_bound_monic(monic_var, mpq(0), false, lambda); } - void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { + void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); j = lp().column_to_reported_index(j); unsigned k; if (!m_improved_lower_bounds.find(j, k)) { m_improved_lower_bounds.insert(j,static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); - } else { + } + else { auto& found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(v, j, true, is_strict, explain_dep); @@ -164,13 +166,14 @@ public: } } - void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { + void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { j = lp().column_to_reported_index(j); unsigned k; if (!m_improved_upper_bounds.find(j, k)) { m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size())); m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); - } else { + } + else { auto& found_bound = m_ibounds[k]; if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { found_bound = implied_bound(bound_val, j, false, is_strict, explain_bound); @@ -204,18 +207,16 @@ public: void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { lp::impq bound_value; bool is_strict; + auto& lps = lp(); if (lower_bound_is_available(non_fixed)) { bound_value = lp().column_lower_bound(non_fixed); is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_lower_bound_witness(non_fixed); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } + auto lambda = [vars, non_fixed,&lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); return dep; }; if (k.is_pos()) @@ -227,14 +228,11 @@ public: if (upper_bound_is_available(non_fixed)) { bound_value = lp().column_upper_bound(non_fixed); is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } + auto lambda = [vars, non_fixed,&lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); return dep; }; if (k.is_neg()) @@ -244,33 +242,31 @@ public: } if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - bound_value = lp().column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + auto lambda = [vars, monic_var, non_fixed,&lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + bound_value = lp().column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); } - + if (upper_bound_is_available(monic_var)) { bound_value = lp().column_upper_bound(monic_var); is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed](int* s) { - auto& l = ((lp_bound_propagator*)s)->lp(); - u_dependency* dep = l.get_column_upper_bound_witness(monic_var); + auto lambda = [vars, monic_var, non_fixed,&lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); for (auto v : vars) { if (v != non_fixed) { - dep = l.join_deps(dep, l.get_bound_constraint_witnesses_for_column(v)); + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); } } return dep; @@ -283,9 +279,10 @@ public: } void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { - auto lambda = [vars](int* s) { return ((lp_bound_propagator*)s)->lp().get_bound_constraint_witnesses_for_columns(vars); }; - add_lower_bound_monic(monic_var, k, false, lambda); - add_upper_bound_monic(monic_var, k, false, lambda); + auto& lps = lp(); + auto lambda = [vars,&lps]() { return lps.get_bound_constraint_witnesses_for_columns(vars); }; + add_lower_bound_monic(monic_var, k, false, lambda); + add_upper_bound_monic(monic_var, k, false, lambda); } column_type get_column_type(unsigned j) const { @@ -313,7 +310,7 @@ public: return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero(); } - void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { + void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) { j = lp().column_to_reported_index(j); lconstraint_kind kind = is_low ? GE : LE; diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 65329f9c4..fd55fb7d7 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - be.explain_implied((int*)&m_bp); + be.explain_implied(); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); From f9de65a464265655557f7656a3a3f6c3c0e11d9d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Sep 2023 15:22:28 -0700 Subject: [PATCH 169/428] indetation Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_bound_propagator.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index e912d4f23..f6cc83825 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -183,27 +183,27 @@ public: } void propagate_monic(lpvar monic_var, const svector& vars) { - lpvar non_fixed, zero_var; - if (!is_linear(vars, zero_var, non_fixed)) - return; - - if (zero_var != null_lpvar) + lpvar non_fixed, zero_var; + if (!is_linear(vars, zero_var, non_fixed)) + return; + + if (zero_var != null_lpvar) add_bounds_for_zero_var(monic_var, zero_var); - else { + else { rational k = rational(1); for (auto v : vars) if (v != non_fixed) { k *= lp().get_column_value(v).x; if (k.is_big()) return; } - + if (non_fixed != null_lpvar) propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); else // all variables are fixed propagate_monic_with_all_fixed(monic_var, vars, k); - } + } } - + void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { lp::impq bound_value; bool is_strict; From 20c02f4f45c4d04545ff74780904e82ee5d8ae8f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 20 Sep 2023 17:02:35 -0700 Subject: [PATCH 170/428] fix a lambda bug Signed-off-by: Lev Nachmanson --- src/math/lp/bound_analyzer_on_row.h | 48 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 074b6e464..576f14599 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -281,15 +281,32 @@ private: // */ // } - - void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ + void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict) + { unsigned row_index = this->m_row_index; - auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index,this]() { - return explain_bound_on_var_on_coeff((B*)&m_bp, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index); + auto* lar = &m_bp.lp(); + auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index, lar]() { + TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << ", row_index = " << row_index << "\n";); + int bound_sign = (is_lower_bound ? 1 : -1); + int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; + + SASSERT(!tv::is_term(bound_j)); + u_dependency* ret = nullptr; + for (auto const& r : lar->get_row(row_index)) { + unsigned j = r.var(); + if (j == bound_j) + continue; + mpq const& a = r.coeff(); + int a_sign = is_pos(a) ? 1 : -1; + int sign = j_sign * a_sign; + u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j); + ret = lar->join_deps(ret, witness); + } + return ret; }; m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain); } - + void advance_u(unsigned j) { m_column_of_u = (m_column_of_u == -1) ? j : -2; } @@ -320,26 +337,7 @@ private: break; } } - static u_dependency* explain_bound_on_var_on_coeff(B* bp, unsigned bound_j, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict, unsigned row_index) { - TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << ", row_index = " << row_index << "\n";); - auto& lar = bp->lp(); - int bound_sign = (is_lower_bound ? 1 : -1); - int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; - - SASSERT(!tv::is_term(bound_j)); - u_dependency* ret = nullptr; - for (auto const& r : lar.get_row(row_index)) { - unsigned j = r.var(); - if (j == bound_j) - continue; - mpq const& a = r.coeff(); - int a_sign = is_pos(a) ? 1 : -1; - int sign = j_sign * a_sign; - u_dependency* witness = sign > 0 ? lar.get_column_upper_bound_witness(j) : lar.get_column_lower_bound_witness(j); - ret = lar.join_deps(ret, witness); - } - return ret; - } + }; From 536930b4a160cf5e1f7260016629f0a17adc8459 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 20 Sep 2023 17:13:25 -0700 Subject: [PATCH 171/428] make m_ibounds inside of lp_bound_propagator a reference --- src/math/lp/lp_bound_propagator.h | 4 ++-- src/sat/smt/arith_solver.cpp | 2 +- src/sat/smt/arith_solver.h | 1 + src/smt/theory_lra.cpp | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index f6cc83825..99755f606 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -20,7 +20,7 @@ class lp_bound_propagator { u_map m_improved_upper_bounds; T& m_imp; - std_vector m_ibounds; + std_vector& m_ibounds; map, default_eq> m_val2fixed_row; // works for rows of the form x + y + sum of fixed = 0 @@ -109,7 +109,7 @@ private: }; public: - lp_bound_propagator(T& imp) : m_imp(imp) {} + lp_bound_propagator(T& imp, std_vector & ibounds) : m_imp(imp), m_ibounds(ibounds) {} const std_vector& ibounds() const { return m_ibounds; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index fd55fb7d7..3cf991c20 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -26,7 +26,7 @@ namespace arith { m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), m_local_search(*this), m_resource_limit(*this), - m_bp(*this), + m_bp(*this, m_implied_bounds), a(m), m_bound_terms(m), m_bound_predicate(m) diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index e23162393..8917a3e42 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -243,6 +243,7 @@ namespace arith { resource_limit m_resource_limit; lp_bounds m_new_bounds; symbol m_farkas; + std_vector m_implied_bounds; lp::lp_bound_propagator m_bp; mutable vector> m_todo_terms; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 9357b7a65..aeb138d3c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -225,6 +225,7 @@ class theory_lra::imp { lp_bounds m_new_bounds; symbol m_farkas; vector m_bound_params; + std_vector m_implied_bounds; lp::lp_bound_propagator m_bp; context& ctx() const { return th.get_context(); } @@ -873,7 +874,7 @@ public: m_solver(nullptr), m_resource_limit(*this), m_farkas("farkas"), - m_bp(*this), + m_bp(*this, m_implied_bounds), m_bounded_range_idx(0), m_bounded_range_lit(null_literal), m_bound_terms(m), From e31cecf5dbe7b6d0542434451e8ed64497bd3075 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 21 Sep 2023 11:27:53 -0700 Subject: [PATCH 172/428] transfer propagate monomial bounds to nla_solver --- src/math/lp/lp_bound_propagator.h | 165 ----------------------- src/math/lp/nla_core.cpp | 213 +++++++++++++++++++++++++++++- src/math/lp/nla_core.h | 22 ++- src/math/lp/nla_solver.cpp | 11 +- src/math/lp/nla_solver.h | 4 +- src/sat/smt/arith_internalize.cpp | 2 +- src/smt/theory_lra.cpp | 9 +- 7 files changed, 243 insertions(+), 183 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 99755f606..7ed872aa2 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -120,171 +120,6 @@ public: m_column_types = &lp().get_column_types(); } - bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) { - zero_var = non_fixed = null_lpvar; - unsigned n_of_non_fixed = 0; - for (lpvar v : m) { - if (!this->column_is_fixed(v)) { - n_of_non_fixed++; - non_fixed = v; - continue; - } - const auto & b = get_lower_bound(v).x; - if (b.is_zero()) { - zero_var = v; - return true; - } - - } - return n_of_non_fixed <= 1; - } - - void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - auto& lps = lp(); - auto lambda = [zero_var,&lps]() { - return lps.get_bound_constraint_witnesses_for_column(zero_var); - }; - TRACE("add_bound", lp().print_column_info(zero_var, tout) << std::endl;); - add_lower_bound_monic(monic_var, mpq(0), false, lambda); - add_upper_bound_monic(monic_var, mpq(0), false, lambda); - } - - void add_lower_bound_monic(lpvar j, const mpq& v, bool is_strict, std::function explain_dep) { - TRACE("add_bound", lp().print_column_info(j, tout) << std::endl;); - j = lp().column_to_reported_index(j); - unsigned k; - if (!m_improved_lower_bounds.find(j, k)) { - m_improved_lower_bounds.insert(j,static_cast(m_ibounds.size())); - m_ibounds.push_back(implied_bound(v, j, true, is_strict, explain_dep)); - } - else { - auto& found_bound = m_ibounds[k]; - if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = implied_bound(v, j, true, is_strict, explain_dep); - TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); - } - } - } - - void add_upper_bound_monic(lpvar j, const mpq& bound_val, bool is_strict, std::function explain_bound) { - j = lp().column_to_reported_index(j); - unsigned k; - if (!m_improved_upper_bounds.find(j, k)) { - m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size())); - m_ibounds.push_back(implied_bound(bound_val, j, false, is_strict, explain_bound)); - } - else { - auto& found_bound = m_ibounds[k]; - if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = implied_bound(bound_val, j, false, is_strict, explain_bound); - TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); - } - } - } - - void propagate_monic(lpvar monic_var, const svector& vars) { - lpvar non_fixed, zero_var; - if (!is_linear(vars, zero_var, non_fixed)) - return; - - if (zero_var != null_lpvar) - add_bounds_for_zero_var(monic_var, zero_var); - else { - rational k = rational(1); - for (auto v : vars) - if (v != non_fixed) { - k *= lp().get_column_value(v).x; - if (k.is_big()) return; - } - - if (non_fixed != null_lpvar) - propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); - else // all variables are fixed - propagate_monic_with_all_fixed(monic_var, vars, k); - } - } - - void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { - lp::impq bound_value; - bool is_strict; - auto& lps = lp(); - - if (lower_bound_is_available(non_fixed)) { - bound_value = lp().column_lower_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed,&lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (upper_bound_is_available(non_fixed)) { - bound_value = lp().column_upper_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed,&lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed,&lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - bound_value = lp().column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - - if (upper_bound_is_available(monic_var)) { - bound_value = lp().column_upper_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed,&lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - } - - void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { - auto& lps = lp(); - auto lambda = [vars,&lps]() { return lps.get_bound_constraint_witnesses_for_columns(vars); }; - add_lower_bound_monic(monic_var, k, false, lambda); - add_upper_bound_monic(monic_var, k, false, lambda); - } - column_type get_column_type(unsigned j) const { return (*m_column_types)[j]; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 63dd29d9b..4e16793ec 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -17,11 +17,12 @@ Author: #include "math/grobner/pdd_solver.h" #include "math/dd/pdd_interval.h" #include "math/dd/pdd_eval.h" +#include "nla_core.h" namespace nla { typedef lp::lar_term term; -core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim) : m_evars(), +core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector& implied_bounds) : m_evars(), lra(s), m_reslim(lim), m_params(p), @@ -37,7 +38,8 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim) : m_evars(), m_grobner(this), m_emons(m_evars), m_use_nra_model(false), - m_nra(s, m_nra_lim, *this) { + m_nra(s, m_nra_lim, *this), + m_implied_bounds(implied_bounds) { m_nlsat_delay = lp_settings().nlsat_delay(); lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { for (const auto& m : m_emons) { @@ -1837,5 +1839,210 @@ bool core::improve_bounds() { return bounds_improved; } -} // end of nla +bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) +{ + zero_var = non_fixed = null_lpvar; + unsigned n_of_non_fixed = 0; + for (lpvar v : m) { + if (!this->var_is_fixed(v)) { + n_of_non_fixed++; + non_fixed = v; + continue; + } + const auto& b = get_lower_bound(v); + if (b.is_zero()) { + zero_var = v; + return true; + } + } + return n_of_non_fixed <= 1; +} + +void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep) +{ + TRACE("add_bound", lra.print_column_info(j, tout) << std::endl;); + j = lra.column_to_reported_index(j); + unsigned k; + if (!m_improved_lower_bounds.find(j, k)) { + m_improved_lower_bounds.insert(j, static_cast(m_implied_bounds.size())); + m_implied_bounds.push_back(lp::implied_bound(v, j, true, is_strict, explain_dep)); + } + else { + auto& found_bound = m_implied_bounds[k]; + if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { + found_bound = lp::implied_bound(v, j, true, is_strict, explain_dep); + TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); + } + } +} + +void core::add_upper_bound_monic(lpvar j, const lp::mpq& bound_val, bool is_strict, std::function explain_dep) +{ + j = lra.column_to_reported_index(j); + unsigned k; + if (!m_improved_upper_bounds.find(j, k)) { + m_improved_upper_bounds.insert(j, static_cast(m_implied_bounds.size())); + m_implied_bounds.push_back(lp::implied_bound(bound_val, j, false, is_strict, explain_dep)); + } + else { + auto& found_bound = m_implied_bounds[k]; + if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { + found_bound = lp::implied_bound(bound_val, j, false, is_strict, explain_dep); + TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); + } + } +} + +bool core::upper_bound_is_available(unsigned j) const +{ + switch (get_column_type(j)) { + case lp::column_type::fixed: + case lp::column_type::boxed: + case lp::column_type::upper_bound: + return true; + default: + return false; + } +} + +bool core::lower_bound_is_available(unsigned j) const +{ + switch (get_column_type(j)) { + case lp::column_type::fixed: + case lp::column_type::boxed: + case lp::column_type::lower_bound: + return true; + default: + return false; + } +} + +void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) +{ + lp::impq bound_value; + bool is_strict; + auto& lps = lra; + + if (lower_bound_is_available(non_fixed)) { + bound_value = lra.column_lower_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_pos()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (upper_bound_is_available(non_fixed)) { + bound_value = lra.column_upper_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (lower_bound_is_available(monic_var)) { + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + bound_value = lra.column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } + + if (upper_bound_is_available(monic_var)) { + bound_value = lra.column_upper_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } +} + +void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) +{ + auto* lps = &lra; + auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; + add_lower_bound_monic(monic_var, k, false, lambda); + add_upper_bound_monic(monic_var, k, false, lambda); +} + +void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) +{ + auto* lps = &lra; + auto lambda = [zero_var, lps]() { + return lps->get_bound_constraint_witnesses_for_column(zero_var); + }; + TRACE("add_bound", lra.print_column_info(zero_var, tout) << std::endl;); + add_lower_bound_monic(monic_var, lp::mpq(0), false, lambda); + add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); +} + +void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) +{ + lpvar non_fixed, zero_var; + const auto& vars = m_emons[monic_var].vars(); + if (!is_linear(vars, zero_var, non_fixed)) + return; + + if (zero_var != null_lpvar) + add_bounds_for_zero_var(monic_var, zero_var); + else { + rational k = rational(1); + for (auto v : vars) + if (v != non_fixed) { + k *= val(v); + if (k.is_big()) return; + } + + if (non_fixed != null_lpvar) + propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); + else // all variables are fixed + propagate_monic_with_all_fixed(monic_var, vars, k); + } +} + +void core::init_bound_propagation() +{ + this->m_implied_bounds.clear(); + this->m_improved_lower_bounds.reset(); + this->m_improved_upper_bounds.reset(); + this->m_column_types = &lra.get_column_types(); +} + +} // namespace nla + diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 3b888f8ef..54c7e1df1 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -103,7 +103,10 @@ class core { emonics m_emons; svector m_add_buffer; mutable indexed_uint_set m_active_var_set; - + // these maps map a column index to the corresponding index in ibounds + u_map m_improved_lower_bounds; + u_map m_improved_upper_bounds; + const vector* m_column_types; reslimit m_nra_lim; bool m_use_nra_model = false; @@ -114,12 +117,13 @@ class core { void check_weighted(unsigned sz, std::pair>* checks); void add_bounds(); + std_vector & m_implied_bounds; // try to improve bounds for variables in monomials. bool improve_bounds(); public: // constructor - core(lp::lar_solver& s, params_ref const& p, reslimit&); + core(lp::lar_solver& s, params_ref const& p, reslimit&, std_vector & implied_bounds); const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; } void reset_monics_with_changed_bounds() { m_monics_with_changed_bounds.reset(); } void insert_to_refine(lpvar j); @@ -431,15 +435,23 @@ public: void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); + bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed); + void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var); + void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); + void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k); + void add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); + void add_upper_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); + bool upper_bound_is_available(unsigned j) const; + bool lower_bound_is_available(unsigned j) const; private: - void restore_patched_values(); + lp::column_type get_column_type(unsigned j) const { return (*m_column_types)[j]; } void constrain_nl_in_tableau(); bool solve_tableau(); void restore_tableau(); void save_tableau(); bool integrality_holds(); - - + void calculate_implied_bounds_for_monic(lp::lpvar v); + void init_bound_propagation(); }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 8be918f01..3475a3509 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -54,8 +54,8 @@ namespace nla { m_core->pop(n); } - solver::solver(lp::lar_solver& s, params_ref const& p, reslimit& limit): - m_core(alloc(core, s, p, limit)) { + solver::solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds): + m_core(alloc(core, s, p, limit, implied_bounds)) { } bool solver::influences_nl_var(lpvar j) const { @@ -88,6 +88,9 @@ namespace nla { m_core->collect_statistics(st); } + void solver::calculate_implied_bounds_for_monic(lp::lpvar v) { + m_core->calculate_implied_bounds_for_monic(v); + } // ensure r = x^y, add abstraction/refinement lemmas lbool solver::check_power(lpvar r, lpvar x, lpvar y, vector& lemmas) { return m_core->check_power(r, x, y, lemmas); @@ -97,4 +100,8 @@ namespace nla { m_core->check_bounded_divisions(lemmas); } + void solver::init_bound_propagation() { + m_core->init_bound_propagation(); + } + } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 07bf095a6..a4de90320 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -24,7 +24,7 @@ namespace nla { core* m_core; public: - solver(lp::lar_solver& s, params_ref const& p, reslimit& limit); + solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds); ~solver(); const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } void reset_monics_with_changed_bounds() { m_core->reset_monics_with_changed_bounds(); } @@ -48,5 +48,7 @@ namespace nla { nlsat::anum_manager& am(); nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); + void calculate_implied_bounds_for_monic(lp::lpvar v); + void init_bound_propagation(); }; } diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 3174ad775..5893c8520 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -61,7 +61,7 @@ namespace arith { void solver::ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), s().params(), m.limit()); + m_nla = alloc(nla::solver, *m_solver.get(), s().params(), m.limit(), m_implied_bounds); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index aeb138d3c..ef7ab7091 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -264,7 +264,7 @@ class theory_lra::imp { void ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), ctx().get_params(), m.limit()); + m_nla = alloc(nla::solver, *m_solver.get(), ctx().get_params(), m.limit(), m_implied_bounds); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); @@ -2198,13 +2198,10 @@ public: finish_bound_propagation(); } - void calculate_implied_bounds_for_monic(lpvar monic_var, const svector& vars) { - m_bp.propagate_monic(monic_var, vars); - } - void propagate_bounds_for_touched_monomials() { + m_nla->init_bound_propagation(); for (unsigned v : m_nla->monics_with_changed_bounds()) { - calculate_implied_bounds_for_monic(v, m_nla->get_core().emons()[v].vars()); + m_nla->calculate_implied_bounds_for_monic(v); } m_nla->reset_monics_with_changed_bounds(); } From f423642e9b609eb4206519ef95cff4abff3910b1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 21 Sep 2023 12:18:21 -0700 Subject: [PATCH 173/428] try the lemma scheme --- src/math/lp/nla_core.cpp | 182 +++++++++++++------------------------ src/math/lp/nla_core.h | 2 +- src/math/lp/nla_solver.cpp | 4 +- src/math/lp/nla_solver.h | 2 +- src/smt/theory_lra.cpp | 5 +- 5 files changed, 72 insertions(+), 123 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4e16793ec..514b2ad61 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1844,7 +1844,7 @@ bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) zero_var = non_fixed = null_lpvar; unsigned n_of_non_fixed = 0; for (lpvar v : m) { - if (!this->var_is_fixed(v)) { + if (!var_is_fixed(v)) { n_of_non_fixed++; non_fixed = v; continue; @@ -1920,129 +1920,75 @@ bool core::lower_bound_is_available(unsigned j) const void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { lp::impq bound_value; - bool is_strict; - auto& lps = lra; + new_lemma lemma(*this, "propagate monic with non fixed"); + // using += to not assert thath the inequality does not hold + lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); + lp::explanation exp; + for (auto v : m_emons[monic_var].vars()) { + if (v == non_fixed) continue; + u_dependency* dep = lra.get_column_lower_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } + dep = lra.get_column_upper_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } + } + lemma &= exp; +} - if (lower_bound_is_available(non_fixed)) { - bound_value = lra.column_lower_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); + void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) + { + auto* lps = &lra; + auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; + add_lower_bound_monic(monic_var, k, false, lambda); + add_upper_bound_monic(monic_var, k, false, lambda); + } + + void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) + { + auto* lps = &lra; + auto lambda = [zero_var, lps]() { + return lps->get_bound_constraint_witnesses_for_column(zero_var); + }; + TRACE("add_bound", lra.print_column_info(zero_var, tout) << std::endl;); + add_lower_bound_monic(monic_var, lp::mpq(0), false, lambda); + add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); + } + + void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) + { + lpvar non_fixed, zero_var; + const auto& vars = m_emons[monic_var].vars(); + if (!is_linear(vars, zero_var, non_fixed)) + return; + + if (zero_var != null_lpvar) + add_bounds_for_zero_var(monic_var, zero_var); + else { + rational k = rational(1); for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (upper_bound_is_available(non_fixed)) { - bound_value = lra.column_upper_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + k *= val(v); + if (k.is_big()) return; } - } - return dep; - }; - bound_value = lra.column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + + if (non_fixed != null_lpvar) + propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); + else // all variables are fixed + propagate_monic_with_all_fixed(monic_var, vars, k); + } } - if (upper_bound_is_available(monic_var)) { - bound_value = lra.column_upper_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + void core::init_bound_propagation(vector & l_vec) + { + m_implied_bounds.clear(); + m_improved_lower_bounds.reset(); + m_improved_upper_bounds.reset(); + m_column_types = &lra.get_column_types(); + m_lemma_vec = &l_vec; + m_lemma_vec->clear(); } -} - -void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) -{ - auto* lps = &lra; - auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; - add_lower_bound_monic(monic_var, k, false, lambda); - add_upper_bound_monic(monic_var, k, false, lambda); -} - -void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) -{ - auto* lps = &lra; - auto lambda = [zero_var, lps]() { - return lps->get_bound_constraint_witnesses_for_column(zero_var); - }; - TRACE("add_bound", lra.print_column_info(zero_var, tout) << std::endl;); - add_lower_bound_monic(monic_var, lp::mpq(0), false, lambda); - add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); -} - -void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) -{ - lpvar non_fixed, zero_var; - const auto& vars = m_emons[monic_var].vars(); - if (!is_linear(vars, zero_var, non_fixed)) - return; - - if (zero_var != null_lpvar) - add_bounds_for_zero_var(monic_var, zero_var); - else { - rational k = rational(1); - for (auto v : vars) - if (v != non_fixed) { - k *= val(v); - if (k.is_big()) return; - } - - if (non_fixed != null_lpvar) - propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); - else // all variables are fixed - propagate_monic_with_all_fixed(monic_var, vars, k); - } -} - -void core::init_bound_propagation() -{ - this->m_implied_bounds.clear(); - this->m_improved_lower_bounds.reset(); - this->m_improved_upper_bounds.reset(); - this->m_column_types = &lra.get_column_types(); -} } // namespace nla - - diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 54c7e1df1..456709771 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -451,7 +451,7 @@ private: void save_tableau(); bool integrality_holds(); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); + void init_bound_propagation(vector&); }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 3475a3509..08c1e1e62 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -100,8 +100,8 @@ namespace nla { m_core->check_bounded_divisions(lemmas); } - void solver::init_bound_propagation() { - m_core->init_bound_propagation(); + void solver::init_bound_propagation(vector& lemmas) { + m_core->init_bound_propagation(lemmas); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index a4de90320..fa206ea40 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -49,6 +49,6 @@ namespace nla { nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); + void init_bound_propagation(vector&); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ef7ab7091..ea1dcd9b9 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2199,11 +2199,14 @@ public: } void propagate_bounds_for_touched_monomials() { - m_nla->init_bound_propagation(); + m_nla->init_bound_propagation(m_nla_lemma_vector); for (unsigned v : m_nla->monics_with_changed_bounds()) { m_nla->calculate_implied_bounds_for_monic(v); } m_nla->reset_monics_with_changed_bounds(); + for (const auto & l:m_nla_lemma_vector) { + false_case_of_check_nla(l); + } } void propagate_bounds_with_nlp() { From c0b55d14352fa76f3a2eabf346a8d20fb93b4e9e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 21 Sep 2023 15:53:53 -0700 Subject: [PATCH 174/428] reject rows with columns with big numbers for lp bound propagation Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 08ff3b0d4..dc114e9e0 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -135,9 +135,15 @@ namespace lp { bool lar_solver::row_has_a_big_num(unsigned i) const { - for (const auto& c : A_r().m_rows[i]) + for (const auto& c : A_r().m_rows[i]) { if (c.coeff().is_big()) return true; + if (column_has_lower_bound(c.var())&& get_lower_bound(c.var()).x.is_big()) + return true; + if (column_has_upper_bound(c.var())&& get_upper_bound(c.var()).x.is_big()) + return true; + } + return false; } From 576309a16fe8e6d30dd19bebaeaa1bc0b4856b6a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 21 Sep 2023 16:30:43 -0700 Subject: [PATCH 175/428] Revert "reject rows with columns with big numbers for lp bound propagation" This reverts commit c0b55d14352fa76f3a2eabf346a8d20fb93b4e9e. --- src/math/lp/lar_solver.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index dc114e9e0..08ff3b0d4 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -135,15 +135,9 @@ namespace lp { bool lar_solver::row_has_a_big_num(unsigned i) const { - for (const auto& c : A_r().m_rows[i]) { + for (const auto& c : A_r().m_rows[i]) if (c.coeff().is_big()) return true; - if (column_has_lower_bound(c.var())&& get_lower_bound(c.var()).x.is_big()) - return true; - if (column_has_upper_bound(c.var())&& get_upper_bound(c.var()).x.is_big()) - return true; - } - return false; } From eff3f5f65eecd6772b7a6827d31676785ab4443f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Sep 2023 09:45:58 -0700 Subject: [PATCH 176/428] port bug fixes from unit prop branch Signed-off-by: Nikolaj Bjorner --- src/math/lp/int_solver.cpp | 2 +- src/math/lp/lar_constraints.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 0ab7d1e40..1964262f3 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -31,7 +31,7 @@ namespace lp { lra.remove_fixed_vars_from_base(); lp_assert(lia.is_feasible()); for (unsigned j : lra.r_basis()) - if (!lra.get_value(j).is_int() && lra.column_is_int(j)) + if (!lra.get_value(j).is_int() && lra.column_is_int(j) && !lia.is_fixed(j)) patch_basic_column(j); if (!lia.has_inf_int()) { lia.settings().stats().m_patches_success++; diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 0a16353c1..f0c937324 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -136,9 +136,10 @@ class constraint_set { } public: - constraint_set(u_dependency_manager& d, column_namer& cn): - m_dep_manager(d), - m_namer(cn) {} + constraint_set(u_dependency_manager& d, column_namer& cn): + m_namer(cn), + m_dep_manager(d) + {} ~constraint_set() { for (auto* c : m_constraints) From caa929f01f4ba66f5fcacd4e83c5fe7c7bfe363f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 22 Sep 2023 14:27:26 -0700 Subject: [PATCH 177/428] do not use lemmase in monomial propagation --- src/math/lp/nla_core.cpp | 88 ++++++++++++++++++++++++++++++-------- src/math/lp/nla_core.h | 2 +- src/math/lp/nla_solver.cpp | 4 +- src/math/lp/nla_solver.h | 2 +- src/smt/theory_lra.cpp | 5 +-- 5 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 514b2ad61..916b80f6c 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1920,22 +1920,76 @@ bool core::lower_bound_is_available(unsigned j) const void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { lp::impq bound_value; - new_lemma lemma(*this, "propagate monic with non fixed"); - // using += to not assert thath the inequality does not hold - lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); - lp::explanation exp; - for (auto v : m_emons[monic_var].vars()) { - if (v == non_fixed) continue; - u_dependency* dep = lra.get_column_lower_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); - } - dep = lra.get_column_upper_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); - } + bool is_strict; + auto& lps = lra; + + if (lower_bound_is_available(non_fixed)) { + bound_value = lra.column_lower_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_pos()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (upper_bound_is_available(non_fixed)) { + bound_value = lra.column_upper_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (lower_bound_is_available(monic_var)) { + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + bound_value = lra.column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } + + if (upper_bound_is_available(monic_var)) { + bound_value = lra.column_upper_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } + } + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); } - lemma &= exp; } void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) @@ -1981,14 +2035,12 @@ void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& } } - void core::init_bound_propagation(vector & l_vec) + void core::init_bound_propagation() { m_implied_bounds.clear(); m_improved_lower_bounds.reset(); m_improved_upper_bounds.reset(); m_column_types = &lra.get_column_types(); - m_lemma_vec = &l_vec; - m_lemma_vec->clear(); } } // namespace nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 456709771..54c7e1df1 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -451,7 +451,7 @@ private: void save_tableau(); bool integrality_holds(); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(vector&); + void init_bound_propagation(); }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 08c1e1e62..3475a3509 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -100,8 +100,8 @@ namespace nla { m_core->check_bounded_divisions(lemmas); } - void solver::init_bound_propagation(vector& lemmas) { - m_core->init_bound_propagation(lemmas); + void solver::init_bound_propagation() { + m_core->init_bound_propagation(); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index fa206ea40..a4de90320 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -49,6 +49,6 @@ namespace nla { nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(vector&); + void init_bound_propagation(); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ea1dcd9b9..ef7ab7091 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2199,14 +2199,11 @@ public: } void propagate_bounds_for_touched_monomials() { - m_nla->init_bound_propagation(m_nla_lemma_vector); + m_nla->init_bound_propagation(); for (unsigned v : m_nla->monics_with_changed_bounds()) { m_nla->calculate_implied_bounds_for_monic(v); } m_nla->reset_monics_with_changed_bounds(); - for (const auto & l:m_nla_lemma_vector) { - false_case_of_check_nla(l); - } } void propagate_bounds_with_nlp() { From 940775d12d1b214bdf010aae4f8476fbbefa9b9b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Sep 2023 16:48:40 -0700 Subject: [PATCH 178/428] indentation Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 121 +++++++++++++++++++-------------------- src/smt/smt_context.cpp | 2 + src/smt/theory_lra.cpp | 15 +++-- 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 514b2ad61..079eccd74 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -22,24 +22,25 @@ namespace nla { typedef lp::lar_term term; -core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector& implied_bounds) : m_evars(), - lra(s), - m_reslim(lim), - m_params(p), - m_tangents(this), - m_basics(this), - m_order(this), - m_monotone(this), - m_powers(*this), - m_divisions(*this), - m_intervals(this, lim), - m_monomial_bounds(this), - m_horner(this), - m_grobner(this), - m_emons(m_evars), - m_use_nra_model(false), - m_nra(s, m_nra_lim, *this), - m_implied_bounds(implied_bounds) { +core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector& implied_bounds) : + m_evars(), + lra(s), + m_reslim(lim), + m_params(p), + m_tangents(this), + m_basics(this), + m_order(this), + m_monotone(this), + m_powers(*this), + m_divisions(*this), + m_intervals(this, lim), + m_monomial_bounds(this), + m_horner(this), + m_grobner(this), + m_emons(m_evars), + m_use_nra_model(false), + m_nra(s, m_nra_lim, *this), + m_implied_bounds(implied_bounds) { m_nlsat_delay = lp_settings().nlsat_delay(); lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { for (const auto& m : m_emons) { @@ -1839,8 +1840,7 @@ bool core::improve_bounds() { return bounds_improved; } -bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) -{ +bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) { zero_var = non_fixed = null_lpvar; unsigned n_of_non_fixed = 0; for (lpvar v : m) { @@ -1858,8 +1858,7 @@ bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) return n_of_non_fixed <= 1; } -void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep) -{ +void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep) { TRACE("add_bound", lra.print_column_info(j, tout) << std::endl;); j = lra.column_to_reported_index(j); unsigned k; @@ -1876,67 +1875,63 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } } -void core::add_upper_bound_monic(lpvar j, const lp::mpq& bound_val, bool is_strict, std::function explain_dep) -{ - j = lra.column_to_reported_index(j); - unsigned k; - if (!m_improved_upper_bounds.find(j, k)) { - m_improved_upper_bounds.insert(j, static_cast(m_implied_bounds.size())); - m_implied_bounds.push_back(lp::implied_bound(bound_val, j, false, is_strict, explain_dep)); - } - else { - auto& found_bound = m_implied_bounds[k]; - if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = lp::implied_bound(bound_val, j, false, is_strict, explain_dep); - TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); + void core::add_upper_bound_monic(lpvar j, const lp::mpq& bound_val, bool is_strict, std::function explain_dep) { + j = lra.column_to_reported_index(j); + unsigned k; + if (!m_improved_upper_bounds.find(j, k)) { + m_improved_upper_bounds.insert(j, static_cast(m_implied_bounds.size())); + m_implied_bounds.push_back(lp::implied_bound(bound_val, j, false, is_strict, explain_dep)); + } + else { + auto& found_bound = m_implied_bounds[k]; + if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { + found_bound = lp::implied_bound(bound_val, j, false, is_strict, explain_dep); + TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); + } } } -} -bool core::upper_bound_is_available(unsigned j) const -{ - switch (get_column_type(j)) { + bool core::upper_bound_is_available(unsigned j) const { + switch (get_column_type(j)) { case lp::column_type::fixed: case lp::column_type::boxed: case lp::column_type::upper_bound: return true; default: return false; + } } -} - -bool core::lower_bound_is_available(unsigned j) const -{ - switch (get_column_type(j)) { + + bool core::lower_bound_is_available(unsigned j) const { + switch (get_column_type(j)) { case lp::column_type::fixed: case lp::column_type::boxed: case lp::column_type::lower_bound: return true; default: return false; + } } -} -void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) -{ - lp::impq bound_value; - new_lemma lemma(*this, "propagate monic with non fixed"); - // using += to not assert thath the inequality does not hold - lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); - lp::explanation exp; - for (auto v : m_emons[monic_var].vars()) { - if (v == non_fixed) continue; - u_dependency* dep = lra.get_column_lower_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); - } - dep = lra.get_column_upper_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); + void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { + lp::impq bound_value; + new_lemma lemma(*this, "propagate monic with non fixed"); + // using += to not assert thath the inequality does not hold + lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); + lp::explanation exp; + for (auto v : m_emons[monic_var].vars()) { + if (v == non_fixed) continue; + u_dependency* dep = lra.get_column_lower_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } + dep = lra.get_column_upper_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } } + lemma &= exp; } - lemma &= exp; -} void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 64c7bbf2e..a3cd99e37 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4522,6 +4522,8 @@ namespace smt { theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_id(); + verbose_stream() << "num parents " << n->get_num_parents() << "\n"; + for (enode * parent : enode::parents(n)) { app* p = parent->get_expr(); family_id fid = p->get_family_id(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ea1dcd9b9..beec02d98 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1480,6 +1480,7 @@ public: m_model_eqs.reset(); svector vars; theory_var sz = static_cast(th.get_num_vars()); + verbose_stream() << "check " << sz << "\n"; for (theory_var v = 0; v < sz; ++v) { enode * n1 = get_enode(v); if (!th.is_relevant_and_shared(n1)) { @@ -1528,12 +1529,16 @@ public: unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; int start = ctx().get_random_value(); + verbose_stream() << "assume-eqs " << sz << "\n"; + unsigned num_relevant = 0; for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); + verbose_stream() << enode_pp(n1, ctx()) << "\n"; if (!th.is_relevant_and_shared(n1)) { continue; } + ++num_relevant; ensure_column(v); if (!is_registered_var(v)) continue; @@ -1551,7 +1556,8 @@ public: num_candidates++; } } - + + verbose_stream() << "candidates " << num_candidates << " num relevant " << num_relevant << "\n"; if (num_candidates > 0) { ctx().push_trail(restore_vector(m_assume_eq_candidates, old_sz)); } @@ -2200,13 +2206,12 @@ public: void propagate_bounds_for_touched_monomials() { m_nla->init_bound_propagation(m_nla_lemma_vector); - for (unsigned v : m_nla->monics_with_changed_bounds()) { + for (unsigned v : m_nla->monics_with_changed_bounds()) m_nla->calculate_implied_bounds_for_monic(v); - } + m_nla->reset_monics_with_changed_bounds(); - for (const auto & l:m_nla_lemma_vector) { + for (const auto & l : m_nla_lemma_vector) false_case_of_check_nla(l); - } } void propagate_bounds_with_nlp() { From 886d3f43511eb997dc16607daa24c75456f57532 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Sep 2023 16:55:34 -0700 Subject: [PATCH 179/428] indentation Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 151 +++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 78 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 904da26cd..38f741388 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1913,91 +1913,88 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } } -void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) -{ - lp::impq bound_value; - bool is_strict; - auto& lps = lra; + void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { + lp::impq bound_value; + bool is_strict; + auto& lps = lra; + + if (lower_bound_is_available(non_fixed)) { + bound_value = lra.column_lower_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_pos()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } + + if (upper_bound_is_available(non_fixed)) { + bound_value = lra.column_upper_bound(non_fixed); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); + for (auto v : vars) + if (v != non_fixed) + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + else + add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); + } - if (lower_bound_is_available(non_fixed)) { - bound_value = lra.column_lower_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (upper_bound_is_available(non_fixed)) { - bound_value = lra.column_upper_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + if (lower_bound_is_available(monic_var)) { + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } } - } - return dep; - }; - bound_value = lra.column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - - if (upper_bound_is_available(monic_var)) { - bound_value = lra.column_upper_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + return dep; + }; + bound_value = lra.column_lower_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + if (k.is_pos()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } + + if (upper_bound_is_available(monic_var)) { + bound_value = lra.column_upper_bound(monic_var); + is_strict = !bound_value.y.is_zero(); + auto lambda = [vars, monic_var, non_fixed, &lps]() { + u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); + for (auto v : vars) { + if (v != non_fixed) { + dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); + } } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + return dep; + }; + if (k.is_neg()) + add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + else + add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); + } } -} - void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) - { + void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { auto* lps = &lra; auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; add_lower_bound_monic(monic_var, k, false, lambda); add_upper_bound_monic(monic_var, k, false, lambda); } - void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) - { + void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { auto* lps = &lra; auto lambda = [zero_var, lps]() { return lps->get_bound_constraint_witnesses_for_column(zero_var); @@ -2007,8 +2004,7 @@ void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); } - void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) - { + void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) { lpvar non_fixed, zero_var; const auto& vars = m_emons[monic_var].vars(); if (!is_linear(vars, zero_var, non_fixed)) @@ -2031,8 +2027,7 @@ void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& } } - void core::init_bound_propagation() - { + void core::init_bound_propagation() { m_implied_bounds.clear(); m_improved_lower_bounds.reset(); m_improved_upper_bounds.reset(); From 421fe946079ffb79ccae99bd85ffc6211c9570f7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Sep 2023 17:59:07 -0700 Subject: [PATCH 180/428] rmove debug out Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8ffeff3fb..8bf17ec4d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1529,12 +1529,10 @@ public: unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; int start = ctx().get_random_value(); - verbose_stream() << "assume-eqs " << sz << "\n"; unsigned num_relevant = 0; for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); - verbose_stream() << enode_pp(n1, ctx()) << "\n"; if (!th.is_relevant_and_shared(n1)) { continue; } @@ -1557,7 +1555,6 @@ public: } } - verbose_stream() << "candidates " << num_candidates << " num relevant " << num_relevant << "\n"; if (num_candidates > 0) { ctx().push_trail(restore_vector(m_assume_eq_candidates, old_sz)); } From 30d1800c3176b6bd6ca5a3e2c6109ad96784ba18 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 10:32:51 -0700 Subject: [PATCH 181/428] #6916 short circuiting equality consequence appears to have the wrong sign --- src/smt/smt_consequences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index 657e222da..cac8d40b1 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -243,7 +243,7 @@ namespace smt { lit.neg(); literal lit = mk_diseq(k, v); - literals.push_back(lit); + literals.push_back(~lit); mk_clause(literals.size(), literals.data(), nullptr); TRACE("context", display_literals_verbose(tout, literals.size(), literals.data());); } From eea9c0bec61e7dd40603e2223ac0719bcc6997ba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 11:22:25 -0700 Subject: [PATCH 182/428] fix #6914 --- src/smt/smt_model_finder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 94a0992f3..4139b3109 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -574,9 +574,9 @@ namespace smt { to_delete.push_back(n); } } - for (expr* e : to_delete) { + for (expr* e : to_delete) s->remove(e); - } + reset_eval_cache(); } } } From acad9fa62c0cab9089ee385098e50545d544b2bb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 16:25:46 -0700 Subject: [PATCH 183/428] Update smt_context.cpp --- src/smt/smt_context.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index a3cd99e37..64c7bbf2e 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4522,8 +4522,6 @@ namespace smt { theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_id(); - verbose_stream() << "num parents " << n->get_num_parents() << "\n"; - for (enode * parent : enode::parents(n)) { app* p = parent->get_expr(); family_id fid = p->get_family_id(); From a3e2e68d93dcb4f2891e779fbe4419cc00db2b5e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 16:26:31 -0700 Subject: [PATCH 184/428] Update theory_lra.cpp --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 8bf17ec4d..21e021ea7 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1480,7 +1480,6 @@ public: m_model_eqs.reset(); svector vars; theory_var sz = static_cast(th.get_num_vars()); - verbose_stream() << "check " << sz << "\n"; for (theory_var v = 0; v < sz; ++v) { enode * n1 = get_enode(v); if (!th.is_relevant_and_shared(n1)) { From 61319ffd854fa144431d6fea4a0b433d4ac15bb2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 17:19:06 -0700 Subject: [PATCH 185/428] cache is_shared information in the enode observed perf overhead for QF_NIA is that assume_eqs in theory_lra incurs significant overhead when calling is_relevant_and_shared. The call to context::is_shared and the loop checking for beta redexes is a main bottleneck. The bottleneck is avoided by caching the result if is_shared inside the enode. It is invalidated for every merge/unmerge. --- src/ast/euf/euf_egraph.cpp | 2 ++ src/ast/euf/euf_enode.h | 4 ++++ src/sat/smt/euf_internalize.cpp | 27 ++++++++++++++++++++++----- src/smt/smt_context.cpp | 14 +++++++++++++- src/smt/smt_enode.cpp | 1 + src/smt/smt_enode.h | 16 ++++++++++++++++ 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index ac60a98ba..3683295d6 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -476,6 +476,7 @@ namespace euf { c->m_root = r2; std::swap(r1->m_next, r2->m_next); r2->inc_class_size(r1->class_size()); + r2->set_is_shared(l_undef); merge_th_eq(r1, r2); reinsert_parents(r1, r2); if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr()))) @@ -553,6 +554,7 @@ namespace euf { enode* r2 = r1->get_root(); TRACE("euf", tout << "undo-eq old-root: " << bpp(r1) << " current-root " << bpp(r2) << " node: " << bpp(n1) << "\n";); r2->dec_class_size(r1->class_size()); + r2->set_is_shared(l_undef); std::swap(r1->m_next, r2->m_next); auto begin = r2->begin_parents() + r2_num_parents, end = r2->end_parents(); for (auto it = begin; it != end; ++it) { diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index d9ae45074..cd47da2b1 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -52,6 +52,7 @@ namespace euf { bool m_merge_tf_enabled = false; bool m_is_equality = false; // Does the expression represent an equality bool m_is_relevant = false; + lbool m_is_shared = l_undef; lbool m_value = l_undef; // Assignment by SAT solver for Boolean node sat::bool_var m_bool_var = sat::null_bool_var; // SAT solver variable associated with Boolean node unsigned m_class_size = 1; // Size of the equivalence class if the enode is the root. @@ -181,6 +182,9 @@ namespace euf { void unmark3() { m_mark3 = false; } bool is_marked3() { return m_mark3; } + lbool is_shared() const { return m_is_shared; } + void set_is_shared(lbool s) { m_is_shared = s; } + template void mark1_targets() { enode* n = this; while (n) { diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index a1d383e45..22e220eaa 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -355,8 +355,16 @@ namespace euf { bool solver::is_shared(enode* n) const { n = n->get_root(); - if (m.is_ite(n->get_expr())) + switch (n->is_shared()) { + case l_true: return true; + case l_false: return false; + default: break; + } + + if (m.is_ite(n->get_expr())) { + n->set_is_shared(l_true); return true; + } // the variable is shared if the equivalence class of n // contains a parent application. @@ -366,21 +374,27 @@ namespace euf { family_id id = p.get_id(); if (m.get_basic_family_id() != id) { - if (th_id != m.get_basic_family_id()) + if (th_id != m.get_basic_family_id()) { + n->set_is_shared(l_true); return true; + } th_id = id; } } - if (m.is_bool(n->get_expr()) && th_id != m.get_basic_family_id()) + if (m.is_bool(n->get_expr()) && th_id != m.get_basic_family_id()) { + n->set_is_shared(l_true); return true; + } for (enode* parent : euf::enode_parents(n)) { app* p = to_app(parent->get_expr()); family_id fid = p->get_family_id(); if (is_beta_redex(parent, n)) continue; - if (fid != th_id && fid != m.get_basic_family_id()) + if (fid != th_id && fid != m.get_basic_family_id()) { + n->set_is_shared(l_true); return true; + } } // Some theories implement families of theories. Examples: @@ -411,9 +425,12 @@ namespace euf { // not marked as shared. for (auto const& p : euf::enode_th_vars(n)) - if (fid2solver(p.get_id())->is_shared(p.get_var())) + if (fid2solver(p.get_id())->is_shared(p.get_var())) { + n->set_is_shared(l_true); return true; + } + n->set_is_shared(l_false); return false; } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 64c7bbf2e..577123ec9 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -560,6 +560,7 @@ namespace smt { // Update "equivalence" class size r2->m_class_size += r1->m_class_size; + r2->m_is_shared = 2; CASSERT("add_eq", check_invariant()); } @@ -920,6 +921,7 @@ namespace smt { // restore r2 class size r2->m_class_size -= r1->m_class_size; + r2->m_is_shared = 2; // unmerge "equivalence" classes std::swap(r1->m_next, r2->m_next); @@ -4504,8 +4506,15 @@ namespace smt { bool context::is_shared(enode * n) const { n = n->get_root(); + switch (n->is_shared()) { + case l_true: return true; + case l_false: return false; + default: break; + } + unsigned num_th_vars = n->get_num_th_vars(); if (m.is_ite(n->get_expr())) { + n->set_is_shared(l_true); return true; } switch (num_th_vars) { @@ -4531,6 +4540,7 @@ namespace smt { TRACE("is_shared", tout << enode_pp(n, *this) << "\nis shared because of:\n" << enode_pp(parent, *this) << "\n";); + n->set_is_shared(l_true); return true; } } @@ -4561,7 +4571,9 @@ namespace smt { // the theories of (array int int) and (array (array int int) int). // Remark: The inconsistency is not going to be detected if they are // not marked as shared. - return get_theory(th_id)->is_shared(l->get_var()); + bool r = get_theory(th_id)->is_shared(l->get_var()); + n->set_is_shared(to_lbool(r)); + return r; } default: return true; diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index 49f05b019..02ea82ba6 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -49,6 +49,7 @@ namespace smt { n->m_iscope_lvl = iscope_lvl; n->m_lbl_hash = -1; n->m_proof_is_logged = false; + n->m_is_shared = 2; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; diff --git a/src/smt/smt_enode.h b/src/smt/smt_enode.h index b3a3bbf69..92902ea0b 100644 --- a/src/smt/smt_enode.h +++ b/src/smt/smt_enode.h @@ -77,6 +77,7 @@ namespace smt { unsigned m_bool:1; //!< True if it is a boolean enode unsigned m_merge_tf:1; //!< True if the enode should be merged with true/false when the associated boolean variable is assigned. unsigned m_cgc_enabled:1; //!< True if congruence closure is enabled for this enode. + unsigned m_is_shared:2; //!< 0 - not shared, 1 - shared, 2 - invalid state unsigned m_iscope_lvl; //!< When the enode was internalized bool m_proof_is_logged; //!< Indicates that the proof for the enode being equal to its root is in the log. signed char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern @@ -179,6 +180,21 @@ namespace smt { return m_owner->hash(); } + lbool is_shared() const { + switch (m_is_shared) { + case 0: return l_false; + case 1: return l_true; + default: return l_undef; + } + } + + void set_is_shared(lbool s) { + switch (s) { + case l_true: m_is_shared = 1; break; + case l_false: m_is_shared = 0; break; + default: m_is_shared = 2; break; + } + } enode * get_root() const { return m_root; From db097bae0556c3cf40918ad15588d09d46f010b4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 17:24:46 -0700 Subject: [PATCH 186/428] use format from unit_prop_on_monomials branch Signed-off-by: Nikolaj Bjorner --- src/math/lp/.clang-format | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/math/lp/.clang-format b/src/math/lp/.clang-format index f5c8a41b2..d7f8d6171 100644 --- a/src/math/lp/.clang-format +++ b/src/math/lp/.clang-format @@ -1,4 +1,5 @@ BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 0 -NamespaceIndentation: All \ No newline at end of file +NamespaceIndentation: All +BreakBeforeBraces: Stroustrup \ No newline at end of file From 87a839c794a78c0ac2962b5769b66e34b78fb653 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2023 18:07:56 -0700 Subject: [PATCH 187/428] fixup unit test for added argument to constructor of nla_solver Signed-off-by: Nikolaj Bjorner --- src/test/lp/nla_solver_test.cpp | 36 ++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/test/lp/nla_solver_test.cpp b/src/test/lp/nla_solver_test.cpp index ce934e7ca..c9ab08f28 100644 --- a/src/test/lp/nla_solver_test.cpp +++ b/src/test/lp/nla_solver_test.cpp @@ -169,7 +169,8 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); v.clear(); @@ -246,7 +247,8 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); @@ -317,7 +319,8 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); create_abcde(nla, lp_a, @@ -379,7 +382,8 @@ void test_basic_lemma_for_mon_zero_from_monomial_to_factors() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial acd unsigned_vector vec; @@ -439,7 +443,8 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); create_abcde(nla, lp_a, @@ -514,7 +519,8 @@ void test_horner() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); vector v; v.push_back(a); v.push_back(b); nla.add_monic(lp_ab, v.size(), v.begin()); @@ -551,7 +557,8 @@ void test_basic_sign_lemma() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial bde vector vec; @@ -626,7 +633,8 @@ void test_order_lemma_params(bool var_equiv, int sign) { reslimit l; params_ref p; - solver nla(s,p,l); + std_vector ib; + solver nla(s,p,l,ib); // create monomial ab vector vec; vec.push_back(lp_a); @@ -757,7 +765,8 @@ void test_monotone_lemma() { reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial ab vector vec; vec.push_back(lp_a); @@ -814,7 +823,8 @@ void test_tangent_lemma_rat() { s_set_column_value_test(s, lp_ab, v); reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial ab vector vec; vec.push_back(lp_a); @@ -841,7 +851,8 @@ void test_tangent_lemma_reg() { s_set_column_value_test(s, lp_ab, rational(11)); reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial ab vector vec; vec.push_back(lp_a); @@ -885,7 +896,8 @@ void test_tangent_lemma_equiv() { s_set_column_value_test(s, lp_a, - s.get_column_value(lp_k)); reslimit l; params_ref p; - solver nla(s, p, l); + std_vector ib; + solver nla(s, p, l, ib); // create monomial ab vector vec; vec.push_back(lp_a); From 029d726eb8aabbf9bc0b7266f1e7aa27b05c1970 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 25 Sep 2023 15:33:40 +0100 Subject: [PATCH 188/428] minor code simplification --- src/smt/smt_context.h | 4 +--- src/smt/smt_context_pp.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 7a267fdec..f9560cb01 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -134,7 +134,6 @@ namespace smt { enode * m_lhs; enode * m_rhs; eq_justification m_justification; - new_eq() {} new_eq(enode * lhs, enode * rhs, eq_justification const & js): m_lhs(lhs), m_rhs(rhs), m_justification(js) {} }; @@ -143,7 +142,6 @@ namespace smt { theory_id m_th_id; theory_var m_lhs; theory_var m_rhs; - new_th_eq():m_th_id(null_theory_id), m_lhs(null_theory_var), m_rhs(null_theory_var) {} new_th_eq(theory_id id, theory_var l, theory_var r):m_th_id(id), m_lhs(l), m_rhs(r) {} }; svector m_th_eq_propagation_queue; @@ -215,7 +213,7 @@ namespace smt { // ----------------------------------- proto_model_ref m_proto_model; model_ref m_model; - std::string m_unknown; + const char * m_unknown; void mk_proto_model(); void reset_model() { m_model = nullptr; m_proto_model = nullptr; } diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index fe86c6811..3925a4e21 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -66,6 +66,7 @@ namespace smt { std::string context::last_failure_as_string() const { std::string r; switch(m_last_search_failure) { + case UNKNOWN: case OK: r = m_unknown; break; case MEMOUT: r = "memout"; break; case CANCELED: r = "canceled"; break; @@ -82,7 +83,6 @@ namespace smt { case RESOURCE_LIMIT: r = "(resource limits reached)"; break; case QUANTIFIERS: r = "(incomplete quantifiers)"; break; case LAMBDAS: r = "(incomplete lambdas)"; break; - case UNKNOWN: r = m_unknown; break; } return r; } From 26a9b776c65076c647f9793fbe512ab43ff46d3e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 25 Sep 2023 12:10:59 -0700 Subject: [PATCH 189/428] clean m_nla_lemma_vector in nla_solver Signed-off-by: Lev Nachmanson --- src/math/lp/nla_core.cpp | 3 ++- src/math/lp/nla_core.h | 2 +- src/math/lp/nla_solver.cpp | 4 ++-- src/math/lp/nla_solver.h | 2 +- src/smt/theory_lra.cpp | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 38f741388..84367fbbc 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -2027,11 +2027,12 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } } - void core::init_bound_propagation() { + void core::init_bound_propagation(vector& lemma_vector) { m_implied_bounds.clear(); m_improved_lower_bounds.reset(); m_improved_upper_bounds.reset(); m_column_types = &lra.get_column_types(); + lemma_vector.clear(); } } // namespace nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 54c7e1df1..c561b91c0 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -451,7 +451,7 @@ private: void save_tableau(); bool integrality_holds(); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); + void init_bound_propagation(vector &); }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 3475a3509..e8b2d9d1d 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -100,8 +100,8 @@ namespace nla { m_core->check_bounded_divisions(lemmas); } - void solver::init_bound_propagation() { - m_core->init_bound_propagation(); + void solver::init_bound_propagation(vector& nla_lemma_vector) { + m_core->init_bound_propagation(nla_lemma_vector); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index a4de90320..7f52b5c92 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -49,6 +49,6 @@ namespace nla { nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); + void init_bound_propagation(vector&); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 21e021ea7..5405f7ff1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2201,7 +2201,7 @@ public: } void propagate_bounds_for_touched_monomials() { - m_nla->init_bound_propagation(); + m_nla->init_bound_propagation(m_nla_lemma_vector); for (unsigned v : m_nla->monics_with_changed_bounds()) m_nla->calculate_implied_bounds_for_monic(v); From 0a1ade6f9547040f7564623ec79fc1efbdf777a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Sep 2023 12:40:52 -0700 Subject: [PATCH 190/428] move m_nla_lemma_vector to be internal to nla_core Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_basics_lemmas.cpp | 2 +- src/math/lp/nla_core.cpp | 49 +++++++++++++++---------------- src/math/lp/nla_core.h | 16 +++++----- src/math/lp/nla_solver.cpp | 20 ++++++++----- src/math/lp/nla_solver.h | 14 +++++---- src/sat/smt/arith_solver.cpp | 4 +-- src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 20 ++++++------- 8 files changed, 65 insertions(+), 61 deletions(-) diff --git a/src/math/lp/nla_basics_lemmas.cpp b/src/math/lp/nla_basics_lemmas.cpp index 7124fd409..191e59a79 100644 --- a/src/math/lp/nla_basics_lemmas.cpp +++ b/src/math/lp/nla_basics_lemmas.cpp @@ -104,7 +104,7 @@ bool basics::basic_sign_lemma_model_based() { return true; } } - return c().m_lemma_vec->size() > 0; + return c().m_lemmas.size() > 0; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 84367fbbc..365f81774 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -829,7 +829,7 @@ void core::print_stats(std::ostream& out) { void core::clear() { - m_lemma_vec->clear(); + m_lemmas.clear(); m_literal_vec->clear(); } @@ -1066,7 +1066,7 @@ rational core::val(const factorization& f) const { } new_lemma::new_lemma(core& c, char const* name):name(name), c(c) { - c.m_lemma_vec->push_back(lemma()); + c.m_lemmas.push_back(lemma()); } new_lemma& new_lemma::operator|=(ineq const& ineq) { @@ -1096,7 +1096,7 @@ new_lemma::~new_lemma() { } lemma& new_lemma::current() const { - return c.m_lemma_vec->back(); + return c.m_lemmas.back(); } new_lemma& new_lemma::operator&=(lp::explanation const& e) { @@ -1209,7 +1209,7 @@ void core::negate_relation(new_lemma& lemma, unsigned j, const rational& a) { } bool core::conflict_found() const { - for (const auto & l : * m_lemma_vec) { + for (const auto & l : m_lemmas) { if (l.is_conflict()) return true; } @@ -1217,7 +1217,7 @@ bool core::conflict_found() const { } bool core::done() const { - return m_lemma_vec->size() >= 10 || + return m_lemmas.size() >= 10 || conflict_found() || lp_settings().get_cancel_flag(); } @@ -1506,7 +1506,7 @@ void core::check_weighted(unsigned sz, std::pair 0 && !done() && m_lemma_vec->empty()) { + while (bound > 0 && !done() && m_lemmas.empty()) { unsigned n = random() % bound; for (unsigned i = 0; i < sz; ++i) { if (seen.contains(i)) @@ -1522,13 +1522,13 @@ void core::check_weighted(unsigned sz, std::pair& l_vec) { - m_lemma_vec = &l_vec; - return m_powers.check(r, x, y, l_vec); +lbool core::check_power(lpvar r, lpvar x, lpvar y) { + m_lemmas.reset(); + return m_powers.check(r, x, y, m_lemmas); } -void core::check_bounded_divisions(vector& l_vec) { - m_lemma_vec = &l_vec; +void core::check_bounded_divisions() { + m_lemmas.reset(); m_divisions.check_bounded_divisions(); } // looking for a free variable inside of a monic to split @@ -1547,11 +1547,10 @@ void core::add_bounds() { } } -lbool core::check(vector& lits, vector& l_vec) { +lbool core::check(vector& lits) { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); - m_lemma_vec = &l_vec; m_literal_vec = &lits; if (!(lra.get_status() == lp::lp_status::OPTIMAL || lra.get_status() == lp::lp_status::FEASIBLE)) { @@ -1572,7 +1571,7 @@ lbool core::check(vector& lits, vector& l_vec) { bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && l_vec.empty() && lits.empty(); }; + auto no_effect = [&]() { return !done() && m_lemmas.empty() && lits.empty(); }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1590,7 +1589,7 @@ lbool core::check(vector& lits, vector& l_vec) { {1, check2}, {1, check3} }; check_weighted(3, checks); - if (!l_vec.empty() || !lits.empty()) + if (!m_lemmas.empty() || !lits.empty()) return l_false; } @@ -1627,15 +1626,15 @@ lbool core::check(vector& lits, vector& l_vec) { m_stats.m_nra_calls++; } - if (ret == l_undef && !l_vec.empty() && m_reslim.inc()) + if (ret == l_undef && !m_lemmas.empty() && m_reslim.inc()) ret = l_false; - m_stats.m_nla_lemmas += l_vec.size(); - for (const auto& l : l_vec) + m_stats.m_nla_lemmas += m_lemmas.size(); + for (const auto& l : m_lemmas) m_stats.m_nla_explanations += static_cast(l.expl().size()); - TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << l_vec.size() << "\n";); + TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";); IF_VERBOSE(2, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());}); CTRACE("nla_solver", ret == l_undef, tout << "Monomials\n"; print_monics(tout);); return ret; @@ -1670,13 +1669,13 @@ lbool core::bounded_nlsat() { m_nlsat_delay /= 2; } if (ret == l_true) { - m_lemma_vec->reset(); + m_lemmas.reset(); } return ret; } bool core::no_lemmas_hold() const { - for (auto & l : * m_lemma_vec) { + for (auto & l : m_lemmas) { if (lemma_holds(l)) { TRACE("nla_solver", print_lemma(l, tout);); return false; @@ -1685,10 +1684,10 @@ bool core::no_lemmas_hold() const { return true; } -lbool core::test_check(vector& l) { +lbool core::test_check() { vector lits; lra.set_status(lp::lp_status::OPTIMAL); - return check(lits, l); + return check(lits); } std::ostream& core::print_terms(std::ostream& out) const { @@ -2027,12 +2026,12 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } } - void core::init_bound_propagation(vector& lemma_vector) { + void core::init_bound_propagation() { m_implied_bounds.clear(); m_improved_lower_bounds.reset(); m_improved_upper_bounds.reset(); m_column_types = &lra.get_column_types(); - lemma_vector.clear(); + m_lemmas.clear(); } } // namespace nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index c561b91c0..90bfa18ca 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -85,7 +85,7 @@ class core { reslimit& m_reslim; smt_params_helper m_params; std::function m_relevant; - vector * m_lemma_vec; + vector m_lemmas; vector * m_literal_vec = nullptr; indexed_uint_set m_to_refine; vector m_monics_with_changed_bounds; @@ -393,15 +393,15 @@ public: bool conflict_found() const; - lbool check(vector& ineqs, vector& l_vec); - lbool check_power(lpvar r, lpvar x, lpvar y, vector& l_vec); - void check_bounded_divisions(vector&); + lbool check(vector& ineqs); + lbool check_power(lpvar r, lpvar x, lpvar y); + void check_bounded_divisions(); bool no_lemmas_hold() const; - void propagate(vector& lemmas); + // void propagate(); - lbool test_check(vector& l); + lbool test_check(); lpvar map_to_root(lpvar) const; std::ostream& print_terms(std::ostream&) const; std::ostream& print_term(const lp::lar_term&, std::ostream&) const; @@ -443,6 +443,8 @@ public: void add_upper_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); bool upper_bound_is_available(unsigned j) const; bool lower_bound_is_available(unsigned j) const; + vector const& lemmas() const { return m_lemmas; } + private: lp::column_type get_column_type(unsigned j) const { return (*m_column_types)[j]; } void constrain_nl_in_tableau(); @@ -451,7 +453,7 @@ private: void save_tableau(); bool integrality_holds(); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(vector &); + void init_bound_propagation(); }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index e8b2d9d1d..b16d14021 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -42,8 +42,8 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check(vector& lits, vector& lemmas) { - return m_core->check(lits, lemmas); + lbool solver::check(vector& lits) { + return m_core->check(lits); } void solver::push(){ @@ -92,16 +92,20 @@ namespace nla { m_core->calculate_implied_bounds_for_monic(v); } // ensure r = x^y, add abstraction/refinement lemmas - lbool solver::check_power(lpvar r, lpvar x, lpvar y, vector& lemmas) { - return m_core->check_power(r, x, y, lemmas); + lbool solver::check_power(lpvar r, lpvar x, lpvar y) { + return m_core->check_power(r, x, y); } - void solver::check_bounded_divisions(vector& lemmas) { - m_core->check_bounded_divisions(lemmas); + void solver::check_bounded_divisions() { + m_core->check_bounded_divisions(); } - void solver::init_bound_propagation(vector& nla_lemma_vector) { - m_core->init_bound_propagation(nla_lemma_vector); + void solver::init_bound_propagation() { + m_core->init_bound_propagation(); + } + + vector const& solver::lemmas() const { + return m_core->lemmas(); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 7f52b5c92..b4fba1f86 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -23,7 +23,7 @@ namespace nla { class solver { core* m_core; public: - + solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds); ~solver(); const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } @@ -32,14 +32,14 @@ namespace nla { void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); void add_bounded_division(lpvar q, lpvar x, lpvar y); - void check_bounded_divisions(vector&); + void check_bounded_divisions(); void set_relevant(std::function& is_relevant); void push(); void pop(unsigned scopes); bool need_check(); - lbool check(vector& lits, vector&); - void propagate(vector& lemmas); - lbool check_power(lpvar r, lpvar x, lpvar y, vector&); + lbool check(vector& lits); + void propagate(); + lbool check_power(lpvar r, lpvar x, lpvar y); bool is_monic_var(lpvar) const; bool influences_nl_var(lpvar) const; std::ostream& display(std::ostream& out) const; @@ -49,6 +49,8 @@ namespace nla { nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(vector&); + void init_bound_propagation(); + vector const& lemmas() const; + }; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 3cf991c20..893fe4a43 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1459,11 +1459,11 @@ namespace arith { return l_true; m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals); switch (r) { case l_false: assume_literals(); - for (const nla::lemma& l : m_nla_lemma_vector) + for (const nla::lemma& l : m_nla->lemmas()) false_case_of_check_nla(l); break; case l_true: diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 8917a3e42..53b49a658 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -249,7 +249,6 @@ namespace arith { // lemmas lp::explanation m_explanation; - vector m_nla_lemma_vector; vector m_nla_literals; literal_vector m_core, m_core2; vector m_coeffs; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 5405f7ff1..ce53b8f25 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1601,11 +1601,11 @@ public: return FC_DONE; if (!m_nla) return FC_GIVEUP; - switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { + switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y))) { case l_true: return FC_DONE; case l_false: - for (const nla::lemma & l : m_nla_lemma_vector) + for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); return FC_CONTINUE; case l_undef: @@ -1802,11 +1802,10 @@ public: bool check_idiv_bounds() { if (!m_nla) return true; - m_nla_lemma_vector.reset(); - m_nla->check_bounded_divisions(m_nla_lemma_vector); - for (auto & lemma : m_nla_lemma_vector) + m_nla->check_bounded_divisions(); + for (auto & lemma : m_nla->lemmas()) false_case_of_check_nla(lemma); - return m_nla_lemma_vector.empty(); + return m_nla->lemmas().empty(); } expr_ref var2expr(lpvar v) { @@ -2025,13 +2024,13 @@ public: final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals); switch (r) { case l_false: for (const nla::ineq& i : m_nla_literals) assume_literal(i); - for (const nla::lemma & l : m_nla_lemma_vector) + for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); return FC_CONTINUE; case l_true: @@ -2201,12 +2200,12 @@ public: } void propagate_bounds_for_touched_monomials() { - m_nla->init_bound_propagation(m_nla_lemma_vector); + m_nla->init_bound_propagation(); for (unsigned v : m_nla->monics_with_changed_bounds()) m_nla->calculate_implied_bounds_for_monic(v); m_nla->reset_monics_with_changed_bounds(); - for (const auto & l : m_nla_lemma_vector) + for (const auto & l : m_nla->lemmas()) false_case_of_check_nla(l); } @@ -3210,7 +3209,6 @@ public: } lp::explanation m_explanation; - vector m_nla_lemma_vector; vector m_nla_literals; literal_vector m_core; svector m_eqs; From d2c0f7dba797f3823f1c82f200e9d6acc98f01a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Sep 2023 12:48:52 -0700 Subject: [PATCH 191/428] fix test build Signed-off-by: Nikolaj Bjorner --- src/test/lp/nla_solver_test.cpp | 37 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/test/lp/nla_solver_test.cpp b/src/test/lp/nla_solver_test.cpp index c9ab08f28..c7ff5b256 100644 --- a/src/test/lp/nla_solver_test.cpp +++ b/src/test/lp/nla_solver_test.cpp @@ -180,7 +180,6 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { v.push_back(lp_a);v.push_back(lp_c); nla.add_monic(lp_ac, v.size(), v.begin()); - vector lv; // set abcde = ac * bde // ac = 1 then abcde = bde, but we have abcde < bde @@ -194,9 +193,9 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { s.set_column_value_test(lp_bde, lp::impq(rational(16))); - VERIFY(nla.get_core().test_check(lv) == l_false); - - nla.get_core().print_lemma(lv.back(), std::cout); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemmas = nla.get_core().lemmas(); + nla.get_core().print_lemma(lemmas.back(), std::cout); ineq i0(lp_ac, llc::NE, 1); lp::lar_term t1, t2; @@ -209,7 +208,7 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { bool found0 = false; bool found1 = false; bool found2 = false; - for (const auto& k : lv[0].ineqs()){ + for (const auto& k : lemmas[0].ineqs()){ if (k == i0) { found0 = true; } else if (k == i1) { @@ -252,7 +251,6 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); - vector lemma; s_set_column_value_test(s, lp_a, rational(1)); s_set_column_value_test(s, lp_b, rational(1)); @@ -261,7 +259,8 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { s_set_column_value_test(s, lp_e, rational(1)); s_set_column_value_test(s, lp_bde, rational(3)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); SASSERT(lemma[0].size() == 4); nla.get_core().print_lemma(lemma.back(), std::cout); @@ -333,7 +332,6 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { lp_bde, lp_acd, lp_be); - vector lemma; // set vars s_set_column_value_test(s, lp_a, rational(1)); @@ -347,7 +345,8 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { s_set_column_value_test(s, lp_acd, rational(1)); s_set_column_value_test(s, lp_be, rational(1)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); SASSERT(lemma.size() == 1 && lemma[0].size() == 2); lp::lar_term t0, t1; @@ -393,13 +392,13 @@ void test_basic_lemma_for_mon_zero_from_monomial_to_factors() { vec.push_back(lp_d); nla.add_monic(lp_acd, vec.size(), vec.begin()); - vector lemma; s_set_column_value_test(s, lp_a, rational(1)); s_set_column_value_test(s, lp_c, rational(1)); s_set_column_value_test(s, lp_d, rational(1)); s_set_column_value_test(s, lp_acd, rational(0)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); @@ -457,7 +456,6 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { lp_bde, lp_acd, lp_be); - vector lemma; // set all vars to 1 s_set_column_value_test(s, lp_a, rational(1)); @@ -476,7 +474,8 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { s_set_column_value_test(s, lp_b, - rational(2)); // we have bde = -b, therefore d = +-1 and e = +-1 s_set_column_value_test(s, lp_d, rational(3)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); @@ -591,8 +590,8 @@ void test_basic_sign_lemma() { s_set_column_value_test(s, lp_bde, rational(5)); s_set_column_value_test(s, lp_acd, rational(3)); - vector lemmas; - VERIFY(nla.get_core().test_check(lemmas) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemmas = nla.get_core().lemmas(); lp::lar_term t; t.add_var(lp_bde); @@ -831,8 +830,8 @@ void test_tangent_lemma_rat() { vec.push_back(lp_b); nla.add_monic(lp_ab, vec.size(), vec.begin()); - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); } @@ -859,8 +858,8 @@ void test_tangent_lemma_reg() { vec.push_back(lp_b); nla.add_monic(lp_ab, vec.size(), vec.begin()); - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); } From 896aba31f8b173a2108e7bf6fee778037acb4662 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 25 Sep 2023 14:20:24 -0700 Subject: [PATCH 192/428] move monomial propagation from theory_lra to nla_solver Signed-off-by: Lev Nachmanson --- src/math/lp/nla_solver.cpp | 6 ++++++ src/math/lp/nla_solver.h | 2 +- src/smt/theory_lra.cpp | 11 +++-------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index b16d14021..236b85d4f 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -108,4 +108,10 @@ namespace nla { return m_core->lemmas(); } + void solver::propagate_bounds_for_touched_monomials() { + init_bound_propagation(); + for (unsigned v : monics_with_changed_bounds()) + calculate_implied_bounds_for_monic(v); + reset_monics_with_changed_bounds(); + } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index b4fba1f86..0448d840f 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -51,6 +51,6 @@ namespace nla { void calculate_implied_bounds_for_monic(lp::lpvar v); void init_bound_propagation(); vector const& lemmas() const; - + void propagate_bounds_for_touched_monomials(); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ce53b8f25..3b6a71b3d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2199,12 +2199,8 @@ public: finish_bound_propagation(); } - void propagate_bounds_for_touched_monomials() { - m_nla->init_bound_propagation(); - for (unsigned v : m_nla->monics_with_changed_bounds()) - m_nla->calculate_implied_bounds_for_monic(v); - - m_nla->reset_monics_with_changed_bounds(); + void propagate_bounds_for_monomials() { + m_nla->propagate_bounds_for_touched_monomials(); for (const auto & l : m_nla->lemmas()) false_case_of_check_nla(l); } @@ -2215,8 +2211,7 @@ public: if (is_infeasible() || !should_propagate()) return; - m_bp.init(); - propagate_bounds_for_touched_monomials(); + propagate_bounds_for_monomials(); if (m.inc()) finish_bound_propagation(); From 6ff4856e38fffa278c2a076ba18038199cddd12a Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 25 Sep 2023 16:47:34 -0700 Subject: [PATCH 193/428] throttle monomial unit prop and and nl params --- src/math/lp/nla_core.cpp | 31 +++++++++++++++++++++++++++- src/math/lp/nla_core.h | 3 ++- src/smt/params/smt_params_helper.pyg | 2 ++ src/smt/theory_lra.cpp | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 365f81774..0788510ef 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1913,6 +1913,10 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { + if (params().arith_nl_use_lemmas_in_unit_prop()) { + propagate_monic_non_fixed_with_lemma(monic_var, vars, non_fixed, k); + return; + } lp::impq bound_value; bool is_strict; auto& lps = lra; @@ -2002,13 +2006,38 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: add_lower_bound_monic(monic_var, lp::mpq(0), false, lambda); add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); } + + void core::propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { + lp::impq bound_value; + new_lemma lemma(*this, "propagate monic with non fixed"); + // using += to not assert thath the inequality does not hold + lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); + lp::explanation exp; + for (auto v : m_emons[monic_var].vars()) { + if (v == non_fixed) continue; + u_dependency* dep = lra.get_column_lower_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } + dep = lra.get_column_upper_bound_witness(v); + for (auto ci : lra.flatten(dep)) { + exp.push_back(ci); + } + } + lemma &= exp; + } void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) { + m_propagated.reserve(monic_var + 1, false); + bool throttle = params().arith_nl_throttle_unit_prop(); + if (throttle && m_propagated[monic_var]) + return; lpvar non_fixed, zero_var; const auto& vars = m_emons[monic_var].vars(); if (!is_linear(vars, zero_var, non_fixed)) return; - + if (throttle) + trail().push(set_bitvector_trail(m_propagated, monic_var)); if (zero_var != null_lpvar) add_bounds_for_zero_var(monic_var, zero_var); else { diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 90bfa18ca..e1fae2aff 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -114,7 +114,7 @@ class core { bool m_cautious_patching = true; lpvar m_patched_var = 0; monic const* m_patched_monic = nullptr; - + bool_vector m_propagated; void check_weighted(unsigned sz, std::pair>* checks); void add_bounds(); std_vector & m_implied_bounds; @@ -438,6 +438,7 @@ public: bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed); void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var); void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); + void core::propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k); void add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); void add_upper_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index f059dccb8..ab9f56c96 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -71,6 +71,8 @@ def_module_params(module_name='smt', ('arith.nl.grobner_row_length_limit', UINT, 10, 'row is disregarded by the heuristic if its length is longer than the value'), ('arith.nl.grobner_frequency', UINT, 4, 'grobner\'s call frequency'), ('arith.nl.grobner', BOOL, True, 'run grobner\'s basis heuristic'), + ('arith.nl.use_lemmas_in_unit_prop', BOOL, False, 'use lemmas in monomial unit propagation'), + ('arith.nl.throttle_unit_prop', BOOL, True, 'unit propogate a monomial only once per scope'), ('arith.nl.grobner_eqs_growth', UINT, 10, 'grobner\'s number of equalities growth '), ('arith.nl.grobner_expr_size_growth', UINT, 2, 'grobner\'s maximum expr size growth'), ('arith.nl.grobner_expr_degree_growth', UINT, 2, 'grobner\'s maximum expr degree growth'), diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 3b6a71b3d..e85b2722a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3209,7 +3209,7 @@ public: svector m_eqs; vector m_params; - void reset_evidence() { + void reset_evidence() { m_core.reset(); m_eqs.reset(); m_params.reset(); From 368fe80b3d31e946fbca1b4758994eb29aa26d1d Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 25 Sep 2023 16:55:02 -0700 Subject: [PATCH 194/428] fix the build --- src/math/lp/nla_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index e1fae2aff..a6bc6b3b4 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -438,7 +438,7 @@ public: bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed); void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var); void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); - void core::propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); + void propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k); void add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); void add_upper_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); From 8d2b65b20bafa7cb016d44e9799047ecda4196fa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 19:18:44 -0700 Subject: [PATCH 195/428] add options to allow testing the effect of non-linear hammers Signed-off-by: Nikolaj Bjorner --- src/smt/params/smt_params_helper.pyg | 5 ++++- src/smt/params/theory_arith_params.cpp | 6 ++++++ src/smt/params/theory_arith_params.h | 3 +++ src/smt/theory_arith_nl.h | 6 +++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index f059dccb8..9fcda7f64 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -78,7 +78,10 @@ def_module_params(module_name='smt', ('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'), ('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'), ('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'), - ('arith.nl.delay', UINT, 500, 'number of calls to final check before invoking bounded nlsat check'), + ('arith.nl.delay', UINT, 500, 'number of calls to final check before invoking bounded nlsat check'), + ('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'), + ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), + ('arith.nl.cross_nested', BOOL, True, 'enable cross-nested consistency checking'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), ('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 7f3f1ca23..9bc830dd1 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -36,6 +36,9 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_eager_eq_axioms = p.arith_eager_eq_axioms(); m_arith_auto_config_simplex = p.arith_auto_config_simplex(); + m_nl_arith_propagate_linear_monomials = p.arith_nl_propagate_linear_monomials(); + m_nl_arith_optimize_bounds = p.arith_nl_optimize_bounds(); + m_nl_arith_cross_nested = p.arith_nl_cross_nested(); arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); @@ -89,4 +92,7 @@ void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_nl_arith_max_degree); DISPLAY_PARAM(m_nl_arith_branching); DISPLAY_PARAM(m_nl_arith_rounds); + DISPLAY_PARAM(m_nl_arith_propagate_linear_monomials); + DISPLAY_PARAM(m_nl_arith_optimize_bounds); + DISPLAY_PARAM(m_nl_arith_cross_nested); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 526cb6f09..f78086300 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -105,6 +105,9 @@ struct theory_arith_params { unsigned m_nl_arith_max_degree = 6; bool m_nl_arith_branching = true; unsigned m_nl_arith_rounds = 1024; + bool m_nl_arith_propagate_linear_monomials = true; + bool m_nl_arith_optimize_bounds = true; + bool m_nl_arith_cross_nested = true; theory_arith_params(params_ref const & p = params_ref()) { diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index f44516cad..ae0af89ec 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -902,6 +902,8 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { */ template bool theory_arith::propagate_linear_monomials() { + if (!m_params.m_nl_arith_propagate_linear_monomials) + return false; if (!reflection_enabled()) return false; TRACE("non_linear", tout << "propagating linear monomials...\n";); @@ -2278,6 +2280,8 @@ typename theory_arith::gb_result theory_arith::compute_grobner(svector */ template bool theory_arith::max_min_nl_vars() { + if (!m_params.m_nl_arith_optimize_bounds) + return true; var_set already_found; svector vars; for (theory_var v : m_nl_monomials) { @@ -2360,7 +2364,7 @@ final_check_status theory_arith::process_non_linear() { } break; case 1: - if (!is_cross_nested_consistent(vars)) + if (m_params.m_nl_arith_cross_nested && !is_cross_nested_consistent(vars)) progress = true; break; case 2: From ec2937e2debb85448c8c008e742a2dbac36a022f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 20:08:30 -0700 Subject: [PATCH 196/428] port over moving m_nla_lemmas into nla_core from the linear monomial propagation branch Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_basics_lemmas.cpp | 2 +- src/math/lp/nla_core.cpp | 50 +++++++++++++++---------------- src/math/lp/nla_core.h | 13 ++++---- src/math/lp/nla_solver.cpp | 19 +++++++----- src/math/lp/nla_solver.h | 9 +++--- src/sat/smt/arith_solver.cpp | 4 +-- src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 16 +++++----- src/test/lp/nla_solver_test.cpp | 50 ++++++++++++++----------------- 9 files changed, 80 insertions(+), 84 deletions(-) diff --git a/src/math/lp/nla_basics_lemmas.cpp b/src/math/lp/nla_basics_lemmas.cpp index 7124fd409..191e59a79 100644 --- a/src/math/lp/nla_basics_lemmas.cpp +++ b/src/math/lp/nla_basics_lemmas.cpp @@ -104,7 +104,7 @@ bool basics::basic_sign_lemma_model_based() { return true; } } - return c().m_lemma_vec->size() > 0; + return c().m_lemmas.size() > 0; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 3ab89e012..4990a087c 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -808,7 +808,7 @@ void core::print_stats(std::ostream& out) { void core::clear() { - m_lemma_vec->clear(); + m_lemmas.clear(); m_literal_vec->clear(); } @@ -1045,7 +1045,7 @@ rational core::val(const factorization& f) const { } new_lemma::new_lemma(core& c, char const* name):name(name), c(c) { - c.m_lemma_vec->push_back(lemma()); + c.m_lemmas.push_back(lemma()); } new_lemma& new_lemma::operator|=(ineq const& ineq) { @@ -1067,7 +1067,7 @@ new_lemma::~new_lemma() { } lemma& new_lemma::current() const { - return c.m_lemma_vec->back(); + return c.m_lemmas.back(); } new_lemma& new_lemma::operator&=(lp::explanation const& e) { @@ -1180,7 +1180,7 @@ void core::negate_relation(new_lemma& lemma, unsigned j, const rational& a) { } bool core::conflict_found() const { - for (const auto & l : * m_lemma_vec) { + for (const auto & l : m_lemmas) { if (l.is_conflict()) return true; } @@ -1188,7 +1188,7 @@ bool core::conflict_found() const { } bool core::done() const { - return m_lemma_vec->size() >= 10 || + return m_lemmas.size() >= 10 || conflict_found() || lp_settings().get_cancel_flag(); } @@ -1477,7 +1477,7 @@ void core::check_weighted(unsigned sz, std::pair 0 && !done() && m_lemma_vec->empty()) { + while (bound > 0 && !done() && m_lemmas.empty()) { unsigned n = random() % bound; for (unsigned i = 0; i < sz; ++i) { if (seen.contains(i)) @@ -1493,13 +1493,13 @@ void core::check_weighted(unsigned sz, std::pair& l_vec) { - m_lemma_vec = &l_vec; - return m_powers.check(r, x, y, l_vec); +lbool core::check_power(lpvar r, lpvar x, lpvar y) { + m_lemmas.reset(); + return m_powers.check(r, x, y, m_lemmas); } -void core::check_bounded_divisions(vector& l_vec) { - m_lemma_vec = &l_vec; +void core::check_bounded_divisions() { + m_lemmas.reset(); m_divisions.check_bounded_divisions(); } // looking for a free variable inside of a monic to split @@ -1518,11 +1518,10 @@ void core::add_bounds() { } } -lbool core::check(vector& lits, vector& l_vec) { +lbool core::check(vector& lits) { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); - m_lemma_vec = &l_vec; m_literal_vec = &lits; if (!(lra.get_status() == lp::lp_status::OPTIMAL || lra.get_status() == lp::lp_status::FEASIBLE)) { @@ -1543,7 +1542,7 @@ lbool core::check(vector& lits, vector& l_vec) { bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && l_vec.empty() && lits.empty(); }; + auto no_effect = [&]() { return !done() && m_lemmas.empty() && lits.empty(); }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1561,7 +1560,7 @@ lbool core::check(vector& lits, vector& l_vec) { {1, check2}, {1, check3} }; check_weighted(3, checks); - if (!l_vec.empty() || !lits.empty()) + if (!m_lemmas.empty() || !lits.empty()) return l_false; } @@ -1598,15 +1597,15 @@ lbool core::check(vector& lits, vector& l_vec) { m_stats.m_nra_calls++; } - if (ret == l_undef && !l_vec.empty() && m_reslim.inc()) + if (ret == l_undef && !m_lemmas.empty() && m_reslim.inc()) ret = l_false; - m_stats.m_nla_lemmas += l_vec.size(); - for (const auto& l : l_vec) + m_stats.m_nla_lemmas += m_lemmas.size(); + for (const auto& l : m_lemmas) m_stats.m_nla_explanations += static_cast(l.expl().size()); - TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << l_vec.size() << "\n";); + TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";); IF_VERBOSE(2, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());}); CTRACE("nla_solver", ret == l_undef, tout << "Monomials\n"; print_monics(tout);); return ret; @@ -1641,13 +1640,13 @@ lbool core::bounded_nlsat() { m_nlsat_delay /= 2; } if (ret == l_true) { - m_lemma_vec->reset(); + m_lemmas.reset(); } return ret; } bool core::no_lemmas_hold() const { - for (auto & l : * m_lemma_vec) { + for (auto & l : m_lemmas) { if (lemma_holds(l)) { TRACE("nla_solver", print_lemma(l, tout);); return false; @@ -1656,10 +1655,10 @@ bool core::no_lemmas_hold() const { return true; } -lbool core::test_check(vector& l) { +lbool core::test_check() { vector lits; lra.set_status(lp::lp_status::OPTIMAL); - return check(lits, l); + return check(lits); } std::ostream& core::print_terms(std::ostream& out) const { @@ -1811,12 +1810,11 @@ bool core::improve_bounds() { return bounds_improved; } -void core::propagate(vector& lemmas) { +void core::propagate() { // disable for now return; // propagate linear monomials - lemmas.reset(); - m_lemma_vec = &lemmas; + m_lemmas.reset(); m_monomial_bounds.unit_propagate(); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 9c96f2fbf..b50e43b32 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -85,7 +85,7 @@ class core { reslimit& m_reslim; smt_params_helper m_params; std::function m_relevant; - vector * m_lemma_vec; + vector m_lemmas; vector * m_literal_vec = nullptr; indexed_uint_set m_to_refine; tangents m_tangents; @@ -386,15 +386,15 @@ public: bool conflict_found() const; - lbool check(vector& ineqs, vector& l_vec); - lbool check_power(lpvar r, lpvar x, lpvar y, vector& l_vec); - void check_bounded_divisions(vector&); + lbool check(vector& ineqs); + lbool check_power(lpvar r, lpvar x, lpvar y); + void check_bounded_divisions(); bool no_lemmas_hold() const; - void propagate(vector& lemmas); + void propagate(); - lbool test_check(vector& l); + lbool test_check(); lpvar map_to_root(lpvar) const; std::ostream& print_terms(std::ostream&) const; std::ostream& print_term(const lp::lar_term&, std::ostream&) const; @@ -428,6 +428,7 @@ public: void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); + vector const& lemmas() const { return m_lemmas; } private: void restore_patched_values(); void constrain_nl_in_tableau(); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index b7197ff2f..5417d5d63 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -42,12 +42,12 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check(vector& lits, vector& lemmas) { - return m_core->check(lits, lemmas); + lbool solver::check(vector& lits) { + return m_core->check(lits); } - void solver::propagate(vector& lemmas) { - m_core->propagate(lemmas); + void solver::propagate() { + m_core->propagate(); } void solver::push(){ @@ -93,12 +93,15 @@ namespace nla { } // ensure r = x^y, add abstraction/refinement lemmas - lbool solver::check_power(lpvar r, lpvar x, lpvar y, vector& lemmas) { - return m_core->check_power(r, x, y, lemmas); + lbool solver::check_power(lpvar r, lpvar x, lpvar y) { + return m_core->check_power(r, x, y); } - void solver::check_bounded_divisions(vector& lemmas) { - m_core->check_bounded_divisions(lemmas); + void solver::check_bounded_divisions() { + m_core->check_bounded_divisions(); } + vector const& solver::lemmas() const { + return m_core->lemmas(); + } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 7a8a97b3f..9a1bf9d14 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -31,14 +31,14 @@ namespace nla { void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); void add_bounded_division(lpvar q, lpvar x, lpvar y); - void check_bounded_divisions(vector&); + void check_bounded_divisions(); void set_relevant(std::function& is_relevant); void push(); void pop(unsigned scopes); bool need_check(); - lbool check(vector& lits, vector&); - void propagate(vector& lemmas); - lbool check_power(lpvar r, lpvar x, lpvar y, vector&); + lbool check(vector& lits); + void propagate(); + lbool check_power(lpvar r, lpvar x, lpvar y); bool is_monic_var(lpvar) const; bool influences_nl_var(lpvar) const; std::ostream& display(std::ostream& out) const; @@ -47,5 +47,6 @@ namespace nla { nlsat::anum_manager& am(); nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); + vector const& lemmas() const; }; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index c06b8fafb..4c6e5b4be 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1459,11 +1459,11 @@ namespace arith { return l_true; m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals); switch (r) { case l_false: assume_literals(); - for (const nla::lemma& l : m_nla_lemma_vector) + for (const nla::lemma& l : m_nla->lemmas()) false_case_of_check_nla(l); break; case l_true: diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index e23162393..f21e41786 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -248,7 +248,6 @@ namespace arith { // lemmas lp::explanation m_explanation; - vector m_nla_lemma_vector; vector m_nla_literals; literal_vector m_core, m_core2; vector m_coeffs; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 41426e0d6..518cffe25 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1598,11 +1598,11 @@ public: return FC_DONE; if (!m_nla) return FC_GIVEUP; - switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { + switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y))) { case l_true: return FC_DONE; case l_false: - for (const nla::lemma & l : m_nla_lemma_vector) + for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); return FC_CONTINUE; case l_undef: @@ -1800,8 +1800,8 @@ public: if (!m_nla) return true; m_nla_lemma_vector.reset(); - m_nla->check_bounded_divisions(m_nla_lemma_vector); - for (auto & lemma : m_nla_lemma_vector) + m_nla->check_bounded_divisions(); + for (auto & lemma : m_nla->lemmas()) false_case_of_check_nla(lemma); return m_nla_lemma_vector.empty(); } @@ -2022,13 +2022,13 @@ public: final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals, m_nla_lemma_vector); + lbool r = m_nla->check(m_nla_literals); switch (r) { case l_false: for (const nla::ineq& i : m_nla_literals) assume_literal(i); - for (const nla::lemma & l : m_nla_lemma_vector) + for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); return FC_CONTINUE; case l_true: @@ -2161,8 +2161,8 @@ public: void propagate_nla() { if (!m_nla) return; - m_nla->propagate(m_nla_lemma_vector); - for (nla::lemma const& l : m_nla_lemma_vector) + m_nla->propagate(); + for (nla::lemma const& l : m_nla->lemmas()) false_case_of_check_nla(l); } diff --git a/src/test/lp/nla_solver_test.cpp b/src/test/lp/nla_solver_test.cpp index ce934e7ca..054c6583f 100644 --- a/src/test/lp/nla_solver_test.cpp +++ b/src/test/lp/nla_solver_test.cpp @@ -179,7 +179,6 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { v.push_back(lp_a);v.push_back(lp_c); nla.add_monic(lp_ac, v.size(), v.begin()); - vector lv; // set abcde = ac * bde // ac = 1 then abcde = bde, but we have abcde < bde @@ -193,8 +192,9 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { s.set_column_value_test(lp_bde, lp::impq(rational(16))); - VERIFY(nla.get_core().test_check(lv) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lv = nla.get_core().lemmas(); nla.get_core().print_lemma(lv.back(), std::cout); ineq i0(lp_ac, llc::NE, 1); @@ -250,8 +250,6 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); - vector lemma; - s_set_column_value_test(s, lp_a, rational(1)); s_set_column_value_test(s, lp_b, rational(1)); s_set_column_value_test(s, lp_c, rational(1)); @@ -259,7 +257,8 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { s_set_column_value_test(s, lp_e, rational(1)); s_set_column_value_test(s, lp_bde, rational(3)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); SASSERT(lemma[0].size() == 4); nla.get_core().print_lemma(lemma.back(), std::cout); @@ -330,7 +329,6 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { lp_bde, lp_acd, lp_be); - vector lemma; // set vars s_set_column_value_test(s, lp_a, rational(1)); @@ -344,7 +342,8 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { s_set_column_value_test(s, lp_acd, rational(1)); s_set_column_value_test(s, lp_be, rational(1)); - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); SASSERT(lemma.size() == 1 && lemma[0].size() == 2); lp::lar_term t0, t1; @@ -389,14 +388,13 @@ void test_basic_lemma_for_mon_zero_from_monomial_to_factors() { vec.push_back(lp_d); nla.add_monic(lp_acd, vec.size(), vec.begin()); - vector lemma; s_set_column_value_test(s, lp_a, rational(1)); s_set_column_value_test(s, lp_c, rational(1)); s_set_column_value_test(s, lp_d, rational(1)); s_set_column_value_test(s, lp_acd, rational(0)); - VERIFY(nla.get_core().test_check(lemma) == l_false); - + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); ineq i0(lp_a, llc::EQ, 0); @@ -452,7 +450,6 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { lp_bde, lp_acd, lp_be); - vector lemma; // set all vars to 1 s_set_column_value_test(s, lp_a, rational(1)); @@ -471,8 +468,8 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { s_set_column_value_test(s, lp_b, - rational(2)); // we have bde = -b, therefore d = +-1 and e = +-1 s_set_column_value_test(s, lp_d, rational(3)); - VERIFY(nla.get_core().test_check(lemma) == l_false); - + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); ineq i0(lp_d, llc::EQ, 1); @@ -584,9 +581,8 @@ void test_basic_sign_lemma() { s_set_column_value_test(s, lp_bde, rational(5)); s_set_column_value_test(s, lp_acd, rational(3)); - vector lemmas; - VERIFY(nla.get_core().test_check(lemmas) == l_false); - + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemmas = nla.get_core().lemmas(); lp::lar_term t; t.add_var(lp_bde); t.add_var(lp_acd); @@ -707,9 +703,9 @@ void test_order_lemma_params(bool var_equiv, int sign) { s_set_column_value_test(s, lp_abef, nla.get_core().mon_value_by_vars(mon_cdij) + rational(1)); } - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); // lp::lar_term t; // t.add_monomial(lp_bde); // t.add_monomial(lp_acd); @@ -792,8 +788,8 @@ void test_monotone_lemma() { // set ef = ij while it has to be ef > ij s_set_column_value_test(s, lp_ef, s.get_column_value(lp_ij)); - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); */ } @@ -821,8 +817,8 @@ void test_tangent_lemma_rat() { vec.push_back(lp_b); nla.add_monic(lp_ab, vec.size(), vec.begin()); - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); + VERIFY(nla.get_core().test_check() == l_false); + auto const& lemma = nla.get_core().lemmas(); nla.get_core().print_lemma(lemma.back(), std::cout); } @@ -848,9 +844,8 @@ void test_tangent_lemma_reg() { vec.push_back(lp_b); nla.add_monic(lp_ab, vec.size(), vec.begin()); - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); - nla.get_core().print_lemma(lemma.back(), std::cout); + VERIFY(nla.get_core().test_check() == l_false); + nla.get_core().print_lemma(nla.get_core().lemmas().back(), std::cout); } void test_tangent_lemma_equiv() { @@ -893,10 +888,9 @@ void test_tangent_lemma_equiv() { int mon_ab = nla.add_monic(lp_ab, vec.size(), vec.begin()); s_set_column_value_test(s, lp_ab, nla.get_core().mon_value_by_vars(mon_ab) + rational(10)); // greater by ten than the correct value - vector lemma; - VERIFY(nla.get_core().test_check(lemma) == l_false); - nla.get_core().print_lemma(lemma.back(), std::cout); + VERIFY(nla.get_core().test_check() == l_false); + nla.get_core().print_lemma(nla.get_core().lemmas().back(), std::cout); */ } From e4e1d6148c336a5d65337e27e04bfda85b528d9d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 20:14:42 -0700 Subject: [PATCH 197/428] port over moving m_nla_lemmas into nla_core from the linear monomial propagation branch Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 518cffe25..c0cfe3876 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1799,11 +1799,10 @@ public: bool check_idiv_bounds() { if (!m_nla) return true; - m_nla_lemma_vector.reset(); m_nla->check_bounded_divisions(); for (auto & lemma : m_nla->lemmas()) false_case_of_check_nla(lemma); - return m_nla_lemma_vector.empty(); + return m_nla->lemmas.empty(); } expr_ref var2expr(lpvar v) { @@ -3192,7 +3191,6 @@ public: } lp::explanation m_explanation; - vector m_nla_lemma_vector; vector m_nla_literals; literal_vector m_core; svector m_eqs; From 36566d619307927df6612696866f72e272d29de1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 20:15:22 -0700 Subject: [PATCH 198/428] port over moving m_nla_lemmas into nla_core from the linear monomial propagation branch Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c0cfe3876..ae658b105 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1802,7 +1802,7 @@ public: m_nla->check_bounded_divisions(); for (auto & lemma : m_nla->lemmas()) false_case_of_check_nla(lemma); - return m_nla->lemmas.empty(); + return m_nla->lemmas().empty(); } expr_ref var2expr(lpvar v) { From 9c63ea31354d8e6b21013153f06c620acbd08a18 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 20:24:49 -0700 Subject: [PATCH 199/428] port over cosmetics from unit_prop_on_monomials branch Signed-off-by: Nikolaj Bjorner --- src/math/lp/lp_bound_propagator.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index f676b0e1d..9bd485b1a 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -103,19 +103,19 @@ class lp_bound_propagator { } const impq& get_lower_bound(unsigned j) const { - return m_imp.lp().get_lower_bound(j); + return lp().get_lower_bound(j); } const mpq& get_lower_bound_rational(unsigned j) const { - return m_imp.lp().get_lower_bound(j).x; + return lp().get_lower_bound(j).x; } const impq& get_upper_bound(unsigned j) const { - return m_imp.lp().get_upper_bound(j); + return lp().get_upper_bound(j); } const mpq& get_upper_bound_rational(unsigned j) const { - return m_imp.lp().get_upper_bound(j).x; + return lp().get_upper_bound(j).x; } // require also the zero infinitesemal part @@ -124,7 +124,7 @@ class lp_bound_propagator { } 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); + j = lp().column_to_reported_index(j); lconstraint_kind kind = is_low ? GE : LE; if (strict) @@ -138,24 +138,24 @@ class lp_bound_propagator { 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);); + TRACE("try_add_bound", lp().print_implied_bound(found_bound, tout);); } } 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);); + TRACE("try_add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, 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);); + TRACE("try_add_bound", lp().print_implied_bound(found_bound, tout);); } } 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);); + TRACE("try_add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); } } } From aaa587398ea84ec5107ae9906bf5d665da262ffd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 20:52:34 -0700 Subject: [PATCH 200/428] add propagation flag to monic and method for updating it to emonics. This replaces the bool-vector tracking for propagation and internalizes it to the emonics class Signed-off-by: Nikolaj Bjorner --- src/math/lp/emonics.cpp | 16 ++++++++++++++++ src/math/lp/emonics.h | 2 ++ src/math/lp/monic.h | 3 +++ 3 files changed, 21 insertions(+) diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index a8ac63689..f051a8f2e 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -595,4 +595,20 @@ bool emonics::invariant() const { return true; } + +void emonics::set_propagated(monic& m) { + struct set_unpropagated : public trail { + emonics& em; + unsigned var; + public: + set_unpropagated(emonics& em, unsigned var): em(em), var(var) {} + void undo() override { + em[var].set_propagated(false); + } + }; + SASSERT(!m.is_propagated()); + m.set_propagated(true); + m_u_f_stack.push(set_unpropagated(*this, m.var())); +} + } diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index e4f4f4848..051e1d6f0 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -142,6 +142,8 @@ public: void merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {} void after_merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {} + void set_propagated(monic& m); + // this method is required by union_find trail_stack & get_trail_stack() { return m_u_f_stack; } diff --git a/src/math/lp/monic.h b/src/math/lp/monic.h index 884adaaf8..d981b2042 100644 --- a/src/math/lp/monic.h +++ b/src/math/lp/monic.h @@ -58,6 +58,7 @@ class monic: public mon_eq { svector m_rvars; bool m_rsign; mutable unsigned m_visited; + bool m_propagated = false; public: // constructors monic(lpvar v, unsigned sz, lpvar const* vs, unsigned idx): @@ -74,6 +75,8 @@ public: void reset_rfields() { m_rsign = false; m_rvars.reset(); SASSERT(m_rvars.size() == 0); } void push_rvar(signed_var sv) { m_rsign ^= sv.sign(); m_rvars.push_back(sv.var()); } void sort_rvars() { std::sort(m_rvars.begin(), m_rvars.end()); } + void set_propagated(bool p) { m_propagated = p; } + bool is_propagated() const { return m_propagated; } svector::const_iterator begin() const { return vars().begin(); } svector::const_iterator end() const { return vars().end(); } From 6559e5fb321c67530f5276751023575d86017953 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 21:15:07 -0700 Subject: [PATCH 201/428] port over std_vector and std-allocator functionality from monomial propagation branch Signed-off-by: Nikolaj Bjorner --- src/util/memory_manager.h | 23 +++++++++++++++++++++++ src/util/vector.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 7dab520df..af56c4507 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -128,6 +128,29 @@ void dealloc_svect(T * ptr) { memory::deallocate(ptr); } +template +struct std_allocator { + using value_type = T; + // the constructors must be provided according to cpp docs + std_allocator() = default; + template constexpr std_allocator(const std_allocator&) noexcept {} + + + T* allocate(std::size_t n) { + return static_cast(memory::allocate(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t n) { + memory::deallocate(p); + } +}; + +// the comparison operators must be provided according to cpp docs +template +bool operator==(const std_allocator&, const std_allocator&) { return true; } +template +bool operator!=(const std_allocator&, const std_allocator&) { return false; } + struct mem_stat { }; diff --git a/src/util/vector.h b/src/util/vector.h index 1cb25a8c4..55b52d745 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -33,6 +33,7 @@ Revision History: #include "util/memory_manager.h" #include "util/hash.h" #include "util/z3_exception.h" +#include // disable warning for constant 'if' expressions. // these are used heavily in templates. @@ -40,6 +41,8 @@ Revision History: #pragma warning(disable:4127) #endif +template +using std_vector = std::vector>; #if 0 From 2297b0334b14a0141dd0a26d4d2942f83bb28ad9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2023 23:53:14 -0700 Subject: [PATCH 202/428] re-introduce simple implementation of linear monomial propagation for evaluation Signed-off-by: Nikolaj Bjorner --- src/math/lp/emonics.cpp | 4 +-- src/math/lp/emonics.h | 2 +- src/math/lp/monomial_bounds.cpp | 55 ++++++++++++++++++++------------- src/math/lp/monomial_bounds.h | 1 - src/math/lp/nla_core.cpp | 7 +---- src/smt/theory_lra.cpp | 2 +- src/util/util.h | 8 +++++ 7 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index f051a8f2e..9a9e4566b 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -596,7 +596,7 @@ bool emonics::invariant() const { } -void emonics::set_propagated(monic& m) { +void emonics::set_propagated(monic const& m) { struct set_unpropagated : public trail { emonics& em; unsigned var; @@ -607,7 +607,7 @@ void emonics::set_propagated(monic& m) { } }; SASSERT(!m.is_propagated()); - m.set_propagated(true); + (*this)[m.var()].set_propagated(true); m_u_f_stack.push(set_unpropagated(*this, m.var())); } diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index 051e1d6f0..fe0b19117 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -142,7 +142,7 @@ public: void merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {} void after_merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {} - void set_propagated(monic& m); + void set_propagated(monic const& m); // this method is required by union_find trail_stack & get_trail_stack() { return m_u_f_stack; } diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 84af29d05..8e4ed6cef 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -263,40 +263,53 @@ namespace nla { unit_propagate(m); } + void monomial_bounds::unit_propagate(monic const& m) { - m_propagated.reserve(m.var() + 1, false); - if (m_propagated[m.var()]) + if (m.is_propagated()) return; if (!is_linear(m)) return; - - c().trail().push(set_bitvector_trail(m_propagated, m.var())); + + c().m_emons.set_propagated(m); rational k = fixed_var_product(m); - new_lemma lemma(c(), "fixed-values"); if (k == 0) { - for (auto v : m) { - if (c().var_is_fixed(v) && c().val(v).is_zero()) { - lemma.explain_fixed(v); - break; - } - } - lemma |= ineq(m.var(), lp::lconstraint_kind::EQ, 0); + ineq ineq(m.var(), lp::lconstraint_kind::EQ, 0); + if (c().ineq_holds(ineq)) + return; + + lpvar zero_var = find(m, [&](lpvar v) { return c().var_is_fixed(v) && c().val(v).is_zero(); }); + + IF_VERBOSE(2, verbose_stream() << "zero " << m.var() << "\n"); + + new_lemma lemma(c(), "fixed-values"); + lemma.explain_fixed(zero_var); + lemma |= ineq; } else { + lpvar w = non_fixed_var(m); + lp::lar_term term; + term.add_monomial(m.rat_sign(), m.var()); + + if (w != null_lpvar) { + IF_VERBOSE(2, verbose_stream() << "linear " << m.var() << " " << k << " " << w << "\n"); + term.add_monomial(-k, w); + k = 0; + } + else + IF_VERBOSE(2, verbose_stream() << "fixed " << m.var() << " " << k << "\n"); + + ineq ineq(term, lp::lconstraint_kind::EQ, k); + if (c().ineq_holds(ineq)) + return; + + new_lemma lemma(c(), "linearized-fixed-values"); for (auto v : m) if (c().var_is_fixed(v)) - lemma.explain_fixed(v); - - lpvar w = non_fixed_var(m); - SASSERT(w != null_lpvar); - - lp::lar_term term; - term.add_monomial(-m.rat_sign(), m.var()); - term.add_monomial(k, w); - lemma |= ineq(term, lp::lconstraint_kind::EQ, 0); + lemma.explain_fixed(v); + lemma |= ineq; } } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index c728d1a4c..74c61dd5f 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -31,7 +31,6 @@ namespace nla { bool is_zero(lpvar v) const; // monomial propagation - bool_vector m_propagated; void unit_propagate(monic const& m); bool is_linear(monic const& m); rational fixed_var_product(monic const& m); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 4990a087c..f147640b8 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1811,13 +1811,8 @@ bool core::improve_bounds() { } void core::propagate() { - // disable for now - return; - // propagate linear monomials - m_lemmas.reset(); - + m_lemmas.reset(); m_monomial_bounds.unit_propagate(); - } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ae658b105..fb84b265d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2149,9 +2149,9 @@ public: case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); + // propagate_nla(); break; case l_undef: - propagate_nla(); break; } return true; diff --git a/src/util/util.h b/src/util/util.h index 1e2310eb3..275374512 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -378,6 +378,14 @@ bool all_of(S const& set, T const& p) { return true; } +template +R find(S const& set, std::function p) { + for (auto const& s : set) + if (p(s)) + return s; + throw default_exception("element not found"); +} + /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). it contains the current value. From 9033b826f4d55f8a4b6d6d98eab3c8dceadf07ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Sep 2023 00:52:26 -0700 Subject: [PATCH 203/428] debug output with the variable that is fixed and its value Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_nl.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index ae0af89ec..0a2b6e938 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -765,10 +765,8 @@ typename theory_arith::numeral theory_arith::get_monomial_fixed_var_pr template expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { SASSERT(is_pure_monomial(m)); - for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { - expr * arg = to_app(m)->get_arg(i); - theory_var _var = expr2var(arg); - if (!is_fixed(_var)) + for (expr* arg : *to_app(m)) { + if (!is_fixed(expr2var(arg))) return arg; } return nullptr; @@ -780,7 +778,7 @@ expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { */ template bool theory_arith::propagate_linear_monomial(theory_var v) { - TRACE("non_linear", tout << "checking whether v" << v << " became linear...\n";); + TRACE("non_linear_verbose", tout << "checking whether v" << v << " became linear...\n";); if (m_data[v].m_nl_propagated) return false; // already propagated this monomial. expr * m = var2expr(v); @@ -819,6 +817,11 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { ctx.mark_as_relevant(rhs); } TRACE("non_linear_bug", tout << "enode: " << ctx.get_enode(rhs) << " enode_id: " << ctx.get_enode(rhs)->get_owner_id() << "\n";); + IF_VERBOSE(3, + for (auto* arg : *to_app(m)) + if (is_fixed(expr2var(arg))) + verbose_stream() << mk_pp(arg, get_manager()) << " = " << -k << "\n"); + theory_var new_v = expr2var(rhs); SASSERT(new_v != null_theory_var); new_lower = alloc(derived_bound, new_v, inf_numeral(0), B_LOWER); @@ -906,7 +909,7 @@ bool theory_arith::propagate_linear_monomials() { return false; if (!reflection_enabled()) return false; - TRACE("non_linear", tout << "propagating linear monomials...\n";); + TRACE("non_linear_verbose", tout << "propagating linear monomials...\n";); bool p = false; // CMW: m_nl_monomials can grow during this loop, so // don't use iterators. From 29b5c47a8d416d2ab1ff2008626c845b58b25d01 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 27 Sep 2023 09:09:38 -0700 Subject: [PATCH 204/428] track changed monics efficiently Signed-off-by: Lev Nachmanson --- src/math/lp/nla_core.cpp | 21 ++++++++++----------- src/math/lp/nla_core.h | 3 +-- src/math/lp/nla_solver.cpp | 1 - src/math/lp/nla_solver.h | 1 - 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index fe4c1a29a..97c9c82d9 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -43,18 +43,17 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector m_lemmas; vector * m_literal_vec = nullptr; indexed_uint_set m_to_refine; - vector m_monics_with_changed_bounds; + indexed_uint_set m_monics_with_changed_bounds; tangents m_tangents; basics m_basics; order m_order; @@ -125,7 +125,6 @@ public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&, std_vector & implied_bounds); const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; } - void reset_monics_with_changed_bounds() { m_monics_with_changed_bounds.reset(); } void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 236b85d4f..144ecefd3 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -112,6 +112,5 @@ namespace nla { init_bound_propagation(); for (unsigned v : monics_with_changed_bounds()) calculate_implied_bounds_for_monic(v); - reset_monics_with_changed_bounds(); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 0448d840f..0fe9733f1 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -27,7 +27,6 @@ namespace nla { solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds); ~solver(); const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } - void reset_monics_with_changed_bounds() { m_core->reset_monics_with_changed_bounds(); } void add_monic(lpvar v, unsigned sz, lpvar const* vs); void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); From 7207f0ff9c1cef27432b5345b60cc4ec85f57d70 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Sep 2023 20:40:55 -0700 Subject: [PATCH 205/428] sketch of internal propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.h | 2 + src/math/lp/monomial_bounds.cpp | 78 +++++++++++++++++---------------- src/math/lp/monomial_bounds.h | 3 ++ src/math/lp/nla_core.cpp | 26 +++++------ src/math/lp/nla_core.h | 9 ++-- src/math/lp/nla_solver.cpp | 8 +++- src/math/lp/nla_solver.h | 3 +- src/sat/smt/arith_solver.cpp | 4 +- src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 30 ++++++------- 10 files changed, 87 insertions(+), 77 deletions(-) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index fc696d4b2..275f7446d 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -142,7 +142,9 @@ class lar_solver : public column_namer { void insert_to_columns_with_changed_bounds(unsigned j); void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); +public: void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); +private: void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 8e4ed6cef..96e4f9ba9 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -274,45 +274,47 @@ namespace nla { c().m_emons.set_propagated(m); rational k = fixed_var_product(m); - - if (k == 0) { - ineq ineq(m.var(), lp::lconstraint_kind::EQ, 0); - if (c().ineq_holds(ineq)) - return; - - lpvar zero_var = find(m, [&](lpvar v) { return c().var_is_fixed(v) && c().val(v).is_zero(); }); - - IF_VERBOSE(2, verbose_stream() << "zero " << m.var() << "\n"); - - new_lemma lemma(c(), "fixed-values"); - lemma.explain_fixed(zero_var); - lemma |= ineq; - } - else { - lpvar w = non_fixed_var(m); - lp::lar_term term; - term.add_monomial(m.rat_sign(), m.var()); - - if (w != null_lpvar) { - IF_VERBOSE(2, verbose_stream() << "linear " << m.var() << " " << k << " " << w << "\n"); - term.add_monomial(-k, w); - k = 0; - } - else - IF_VERBOSE(2, verbose_stream() << "fixed " << m.var() << " " << k << "\n"); - - ineq ineq(term, lp::lconstraint_kind::EQ, k); - if (c().ineq_holds(ineq)) - return; - - new_lemma lemma(c(), "linearized-fixed-values"); - for (auto v : m) - if (c().var_is_fixed(v)) - lemma.explain_fixed(v); - lemma |= ineq; - } - + lpvar w = non_fixed_var(m); + if (w == null_lpvar) + propagate_fixed(m, k); + else + propagate_nonfixed(m, k, w); } + + void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { + auto* dep = explain_fixed(m, k); + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); + } + + void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { + vector> coeffs; + coeffs.push_back(std::make_pair(-k, w)); + coeffs.push_back(std::make_pair(rational::one(), m.var())); + lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); + auto* dep = explain_fixed(m, k); + term_index = c().lra.map_term_index_to_column_index(term_index); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, k, dep); + } + + u_dependency* monomial_bounds::explain_fixed(monic const& m, rational const& k) { + u_dependency* dep = nullptr; + for (auto j : m.vars()) { + if (k == 0) { + if (c().var_is_fixed_to_zero(j)) { + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); + return dep; + } + continue; + } + if (c().var_is_fixed(j)) { + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); + } + } + return dep; + } + bool monomial_bounds::is_linear(monic const& m) { unsigned non_fixed = 0; diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 74c61dd5f..ae05e2026 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -25,6 +25,9 @@ namespace nla { bool propagate_value(dep_interval& range, lpvar v, unsigned power); void compute_product(unsigned start, monic const& m, scoped_dep_interval& i); bool propagate(monic const& m); + void propagate_fixed(monic const& m, rational const& k); + void propagate_nonfixed(monic const& m, rational const& k, lpvar w); + u_dependency* explain_fixed(monic const& m, rational const& k); bool propagate_down(monic const& m, dep_interval& mi, lpvar v, unsigned power, dep_interval& product); void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const; bool is_free(lpvar v) const; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index f147640b8..cf0be88fd 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -809,7 +809,7 @@ void core::print_stats(std::ostream& out) { void core::clear() { m_lemmas.clear(); - m_literal_vec->clear(); + m_literals.clear(); } void core::init_search() { @@ -1494,12 +1494,12 @@ void core::check_weighted(unsigned sz, std::pair 0), and return - m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); + m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); ++lp_settings().stats().m_nla_bounds; return; } } } -lbool core::check(vector& lits) { +lbool core::check() { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); - m_literal_vec = &lits; if (!(lra.get_status() == lp::lp_status::OPTIMAL || lra.get_status() == lp::lp_status::FEASIBLE)) { TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";); @@ -1542,7 +1541,7 @@ lbool core::check(vector& lits) { bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && m_lemmas.empty() && lits.empty(); }; + auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty(); }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1560,7 +1559,7 @@ lbool core::check(vector& lits) { {1, check2}, {1, check3} }; check_weighted(3, checks); - if (!m_lemmas.empty() || !lits.empty()) + if (!m_lemmas.empty() || !m_literals.empty()) return l_false; } @@ -1639,9 +1638,8 @@ lbool core::bounded_nlsat() { m_nlsat_fails = 0; m_nlsat_delay /= 2; } - if (ret == l_true) { - m_lemmas.reset(); - } + if (ret == l_true) + clear(); return ret; } @@ -1655,10 +1653,10 @@ bool core::no_lemmas_hold() const { return true; } + lbool core::test_check() { - vector lits; lra.set_status(lp::lp_status::OPTIMAL); - return check(lits); + return check(); } std::ostream& core::print_terms(std::ostream& out) const { @@ -1811,7 +1809,7 @@ bool core::improve_bounds() { } void core::propagate() { - m_lemmas.reset(); + clear(); m_monomial_bounds.unit_propagate(); } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index b50e43b32..ddf6d0687 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -86,8 +86,8 @@ class core { smt_params_helper m_params; std::function m_relevant; vector m_lemmas; - vector * m_literal_vec = nullptr; - indexed_uint_set m_to_refine; + vector m_literals; + indexed_uint_set m_to_refine; tangents m_tangents; basics m_basics; order m_order; @@ -386,7 +386,7 @@ public: bool conflict_found() const; - lbool check(vector& ineqs); + lbool check(); lbool check_power(lpvar r, lpvar x, lpvar y); void check_bounded_divisions(); @@ -428,7 +428,8 @@ public: void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); - vector const& lemmas() const { return m_lemmas; } + vector const& lemmas() const { return m_lemmas; } + vector const& literals() const { return m_literals; } private: void restore_patched_values(); void constrain_nl_in_tableau(); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 5417d5d63..dfbdca4e7 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -42,8 +42,8 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check(vector& lits) { - return m_core->check(lits); + lbool solver::check() { + return m_core->check(); } void solver::propagate() { @@ -104,4 +104,8 @@ namespace nla { vector const& solver::lemmas() const { return m_core->lemmas(); } + + vector const& solver::literals() const { + return m_core->literals(); + } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 9a1bf9d14..32a3b668e 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -36,7 +36,7 @@ namespace nla { void push(); void pop(unsigned scopes); bool need_check(); - lbool check(vector& lits); + lbool check(); void propagate(); lbool check_power(lpvar r, lpvar x, lpvar y); bool is_monic_var(lpvar) const; @@ -48,5 +48,6 @@ namespace nla { nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); vector const& lemmas() const; + vector const& literals() const; }; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 4c6e5b4be..d64af9bb9 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1416,7 +1416,7 @@ namespace arith { } void solver::assume_literals() { - for (auto const& ineq : m_nla_literals) { + for (auto const& ineq : m_nla->literals()) { auto lit = mk_ineq_literal(ineq); ctx.mark_relevant(lit); s().set_phase(lit); @@ -1459,7 +1459,7 @@ namespace arith { return l_true; m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals); + lbool r = m_nla->check(); switch (r) { case l_false: assume_literals(); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index f21e41786..6a577e753 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -248,7 +248,6 @@ namespace arith { // lemmas lp::explanation m_explanation; - vector m_nla_literals; literal_vector m_core, m_core2; vector m_coeffs; svector m_eqs; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index fb84b265d..1cf476db4 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1602,8 +1602,7 @@ public: case l_true: return FC_DONE; case l_false: - for (const nla::lemma & l : m_nla->lemmas()) - false_case_of_check_nla(l); + add_lemmas(); return FC_CONTINUE; case l_undef: return FC_GIVEUP; @@ -1800,8 +1799,7 @@ public: if (!m_nla) return true; m_nla->check_bounded_divisions(); - for (auto & lemma : m_nla->lemmas()) - false_case_of_check_nla(lemma); + add_lemmas(); return m_nla->lemmas().empty(); } @@ -2000,7 +1998,7 @@ public: // create term >= 0 (or term <= 0) atom = mk_bound(ineq.term(), ineq.rs(), is_lower); return literal(ctx().get_bool_var(atom), pos); - } + } void false_case_of_check_nla(const nla::lemma & l) { m_lemma = l; //todo avoid the copy @@ -2021,14 +2019,11 @@ public: final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals); + lbool r = m_nla->check(); switch (r) { case l_false: - for (const nla::ineq& i : m_nla_literals) - assume_literal(i); - for (const nla::lemma & l : m_nla->lemmas()) - false_case_of_check_nla(l); + add_lemmas(); return FC_CONTINUE; case l_true: return assume_eqs()? FC_CONTINUE: FC_DONE; @@ -2158,10 +2153,16 @@ public: } void propagate_nla() { - if (!m_nla) - return; - m_nla->propagate(); - for (nla::lemma const& l : m_nla->lemmas()) + if (m_nla) { + m_nla->propagate(); + add_lemmas(); + } + } + + void add_lemmas() { + for (const nla::ineq& i : m_nla->literals()) + assume_literal(i); + for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); } @@ -3191,7 +3192,6 @@ public: } lp::explanation m_explanation; - vector m_nla_literals; literal_vector m_core; svector m_eqs; vector m_params; From 65e59e3ec4ebd9c06469c15739a308d3c5f22d08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Sep 2023 20:43:38 -0700 Subject: [PATCH 206/428] sketch of internal propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 96e4f9ba9..22b7ed6c6 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -298,19 +298,21 @@ namespace nla { u_dependency* monomial_bounds::explain_fixed(monic const& m, rational const& k) { u_dependency* dep = nullptr; - for (auto j : m.vars()) { - if (k == 0) { - if (c().var_is_fixed_to_zero(j)) { - dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); - dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); - return dep; - } - continue; - } - if (c().var_is_fixed(j)) { - dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); - dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); - } + auto update_dep = [&](unsigned j) { + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); + return dep; + }; + + if (k == 0) { + for (auto j : m.vars()) + if (c().var_is_fixed_to_zero(j)) + return update_dep(j); + } + else { + for (auto j : m.vars()) + if (c().var_is_fixed(j)) + update_dep(j); } return dep; } From ddcd1ee992e999e061161e4541cf70d18d8d21b1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 28 Sep 2023 09:25:36 -0700 Subject: [PATCH 207/428] non-fixed term should have bound 0 Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 2 +- src/smt/theory_lra.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 22b7ed6c6..8e049f059 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -293,7 +293,7 @@ namespace nla { lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(m, k); term_index = c().lra.map_term_index_to_column_index(term_index); - c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, k, dep); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); } u_dependency* monomial_bounds::explain_fixed(monic const& m, rational const& k) { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1cf476db4..14cb12890 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2144,7 +2144,7 @@ public: case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); - // propagate_nla(); + // propagate_nla(); break; case l_undef: break; From f30a2c13be7384eda00ecc3e336bcf9a21f0fa8f Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 28 Sep 2023 17:24:34 -0700 Subject: [PATCH 208/428] propagate only one non-fixed monomial intrernally lar_solver --- src/math/lp/monomial_bounds.cpp | 20 +++++++++ src/math/lp/monomial_bounds.h | 4 +- src/math/lp/nla_core.cpp | 80 +-------------------------------- src/math/lp/nla_core.h | 1 - 4 files changed, 23 insertions(+), 82 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 7d2dc5ce6..981677ab0 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -12,6 +12,26 @@ #include "math/lp/nla_intervals.h" namespace nla { + // here non_fixed is the only non-fixed variable in the monomial, + // vars is the vector of the monomial variables, + // k is the product of all fixed variables in vars + void monomial_bounds::propagate_nonfixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { + vector> coeffs; + coeffs.push_back(std::make_pair(-k, non_fixed)); + coeffs.push_back(std::make_pair(rational::one(), monic_var)); + lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); + auto* dep = explain_fixed(vars, non_fixed); + term_index = c().lra.map_term_index_to_column_index(term_index); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); + } + + u_dependency* monomial_bounds::explain_fixed(const svector& vars, lpvar non_fixed) { + u_dependency* dep = nullptr; + for (auto v : vars) + if (v != non_fixed) + dep = c().lra.join_deps(dep, c().lra.get_bound_constraint_witnesses_for_column(v)); + return dep; + } monomial_bounds::monomial_bounds(core* c): common(c), diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 15477e19a..76524012f 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -17,7 +17,7 @@ namespace nla { class monomial_bounds : common { dep_intervals& dep; - + u_dependency* explain_fixed(const svector& vars, lpvar non_fixed); void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; bool propagate_value(dep_interval& range, lpvar v); @@ -35,6 +35,6 @@ namespace nla { public: monomial_bounds(core* core); void propagate(); - + void propagate_nonfixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 97c9c82d9..f574f20ad 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1912,84 +1912,6 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } } - void core::propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { - if (params().arith_nl_use_lemmas_in_unit_prop()) { - propagate_monic_non_fixed_with_lemma(monic_var, vars, non_fixed, k); - return; - } - lp::impq bound_value; - bool is_strict; - auto& lps = lra; - - if (lower_bound_is_available(non_fixed)) { - bound_value = lra.column_lower_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_pos()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (upper_bound_is_available(non_fixed)) { - bound_value = lra.column_upper_bound(non_fixed); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(non_fixed); - for (auto v : vars) - if (v != non_fixed) - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - else - add_upper_bound_monic(monic_var, k * bound_value.x, is_strict, lambda); - } - - if (lower_bound_is_available(monic_var)) { - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_lower_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - bound_value = lra.column_lower_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - if (k.is_pos()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - - if (upper_bound_is_available(monic_var)) { - bound_value = lra.column_upper_bound(monic_var); - is_strict = !bound_value.y.is_zero(); - auto lambda = [vars, monic_var, non_fixed, &lps]() { - u_dependency* dep = lps.get_column_upper_bound_witness(monic_var); - for (auto v : vars) { - if (v != non_fixed) { - dep = lps.join_deps(dep, lps.get_bound_constraint_witnesses_for_column(v)); - } - } - return dep; - }; - if (k.is_neg()) - add_lower_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - else - add_upper_bound_monic(non_fixed, bound_value.x / k, is_strict, lambda); - } - } - void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { auto* lps = &lra; auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; @@ -2049,7 +1971,7 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } if (non_fixed != null_lpvar) - propagate_monic_with_non_fixed(monic_var, vars, non_fixed, k); + m_monomial_bounds.propagate_nonfixed(monic_var, vars, non_fixed, k); else // all variables are fixed propagate_monic_with_all_fixed(monic_var, vars, k); } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index d5feac8a2..ef7863817 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -435,7 +435,6 @@ public: bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed); void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var); - void propagate_monic_with_non_fixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); void propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k); void add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); From b64fdef41f4e1ff9b7c700d10541c7552286fa82 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 29 Sep 2023 15:27:22 -0700 Subject: [PATCH 209/428] better tracking changinc rows and monomials --- src/math/lp/lar_solver.cpp | 18 +++++------------- src/math/lp/lar_solver.h | 18 +++++++++++------- src/math/lp/nla_core.cpp | 25 +++++++++++-------------- src/math/lp/nla_core.h | 2 +- src/math/lp/nla_solver.cpp | 9 +++++++-- src/math/lp/nla_solver.h | 1 - 6 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 08ff3b0d4..4db3a9531 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -200,16 +200,10 @@ namespace lp { } lp_status lar_solver::solve() { - if (m_status == lp_status::INFEASIBLE) { + if (m_status == lp_status::INFEASIBLE) return m_status; - } + solve_with_core_solver(); - if (m_status != lp_status::INFEASIBLE) { - if (m_settings.bound_propagation()) - detect_rows_with_changed_bounds(); - } - - clear_columns_with_changed_bounds(); return m_status; } @@ -789,8 +783,7 @@ namespace lp { void lar_solver::detect_rows_with_changed_bounds() { for (auto j : m_columns_with_changed_bounds) detect_rows_with_changed_bounds_for_column(j); - if (m_find_monics_with_changed_bounds_func) - m_find_monics_with_changed_bounds_func(m_columns_with_changed_bounds); + } void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { @@ -1623,10 +1616,9 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if (!coeffs.empty()) { + if (!coeffs.empty()) add_row_from_term_no_constraint(m_terms.back(), ret); - add_touched_row(A_r().row_count() - 1); - } + lp_assert(m_var_register.size() == A_r().column_count()); if (m_need_register_terms) register_normalized_term(*t, A_r().column_count() - 1); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 9f878e363..2a1f8644c 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -89,6 +89,11 @@ class lar_solver : public column_namer { constraint_set m_constraints; // the set of column indices j such that bounds have changed for j indexed_uint_set m_columns_with_changed_bounds; +public: + const indexed_uint_set& columns_with_changed_bounds() const { return m_columns_with_changed_bounds; } + inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } +private: + // m_touched_rows contains rows that have been changed by a pivoting or have a column with changed bounds indexed_uint_set m_touched_rows; unsigned_vector m_row_bounds_to_replay; u_dependency_manager m_dependencies; @@ -138,9 +143,7 @@ class lar_solver : public column_namer { 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); - - inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } - public: +public: void insert_to_columns_with_changed_bounds(unsigned j); const u_dependency* crossed_bounds_deps() const { return m_crossed_bounds_deps;} u_dependency*& crossed_bounds_deps() { return m_crossed_bounds_deps;} @@ -149,12 +152,12 @@ class lar_solver : public column_namer { lpvar& crossed_bounds_column() { return m_crossed_bounds_column; } - private: +private: void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); - public: +public: void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - private: +private: void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); @@ -358,6 +361,8 @@ class lar_solver : public column_namer { void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { + detect_rows_with_changed_bounds(); + clear_columns_with_changed_bounds(); if (settings().propagate_eqs()) { if (settings().random_next() % 10 == 0) remove_fixed_vars_from_base(); @@ -688,7 +693,6 @@ class lar_solver : public column_namer { return 0; return m_usage_in_terms[j]; } - std::function m_find_monics_with_changed_bounds_func = nullptr; friend int_solver; friend int_branch; }; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index f574f20ad..6641347e4 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -41,19 +41,7 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector & m_implied_bounds; // try to improve bounds for variables in monomials. bool improve_bounds(); - + void clear_monics_with_changed_bounds() { m_monics_with_changed_bounds.reset(); } public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&, std_vector & implied_bounds); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 144ecefd3..53d8b0da6 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -110,7 +110,12 @@ namespace nla { void solver::propagate_bounds_for_touched_monomials() { init_bound_propagation(); - for (unsigned v : monics_with_changed_bounds()) - calculate_implied_bounds_for_monic(v); + for (unsigned v : m_core->monics_with_changed_bounds()) { + calculate_implied_bounds_for_monic(v); + if (m_core->lra.get_status() == lp::lp_status::INFEASIBLE) { + break; + } + } + m_core->clear_monics_with_changed_bounds(); } } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 0fe9733f1..acd724af9 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -26,7 +26,6 @@ namespace nla { solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds); ~solver(); - const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } void add_monic(lpvar v, unsigned sz, lpvar const* vs); void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); From 702322a6e92ae794c8b8dd35de0b963e97795d81 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 29 Sep 2023 15:31:32 -0700 Subject: [PATCH 210/428] change the order of lp and nlp propagation --- src/smt/theory_lra.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index cd4aa5388..d355b9118 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2151,8 +2151,8 @@ public: break; case l_true: propagate_basic_bounds(); + propagate_bounds_with_nlp(); propagate_bounds_with_lp_solver(); - propagate_bounds_with_nlp(); break; case l_undef: UNREACHABLE(); From 50654f1f4620bf58e132d44d3a22e48a17630922 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Sep 2023 08:52:09 +0900 Subject: [PATCH 211/428] add theory propagation to linear monomial propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 20 +++++++++++++++++++- src/math/lp/monomial_bounds.h | 1 + src/math/lp/nla_core.cpp | 2 ++ src/math/lp/nla_core.h | 8 +++++++- src/math/lp/nla_solver.cpp | 9 +++++++++ src/math/lp/nla_solver.h | 2 ++ src/math/lp/nla_types.h | 14 ++++++++++++++ src/smt/smt_conflict_resolution.cpp | 1 + src/smt/theory_lra.cpp | 27 ++++++++++++++++++++++++++- 9 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 8e049f059..e3b14d1b7 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -10,6 +10,7 @@ #include "math/lp/monomial_bounds.h" #include "math/lp/nla_core.h" #include "math/lp/nla_intervals.h" +#include "math/lp/numeric_pair.h" namespace nla { @@ -281,9 +282,21 @@ namespace nla { propagate_nonfixed(m, k, w); } + lp::explanation monomial_bounds::get_explanation(u_dependency* dep) { + lp::explanation exp; + svector cs; + c().lra.dep_manager().linearize(dep, cs); + for (auto d : cs) + exp.add_pair(d, mpq(1)); + return exp; + } + void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { auto* dep = explain_fixed(m, k); - c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); + // propagate fixed equality + auto exp = get_explanation(dep); + c().add_fixed_equality(m.var(), k, exp); } void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { @@ -294,6 +307,11 @@ namespace nla { auto* dep = explain_fixed(m, k); term_index = c().lra.map_term_index_to_column_index(term_index); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); + + if (k == 1) { + lp::explanation exp = get_explanation(dep); + c().add_equality(m.var(), w, exp); + } } u_dependency* monomial_bounds::explain_fixed(monic const& m, rational const& k) { diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index ae05e2026..747aca9a2 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -28,6 +28,7 @@ namespace nla { void propagate_fixed(monic const& m, rational const& k); void propagate_nonfixed(monic const& m, rational const& k, lpvar w); u_dependency* explain_fixed(monic const& m, rational const& k); + lp::explanation get_explanation(u_dependency* dep); bool propagate_down(monic const& m, dep_interval& mi, lpvar v, unsigned power, dep_interval& product); void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const; bool is_free(lpvar v) const; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index cf0be88fd..e65829ed7 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -810,6 +810,8 @@ void core::print_stats(std::ostream& out) { void core::clear() { m_lemmas.clear(); m_literals.clear(); + m_fixed_equalities.clear(); + m_equalities.clear(); } void core::init_search() { diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index ddf6d0687..17aa304ac 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -44,7 +44,6 @@ bool try_insert(const A& elem, B& collection) { return true; } - class core { friend struct common; friend class new_lemma; @@ -87,6 +86,8 @@ class core { std::function m_relevant; vector m_lemmas; vector m_literals; + vector m_equalities; + vector m_fixed_equalities; indexed_uint_set m_to_refine; tangents m_tangents; basics m_basics; @@ -430,6 +431,11 @@ public: void collect_statistics(::statistics&); vector const& lemmas() const { return m_lemmas; } vector const& literals() const { return m_literals; } + vector const& equalities() const { return m_equalities; } + vector const& fixed_equalities() const { return m_fixed_equalities; } + + void add_fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e) { m_fixed_equalities.push_back({v, k, e}); } + void add_equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e) { m_equalities.push_back({i, j, e}); } private: void restore_patched_values(); void constrain_nl_in_tableau(); diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index dfbdca4e7..f4d09810e 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -108,4 +108,13 @@ namespace nla { vector const& solver::literals() const { return m_core->literals(); } + + vector const& solver::equalities() const { + return m_core->equalities(); + } + + vector const& solver::fixed_equalities() const { + return m_core->fixed_equalities(); + } + } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 32a3b668e..fec27c32b 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -49,5 +49,7 @@ namespace nla { void collect_statistics(::statistics & st); vector const& lemmas() const; vector const& literals() const; + vector const& fixed_equalities() const; + vector const& equalities() const; }; } diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index 8169266cc..3930a62a9 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -24,6 +24,20 @@ namespace nla { typedef lp::explanation expl_set; typedef lp::var_index lpvar; const lpvar null_lpvar = UINT_MAX; + + struct equality { + lp::lpvar i, j; + lp::explanation e; + equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e):i(i),j(j),e(e) {} + }; + + struct fixed_equality { + lp::lpvar v; + rational k; + lp::explanation e; + fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e):v(v),k(k),e(e) {} + }; + inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); } inline rational rrat_sign(const rational& r) { return rational(rat_sign(r)); } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index d075c0652..2561fbb5a 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -601,6 +601,7 @@ namespace smt { finalize_resolve(conflict, not_l); + return true; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 14cb12890..1a64d4081 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2112,6 +2112,7 @@ public: bool propagate_core() { m_model_is_initialized = false; flush_bound_axioms(); + // disabled in master: propagate_nla(); if (!can_propagate_core()) return false; m_new_def = false; @@ -2144,7 +2145,6 @@ public: case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); - // propagate_nla(); break; case l_undef: break; @@ -2156,9 +2156,34 @@ public: if (m_nla) { m_nla->propagate(); add_lemmas(); + add_equalities(); } } + void add_equalities() { + for (auto const& [v,k,e] : m_nla->fixed_equalities()) + add_equality(v, k, e); + for (auto const& [i,j,e] : m_nla->equalities()) + add_eq(i,j,e,false); + } + + void add_equality(lpvar j, rational const& k, lp::explanation const& exp) { + verbose_stream() << "equality " << j << " " << k << "\n"; + TRACE("arith", tout << "equality " << j << " " << k << "\n"); + theory_var v; + if (k == 1) + v = m_one_var; + else if (k == 0) + v = m_zero_var; + else if (!m_value2var.find(k, v)) + return; + theory_var w = lp().local_to_external(j); + if (w < 0) + return; + lpvar i = register_theory_var_in_lar_solver(v); + add_eq(i, j, exp, true); + } + void add_lemmas() { for (const nla::ineq& i : m_nla->literals()) assume_literal(i); From ab8fe199c59ac7721c0a8fe03c79af7aa77dd8a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Oct 2023 18:41:23 +0900 Subject: [PATCH 212/428] fix case for 0 multiplier in monomial_bounds disabled in master - it violates invariants in the lra solver. --- src/math/lp/monomial_bounds.cpp | 7 ++++++- src/sat/smt/euf_proof_checker.cpp | 6 ++---- src/smt/smt_clause_proof.cpp | 22 +++++++++++++++------- src/smt/smt_clause_proof.h | 6 +++--- src/smt/smt_internalizer.cpp | 6 +++--- src/smt/theory_lra.cpp | 13 +++++++++---- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index e3b14d1b7..9bc49fc43 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -276,7 +276,7 @@ namespace nla { rational k = fixed_var_product(m); lpvar w = non_fixed_var(m); - if (w == null_lpvar) + if (w == null_lpvar || k == 0) propagate_fixed(m, k); else propagate_nonfixed(m, k, w); @@ -293,6 +293,10 @@ namespace nla { void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { auto* dep = explain_fixed(m, k); + if (!c().lra.is_base(m.var())) { + lp::impq val(k); + c().lra.set_value_for_nbasic_column(m.var(), val); + } c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); // propagate fixed equality auto exp = get_explanation(dep); @@ -300,6 +304,7 @@ namespace nla { } void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { + VERIFY(k != 0); vector> coeffs; coeffs.push_back(std::make_pair(-k, w)); coeffs.push_back(std::make_pair(rational::one(), m.var())); diff --git a/src/sat/smt/euf_proof_checker.cpp b/src/sat/smt/euf_proof_checker.cpp index a538b2a80..42cda4bfb 100644 --- a/src/sat/smt/euf_proof_checker.cpp +++ b/src/sat/smt/euf_proof_checker.cpp @@ -501,8 +501,9 @@ namespace euf { for (expr* arg : clause) std::cout << "\n " << mk_bounded_pp(arg, m); std::cout << ")\n"; + std::cout.flush(); - if (is_rup(proof_hint)) + if (false && is_rup(proof_hint)) diagnose_rup_failure(clause); add_clause(clause); @@ -527,9 +528,6 @@ namespace euf { for (expr* f : core) std::cout << mk_pp(f, m) << "\n"; } - SASSERT(false); - - exit(0); } void smt_proof_checker::collect_statistics(statistics& st) const { diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 777961334..bf27777c4 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -90,14 +90,14 @@ namespace smt { return proof_ref(m); } - void clause_proof::add(clause& c) { + void clause_proof::add(clause& c, literal_buffer const* simp_lits) { if (!is_enabled()) return; justification* j = c.get_justification(); auto st = kind2st(c.get_kind()); auto pr = justification2proof(st, j); CTRACE("mk_clause", pr.get(), tout << mk_bounded_pp(pr, m, 4) << "\n";); - update(c, st, pr); + update(c, st, pr, simp_lits); } void clause_proof::add(unsigned n, literal const* lits, clause_kind k, justification* j) { @@ -137,12 +137,15 @@ namespace smt { update(st, m_lits, pr); } - void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j) { + void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j, literal_buffer const* simp_lits) { if (!is_enabled()) return; m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit1)); m_lits.push_back(ctx.literal2expr(lit2)); + if (simp_lits) + for (auto lit : *simp_lits) + m_lits.push_back(ctx.literal2expr(~lit)); auto st = kind2st(k); auto pr = justification2proof(st, j); update(st, m_lits, pr); @@ -160,7 +163,7 @@ namespace smt { } void clause_proof::del(clause& c) { - update(c, status::deleted, justification2proof(status::deleted, nullptr)); + update(c, status::deleted, justification2proof(status::deleted, nullptr), nullptr); } std::ostream& clause_proof::display_literals(std::ostream& out, expr_ref_vector const& v) { @@ -190,7 +193,9 @@ namespace smt { if (ctx.get_fparams().m_clause_proof) m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); + m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); + static unsigned s_count = 0; + if (m_has_log) { init_pp_out(); auto& out = *m_pp_out; @@ -220,12 +225,15 @@ namespace smt { } } - void clause_proof::update(clause& c, status st, proof* p) { + void clause_proof::update(clause& c, status st, proof* p, literal_buffer const* simp_lits) { if (!is_enabled()) return; m_lits.reset(); for (literal lit : c) - m_lits.push_back(ctx.literal2expr(lit)); + m_lits.push_back(ctx.literal2expr(lit)); + if (simp_lits) + for (auto lit : *simp_lits) + m_lits.push_back(ctx.literal2expr(~lit)); update(st, m_lits, p); } diff --git a/src/smt/smt_clause_proof.h b/src/smt/smt_clause_proof.h index 1c5931136..d7cc421cf 100644 --- a/src/smt/smt_clause_proof.h +++ b/src/smt/smt_clause_proof.h @@ -68,7 +68,7 @@ namespace smt { void init_pp_out(); void update(status st, expr_ref_vector& v, proof* p); - void update(clause& c, status st, proof* p); + void update(clause& c, status st, proof* p, literal_buffer const* simp_lits); status kind2st(clause_kind k); proof_ref justification2proof(status st, justification* j); void log(status st, proof* p); @@ -79,8 +79,8 @@ namespace smt { clause_proof(context& ctx); void shrink(clause& c, unsigned new_size); void add(literal lit, clause_kind k, justification* j); - void add(literal lit1, literal lit2, clause_kind k, justification* j); - void add(clause& c); + void add(literal lit1, literal lit2, clause_kind k, justification* j, literal_buffer const* simp_lits = nullptr); + void add(clause& c, literal_buffer const* simp_lits = nullptr); void add(unsigned n, literal const* lits, clause_kind k, justification* j); void propagate(literal lit, justification const& j, literal_vector const& ante); void del(clause& c); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 0e9e39996..b6d1e2f2b 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1378,12 +1378,12 @@ namespace smt { clause * context::mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k, clause_del_eh * del_eh) { TRACE("mk_clause", display_literals_verbose(tout << "creating clause: " << literal_vector(num_lits, lits) << "\n", num_lits, lits) << "\n";); m_clause_proof.add(num_lits, lits, k, j); + literal_buffer simp_lits; switch (k) { case CLS_TH_AXIOM: dump_axiom(num_lits, lits); Z3_fallthrough; case CLS_AUX: { - literal_buffer simp_lits; if (m_searching) dump_lemma(num_lits, lits); if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) { @@ -1451,7 +1451,7 @@ namespace smt { else if (get_assignment(l2) == l_false) { assign(l1, b_justification(~l2)); } - m_clause_proof.add(l1, l2, k, j); + m_clause_proof.add(l1, l2, k, j, &simp_lits); m_stats.m_num_mk_bin_clause++; return nullptr; } @@ -1464,7 +1464,7 @@ namespace smt { bool reinit = save_atoms; SASSERT(!lemma || j == 0 || !j->in_region()); clause * cls = clause::mk(m, num_lits, lits, k, j, del_eh, save_atoms, m_bool_var2expr.data()); - m_clause_proof.add(*cls); + m_clause_proof.add(*cls, &simp_lits); if (lemma) { cls->set_activity(activity); if (k == CLS_LEARNED) { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1a64d4081..96cd3dd47 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2112,7 +2112,8 @@ public: bool propagate_core() { m_model_is_initialized = false; flush_bound_axioms(); - // disabled in master: propagate_nla(); + // disabled in master: + // propagate_nla(); if (!can_propagate_core()) return false; m_new_def = false; @@ -2161,6 +2162,8 @@ public: } void add_equalities() { + if (!propagate_eqs()) + return; for (auto const& [v,k,e] : m_nla->fixed_equalities()) add_equality(v, k, e); for (auto const& [i,j,e] : m_nla->equalities()) @@ -2168,7 +2171,7 @@ public: } void add_equality(lpvar j, rational const& k, lp::explanation const& exp) { - verbose_stream() << "equality " << j << " " << k << "\n"; + //verbose_stream() << "equality " << j << " " << k << "\n"; TRACE("arith", tout << "equality " << j << " " << k << "\n"); theory_var v; if (k == 1) @@ -3174,8 +3177,7 @@ public: std::function fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); }; scoped_trace_stream _sts(th, fn); - - // SASSERT(validate_eq(x, y)); + //VERIFY(validate_eq(x, y)); ctx().assign_eq(x, y, eq_justification(js)); } @@ -3288,6 +3290,7 @@ public: display(tout << "is-conflict: " << is_conflict << "\n");); for (auto ev : m_explanation) set_evidence(ev.ci(), m_core, m_eqs); + // SASSERT(validate_conflict(m_core, m_eqs)); if (is_conflict) { @@ -3543,6 +3546,8 @@ public: lbool r = nctx.check(); if (r == l_true) { nctx.display_asserted_formulas(std::cout); + std::cout.flush(); + std::cout.flush(); } return l_true != r; } From a297a2b25c06ee541c8d414a060f93de0c770c16 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 1 Oct 2023 11:39:58 -0700 Subject: [PATCH 213/428] fixes in lar_solver around nl unit propagation Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 11 +++++++++-- src/math/lp/lar_solver.h | 2 ++ src/math/lp/lp_core_solver_base.h | 2 +- src/math/lp/lp_core_solver_base_def.h | 4 +++- src/math/lp/lp_primal_core_solver.h | 5 +++-- src/math/lp/monomial_bounds.cpp | 6 ++++++ src/math/lp/nla_core.cpp | 2 ++ src/smt/theory_lra.cpp | 2 ++ 8 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 4db3a9531..729b5865e 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -476,6 +476,9 @@ namespace lp { auto& x = m_mpq_lar_core_solver.m_r_x[j]; auto delta = new_val - x; x = new_val; + TRACE("lar_solver_feas", tout << "setting " << j << " to " + << new_val << (column_is_feasible(j)?"feas":"non-feas") << "\n";); + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); change_basic_columns_dependend_on_a_given_nb_column(j, delta); } @@ -1101,7 +1104,6 @@ namespace lp { mpq lar_solver::get_value(column_index const& j) const { SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE); - SASSERT(m_columns_with_changed_bounds.empty()); numeric_pair const& rp = get_column_value(j); return from_model_in_impq_to_mpq(rp); } @@ -1818,7 +1820,7 @@ namespace lp { if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); - 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;); + 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") << " val = " << get_column_value(j) << std::endl;); } void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { @@ -2363,6 +2365,11 @@ namespace lp { SASSERT(bdep != nullptr); m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); } + + void lar_solver::track_column_feasibility(lpvar j) { + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + } + } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 2a1f8644c..0748d2507 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -92,6 +92,8 @@ class lar_solver : public column_namer { public: const indexed_uint_set& columns_with_changed_bounds() const { return m_columns_with_changed_bounds; } inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } + void track_column_feasibility(lpvar j); + private: // m_touched_rows contains rows that have been changed by a pivoting or have a column with changed bounds indexed_uint_set m_touched_rows; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index d7d1666d0..1d6634c06 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -571,7 +571,7 @@ public: } void remove_column_from_inf_heap(unsigned j) { if (m_inf_heap.contains(j)) { - TRACE("lar_solver_inf_heap", tout << "insert into heap j = " << j << "\n";); + TRACE("lar_solver_inf_heap", tout << "erase from heap j = " << j << "\n";); m_inf_heap.erase(j); } lp_assert(column_is_feasible(j)); diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index 0552b8e82..b4a53e872 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -115,10 +115,12 @@ pretty_print(std::ostream & out) { template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { - m_x[entering] += delta; + m_x[entering] += delta; + TRACE("lar_solver_feas", tout << "not tracking feas entering = " << entering << " = " << m_x[entering] << (column_is_feasible(entering) ? " feas" : " non-feas") << "\n";); for (const auto & c : m_A.m_columns[entering]) { unsigned i = c.var(); m_x[m_basis[i]] -= delta * m_A.get_val(c); + TRACE("lar_solver_feas", tout << "not tracking feas m_basis[i] = " << m_basis[i] << " = " << m_x[m_basis[i]] << (column_is_feasible(m_basis[i]) ? " feas" : " non-feas") << "\n";); } } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 9871ec691..f5d7314db 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -394,9 +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 will remove the leaving from the heap - TRACE("lar_solver_inf_heap", tout << "leaving = " << leaving + TRACE("lar_solver_feas", tout << "entering = " << entering << ", leaving = " << leaving << ", new_val_for_leaving = " << new_val_for_leaving << ", theta = " << theta << "\n";); + TRACE("lar_solver_feas", tout << "leaving = " << leaving << " removed from inf_heap()\n";); + // this will remove the leaving from the heap this->inf_heap().erase_min(); advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); if (this->current_x_is_feasible()) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 981677ab0..177cfb3d3 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -21,8 +21,14 @@ namespace nla { coeffs.push_back(std::make_pair(rational::one(), monic_var)); lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(vars, non_fixed); + // term_index becomes the column index of the term slack variable term_index = c().lra.map_term_index_to_column_index(term_index); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); + c().lra.track_column_feasibility(term_index); + if (!c().lra.column_is_feasible(term_index)) { + c().lra.set_status(lp::lp_status::UNKNOWN); + } + } u_dependency* monomial_bounds::explain_fixed(const svector& vars, lpvar non_fixed) { diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 6641347e4..5a6af5659 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -138,6 +138,7 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { m_add_buffer[i] = j; } m_emons.add(v, m_add_buffer); + m_monics_with_changed_bounds.insert(v); } void core::push() { @@ -1938,6 +1939,7 @@ void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std: } void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) { + if (!is_monic_var(monic_var)) return; m_propagated.reserve(monic_var + 1, false); bool throttle = params().arith_nl_throttle_unit_prop(); if (throttle && m_propagated[monic_var]) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d355b9118..2a7412c29 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3191,6 +3191,8 @@ public: lbool make_feasible() { TRACE("pcs", tout << lp().constraints();); TRACE("arith_verbose", tout << "before calling lp().find_feasible_solution()\n"; display(tout);); + // todo: remove later : debug!!!!! + lp().move_non_basic_columns_to_bounds(false); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); if (lp().is_feasible()) From 7de06c4350fa7f387c1823e3c8f0ecdda45249e7 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 2 Oct 2023 16:42:59 -0700 Subject: [PATCH 214/428] merging master to unit_prop_on_monomials --- src/math/lp/monomial_bounds.cpp | 166 ++++++++++++++------ src/math/lp/monomial_bounds.h | 16 +- src/math/lp/nla_core.cpp | 216 ++++----------------------- src/math/lp/nla_core.h | 49 +++--- src/math/lp/nla_solver.cpp | 41 +++-- src/math/lp/nla_solver.h | 15 +- src/math/lp/nla_types.h | 15 +- src/sat/smt/arith_internalize.cpp | 2 +- src/sat/smt/arith_solver.cpp | 6 +- src/sat/smt/arith_solver.h | 1 - src/sat/smt/euf_proof_checker.cpp | 6 +- src/smt/params/smt_params_helper.pyg | 2 - src/smt/smt_clause_proof.cpp | 22 ++- src/smt/smt_clause_proof.h | 6 +- src/smt/smt_conflict_resolution.cpp | 1 + src/smt/smt_internalizer.cpp | 6 +- src/smt/theory_arith_aux.h | 3 +- src/smt/theory_arith_nl.h | 15 +- src/smt/theory_lra.cpp | 120 ++++++++------- 19 files changed, 333 insertions(+), 375 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 177cfb3d3..8bb6ab306 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -10,34 +10,9 @@ #include "math/lp/monomial_bounds.h" #include "math/lp/nla_core.h" #include "math/lp/nla_intervals.h" +#include "math/lp/numeric_pair.h" namespace nla { - // here non_fixed is the only non-fixed variable in the monomial, - // vars is the vector of the monomial variables, - // k is the product of all fixed variables in vars - void monomial_bounds::propagate_nonfixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { - vector> coeffs; - coeffs.push_back(std::make_pair(-k, non_fixed)); - coeffs.push_back(std::make_pair(rational::one(), monic_var)); - lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); - auto* dep = explain_fixed(vars, non_fixed); - // term_index becomes the column index of the term slack variable - term_index = c().lra.map_term_index_to_column_index(term_index); - c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); - c().lra.track_column_feasibility(term_index); - if (!c().lra.column_is_feasible(term_index)) { - c().lra.set_status(lp::lp_status::UNKNOWN); - } - - } - - u_dependency* monomial_bounds::explain_fixed(const svector& vars, lpvar non_fixed) { - u_dependency* dep = nullptr; - for (auto v : vars) - if (v != non_fixed) - dep = c().lra.join_deps(dep, c().lra.get_bound_constraint_witnesses_for_column(v)); - return dep; - } monomial_bounds::monomial_bounds(core* c): common(c), @@ -50,6 +25,7 @@ namespace nla { } } + bool monomial_bounds::is_too_big(mpq const& q) const { return rational(q).bitsize() > 256; } @@ -283,25 +259,127 @@ namespace nla { } } - // returns true iff (all variables are fixed, - // or all but one variable are fixed) and the bounds are not big, - // or at least one variable is fixed to zero. - bool monomial_bounds::is_linear(monic const& m, lpvar& zero_var, lpvar& non_fixed) { - zero_var = non_fixed = null_lpvar; - unsigned n_of_non_fixed = 0; - bool big_bound = false; - for (lpvar v : m) { - if (!c().var_is_fixed(v)) { - n_of_non_fixed++; - non_fixed = v; - } else if (c().var_is_fixed_to_zero(v)) { - zero_var = v; - return true; - } else if (c().fixed_var_has_big_bound(v)) { - big_bound |= true; + void monomial_bounds::unit_propagate() { + for (auto const& m : c().m_emons) { + unit_propagate(m); + if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { + lp::explanation exp; + c().lra.get_infeasibility_explanation(exp); + new_lemma lemma(c(), "propagate fixed - infeasible lra"); + lemma &= exp; + return; } - } - return n_of_non_fixed <= 1 && !big_bound; + if (c().m_conflicts > 0 ) { + return; + } + } } + + + void monomial_bounds::unit_propagate(monic const& m) { + if (m.is_propagated()) + return; + + if (!is_linear(m)) + return; + + + rational k = fixed_var_product(m); + lpvar w = non_fixed_var(m); + if (w == null_lpvar || k == 0) { + propagate_fixed(m, k); + } + else + propagate_nonfixed(m, k, w); + } + + lp::explanation monomial_bounds::get_explanation(u_dependency* dep) { + lp::explanation exp; + svector cs; + c().lra.dep_manager().linearize(dep, cs); + for (auto d : cs) + exp.add_pair(d, mpq(1)); + return exp; + } + + void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { + auto* dep = explain_fixed(m, k); + if (!c().lra.is_base(m.var())) { + lp::impq val(k); + c().lra.set_value_for_nbasic_column(m.var(), val); + } + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); + + // propagate fixed equality + auto exp = get_explanation(dep); + c().add_fixed_equality(m.var(), k, exp); + } + + void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { + VERIFY(k != 0); + vector> coeffs; + coeffs.push_back(std::make_pair(-k, w)); + coeffs.push_back(std::make_pair(rational::one(), m.var())); + lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); + auto* dep = explain_fixed(m, k); + term_index = c().lra.map_term_index_to_column_index(term_index); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); + + if (k == 1) { + lp::explanation exp = get_explanation(dep); + c().add_equality(m.var(), w, exp); + } + } + + u_dependency* monomial_bounds::explain_fixed(monic const& m, rational const& k) { + u_dependency* dep = nullptr; + auto update_dep = [&](unsigned j) { + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_lower_bound_witness(j)); + dep = c().lra.dep_manager().mk_join(dep, c().lra.get_column_upper_bound_witness(j)); + return dep; + }; + + if (k == 0) { + for (auto j : m.vars()) + if (c().var_is_fixed_to_zero(j)) + return update_dep(j); + } + else { + for (auto j : m.vars()) + if (c().var_is_fixed(j)) + update_dep(j); + } + return dep; + } + + + bool monomial_bounds::is_linear(monic const& m) { + unsigned non_fixed = 0; + for (lpvar v : m) { + if (!c().var_is_fixed(v)) + ++non_fixed; + else if (c().val(v).is_zero()) + return true; + } + return non_fixed <= 1; + } + + + rational monomial_bounds::fixed_var_product(monic const& m) { + rational r(1); + for (lpvar v : m) { + if (c().var_is_fixed(v)) + r *= c().lra.get_column_value(v).x; + } + return r; + } + + lpvar monomial_bounds::non_fixed_var(monic const& m) { + for (lpvar v : m) + if (!c().var_is_fixed(v)) + return v; + return null_lpvar; + } + } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 76524012f..747aca9a2 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -17,24 +17,32 @@ namespace nla { class monomial_bounds : common { dep_intervals& dep; - u_dependency* explain_fixed(const svector& vars, lpvar non_fixed); + void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; + bool propagate_down(monic const& m, lpvar u); bool propagate_value(dep_interval& range, lpvar v); bool propagate_value(dep_interval& range, lpvar v, unsigned power); void compute_product(unsigned start, monic const& m, scoped_dep_interval& i); bool propagate(monic const& m); + void propagate_fixed(monic const& m, rational const& k); + void propagate_nonfixed(monic const& m, rational const& k, lpvar w); + u_dependency* explain_fixed(monic const& m, rational const& k); + lp::explanation get_explanation(u_dependency* dep); bool propagate_down(monic const& m, dep_interval& mi, lpvar v, unsigned power, dep_interval& product); void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const; bool is_free(lpvar v) const; bool is_zero(lpvar v) const; // monomial propagation - bool_vector m_propagated; - bool is_linear(monic const& m, lpvar& zero_var, lpvar& non_fixed); + void unit_propagate(monic const& m); + bool is_linear(monic const& m); + rational fixed_var_product(monic const& m); + lpvar non_fixed_var(monic const& m); + public: monomial_bounds(core* core); void propagate(); - void propagate_nonfixed(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); + void unit_propagate(); }; } diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 5a6af5659..6a241e951 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -17,12 +17,11 @@ Author: #include "math/grobner/pdd_solver.h" #include "math/dd/pdd_interval.h" #include "math/dd/pdd_eval.h" -#include "nla_core.h" namespace nla { typedef lp::lar_term term; -core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector& implied_bounds) : +core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_evars(), lra(s), m_reslim(lim), @@ -39,11 +38,11 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit& lim, std_vector(); } - -bool core::fixed_var_has_big_bound(lpvar j) const { - SASSERT(lra.column_is_fixed(j)); - const auto& b = lra.get_lower_bound(j); - return b.x.is_big() || b.y.is_big(); -} - bool core::var_is_fixed_to_val(lpvar j, const rational& v) const { return lra.column_is_fixed(j) && @@ -818,7 +809,10 @@ void core::print_stats(std::ostream& out) { void core::clear() { m_lemmas.clear(); - m_literal_vec->clear(); + m_literals.clear(); + m_fixed_equalities.clear(); + m_equalities.clear(); + m_conflicts = 0; } void core::init_search() { @@ -1065,14 +1059,6 @@ new_lemma& new_lemma::operator|=(ineq const& ineq) { } return *this; } - -// Contrary to new_lemma::operator|=, this method does not assert that the model does not satisfy the ineq. -new_lemma& new_lemma::operator+=(ineq const& ineq) { - if (!c.explain_ineq(*this, ineq.term(), ineq.cmp(), ineq.rs())) { - current().push_back(ineq); - } - return *this; -} new_lemma::~new_lemma() { @@ -1080,6 +1066,9 @@ new_lemma::~new_lemma() { (void)i; (void)name; // code for checking lemma can be added here + if (current().is_conflict()) { + c.m_conflicts++; + } TRACE("nla_solver", tout << name << " " << (++i) << "\n" << *this; ); } @@ -1511,12 +1500,12 @@ void core::check_weighted(unsigned sz, std::pair 0), and return - m_literal_vec->push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); + m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); ++lp_settings().stats().m_nla_bounds; return; } } } -lbool core::check(vector& lits) { +lbool core::check() { lp_settings().stats().m_nla_calls++; TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";); lra.get_rid_of_inf_eps(); - m_literal_vec = &lits; if (!(lra.get_status() == lp::lp_status::OPTIMAL || lra.get_status() == lp::lp_status::FEASIBLE)) { TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";); @@ -1559,7 +1547,7 @@ lbool core::check(vector& lits) { bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && m_lemmas.empty() && lits.empty(); }; + auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty(); }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1577,7 +1565,7 @@ lbool core::check(vector& lits) { {1, check2}, {1, check3} }; check_weighted(3, checks); - if (!m_lemmas.empty() || !lits.empty()) + if (!m_lemmas.empty() || !m_literals.empty()) return l_false; } @@ -1656,9 +1644,8 @@ lbool core::bounded_nlsat() { m_nlsat_fails = 0; m_nlsat_delay /= 2; } - if (ret == l_true) { - m_lemmas.reset(); - } + if (ret == l_true) + clear(); return ret; } @@ -1672,10 +1659,10 @@ bool core::no_lemmas_hold() const { return true; } + lbool core::test_check() { - vector lits; lra.set_status(lp::lp_status::OPTIMAL); - return check(lits); + return check(); } std::ostream& core::print_terms(std::ostream& out) const { @@ -1826,162 +1813,13 @@ bool core::improve_bounds() { } return bounds_improved; } - -bool core::is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed) { - zero_var = non_fixed = null_lpvar; - unsigned n_of_non_fixed = 0; - for (lpvar v : m) { - if (!var_is_fixed(v)) { - n_of_non_fixed++; - non_fixed = v; - continue; - } - const auto& b = get_lower_bound(v); - if (b.is_zero()) { - zero_var = v; - return true; - } - } - return n_of_non_fixed <= 1; - + +void core::propagate() { + clear(); + m_monomial_bounds.unit_propagate(); } -void core::add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep) { - TRACE("add_bound", lra.print_column_info(j, tout) << std::endl;); - j = lra.column_to_reported_index(j); - unsigned k; - if (!m_improved_lower_bounds.find(j, k)) { - m_improved_lower_bounds.insert(j, static_cast(m_implied_bounds.size())); - m_implied_bounds.push_back(lp::implied_bound(v, j, true, is_strict, explain_dep)); - } - else { - auto& found_bound = m_implied_bounds[k]; - if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = lp::implied_bound(v, j, true, is_strict, explain_dep); - TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); - } - } -} - void core::add_upper_bound_monic(lpvar j, const lp::mpq& bound_val, bool is_strict, std::function explain_dep) { - j = lra.column_to_reported_index(j); - unsigned k; - if (!m_improved_upper_bounds.find(j, k)) { - m_improved_upper_bounds.insert(j, static_cast(m_implied_bounds.size())); - m_implied_bounds.push_back(lp::implied_bound(bound_val, j, false, is_strict, explain_dep)); - } - else { - auto& found_bound = m_implied_bounds[k]; - if (bound_val > found_bound.m_bound || (bound_val == found_bound.m_bound && !found_bound.m_strict && is_strict)) { - found_bound = lp::implied_bound(bound_val, j, false, is_strict, explain_dep); - TRACE("add_bound", lra.print_implied_bound(found_bound, tout);); - } - } - } - bool core::upper_bound_is_available(unsigned j) const { - switch (get_column_type(j)) { - case lp::column_type::fixed: - case lp::column_type::boxed: - case lp::column_type::upper_bound: - return true; - default: - return false; - } - } - - bool core::lower_bound_is_available(unsigned j) const { - switch (get_column_type(j)) { - case lp::column_type::fixed: - case lp::column_type::boxed: - case lp::column_type::lower_bound: - return true; - default: - return false; - } - } +} // end of nla - void core::propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k) { - auto* lps = &lra; - auto lambda = [vars, lps]() { return lps->get_bound_constraint_witnesses_for_columns(vars); }; - add_lower_bound_monic(monic_var, k, false, lambda); - add_upper_bound_monic(monic_var, k, false, lambda); - } - - void core::add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var) { - auto* lps = &lra; - auto lambda = [zero_var, lps]() { - return lps->get_bound_constraint_witnesses_for_column(zero_var); - }; - TRACE("add_bound", lra.print_column_info(zero_var, tout) << std::endl;); - add_lower_bound_monic(monic_var, lp::mpq(0), false, lambda); - add_upper_bound_monic(monic_var, lp::mpq(0), false, lambda); - } - - void core::propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k) { - lp::impq bound_value; - new_lemma lemma(*this, "propagate monic with non fixed"); - // using += to not assert thath the inequality does not hold - lemma += ineq(term(rational(1), monic_var, -k, non_fixed), llc::EQ, 0); - lp::explanation exp; - for (auto v : m_emons[monic_var].vars()) { - if (v == non_fixed) continue; - u_dependency* dep = lra.get_column_lower_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); - } - dep = lra.get_column_upper_bound_witness(v); - for (auto ci : lra.flatten(dep)) { - exp.push_back(ci); - } - } - lemma &= exp; - } - - void core::calculate_implied_bounds_for_monic(lp::lpvar monic_var) { - if (!is_monic_var(monic_var)) return; - m_propagated.reserve(monic_var + 1, false); - bool throttle = params().arith_nl_throttle_unit_prop(); - if (throttle && m_propagated[monic_var]) - return; - lpvar non_fixed, zero_var; - const auto& vars = m_emons[monic_var].vars(); - if (!is_linear(vars, zero_var, non_fixed)) - return; - if (throttle) - trail().push(set_bitvector_trail(m_propagated, monic_var)); - if (zero_var != null_lpvar) - add_bounds_for_zero_var(monic_var, zero_var); - else { - rational k = rational(1); - for (auto v : vars) - if (v != non_fixed) { - k *= val(v); - if (k.is_big()) return; - } - - if (non_fixed != null_lpvar) - m_monomial_bounds.propagate_nonfixed(monic_var, vars, non_fixed, k); - else // all variables are fixed - propagate_monic_with_all_fixed(monic_var, vars, k); - } - } - - void core::init_bound_propagation() { - m_implied_bounds.clear(); - m_improved_lower_bounds.reset(); - m_improved_upper_bounds.reset(); - m_column_types = &lra.get_column_types(); - m_lemmas.clear(); - // find m_monics_with_changed_bounds - for (lpvar j : lra.columns_with_changed_bounds()) { - if (is_monic_var(j)) - m_monics_with_changed_bounds.insert(j); - else { - for (const auto & m: m_emons.get_use_list(j)) { - m_monics_with_changed_bounds.insert(m.var()); - } - } - } - } -} // namespace nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 76397d529..5a597ae67 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -44,7 +44,6 @@ bool try_insert(const A& elem, B& collection) { return true; } - class core { friend struct common; friend class new_lemma; @@ -86,9 +85,10 @@ class core { smt_params_helper m_params; std::function m_relevant; vector m_lemmas; - vector * m_literal_vec = nullptr; + vector m_literals; + vector m_equalities; + vector m_fixed_equalities; indexed_uint_set m_to_refine; - indexed_uint_set m_monics_with_changed_bounds; tangents m_tangents; basics m_basics; order m_order; @@ -97,16 +97,13 @@ class core { divisions m_divisions; intervals m_intervals; monomial_bounds m_monomial_bounds; - + unsigned m_conflicts; horner m_horner; grobner m_grobner; emonics m_emons; svector m_add_buffer; mutable indexed_uint_set m_active_var_set; - // these maps map a column index to the corresponding index in ibounds - u_map m_improved_lower_bounds; - u_map m_improved_upper_bounds; - const vector* m_column_types; + reslimit m_nra_lim; bool m_use_nra_model = false; @@ -114,17 +111,16 @@ class core { bool m_cautious_patching = true; lpvar m_patched_var = 0; monic const* m_patched_monic = nullptr; - bool_vector m_propagated; + void check_weighted(unsigned sz, std::pair>* checks); void add_bounds(); - std_vector & m_implied_bounds; // try to improve bounds for variables in monomials. bool improve_bounds(); - void clear_monics_with_changed_bounds() { m_monics_with_changed_bounds.reset(); } + public: // constructor - core(lp::lar_solver& s, params_ref const& p, reslimit&, std_vector & implied_bounds); - const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; } + core(lp::lar_solver& s, params_ref const& p, reslimit&); + void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); @@ -314,7 +310,6 @@ public: bool sign_contradiction(const monic& m) const; bool var_is_fixed_to_zero(lpvar j) const; - bool fixed_var_has_big_bound(lpvar j) const; bool var_is_fixed_to_val(lpvar j, const rational& v) const; bool var_is_fixed(lpvar j) const; @@ -392,11 +387,13 @@ public: bool conflict_found() const; - lbool check(vector& ineqs); + lbool check(); lbool check_power(lpvar r, lpvar x, lpvar y); void check_bounded_divisions(); bool no_lemmas_hold() const; + + void propagate(); lbool test_check(); lpvar map_to_root(lpvar) const; @@ -432,26 +429,22 @@ public: void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); + vector const& lemmas() const { return m_lemmas; } + vector const& literals() const { return m_literals; } + vector const& equalities() const { return m_equalities; } + vector const& fixed_equalities() const { return m_fixed_equalities; } - bool is_linear(const svector& m, lpvar& zero_var, lpvar& non_fixed); - void add_bounds_for_zero_var(lpvar monic_var, lpvar zero_var); - void propagate_monic_non_fixed_with_lemma(lpvar monic_var, const svector& vars, lpvar non_fixed, const rational& k); - void propagate_monic_with_all_fixed(lpvar monic_var, const svector& vars, const rational& k); - void add_lower_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); - void add_upper_bound_monic(lpvar j, const lp::mpq& v, bool is_strict, std::function explain_dep); - bool upper_bound_is_available(unsigned j) const; - bool lower_bound_is_available(unsigned j) const; - vector const& lemmas() const { return m_lemmas; } - + void add_fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e) { m_fixed_equalities.push_back({v, k, e}); } + void add_equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e) { m_equalities.push_back({i, j, e}); } private: - lp::column_type get_column_type(unsigned j) const { return (*m_column_types)[j]; } + void restore_patched_values(); void constrain_nl_in_tableau(); bool solve_tableau(); void restore_tableau(); void save_tableau(); bool integrality_holds(); - void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); + + }; // end of core struct pp_mon { diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 53d8b0da6..f4d09810e 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -42,10 +42,14 @@ namespace nla { bool solver::need_check() { return m_core->has_relevant_monomial(); } - lbool solver::check(vector& lits) { - return m_core->check(lits); + lbool solver::check() { + return m_core->check(); } + void solver::propagate() { + m_core->propagate(); + } + void solver::push(){ m_core->push(); } @@ -54,8 +58,8 @@ namespace nla { m_core->pop(n); } - solver::solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds): - m_core(alloc(core, s, p, limit, implied_bounds)) { + solver::solver(lp::lar_solver& s, params_ref const& p, reslimit& limit): + m_core(alloc(core, s, p, limit)) { } bool solver::influences_nl_var(lpvar j) const { @@ -88,9 +92,6 @@ namespace nla { m_core->collect_statistics(st); } - void solver::calculate_implied_bounds_for_monic(lp::lpvar v) { - m_core->calculate_implied_bounds_for_monic(v); - } // ensure r = x^y, add abstraction/refinement lemmas lbool solver::check_power(lpvar r, lpvar x, lpvar y) { return m_core->check_power(r, x, y); @@ -100,22 +101,20 @@ namespace nla { m_core->check_bounded_divisions(); } - void solver::init_bound_propagation() { - m_core->init_bound_propagation(); - } - vector const& solver::lemmas() const { return m_core->lemmas(); } - - void solver::propagate_bounds_for_touched_monomials() { - init_bound_propagation(); - for (unsigned v : m_core->monics_with_changed_bounds()) { - calculate_implied_bounds_for_monic(v); - if (m_core->lra.get_status() == lp::lp_status::INFEASIBLE) { - break; - } - } - m_core->clear_monics_with_changed_bounds(); + + vector const& solver::literals() const { + return m_core->literals(); } + + vector const& solver::equalities() const { + return m_core->equalities(); + } + + vector const& solver::fixed_equalities() const { + return m_core->fixed_equalities(); + } + } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index acd724af9..fec27c32b 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -23,9 +23,10 @@ namespace nla { class solver { core* m_core; public: - - solver(lp::lar_solver& s, params_ref const& p, reslimit& limit, std_vector & implied_bounds); + + solver(lp::lar_solver& s, params_ref const& p, reslimit& limit); ~solver(); + void add_monic(lpvar v, unsigned sz, lpvar const* vs); void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); @@ -35,7 +36,7 @@ namespace nla { void push(); void pop(unsigned scopes); bool need_check(); - lbool check(vector& lits); + lbool check(); void propagate(); lbool check_power(lpvar r, lpvar x, lpvar y); bool is_monic_var(lpvar) const; @@ -46,9 +47,9 @@ namespace nla { nlsat::anum_manager& am(); nlsat::anum const& am_value(lp::var_index v) const; void collect_statistics(::statistics & st); - void calculate_implied_bounds_for_monic(lp::lpvar v); - void init_bound_propagation(); - vector const& lemmas() const; - void propagate_bounds_for_touched_monomials(); + vector const& lemmas() const; + vector const& literals() const; + vector const& fixed_equalities() const; + vector const& equalities() const; }; } diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index 186c2e902..3930a62a9 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -24,6 +24,20 @@ namespace nla { typedef lp::explanation expl_set; typedef lp::var_index lpvar; const lpvar null_lpvar = UINT_MAX; + + struct equality { + lp::lpvar i, j; + lp::explanation e; + equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e):i(i),j(j),e(e) {} + }; + + struct fixed_equality { + lp::lpvar v; + rational k; + lp::explanation e; + fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e):v(v),k(k),e(e) {} + }; + inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); } inline rational rrat_sign(const rational& r) { return rational(rat_sign(r)); } @@ -83,7 +97,6 @@ namespace nla { new_lemma& operator&=(const factorization& f); new_lemma& operator&=(lpvar j); new_lemma& operator|=(ineq const& i); - new_lemma& operator+=(ineq const& i); new_lemma& explain_fixed(lpvar j); new_lemma& explain_equiv(lpvar u, lpvar v); new_lemma& explain_var_separated_from_zero(lpvar j); diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 5893c8520..3174ad775 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -61,7 +61,7 @@ namespace arith { void solver::ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), s().params(), m.limit(), m_implied_bounds); + m_nla = alloc(nla::solver, *m_solver.get(), s().params(), m.limit()); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 893fe4a43..17ab03ee6 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -253,7 +253,7 @@ namespace arith { first = false; reset_evidence(); m_explanation.clear(); - be.explain_implied(); + lp().explain_implied_bound(be, m_bp); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); @@ -1416,7 +1416,7 @@ namespace arith { } void solver::assume_literals() { - for (auto const& ineq : m_nla_literals) { + for (auto const& ineq : m_nla->literals()) { auto lit = mk_ineq_literal(ineq); ctx.mark_relevant(lit); s().set_phase(lit); @@ -1459,7 +1459,7 @@ namespace arith { return l_true; m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals); + lbool r = m_nla->check(); switch (r) { case l_false: assume_literals(); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 53b49a658..801eb474c 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -249,7 +249,6 @@ namespace arith { // lemmas lp::explanation m_explanation; - vector m_nla_literals; literal_vector m_core, m_core2; vector m_coeffs; svector m_eqs; diff --git a/src/sat/smt/euf_proof_checker.cpp b/src/sat/smt/euf_proof_checker.cpp index a538b2a80..42cda4bfb 100644 --- a/src/sat/smt/euf_proof_checker.cpp +++ b/src/sat/smt/euf_proof_checker.cpp @@ -501,8 +501,9 @@ namespace euf { for (expr* arg : clause) std::cout << "\n " << mk_bounded_pp(arg, m); std::cout << ")\n"; + std::cout.flush(); - if (is_rup(proof_hint)) + if (false && is_rup(proof_hint)) diagnose_rup_failure(clause); add_clause(clause); @@ -527,9 +528,6 @@ namespace euf { for (expr* f : core) std::cout << mk_pp(f, m) << "\n"; } - SASSERT(false); - - exit(0); } void smt_proof_checker::collect_statistics(statistics& st) const { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index fca7fcacf..9fcda7f64 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -71,8 +71,6 @@ def_module_params(module_name='smt', ('arith.nl.grobner_row_length_limit', UINT, 10, 'row is disregarded by the heuristic if its length is longer than the value'), ('arith.nl.grobner_frequency', UINT, 4, 'grobner\'s call frequency'), ('arith.nl.grobner', BOOL, True, 'run grobner\'s basis heuristic'), - ('arith.nl.use_lemmas_in_unit_prop', BOOL, False, 'use lemmas in monomial unit propagation'), - ('arith.nl.throttle_unit_prop', BOOL, True, 'unit propogate a monomial only once per scope'), ('arith.nl.grobner_eqs_growth', UINT, 10, 'grobner\'s number of equalities growth '), ('arith.nl.grobner_expr_size_growth', UINT, 2, 'grobner\'s maximum expr size growth'), ('arith.nl.grobner_expr_degree_growth', UINT, 2, 'grobner\'s maximum expr degree growth'), diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 777961334..bf27777c4 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -90,14 +90,14 @@ namespace smt { return proof_ref(m); } - void clause_proof::add(clause& c) { + void clause_proof::add(clause& c, literal_buffer const* simp_lits) { if (!is_enabled()) return; justification* j = c.get_justification(); auto st = kind2st(c.get_kind()); auto pr = justification2proof(st, j); CTRACE("mk_clause", pr.get(), tout << mk_bounded_pp(pr, m, 4) << "\n";); - update(c, st, pr); + update(c, st, pr, simp_lits); } void clause_proof::add(unsigned n, literal const* lits, clause_kind k, justification* j) { @@ -137,12 +137,15 @@ namespace smt { update(st, m_lits, pr); } - void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j) { + void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j, literal_buffer const* simp_lits) { if (!is_enabled()) return; m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit1)); m_lits.push_back(ctx.literal2expr(lit2)); + if (simp_lits) + for (auto lit : *simp_lits) + m_lits.push_back(ctx.literal2expr(~lit)); auto st = kind2st(k); auto pr = justification2proof(st, j); update(st, m_lits, pr); @@ -160,7 +163,7 @@ namespace smt { } void clause_proof::del(clause& c) { - update(c, status::deleted, justification2proof(status::deleted, nullptr)); + update(c, status::deleted, justification2proof(status::deleted, nullptr), nullptr); } std::ostream& clause_proof::display_literals(std::ostream& out, expr_ref_vector const& v) { @@ -190,7 +193,9 @@ namespace smt { if (ctx.get_fparams().m_clause_proof) m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) - m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); + m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); + static unsigned s_count = 0; + if (m_has_log) { init_pp_out(); auto& out = *m_pp_out; @@ -220,12 +225,15 @@ namespace smt { } } - void clause_proof::update(clause& c, status st, proof* p) { + void clause_proof::update(clause& c, status st, proof* p, literal_buffer const* simp_lits) { if (!is_enabled()) return; m_lits.reset(); for (literal lit : c) - m_lits.push_back(ctx.literal2expr(lit)); + m_lits.push_back(ctx.literal2expr(lit)); + if (simp_lits) + for (auto lit : *simp_lits) + m_lits.push_back(ctx.literal2expr(~lit)); update(st, m_lits, p); } diff --git a/src/smt/smt_clause_proof.h b/src/smt/smt_clause_proof.h index 1c5931136..d7cc421cf 100644 --- a/src/smt/smt_clause_proof.h +++ b/src/smt/smt_clause_proof.h @@ -68,7 +68,7 @@ namespace smt { void init_pp_out(); void update(status st, expr_ref_vector& v, proof* p); - void update(clause& c, status st, proof* p); + void update(clause& c, status st, proof* p, literal_buffer const* simp_lits); status kind2st(clause_kind k); proof_ref justification2proof(status st, justification* j); void log(status st, proof* p); @@ -79,8 +79,8 @@ namespace smt { clause_proof(context& ctx); void shrink(clause& c, unsigned new_size); void add(literal lit, clause_kind k, justification* j); - void add(literal lit1, literal lit2, clause_kind k, justification* j); - void add(clause& c); + void add(literal lit1, literal lit2, clause_kind k, justification* j, literal_buffer const* simp_lits = nullptr); + void add(clause& c, literal_buffer const* simp_lits = nullptr); void add(unsigned n, literal const* lits, clause_kind k, justification* j); void propagate(literal lit, justification const& j, literal_vector const& ante); void del(clause& c); diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index d075c0652..2561fbb5a 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -601,6 +601,7 @@ namespace smt { finalize_resolve(conflict, not_l); + return true; } diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 0e9e39996..b6d1e2f2b 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1378,12 +1378,12 @@ namespace smt { clause * context::mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k, clause_del_eh * del_eh) { TRACE("mk_clause", display_literals_verbose(tout << "creating clause: " << literal_vector(num_lits, lits) << "\n", num_lits, lits) << "\n";); m_clause_proof.add(num_lits, lits, k, j); + literal_buffer simp_lits; switch (k) { case CLS_TH_AXIOM: dump_axiom(num_lits, lits); Z3_fallthrough; case CLS_AUX: { - literal_buffer simp_lits; if (m_searching) dump_lemma(num_lits, lits); if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) { @@ -1451,7 +1451,7 @@ namespace smt { else if (get_assignment(l2) == l_false) { assign(l1, b_justification(~l2)); } - m_clause_proof.add(l1, l2, k, j); + m_clause_proof.add(l1, l2, k, j, &simp_lits); m_stats.m_num_mk_bin_clause++; return nullptr; } @@ -1464,7 +1464,7 @@ namespace smt { bool reinit = save_atoms; SASSERT(!lemma || j == 0 || !j->in_region()); clause * cls = clause::mk(m, num_lits, lits, k, j, del_eh, save_atoms, m_bool_var2expr.data()); - m_clause_proof.add(*cls); + m_clause_proof.add(*cls, &simp_lits); if (lemma) { cls->set_activity(activity); if (k == CLS_LEARNED) { diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index dd9ba2dfe..470ea5f7b 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -1535,7 +1535,8 @@ namespace smt { m_stats.m_max_min++; unsigned best_efforts = 0; bool inc = false; - + + SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index ae0af89ec..0a2b6e938 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -765,10 +765,8 @@ typename theory_arith::numeral theory_arith::get_monomial_fixed_var_pr template expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { SASSERT(is_pure_monomial(m)); - for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { - expr * arg = to_app(m)->get_arg(i); - theory_var _var = expr2var(arg); - if (!is_fixed(_var)) + for (expr* arg : *to_app(m)) { + if (!is_fixed(expr2var(arg))) return arg; } return nullptr; @@ -780,7 +778,7 @@ expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { */ template bool theory_arith::propagate_linear_monomial(theory_var v) { - TRACE("non_linear", tout << "checking whether v" << v << " became linear...\n";); + TRACE("non_linear_verbose", tout << "checking whether v" << v << " became linear...\n";); if (m_data[v].m_nl_propagated) return false; // already propagated this monomial. expr * m = var2expr(v); @@ -819,6 +817,11 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { ctx.mark_as_relevant(rhs); } TRACE("non_linear_bug", tout << "enode: " << ctx.get_enode(rhs) << " enode_id: " << ctx.get_enode(rhs)->get_owner_id() << "\n";); + IF_VERBOSE(3, + for (auto* arg : *to_app(m)) + if (is_fixed(expr2var(arg))) + verbose_stream() << mk_pp(arg, get_manager()) << " = " << -k << "\n"); + theory_var new_v = expr2var(rhs); SASSERT(new_v != null_theory_var); new_lower = alloc(derived_bound, new_v, inf_numeral(0), B_LOWER); @@ -906,7 +909,7 @@ bool theory_arith::propagate_linear_monomials() { return false; if (!reflection_enabled()) return false; - TRACE("non_linear", tout << "propagating linear monomials...\n";); + TRACE("non_linear_verbose", tout << "propagating linear monomials...\n";); bool p = false; // CMW: m_nl_monomials can grow during this loop, so // don't use iterators. diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2a7412c29..a5c5084ab 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -264,7 +264,7 @@ class theory_lra::imp { void ensure_nla() { if (!m_nla) { - m_nla = alloc(nla::solver, *m_solver.get(), ctx().get_params(), m.limit(), m_implied_bounds); + m_nla = alloc(nla::solver, *m_solver.get(), ctx().get_params(), m.limit()); for (auto const& _s : m_scopes) { (void)_s; m_nla->push(); @@ -1528,14 +1528,12 @@ public: unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; int start = ctx().get_random_value(); - unsigned num_relevant = 0; for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); if (!th.is_relevant_and_shared(n1)) { continue; } - ++num_relevant; ensure_column(v); if (!is_registered_var(v)) continue; @@ -1553,7 +1551,7 @@ public: num_candidates++; } } - + if (num_candidates > 0) { ctx().push_trail(restore_vector(m_assume_eq_candidates, old_sz)); } @@ -1605,8 +1603,7 @@ public: case l_true: return FC_DONE; case l_false: - for (const nla::lemma & l : m_nla->lemmas()) - false_case_of_check_nla(l); + add_lemmas(); return FC_CONTINUE; case l_undef: return FC_GIVEUP; @@ -1803,8 +1800,7 @@ public: if (!m_nla) return true; m_nla->check_bounded_divisions(); - for (auto & lemma : m_nla->lemmas()) - false_case_of_check_nla(lemma); + add_lemmas(); return m_nla->lemmas().empty(); } @@ -2003,7 +1999,7 @@ public: // create term >= 0 (or term <= 0) atom = mk_bound(ineq.term(), ineq.rs(), is_lower); return literal(ctx().get_bool_var(atom), pos); - } + } void false_case_of_check_nla(const nla::lemma & l) { m_lemma = l; //todo avoid the copy @@ -2024,14 +2020,11 @@ public: final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; - lbool r = m_nla->check(m_nla_literals); + lbool r = m_nla->check(); switch (r) { case l_false: - for (const nla::ineq& i : m_nla_literals) - assume_literal(i); - for (const nla::lemma & l : m_nla->lemmas()) - false_case_of_check_nla(l); + add_lemmas(); return FC_CONTINUE; case l_true: return assume_eqs()? FC_CONTINUE: FC_DONE; @@ -2120,6 +2113,8 @@ public: bool propagate_core() { m_model_is_initialized = false; flush_bound_axioms(); + // disabled in master: + propagate_nla(); if (!can_propagate_core()) return false; m_new_def = false; @@ -2151,7 +2146,6 @@ public: break; case l_true: propagate_basic_bounds(); - propagate_bounds_with_nlp(); propagate_bounds_with_lp_solver(); break; case l_undef: @@ -2161,6 +2155,47 @@ public: return true; } + void propagate_nla() { + if (m_nla) { + m_nla->propagate(); + add_lemmas(); + add_equalities(); + } + } + + void add_equalities() { + if (!propagate_eqs()) + return; + for (auto const& [v,k,e] : m_nla->fixed_equalities()) + add_equality(v, k, e); + for (auto const& [i,j,e] : m_nla->equalities()) + add_eq(i,j,e,false); + } + + void add_equality(lpvar j, rational const& k, lp::explanation const& exp) { + //verbose_stream() << "equality " << j << " " << k << "\n"; + TRACE("arith", tout << "equality " << j << " " << k << "\n"); + theory_var v; + if (k == 1) + v = m_one_var; + else if (k == 0) + v = m_zero_var; + else if (!m_value2var.find(k, v)) + return; + theory_var w = lp().local_to_external(j); + if (w < 0) + return; + lpvar i = register_theory_var_in_lar_solver(v); + add_eq(i, j, exp, true); + } + + void add_lemmas() { + for (const nla::ineq& i : m_nla->literals()) + assume_literal(i); + for (const nla::lemma & l : m_nla->lemmas()) + false_case_of_check_nla(l); + } + bool should_propagate() const { return bound_prop_mode::BP_NONE != propagation_mode(); } @@ -2173,50 +2208,33 @@ public: set_evidence(j, m_core, m_eqs); m_explanation.add_pair(j, v); } + + void propagate_bounds_with_lp_solver() { + if (!should_propagate()) + return; + + m_bp.init(); + lp().propagate_bounds_for_touched_rows(m_bp); + + if (!m.inc()) + return; - void finish_bound_propagation() { if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); // verbose_stream() << "unsat\n"; } else { - for (auto &ib : m_bp.ibounds()) { + unsigned count = 0, prop = 0; + for (auto& ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - propagate_lp_solver_bound(ib); + ++prop; + count += propagate_lp_solver_bound(ib); } } } - void propagate_bounds_with_lp_solver() { - if (!should_propagate()) - return; - m_bp.init(); - lp().propagate_bounds_for_touched_rows(m_bp); - - if (m.inc()) - finish_bound_propagation(); - } - - void propagate_bounds_for_monomials() { - m_nla->propagate_bounds_for_touched_monomials(); - for (const auto & l : m_nla->lemmas()) - false_case_of_check_nla(l); - } - - void propagate_bounds_with_nlp() { - if (!m_nla) - return; - if (is_infeasible() || !should_propagate()) - return; - - propagate_bounds_for_monomials(); - - if (m.inc()) - finish_bound_propagation(); - } - bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { theory_var v = lp().local_to_external(vi); if (v == null_theory_var) @@ -3161,8 +3179,7 @@ public: std::function fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); }; scoped_trace_stream _sts(th, fn); - - // SASSERT(validate_eq(x, y)); + //VERIFY(validate_eq(x, y)); ctx().assign_eq(x, y, eq_justification(js)); } @@ -3206,12 +3223,11 @@ public: } lp::explanation m_explanation; - vector m_nla_literals; literal_vector m_core; svector m_eqs; vector m_params; - void reset_evidence() { + void reset_evidence() { m_core.reset(); m_eqs.reset(); m_params.reset(); @@ -3278,6 +3294,7 @@ public: display(tout << "is-conflict: " << is_conflict << "\n");); for (auto ev : m_explanation) set_evidence(ev.ci(), m_core, m_eqs); + // SASSERT(validate_conflict(m_core, m_eqs)); if (is_conflict) { @@ -3533,6 +3550,8 @@ public: lbool r = nctx.check(); if (r == l_true) { nctx.display_asserted_formulas(std::cout); + std::cout.flush(); + std::cout.flush(); } return l_true != r; } @@ -3882,6 +3901,7 @@ public: IF_VERBOSE(1, verbose_stream() << enode_pp(n, ctx()) << " evaluates to " << r2 << " but arith solver has " << r1 << "\n"); } } + }; theory_lra::theory_lra(context& ctx): From 4133a1cc5a94e1be0b7853b46fd7efa48358e8fb Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Tue, 3 Oct 2023 02:51:02 +0200 Subject: [PATCH 215/428] Fix UP registration in final callback (#6929) * Fix registration in final * Don't make it too complicated... --- src/smt/theory_user_propagator.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 7c72419c1..6735272a1 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -62,8 +62,7 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { enode* n = ensure_enode ? this->ensure_enode(e) : ctx.get_enode(e); if (is_attached_to_var(n)) return; - - + theory_var v = mk_var(n); m_var2expr.reserve(v + 1); m_var2expr[v] = term; @@ -146,7 +145,7 @@ final_check_status theory_user_propagator::final_check_eh() { return FC_DONE; force_push(); unsigned sz1 = m_prop.size(); - unsigned sz2 = m_expr2var.size(); + unsigned sz2 = get_num_vars(); try { m_final_eh(m_user_context, this); } @@ -157,7 +156,7 @@ final_check_status theory_user_propagator::final_check_eh() { propagate(); CTRACE("user_propagate", ctx.inconsistent(), tout << "inconsistent\n"); // check if it became inconsistent or something new was propagated/registered - bool done = (sz1 == m_prop.size()) && (sz2 == m_expr2var.size()) && !ctx.inconsistent(); + bool done = (sz1 == m_prop.size()) && (sz2 == get_num_vars()) && !ctx.inconsistent(); return done ? FC_DONE : FC_CONTINUE; } From 00ba064cd3735ef8035edbbde4be7a0d71b4104c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 3 Oct 2023 14:28:59 +0900 Subject: [PATCH 216/428] ensure bounds propagation on changed columns after nla propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 2 ++ src/smt/theory_lra.cpp | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 729b5865e..4049d7204 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1067,6 +1067,8 @@ namespace lp { } bool lar_solver::init_model() const { + CTRACE("lar_solver_model",!m_columns_with_changed_bounds.empty(), tout << "non-empty changed bounds\n"); + TRACE("lar_solver_model", tout << get_status() << "\n"); if (get_status() != lp_status::OPTIMAL && get_status() != lp_status::FEASIBLE) return false; if (!m_columns_with_changed_bounds.empty()) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a5c5084ab..b746a88bf 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2115,8 +2115,9 @@ public: flush_bound_axioms(); // disabled in master: propagate_nla(); - if (!can_propagate_core()) + if (!can_propagate_core()) return false; + m_new_def = false; while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent() && m.inc()) { auto [bv, is_true] = m_asserted_atoms[m_asserted_qhead]; @@ -2160,6 +2161,7 @@ public: m_nla->propagate(); add_lemmas(); add_equalities(); + propagate_bounds_with_lp_solver(); } } @@ -2210,9 +2212,6 @@ public: } void propagate_bounds_with_lp_solver() { - if (!should_propagate()) - return; - m_bp.init(); lp().propagate_bounds_for_touched_rows(m_bp); @@ -2224,13 +2223,11 @@ public: // verbose_stream() << "unsat\n"; } else { - unsigned count = 0, prop = 0; for (auto& ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - ++prop; - count += propagate_lp_solver_bound(ib); + propagate_lp_solver_bound(ib); } } } From a88aa7ffa51769e607c5a6a9384a4f34f2320b74 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 3 Oct 2023 16:25:49 -0700 Subject: [PATCH 217/428] debug new propagation scheme Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 4 +++- src/math/lp/lar_solver.h | 3 ++- src/math/lp/monomial_bounds.cpp | 6 +++--- src/smt/theory_lra.cpp | 4 ++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 4049d7204..43acce576 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1822,6 +1822,7 @@ namespace lp { if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); + track_column_feasibility(j); 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") << " val = " << get_column_value(j) << std::endl;); } @@ -2359,7 +2360,8 @@ namespace lp { // dep is the reason for the new bound void lar_solver::set_crossed_bounds_column_and_deps(unsigned j, bool lower_bound, u_dependency* dep) { - SASSERT(m_crossed_bounds_deps == nullptr && m_crossed_bounds_deps == nullptr); + if (m_crossed_bounds_column != null_lpvar) return; // already set + SASSERT(m_crossed_bounds_deps == nullptr); set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; const auto& ul = this->m_columns_to_ul_pairs()[j]; diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 0748d2507..9b0dd8381 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -152,7 +152,8 @@ public: lpvar crossed_bounds_column() const { return m_crossed_bounds_column; } lpvar& crossed_bounds_column() { return m_crossed_bounds_column; } - + bool current_x_is_feasible() const { return m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible(); } + private: void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 8bb6ab306..0e4a5041e 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -311,8 +311,8 @@ namespace nla { c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); // propagate fixed equality - auto exp = get_explanation(dep); - c().add_fixed_equality(m.var(), k, exp); + auto exp = get_explanation(dep); + c().add_fixed_equality(c().lra.column_to_reported_index(m.var()), k, exp); } void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { @@ -327,7 +327,7 @@ namespace nla { if (k == 1) { lp::explanation exp = get_explanation(dep); - c().add_equality(m.var(), w, exp); + c().add_equality(c().lra.column_to_reported_index(m.var()), c().lra.column_to_reported_index(w), exp); } } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index b746a88bf..7f7951b20 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2212,6 +2212,10 @@ public: } void propagate_bounds_with_lp_solver() { + if (!lp().current_x_is_feasible()) { + lp().clear_columns_with_changed_bounds(); + return; + } m_bp.init(); lp().propagate_bounds_for_touched_rows(m_bp); From edd1761ff3c3bfbaff638526e3e78e97a20188e4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 4 Oct 2023 11:06:24 -0700 Subject: [PATCH 218/428] restore the scheme of m_columns_with_changed_bounds Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 20 ++++++++++++++------ src/math/lp/lar_solver.h | 25 +++++++++---------------- src/math/lp/monomial_bounds.cpp | 10 ++++++---- src/math/lp/monomial_bounds.h | 2 +- src/math/lp/nla_core.cpp | 13 +++++++++++++ src/math/lp/nla_core.h | 3 ++- src/math/lp/nla_solver.h | 2 +- src/smt/theory_lra.cpp | 6 +----- 8 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 43acce576..d47447b55 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -204,6 +204,12 @@ namespace lp { return m_status; solve_with_core_solver(); + if (m_status != lp_status::INFEASIBLE) { + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); + } + + clear_columns_with_changed_bounds(); return m_status; } @@ -478,7 +484,6 @@ namespace lp { x = new_val; TRACE("lar_solver_feas", tout << "setting " << j << " to " << new_val << (column_is_feasible(j)?"feas":"non-feas") << "\n";); - m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); change_basic_columns_dependend_on_a_given_nb_column(j, delta); } @@ -786,7 +791,8 @@ namespace lp { void lar_solver::detect_rows_with_changed_bounds() { for (auto j : m_columns_with_changed_bounds) detect_rows_with_changed_bounds_for_column(j); - + if (m_find_monics_with_changed_bounds_func) + m_find_monics_with_changed_bounds_func(m_columns_with_changed_bounds); } void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { @@ -1106,6 +1112,7 @@ namespace lp { mpq lar_solver::get_value(column_index const& j) const { SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE); + SASSERT(m_columns_with_changed_bounds.empty()); numeric_pair const& rp = get_column_value(j); return from_model_in_impq_to_mpq(rp); } @@ -1822,8 +1829,7 @@ namespace lp { if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); - track_column_feasibility(j); - 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") << " val = " << get_column_value(j) << std::endl;); + 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;); } void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { @@ -2370,10 +2376,12 @@ namespace lp { m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); } - void lar_solver::track_column_feasibility(lpvar j) { - m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + void lar_solver::collect_more_rows_for_lp_propagation(){ + for (auto j : m_columns_with_changed_bounds) + detect_rows_with_changed_bounds_for_column(j); } + } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 9b0dd8381..298d1f879 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -89,13 +89,6 @@ class lar_solver : public column_namer { constraint_set m_constraints; // the set of column indices j such that bounds have changed for j indexed_uint_set m_columns_with_changed_bounds; -public: - const indexed_uint_set& columns_with_changed_bounds() const { return m_columns_with_changed_bounds; } - inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } - void track_column_feasibility(lpvar j); - -private: - // m_touched_rows contains rows that have been changed by a pivoting or have a column with changed bounds indexed_uint_set m_touched_rows; unsigned_vector m_row_bounds_to_replay; u_dependency_manager m_dependencies; @@ -145,22 +138,23 @@ private: 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); -public: + + inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } + public: void insert_to_columns_with_changed_bounds(unsigned j); const u_dependency* crossed_bounds_deps() const { return m_crossed_bounds_deps;} u_dependency*& crossed_bounds_deps() { return m_crossed_bounds_deps;} lpvar crossed_bounds_column() const { return m_crossed_bounds_column; } lpvar& crossed_bounds_column() { return m_crossed_bounds_column; } - bool current_x_is_feasible() const { return m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible(); } - + -private: + private: void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); -public: + public: void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); -private: + private: void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); @@ -364,8 +358,6 @@ private: void add_column_rows_to_touched_rows(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator& bp) { - detect_rows_with_changed_bounds(); - clear_columns_with_changed_bounds(); if (settings().propagate_eqs()) { if (settings().random_next() % 10 == 0) remove_fixed_vars_from_base(); @@ -386,7 +378,7 @@ private: } m_touched_rows.reset(); } - + void collect_more_rows_for_lp_propagation(); template void check_missed_propagations(lp_bound_propagator& bp) { for (unsigned i = 0; i < A_r().row_count(); i++) @@ -696,6 +688,7 @@ private: return 0; return m_usage_in_terms[j]; } + std::function m_find_monics_with_changed_bounds_func = nullptr; friend int_solver; friend int_branch; }; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 0e4a5041e..95f4586d8 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -260,8 +260,9 @@ namespace nla { } void monomial_bounds::unit_propagate() { - for (auto const& m : c().m_emons) { - unit_propagate(m); + for (lpvar v : c().m_monics_with_changed_bounds) { + if (!c().is_monic_var(v)) continue; + unit_propagate(c().emons()[v]); if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { lp::explanation exp; c().lra.get_infeasibility_explanation(exp); @@ -276,14 +277,15 @@ namespace nla { } - void monomial_bounds::unit_propagate(monic const& m) { + void monomial_bounds::unit_propagate(monic & m) { if (m.is_propagated()) return; if (!is_linear(m)) return; - + c().emons().set_propagated(m); + rational k = fixed_var_product(m); lpvar w = non_fixed_var(m); if (w == null_lpvar || k == 0) { diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 747aca9a2..a65ca7f4a 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -35,7 +35,7 @@ namespace nla { bool is_zero(lpvar v) const; // monomial propagation - void unit_propagate(monic const& m); + void unit_propagate(monic & m); bool is_linear(monic const& m); rational fixed_var_product(monic const& m); lpvar non_fixed_var(monic const& m); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 6a241e951..2a23ba47b 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -41,6 +41,17 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_nra(s, m_nra_lim, *this) { m_nlsat_delay = lp_settings().nlsat_delay(); + lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { + for (lpvar j : columns_with_changed_bounds) { + if (is_monic_var(j)) + m_monics_with_changed_bounds.insert(j); + else { + for (const auto & m: m_emons.get_use_list(j)) { + m_monics_with_changed_bounds.insert(m.var()); + } + } + } + }; } bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const { @@ -137,6 +148,7 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) { m_add_buffer[i] = j; } m_emons.add(v, m_add_buffer); + m_monics_with_changed_bounds.insert(v); } void core::push() { @@ -1817,6 +1829,7 @@ bool core::improve_bounds() { void core::propagate() { clear(); m_monomial_bounds.unit_propagate(); + m_monics_with_changed_bounds.reset(); } diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 5a597ae67..ae76f6d5a 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -89,6 +89,7 @@ class core { vector m_equalities; vector m_fixed_equalities; indexed_uint_set m_to_refine; + indexed_uint_set m_monics_with_changed_bounds; tangents m_tangents; basics m_basics; order m_order; @@ -120,7 +121,7 @@ class core { public: // constructor core(lp::lar_solver& s, params_ref const& p, reslimit&); - + const auto& monics_with_changed_bounds() const { return m_monics_with_changed_bounds; } void insert_to_refine(lpvar j); void erase_from_to_refine(lpvar j); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index fec27c32b..c508e68d0 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -26,7 +26,7 @@ namespace nla { solver(lp::lar_solver& s, params_ref const& p, reslimit& limit); ~solver(); - + const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); } void add_monic(lpvar v, unsigned sz, lpvar const* vs); void add_idivision(lpvar q, lpvar x, lpvar y); void add_rdivision(lpvar q, lpvar x, lpvar y); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 7f7951b20..e854db802 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2161,7 +2161,7 @@ public: m_nla->propagate(); add_lemmas(); add_equalities(); - propagate_bounds_with_lp_solver(); + lp().collect_more_rows_for_lp_propagation(); } } @@ -2212,10 +2212,6 @@ public: } void propagate_bounds_with_lp_solver() { - if (!lp().current_x_is_feasible()) { - lp().clear_columns_with_changed_bounds(); - return; - } m_bp.init(); lp().propagate_bounds_for_touched_rows(m_bp); From 45c0ed126e44bef4ff75d9179424f0c538a4f454 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 4 Oct 2023 17:39:22 -0700 Subject: [PATCH 219/428] remove unnecessery call Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 5 ++++- src/math/lp/lar_solver.h | 3 ++- src/math/lp/monomial_bounds.cpp | 10 +++++----- src/math/lp/monomial_bounds.h | 1 - src/smt/theory_lra.cpp | 2 -- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index d47447b55..05525c536 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -419,7 +419,9 @@ namespace lp { void lar_solver::move_non_basic_columns_to_bounds(bool shift_randomly) { auto& lcs = m_mpq_lar_core_solver; bool change = false; - for (unsigned j : lcs.m_r_nbasis) { + for (unsigned j : m_columns_with_changed_bounds) { + if (lcs.m_r_heading[j] >= 0) + continue; if (move_non_basic_column_to_bounds(j, shift_randomly)) change = true; } @@ -2374,6 +2376,7 @@ namespace lp { u_dependency* bdep = lower_bound? ul.lower_bound_witness() : ul.upper_bound_witness(); SASSERT(bdep != nullptr); m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); + insert_to_columns_with_changed_bounds(j); } void lar_solver::collect_more_rows_for_lp_propagation(){ diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 298d1f879..09cb7796c 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -140,7 +140,8 @@ class lar_solver : public column_namer { bool compare_values(impq const& lhs, lconstraint_kind k, const mpq& rhs); inline void clear_columns_with_changed_bounds() { m_columns_with_changed_bounds.reset(); } - public: + public: + const auto& columns_with_changed_bounds() const { return m_columns_with_changed_bounds; } void insert_to_columns_with_changed_bounds(unsigned j); const u_dependency* crossed_bounds_deps() const { return m_crossed_bounds_deps;} u_dependency*& crossed_bounds_deps() { return m_crossed_bounds_deps;} diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 95f4586d8..1b46cdf6b 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -268,15 +268,14 @@ namespace nla { c().lra.get_infeasibility_explanation(exp); new_lemma lemma(c(), "propagate fixed - infeasible lra"); lemma &= exp; - return; + break; } if (c().m_conflicts > 0 ) { - return; + break; } } } - void monomial_bounds::unit_propagate(monic & m) { if (m.is_propagated()) return; @@ -288,9 +287,8 @@ namespace nla { rational k = fixed_var_product(m); lpvar w = non_fixed_var(m); - if (w == null_lpvar || k == 0) { + if (w == null_lpvar || k == 0) propagate_fixed(m, k); - } else propagate_nonfixed(m, k, w); } @@ -310,6 +308,7 @@ namespace nla { lp::impq val(k); c().lra.set_value_for_nbasic_column(m.var(), val); } + TRACE("nla_solver", tout << "propagate fixed " << m << " = " << k << "\n";); c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); // propagate fixed equality @@ -325,6 +324,7 @@ namespace nla { lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(m, k); term_index = c().lra.map_term_index_to_column_index(term_index); + TRACE("nla_solver", tout << "propagate nonfixed " << m << " = " << k << "\n";); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); if (k == 1) { diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index a65ca7f4a..b2079b4f1 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -39,7 +39,6 @@ namespace nla { bool is_linear(monic const& m); rational fixed_var_product(monic const& m); lpvar non_fixed_var(monic const& m); - public: monomial_bounds(core* core); void propagate(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index e854db802..40cecd321 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -3205,8 +3205,6 @@ public: lbool make_feasible() { TRACE("pcs", tout << lp().constraints();); TRACE("arith_verbose", tout << "before calling lp().find_feasible_solution()\n"; display(tout);); - // todo: remove later : debug!!!!! - lp().move_non_basic_columns_to_bounds(false); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); if (lp().is_feasible()) From b0df74c1c1f4ab17b4aba4891bd7ab9b3457b748 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 5 Oct 2023 17:23:03 +0900 Subject: [PATCH 220/428] #6930 simplify assumptions and only replay assumptions after constraints are simplified. This allows simplifying assumptions with the current set of constraints independently of whether there is another check-sat. --- src/smt/theory_lra.cpp | 6 ++++-- src/solver/simplifier_solver.cpp | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 96cd3dd47..b33688209 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2113,7 +2113,10 @@ public: m_model_is_initialized = false; flush_bound_axioms(); // disabled in master: - // propagate_nla(); + // propagate_nla(); + if (ctx().inconsistent()) + return true; + if (!can_propagate_core()) return false; m_new_def = false; @@ -3547,7 +3550,6 @@ public: if (r == l_true) { nctx.display_asserted_formulas(std::cout); std::cout.flush(); - std::cout.flush(); } return l_true != r; } diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 0c4c2a7b6..bac75f1e5 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -24,6 +24,7 @@ Author: #include "ast/rewriter/expr_safe_replace.h" #include "ast/simplifiers/dependent_expr_state.h" #include "ast/simplifiers/then_simplifier.h" +#include "ast/rewriter/th_rewriter.h" #include "solver/solver.h" #include "solver/simplifier_solver.h" #include "solver/solver_preprocess.h" @@ -62,7 +63,16 @@ class simplifier_solver : public solver { if (s.m.is_false(f)) s.set_inconsistent(); } - void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } + void replay(unsigned qhead, expr_ref_vector& assumptions) { + m_reconstruction_trail.replay(qhead, assumptions, *this); + th_rewriter rw(s.m); + expr_ref tmp(s.m); + for (unsigned i = 0; i < assumptions.size(); ++i) { + tmp = assumptions.get(i); + rw(tmp); + assumptions[i] = tmp; + } + } void flatten_suffix() override { expr_mark seen; unsigned j = qhead(); @@ -109,15 +119,16 @@ class simplifier_solver : public solver { unsigned qhead = m_preprocess_state.qhead(); expr_ref_vector orig_assumptions(assumptions); m_core_replace.reset(); - if (qhead < m_fmls.size() || !assumptions.empty()) { - m_preprocess_state.replay(qhead, assumptions); - m_preprocess_state.freeze(assumptions); + if (qhead < m_fmls.size()) { m_preprocess.reduce(); if (!m.inc()) return; TRACE("solver", tout << "qhead " << qhead << "\n"; m_preprocess_state.display(tout)); m_preprocess_state.advance_qhead(); + } + if (!assumptions.empty()) { + m_preprocess_state.replay(m_preprocess_state.qhead(), assumptions); for (unsigned i = 0; i < assumptions.size(); ++i) m_core_replace.insert(assumptions.get(i), orig_assumptions.get(i)); } @@ -203,6 +214,7 @@ public: lbool check_sat_core(unsigned num_assumptions, expr* const* assumptions) override { expr_ref_vector _assumptions(m, num_assumptions, assumptions); flush(_assumptions); + TRACE("simplifier", tout << _assumptions); return s->check_sat_core(num_assumptions, _assumptions.data()); } From b61f4ac51f527416dc1a76ba1a76d1a55dfacec0 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 5 Oct 2023 07:50:13 -0700 Subject: [PATCH 221/428] merge changes from master Signed-off-by: Lev Nachmanson --- src/math/lp/lar_solver.cpp | 6 ++++-- src/smt/theory_lra.cpp | 7 ++++++- src/smt/theory_user_propagator.cpp | 7 +++---- src/solver/simplifier_solver.cpp | 20 ++++++++++++++++---- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 05525c536..55a1686fa 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1077,12 +1077,14 @@ namespace lp { bool lar_solver::init_model() const { CTRACE("lar_solver_model",!m_columns_with_changed_bounds.empty(), tout << "non-empty changed bounds\n"); TRACE("lar_solver_model", tout << get_status() << "\n"); - if (get_status() != lp_status::OPTIMAL && get_status() != lp_status::FEASIBLE) + auto status = get_status(); + SASSERT((status != lp_status::OPTIMAL && status != lp_status::FEASIBLE) + || m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()); + if (status != lp_status::OPTIMAL && status != lp_status::FEASIBLE) return false; if (!m_columns_with_changed_bounds.empty()) return false; - lp_assert(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()); m_delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1)); unsigned j; unsigned n = m_mpq_lar_core_solver.m_r_x.size(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 40cecd321..db4dba137 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2115,6 +2115,9 @@ public: flush_bound_axioms(); // disabled in master: propagate_nla(); + if (ctx().inconsistent()) + return true; + if (!can_propagate_core()) return false; @@ -2212,6 +2215,9 @@ public: } void propagate_bounds_with_lp_solver() { + if (!should_propagate()) + return; + m_bp.init(); lp().propagate_bounds_for_touched_rows(m_bp); @@ -3546,7 +3552,6 @@ public: if (r == l_true) { nctx.display_asserted_formulas(std::cout); std::cout.flush(); - std::cout.flush(); } return l_true != r; } diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 7c72419c1..6735272a1 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -62,8 +62,7 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { enode* n = ensure_enode ? this->ensure_enode(e) : ctx.get_enode(e); if (is_attached_to_var(n)) return; - - + theory_var v = mk_var(n); m_var2expr.reserve(v + 1); m_var2expr[v] = term; @@ -146,7 +145,7 @@ final_check_status theory_user_propagator::final_check_eh() { return FC_DONE; force_push(); unsigned sz1 = m_prop.size(); - unsigned sz2 = m_expr2var.size(); + unsigned sz2 = get_num_vars(); try { m_final_eh(m_user_context, this); } @@ -157,7 +156,7 @@ final_check_status theory_user_propagator::final_check_eh() { propagate(); CTRACE("user_propagate", ctx.inconsistent(), tout << "inconsistent\n"); // check if it became inconsistent or something new was propagated/registered - bool done = (sz1 == m_prop.size()) && (sz2 == m_expr2var.size()) && !ctx.inconsistent(); + bool done = (sz1 == m_prop.size()) && (sz2 == get_num_vars()) && !ctx.inconsistent(); return done ? FC_DONE : FC_CONTINUE; } diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index 0c4c2a7b6..bac75f1e5 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -24,6 +24,7 @@ Author: #include "ast/rewriter/expr_safe_replace.h" #include "ast/simplifiers/dependent_expr_state.h" #include "ast/simplifiers/then_simplifier.h" +#include "ast/rewriter/th_rewriter.h" #include "solver/solver.h" #include "solver/simplifier_solver.h" #include "solver/solver_preprocess.h" @@ -62,7 +63,16 @@ class simplifier_solver : public solver { if (s.m.is_false(f)) s.set_inconsistent(); } - void replay(unsigned qhead, expr_ref_vector& assumptions) { m_reconstruction_trail.replay(qhead, assumptions, *this); } + void replay(unsigned qhead, expr_ref_vector& assumptions) { + m_reconstruction_trail.replay(qhead, assumptions, *this); + th_rewriter rw(s.m); + expr_ref tmp(s.m); + for (unsigned i = 0; i < assumptions.size(); ++i) { + tmp = assumptions.get(i); + rw(tmp); + assumptions[i] = tmp; + } + } void flatten_suffix() override { expr_mark seen; unsigned j = qhead(); @@ -109,15 +119,16 @@ class simplifier_solver : public solver { unsigned qhead = m_preprocess_state.qhead(); expr_ref_vector orig_assumptions(assumptions); m_core_replace.reset(); - if (qhead < m_fmls.size() || !assumptions.empty()) { - m_preprocess_state.replay(qhead, assumptions); - m_preprocess_state.freeze(assumptions); + if (qhead < m_fmls.size()) { m_preprocess.reduce(); if (!m.inc()) return; TRACE("solver", tout << "qhead " << qhead << "\n"; m_preprocess_state.display(tout)); m_preprocess_state.advance_qhead(); + } + if (!assumptions.empty()) { + m_preprocess_state.replay(m_preprocess_state.qhead(), assumptions); for (unsigned i = 0; i < assumptions.size(); ++i) m_core_replace.insert(assumptions.get(i), orig_assumptions.get(i)); } @@ -203,6 +214,7 @@ public: lbool check_sat_core(unsigned num_assumptions, expr* const* assumptions) override { expr_ref_vector _assumptions(m, num_assumptions, assumptions); flush(_assumptions); + TRACE("simplifier", tout << _assumptions); return s->check_sat_core(num_assumptions, _assumptions.data()); } From bf3817ef7c1fd7db29135e258683c90ec2e88835 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 5 Oct 2023 18:14:52 -0700 Subject: [PATCH 222/428] restore move_non_basic_to_bounds Signed-off-by: Lev Nachmanson --- src/math/lp/core_solver_pretty_printer_def.h | 6 ++++-- src/math/lp/lar_solver.cpp | 11 ++++++----- src/math/lp/lp_core_solver_base.h | 1 + src/util/heap.h | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index b8048b241..cbe67ea36 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -279,10 +279,12 @@ template void core_solver_pretty_printer::print() print_row(i); } m_out << std::endl; - if (m_core_solver.inf_heap().size()) { - m_out << "inf columns: "; + if (!m_core_solver.inf_heap().empty()) { + m_out << "inf columns: size() = " << m_core_solver.inf_heap().size() << std::endl; print_vector(m_core_solver.inf_heap(), m_out); m_out << std::endl; + } else { + m_out << "inf columns: none\n"; } } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 55a1686fa..931e74f57 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -181,7 +181,10 @@ namespace lp { lp_status lar_solver::get_status() const { return m_status; } - void lar_solver::set_status(lp_status s) { m_status = s; } + void lar_solver::set_status(lp_status s) { + TRACE("lar_solver", tout << "setting status to " << s << "\n";); + m_status = s; + } lp_status lar_solver::find_feasible_solution() { stats().m_make_feasible++; @@ -419,9 +422,7 @@ namespace lp { void lar_solver::move_non_basic_columns_to_bounds(bool shift_randomly) { auto& lcs = m_mpq_lar_core_solver; bool change = false; - for (unsigned j : m_columns_with_changed_bounds) { - if (lcs.m_r_heading[j] >= 0) - continue; + for (unsigned j : lcs.m_r_nbasis) { if (move_non_basic_column_to_bounds(j, shift_randomly)) change = true; } @@ -439,7 +440,7 @@ namespace lp { switch (lcs.m_column_types()[j]) { case column_type::boxed: { bool at_l = val == lcs.m_r_lower_bounds()[j]; - bool at_u = !at_l && (val == lcs.m_r_upper_bounds()[j]); + bool at_u = (!at_l && (val == lcs.m_r_upper_bounds()[j])); if (!at_l && !at_u) { if (m_settings.random_next() % 2) set_value_for_nbasic_column(j, lcs.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 1d6634c06..2c0993d71 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -564,6 +564,7 @@ public: } void insert_column_into_inf_heap(unsigned j) { if (!m_inf_heap.contains(j)) { + m_inf_heap.reserve(j+1); m_inf_heap.insert(j); TRACE("lar_solver_inf_heap", tout << "insert into inf_heap j = " << j << "\n";); } diff --git a/src/util/heap.h b/src/util/heap.h index df67b31be..332f4e53e 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -159,7 +159,7 @@ public: } unsigned size() const { - return m_value2indices.size(); + return m_values.size() - 1; } void reserve(int s) { From 23de8056d70c38de5a0aee04ff1332a51bdb6040 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 6 Oct 2023 03:49:22 +0100 Subject: [PATCH 223/428] Add RCF functions to OCaml API (#6932) --- examples/ml/ml_example.ml | 17 ++++++++- src/api/ml/z3.ml | 37 +++++++++++++++++++ src/api/ml/z3.mli | 75 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index 5b4e6e9ed..b6a71b69d 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -315,7 +315,21 @@ let fpa_example ( ctx : context ) = Printf.printf "Test passed.\n" ) -let _ = +(** + A basic example of RCF usage +**) +let rcf_example ( ctx : context ) = + Printf.printf "RCFExample\n" ; + let pi = RCF.mk_pi ctx in + let e = RCF.mk_e ctx in + (Printf.printf "e: %s, pi: %s, e==pi: %b, e < pi: %b\n" + (RCF.num_to_string ctx e true false) + (RCF.num_to_string ctx pi true false) + (RCF.eq ctx e pi) + (RCF.lt ctx e pi)) ; + Printf.printf "Test passed.\n" + +let _ = try ( if not (Log.open_ "z3.log") then raise (TestFailedException "Log couldn't be opened.") @@ -340,6 +354,7 @@ let _ = basic_tests ctx ; quantifier_example1 ctx ; fpa_example ctx ; + rcf_example ctx ; Printf.printf "Disposing...\n"; Gc.full_major () ); diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 98807bedd..4d195a0b1 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -2077,6 +2077,43 @@ struct end +module RCF = +struct + type rcf_num = Z3native.rcf_num + + let del (ctx:context) (a:rcf_num) = Z3native.rcf_del ctx a + let mk_rational (ctx:context) (v:string) = Z3native.rcf_mk_rational ctx v + let mk_small_int (ctx:context) (v:int) = Z3native.rcf_mk_small_int ctx v + + let mk_pi (ctx:context) = Z3native.rcf_mk_pi ctx + let mk_e (ctx:context) = Z3native.rcf_mk_e ctx + let mk_infinitesimal (ctx:context) = Z3native.rcf_mk_infinitesimal ctx + + let mk_roots (ctx:context) (n:int) (a:rcf_num list) = let n, r = Z3native.rcf_mk_roots ctx n a in r + + let add (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_add ctx a b + let sub (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_sub ctx a b + let mul (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_mul ctx a b + let div (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_div ctx a b + + let neg (ctx:context) (a:rcf_num) = Z3native.rcf_neg ctx a + let inv (ctx:context) (a:rcf_num) = Z3native.rcf_neg ctx a + + let power (ctx:context) (a:rcf_num) (k:int) = Z3native.rcf_power ctx a k + + let lt (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_lt ctx a b + let gt (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_gt ctx a b + let le (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_le ctx a b + let ge (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_ge ctx a b + let eq (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_eq ctx a b + let neq (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_neq ctx a b + + let num_to_string (ctx:context) (a:rcf_num) (compact:bool) (html:bool) = Z3native.rcf_num_to_string ctx a compact html + let num_to_decimal_string (ctx:context) (a:rcf_num) (prec:int) = Z3native.rcf_num_to_decimal_string ctx a prec + let get_numerator_denominator (ctx:context) (a:rcf_num) = Z3native.rcf_get_numerator_denominator ctx a +end + + let set_global_param = Z3native.global_param_set let get_global_param id = diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 53e92b491..006ee6051 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3535,6 +3535,81 @@ sig val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector end +(** Real closed field *) +module RCF : +sig + type rcf_num + + (** Delete a RCF numeral created using the RCF API. *) + val del : context -> rcf_num -> unit + + (** Return a RCF rational using the given string. *) + val mk_rational : context -> string -> rcf_num + + (** Return a RCF small integer. *) + val mk_small_int : context -> int -> rcf_num + + (** Return Pi *) + val mk_pi : context -> rcf_num + + (** Return e (Euler's constant) *) + val mk_e : context -> rcf_num + + (** Return a new infinitesimal that is smaller than all elements in the Z3 field. *) + val mk_infinitesimal : context -> rcf_num + + (** Extract the roots of a polynomial. Precondition: The input polynomial is not the zero polynomial. *) + val mk_roots : context -> int -> rcf_num list -> rcf_num list + + (** Addition *) + val add : context -> rcf_num -> rcf_num -> rcf_num + + (** Subtraction *) + val sub : context -> rcf_num -> rcf_num -> rcf_num + + (** Multiplication *) + val mul : context -> rcf_num -> rcf_num -> rcf_num + + (** Division *) + val div : context -> rcf_num -> rcf_num -> rcf_num + + (** Negation *) + val neg : context -> rcf_num -> rcf_num + + (** Multiplicative Inverse *) + val inv : context -> rcf_num -> rcf_num + + (** Power *) + val power : context -> rcf_num -> int -> rcf_num + + (** less-than *) + val lt : context -> rcf_num -> rcf_num -> bool + + (** greater-than *) + val gt : context -> rcf_num -> rcf_num -> bool + + (** less-than or equal *) + val le : context -> rcf_num -> rcf_num -> bool + + (** greater-than or equal *) + val ge : context -> rcf_num -> rcf_num -> bool + + (** equality *) + val eq : context -> rcf_num -> rcf_num -> bool + + (** not equal *) + val neq : context -> rcf_num -> rcf_num -> bool + + (** Convert the RCF numeral into a string. *) + val num_to_string : context -> rcf_num -> bool -> bool -> string + + (** Convert the RCF numeral into a string in decimal notation. *) + val num_to_decimal_string : context -> rcf_num -> int -> string + + (** Extract the "numerator" and "denominator" of the given RCF numeral. + We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. *) + val get_numerator_denominator : context -> rcf_num -> (rcf_num * rcf_num) +end (** Set a global (or module) parameter, which is shared by all Z3 contexts. From 19e921290e5c08c547b4ebe73752df262166b12c Mon Sep 17 00:00:00 2001 From: LufyCZ Date: Fri, 6 Oct 2023 04:49:54 +0200 Subject: [PATCH 224/428] increase wasm stack size (#6931) --- src/api/js/scripts/build-wasm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/js/scripts/build-wasm.ts b/src/api/js/scripts/build-wasm.ts index 0f51c84d3..5bdbdea3b 100644 --- a/src/api/js/scripts/build-wasm.ts +++ b/src/api/js/scripts/build-wasm.ts @@ -69,7 +69,7 @@ const fns = JSON.stringify(exportedFuncs()); const methods = '["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]'; const libz3a = path.normalize('../../../build/libz3.a'); spawnSync( - `emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -I z3/src/api/ -o build/z3-built.js`, + `emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=1GB -s TOTAL_STACK=20MB -I z3/src/api/ -o build/z3-built.js`, ); fs.rmSync(ccWrapperPath); From f847d039bc4f6c79874727f6da6cb2ecc070211b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 5 Oct 2023 20:57:54 -0700 Subject: [PATCH 225/428] use the simple version of move_non_basic_column_to_bounds --- src/math/lp/gomory.cpp | 2 +- src/math/lp/int_branch.cpp | 2 +- src/math/lp/int_cube.cpp | 2 +- src/math/lp/lar_solver.cpp | 50 +++++++++++++++++--------------------- src/math/lp/lar_solver.h | 4 +-- 5 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 775025018..e4267cbd4 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -417,7 +417,7 @@ int gomory::find_basic_var() { } lia_move gomory::operator()() { - lra.move_non_basic_columns_to_bounds(true); + lra.move_non_basic_columns_to_bounds(); int j = find_basic_var(); if (j == -1) return lia_move::undef; diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index 4bfe2b827..d6262a4d0 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -24,7 +24,7 @@ namespace lp { int_branch::int_branch(int_solver& lia):lia(lia), lra(lia.lra) {} lia_move int_branch::operator()() { - lra.move_non_basic_columns_to_bounds(true); + lra.move_non_basic_columns_to_bounds(); int j = find_inf_int_base_column(); return j == -1? lia_move::sat : create_branch_on_column(j); } diff --git a/src/math/lp/int_cube.cpp b/src/math/lp/int_cube.cpp index da724a543..9ef9aa341 100644 --- a/src/math/lp/int_cube.cpp +++ b/src/math/lp/int_cube.cpp @@ -43,7 +43,7 @@ namespace lp { if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { TRACE("cube", tout << "cannot find a feasible solution";); lra.pop(); - lra.move_non_basic_columns_to_bounds(false); + lra.move_non_basic_columns_to_bounds(); // it can happen that we found an integer solution here return !lra.r_basis_has_inf_int()? lia_move::sat: lia_move::undef; } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 931e74f57..c3f0cd771 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -402,7 +402,7 @@ namespace lp { void lar_solver::prepare_costs_for_r_solver(const lar_term& term) { TRACE("lar_solver", print_term(term, tout << "prepare: ") << "\n";); - move_non_basic_columns_to_bounds(false); + move_non_basic_columns_to_bounds(); auto& rslv = m_mpq_lar_core_solver.m_r_solver; lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); @@ -419,11 +419,11 @@ namespace lp { lp_assert(rslv.reduced_costs_are_correct_tableau()); } - void lar_solver::move_non_basic_columns_to_bounds(bool shift_randomly) { + void lar_solver::move_non_basic_columns_to_bounds() { auto& lcs = m_mpq_lar_core_solver; bool change = false; for (unsigned j : lcs.m_r_nbasis) { - if (move_non_basic_column_to_bounds(j, shift_randomly)) + if (move_non_basic_column_to_bounds(j)) change = true; } if (!change) @@ -434,46 +434,40 @@ namespace lp { find_feasible_solution(); } - bool lar_solver::move_non_basic_column_to_bounds(unsigned j, bool force_change) { + bool lar_solver::move_non_basic_column_to_bounds(unsigned j) { auto& lcs = m_mpq_lar_core_solver; auto& val = lcs.m_r_x[j]; switch (lcs.m_column_types()[j]) { case column_type::boxed: { - bool at_l = val == lcs.m_r_lower_bounds()[j]; - bool at_u = (!at_l && (val == lcs.m_r_upper_bounds()[j])); - if (!at_l && !at_u) { - if (m_settings.random_next() % 2) - set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); - else - set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); - return true; - } - else if (force_change && m_settings.random_next() % 3 == 0) { - set_value_for_nbasic_column(j, - at_l ? lcs.m_r_upper_bounds()[j] : lcs.m_r_lower_bounds()[j]); - return true; - } - break; - } - case column_type::lower_bound: + const auto& l = lcs.m_r_lower_bounds()[j]; + if (val == l || val == lcs.m_r_upper_bounds()[j]) return false; + set_value_for_nbasic_column(j, l); + return true; + } + + case column_type::lower_bound: { + const auto& l = lcs.m_r_lower_bounds()[j]; if (val != lcs.m_r_lower_bounds()[j]) { - set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); + set_value_for_nbasic_column(j, l); return true; } - break; + return false; + } case column_type::fixed: - case column_type::upper_bound: - if (val != lcs.m_r_upper_bounds()[j]) { - set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); + case column_type::upper_bound: { + const auto & u = lcs.m_r_upper_bounds()[j]; + if (val != u) { + set_value_for_nbasic_column(j, u); return true; } - break; + return false; + } case column_type::free_column: if (column_is_int(j) && !val.is_int()) { set_value_for_nbasic_column(j, impq(floor(val))); return true; } - break; + return false; default: SASSERT(false); } diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 09cb7796c..d902f9a3a 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -635,8 +635,8 @@ class lar_solver : public column_namer { 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); + void move_non_basic_columns_to_bounds(); + bool move_non_basic_column_to_bounds(unsigned j); inline bool r_basis_has_inf_int() const { for (unsigned j : r_basis()) { if (column_is_int(j) && !column_value_is_int(j)) From a94a75459e7146d9f92ec960f59c681bfde0089b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 6 Oct 2023 06:51:00 -0700 Subject: [PATCH 226/428] fixin nla_solver_test.cpp --- src/test/lp/nla_solver_test.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/test/lp/nla_solver_test.cpp b/src/test/lp/nla_solver_test.cpp index 4e1dde11a..fa5a29e99 100644 --- a/src/test/lp/nla_solver_test.cpp +++ b/src/test/lp/nla_solver_test.cpp @@ -169,8 +169,7 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_0() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); v.clear(); @@ -247,8 +246,7 @@ void test_basic_lemma_for_mon_neutral_from_factors_to_monomial_1() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); svector v; v.push_back(lp_b);v.push_back(lp_d);v.push_back(lp_e); nla.add_monic(lp_bde, v.size(), v.begin()); @@ -319,8 +317,7 @@ void test_basic_lemma_for_mon_zero_from_factors_to_monomial() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); create_abcde(nla, lp_a, @@ -382,8 +379,7 @@ void test_basic_lemma_for_mon_zero_from_monomial_to_factors() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); // create monomial acd unsigned_vector vec; @@ -443,8 +439,7 @@ void test_basic_lemma_for_mon_neutral_from_monomial_to_factors() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); create_abcde(nla, lp_a, @@ -556,8 +551,7 @@ void test_basic_sign_lemma() { reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); // create monomial bde vector vec; @@ -822,8 +816,7 @@ void test_tangent_lemma_rat() { s_set_column_value_test(s, lp_ab, v); reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); @@ -850,8 +843,7 @@ void test_tangent_lemma_reg() { s_set_column_value_test(s, lp_ab, rational(11)); reslimit l; params_ref p; - std_vector ib; - solver nla(s, p, l, ib); + solver nla(s, p, l); // create monomial ab vector vec; vec.push_back(lp_a); From 54f7aac0bc51556906af6ef6c2e47e960c85055d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2023 17:18:26 +0900 Subject: [PATCH 227/428] column value is not necessarily at bounds --- src/math/lp/monomial_bounds.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 1b46cdf6b..1571d6cbf 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -324,7 +324,7 @@ namespace nla { lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(m, k); term_index = c().lra.map_term_index_to_column_index(term_index); - TRACE("nla_solver", tout << "propagate nonfixed " << m << " = " << k << "\n";); + TRACE("nla_solver", tout << "propagate nonfixed " << m << " = " << k << " " << w << "\n";); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, mpq(0), dep); if (k == 1) { @@ -370,8 +370,9 @@ namespace nla { rational monomial_bounds::fixed_var_product(monic const& m) { rational r(1); for (lpvar v : m) { + // VERIFY(!c().var_is_fixed(v) || c().lra.get_column_value(v).x == c().lra.get_lower_bound(v).x); if (c().var_is_fixed(v)) - r *= c().lra.get_column_value(v).x; + r *= c().lra.get_lower_bound(v).x; } return r; } From 3aac528aef172ef97b41a3be7cf574801e5d9358 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 8 Oct 2023 07:34:03 -0700 Subject: [PATCH 228/428] add a comment Signed-off-by: Lev Nachmanson --- src/math/lp/monomial_bounds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 1571d6cbf..b2e62073a 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -370,7 +370,7 @@ namespace nla { rational monomial_bounds::fixed_var_product(monic const& m) { rational r(1); for (lpvar v : m) { - // VERIFY(!c().var_is_fixed(v) || c().lra.get_column_value(v).x == c().lra.get_lower_bound(v).x); + // we have to use the column bounds here, because the value of the column value may be outside the bounds if (c().var_is_fixed(v)) r *= c().lra.get_lower_bound(v).x; } From 1ba0f5aba944a28342a12ea194c663449fec3366 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sun, 8 Oct 2023 10:16:40 -0700 Subject: [PATCH 229/428] cosmetic changes --- src/math/lp/lar_solver.cpp | 2 +- src/math/lp/monomial_bounds.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index c3f0cd771..ebe14e407 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -447,7 +447,7 @@ namespace lp { case column_type::lower_bound: { const auto& l = lcs.m_r_lower_bounds()[j]; - if (val != lcs.m_r_lower_bounds()[j]) { + if (val != l) { set_value_for_nbasic_column(j, l); return true; } diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index b2e62073a..81c00d2f0 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -370,7 +370,7 @@ namespace nla { rational monomial_bounds::fixed_var_product(monic const& m) { rational r(1); for (lpvar v : m) { - // we have to use the column bounds here, because the value of the column value may be outside the bounds + // we have to use the column bounds here, because the column value may be outside the bounds if (c().var_is_fixed(v)) r *= c().lra.get_lower_bound(v).x; } From 1bdf66b918f3a4be5db5505a10388cfe70a138c5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Oct 2023 10:55:43 +0900 Subject: [PATCH 230/428] move initialization to header file --- src/math/lp/lar_solver.cpp | 2 -- src/math/lp/lar_solver.h | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index ebe14e407..069709126 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -22,8 +22,6 @@ namespace lp { } lar_solver::lar_solver() : - m_crossed_bounds_column(null_lpvar), - m_crossed_bounds_deps(nullptr), m_mpq_lar_core_solver(m_settings, *this), m_var_register(false), m_term_register(true), diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index d902f9a3a..949cb0910 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -78,8 +78,8 @@ class lar_solver : public column_namer { lp_status m_status = lp_status::UNKNOWN; stacked_value m_simplex_strategy; // such can be found at the initialization step: u < l - lpvar m_crossed_bounds_column; - u_dependency* m_crossed_bounds_deps; + lpvar m_crossed_bounds_column = null_lpvar; + u_dependency* m_crossed_bounds_deps = nullptr; lar_core_solver m_mpq_lar_core_solver; int_solver* m_int_solver = nullptr; bool m_need_register_terms = false; From 4a870966ad1cf9ffc839c4b3d244cba521b7c0ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Oct 2023 16:04:39 +0900 Subject: [PATCH 231/428] add code to enable unit propagation of bounds set UNIT_PROPAGATE_BOUNDS 1 to use the unit propagation version. It applies unit propagation eagerly (does not depend on checking LIA consistency before final check) and avoid creating new literals in most cases --- src/math/interval/dep_intervals.h | 2 + src/math/lp/lp_settings.h | 15 ++++++- src/math/lp/monomial_bounds.cpp | 66 ++++++++++++++++++++++--------- src/math/lp/monomial_bounds.h | 1 - src/math/lp/nla_core.cpp | 37 +++++++---------- src/math/lp/nla_core.h | 14 +------ 6 files changed, 79 insertions(+), 56 deletions(-) diff --git a/src/math/interval/dep_intervals.h b/src/math/interval/dep_intervals.h index f9768b6f4..f4774c186 100644 --- a/src/math/interval/dep_intervals.h +++ b/src/math/interval/dep_intervals.h @@ -175,6 +175,8 @@ public: void set_upper_is_inf(interval& a, bool inf) const { m_config.set_upper_is_inf(a, inf); } void set_lower_dep(interval& a, u_dependency* d) const { m_config.set_lower_dep(a, d); } void set_upper_dep(interval& a, u_dependency* d) const { m_config.set_upper_dep(a, d); } + u_dependency* get_lower_dep(interval const& a) const { return a.m_lower_dep; } + u_dependency* get_upper_dep(interval const& a) const { return a.m_upper_dep; } void reset(interval& a) const { set_lower_is_inf(a, true); set_upper_is_inf(a, true); } void set_value(interval& a, rational const& n) const { set_lower(a, n); diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 7ffffe5c5..362d8966a 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -115,7 +115,12 @@ struct statistics { unsigned m_hnf_cutter_calls; unsigned m_hnf_cuts; unsigned m_nla_calls; - unsigned m_nla_bounds; + unsigned m_nla_add_bounds; + unsigned m_nla_propagate_bounds; + unsigned m_nla_propagate_eq; + unsigned m_nla_lemmas; + unsigned m_nra_calls; + unsigned m_nla_bounds_improvements; unsigned m_horner_calls; unsigned m_horner_conflicts; unsigned m_cross_nested_forms; @@ -145,7 +150,13 @@ struct statistics { st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-offset-eqs", m_offset_eqs); st.update("arith-fixed-eqs", m_fixed_eqs); - st.update("arith-nla-bounds", m_nla_bounds); + st.update("arith-nla-add-bounds", m_nla_add_bounds); + st.update("arith-nla-propagate-bounds", m_nla_propagate_bounds); + st.update("arith-nla-propagate-eq", m_nla_propagate_eq); + st.update("arith-nla-lemmas", m_nla_lemmas); + st.update("arith-nra-calls", m_nra_calls); + st.update("arith-bounds-improvements", m_nla_bounds_improvements); + } }; diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 81c00d2f0..46fa6db81 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -12,6 +12,8 @@ #include "math/lp/nla_intervals.h" #include "math/lp/numeric_pair.h" +#define UNIT_PROPAGATE_BOUNDS 0 + namespace nla { monomial_bounds::monomial_bounds(core* c): @@ -19,10 +21,8 @@ namespace nla { dep(c->m_intervals.get_dep_intervals()) {} void monomial_bounds::propagate() { - for (lpvar v : c().m_to_refine) { - monic const& m = c().emons()[v]; - propagate(m); - } + for (lpvar v : c().m_to_refine) + propagate(c().emon(v)); } @@ -55,29 +55,41 @@ namespace nla { bool monomial_bounds::propagate_value(dep_interval& range, lpvar v) { auto val = c().val(v); if (dep.is_below(range, val)) { + auto const& upper = dep.upper(range); + auto cmp = dep.upper_is_open(range) ? llc::LT : llc::LE; + ++c().lra.settings().stats().m_nla_propagate_bounds; +#if UNIT_PROPAGATE_BOUNDS + auto* d = dep.get_upper_dep(range); + c().lra.update_column_type_and_bound(v, cmp, upper, d); +#else lp::explanation ex; dep.get_upper_dep(range, ex); - auto const& upper = dep.upper(range); if (is_too_big(upper)) return false; - auto cmp = dep.upper_is_open(range) ? llc::LT : llc::LE; new_lemma lemma(c(), "propagate value - upper bound of range is below value"); lemma &= ex; lemma |= ineq(v, cmp, upper); - TRACE("nla_solver", dep.display(tout << val << " > ", range) << "\n" << lemma << "\n";); + TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); +#endif return true; } else if (dep.is_above(range, val)) { + auto const& lower = dep.lower(range); + auto cmp = dep.lower_is_open(range) ? llc::GT : llc::GE; + ++c().lra.settings().stats().m_nla_propagate_bounds; +#if UNIT_PROPAGATE_BOUNDS + auto* d = dep.get_lower_dep(range); + c().lra.update_column_type_and_bound(v, cmp, lower, d); +#else lp::explanation ex; dep.get_lower_dep(range, ex); - auto const& lower = dep.lower(range); if (is_too_big(lower)) return false; - auto cmp = dep.lower_is_open(range) ? llc::GT : llc::GE; new_lemma lemma(c(), "propagate value - lower bound of range is above value"); lemma &= ex; lemma |= ineq(v, cmp, lower); - TRACE("nla_solver", dep.display(tout << val << " < ", range) << "\n" << lemma << "\n";); + TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); +#endif return true; } else { @@ -106,6 +118,7 @@ namespace nla { lp::explanation ex; dep.get_upper_dep(range, ex); if (p % 2 == 0 && rational(dep.upper(range)).is_neg()) { + ++c().lra.settings().stats().m_nla_propagate_bounds; new_lemma lemma(c(), "range requires a non-negative upper bound"); lemma &= ex; return true; @@ -114,18 +127,30 @@ namespace nla { // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 if ((p % 2 == 1) || val_v.is_pos()) { + ++c().lra.settings().stats().m_nla_propagate_bounds; auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; +#if UNIT_PROPAGATE_BOUNDS + auto* d = dep.get_upper_dep(); + c().lra.update_column_type_and_bound(v, le, r, d); +#else new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); lemma &= ex; lemma |= ineq(v, le, r); +#endif return true; } if (p % 2 == 0 && val_v.is_neg()) { + ++c().lra.settings().stats().m_nla_propagate_bounds; SASSERT(!r.is_neg()); auto ge = dep.upper_is_open(range) ? llc::GT : llc::GE; +#if UNIT_PROPAGATE_BOUNDS + auto* d = dep.get_upper_dep(); + c().lra.update_column_type_and_bound(v, ge, -r, d); +#else new_lemma lemma(c(), "propagate value - root case - upper bound of range is below negative value"); lemma &= ex; lemma |= ineq(v, ge, -r); +#endif return true; } } @@ -133,6 +158,7 @@ namespace nla { } else if (dep.is_above(range, val)) { if (rational(dep.lower(range)).root(p, r)) { + ++c().lra.settings().stats().m_nla_propagate_bounds; lp::explanation ex; dep.get_lower_dep(range, ex); auto ge = dep.lower_is_open(range) ? llc::GT : llc::GE; @@ -140,9 +166,8 @@ namespace nla { new_lemma lemma(c(), "propagate value - root case - lower bound of range is above value"); lemma &= ex; lemma |= ineq(v, ge, r); - if (p % 2 == 0) { + if (p % 2 == 0) lemma |= ineq(v, le, -r); - } return true; } // TBD: add bounds as long as difference to val is above some epsilon. @@ -261,8 +286,10 @@ namespace nla { void monomial_bounds::unit_propagate() { for (lpvar v : c().m_monics_with_changed_bounds) { - if (!c().is_monic_var(v)) continue; - unit_propagate(c().emons()[v]); + if (!c().is_monic_var(v)) + continue; + monic& m = c().emon(v); + unit_propagate(m); if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { lp::explanation exp; c().lra.get_infeasibility_explanation(exp); @@ -270,9 +297,8 @@ namespace nla { lemma &= exp; break; } - if (c().m_conflicts > 0 ) { + if (c().m_conflicts > 0) break; - } } } @@ -280,8 +306,12 @@ namespace nla { if (m.is_propagated()) return; - if (!is_linear(m)) + if (!is_linear(m)) { +#if UNIT_PROPAGATE_BOUNDS + propagate(m); +#endif return; + } c().emons().set_propagated(m); @@ -291,6 +321,7 @@ namespace nla { propagate_fixed(m, k); else propagate_nonfixed(m, k, w); + ++c().lra.settings().stats().m_nla_propagate_eq; } lp::explanation monomial_bounds::get_explanation(u_dependency* dep) { @@ -317,7 +348,6 @@ namespace nla { } void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { - VERIFY(k != 0); vector> coeffs; coeffs.push_back(std::make_pair(-k, w)); coeffs.push_back(std::make_pair(rational::one(), m.var())); diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index b2079b4f1..6fa439e01 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -17,7 +17,6 @@ namespace nla { class monomial_bounds : common { dep_intervals& dep; - void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; bool propagate_down(monic const& m, lpvar u); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 2a23ba47b..056df3b69 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -45,11 +45,8 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : for (lpvar j : columns_with_changed_bounds) { if (is_monic_var(j)) m_monics_with_changed_bounds.insert(j); - else { - for (const auto & m: m_emons.get_use_list(j)) { - m_monics_with_changed_bounds.insert(m.var()); - } - } + for (const auto & m: m_emons.get_use_list(j)) + m_monics_with_changed_bounds.insert(m.var()); } }; } @@ -1257,7 +1254,7 @@ bool core::var_breaks_correct_monic_as_factor(lpvar j, const monic& m) const { bool core::var_breaks_correct_monic(lpvar j) const { if (is_monic_var(j) && !m_to_refine.contains(j)) { - TRACE("nla_solver", tout << "j = " << j << ", m = "; print_monic(emons()[j], tout) << "\n";); + TRACE("nla_solver", tout << "j = " << j << ", m = "; print_monic(emon(j), tout) << "\n";); return true; // changing the value of a correct monic } @@ -1279,7 +1276,7 @@ void core::update_to_refine_of_var(lpvar j) { insert_to_refine(var(m)); } if (is_monic_var(j)) { - const monic& m = emons()[j]; + const monic& m = emon(j); if (var_val(m) == mul_val(m)) erase_from_to_refine(j); else @@ -1362,10 +1359,10 @@ bool in_power(const svector& vs, unsigned l) { bool core::to_refine_is_correct() const { for (unsigned j = 0; j < lra.number_of_vars(); j++) { if (!is_monic_var(j)) continue; - bool valid = check_monic(emons()[j]); + bool valid = check_monic(emon(j)); if (valid == m_to_refine.contains(j)) { TRACE("nla_solver", tout << "inconstency in m_to_refine : "; - print_monic(emons()[j], tout) << "\n"; + print_monic(emon(j), tout) << "\n"; if (valid) tout << "should NOT be in to_refine\n"; else tout << "should be in to_refine\n";); return false; @@ -1375,7 +1372,7 @@ bool core::to_refine_is_correct() const { } void core::patch_monomial(lpvar j) { - m_patched_monic =& (emons()[j]); + m_patched_monic =& (emon(j)); m_patched_var = j; TRACE("nla_solver", tout << "m = "; print_monic(*m_patched_monic, tout) << "\n";); rational v = mul_val(*m_patched_monic); @@ -1530,7 +1527,7 @@ void core::add_bounds() { if (!var_is_free(j)) continue; // split the free variable (j <= 0, or j > 0), and return m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); - ++lp_settings().stats().m_nla_bounds; + ++lp_settings().stats().m_nla_add_bounds; return; } } @@ -1611,15 +1608,13 @@ lbool core::check() { if (no_effect() && params().arith_nl_nra() && ret == l_undef) { ret = m_nra.check(); - m_stats.m_nra_calls++; + lp_settings().stats().m_nra_calls++; } if (ret == l_undef && !m_lemmas.empty() && m_reslim.inc()) ret = l_false; - m_stats.m_nla_lemmas += m_lemmas.size(); - for (const auto& l : m_lemmas) - m_stats.m_nla_explanations += static_cast(l.expl().size()); + lp_settings().stats().m_nla_lemmas += m_lemmas.size(); TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";); @@ -1649,7 +1644,7 @@ lbool core::bounded_nlsat() { } p.set_uint("max_conflicts", UINT_MAX); m_nra.updt_params(p); - m_stats.m_nra_calls++; + lp_settings().stats().m_nra_calls++; if (ret == l_undef) ++m_nlsat_delay; else { @@ -1678,7 +1673,7 @@ lbool core::test_check() { } std::ostream& core::print_terms(std::ostream& out) const { - for (unsigned i = 0; i< lra.terms().size(); i++) { + for (unsigned i = 0; i < lra.terms().size(); i++) { unsigned ext = lp::tv::mask_term(i); if (!lra.var_is_registered(ext)) { out << "term is not registered\n"; @@ -1798,10 +1793,6 @@ void core::set_use_nra_model(bool m) { } void core::collect_statistics(::statistics & st) { - st.update("arith-nla-explanations", m_stats.m_nla_explanations); - st.update("arith-nla-lemmas", m_stats.m_nla_lemmas); - st.update("arith-nra-calls", m_stats.m_nra_calls); - st.update("arith-bounds-improvements", m_stats.m_bounds_improvements); } bool core::improve_bounds() { @@ -1814,9 +1805,9 @@ bool core::improve_bounds() { return; seen.insert(v); if (lra.improve_bound(v, false)) - bounds_improved = true, m_stats.m_bounds_improvements++; + bounds_improved = true, lp_settings().stats().m_nla_bounds_improvements++; if (lra.improve_bound(v, true)) - bounds_improved = true, m_stats.m_bounds_improvements++; + bounds_improved = true, lp_settings().stats().m_nla_bounds_improvements++; }; for (auto & m : m_emons) { insert(m.var()); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index ae76f6d5a..3ff33a879 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -60,18 +60,6 @@ class core { friend class nra::solver; friend class divisions; - struct stats { - unsigned m_nla_explanations; - unsigned m_nla_lemmas; - unsigned m_nra_calls; - unsigned m_bounds_improvements; - stats() { reset(); } - void reset() { - memset(this, 0, sizeof(*this)); - } - }; - - stats m_stats; unsigned m_nlsat_delay = 50; unsigned m_nlsat_fails = 0; @@ -139,6 +127,8 @@ public: reslimit& reslim() { return m_reslim; } emonics& emons() { return m_emons; } const emonics& emons() const { return m_emons; } + monic& emon(unsigned i) { return m_emons[i]; } + monic const& emon(unsigned i) const { return m_emons[i]; } bool has_relevant_monomial() const; From 267e9e827db145d2986b789fad154ec44e80c1f0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 15:52:54 +0900 Subject: [PATCH 232/428] #6935 --- src/model/model_evaluator.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 28b726100..6ecb3c337 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -428,11 +428,15 @@ struct evaluator_cfg : public default_rewriter_cfg { } else if (!fi && m_au.is_considered_partially_interpreted(f, num, args, f_ui)) { fi = m_model.get_func_interp(f_ui); + if (fi) { - var_subst vs(m, false); - result = vs(fi->get_interp(), num, args); - result = m.mk_ite(m.mk_eq(m_au.mk_real(rational(0)), args[1]), result, m.mk_app(f, num, args)); - return BR_DONE; + auto interp = fi->get_interp(); + if (interp) { + var_subst vs(m, false); + result = vs(fi->get_interp(), num, args); + result = m.mk_ite(m.mk_eq(m_au.mk_real(rational(0)), args[1]), result, m.mk_app(f, num, args)); + return BR_DONE; + } } } else if (!fi && m_fpau.is_considered_uninterpreted(f, num, args)) { From adbee0cd3fdfa9b9c7a3a96469e0bc9b8786b350 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 16:02:59 +0900 Subject: [PATCH 233/428] fix #6937 --- src/smt/smt_enode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index 02ea82ba6..df0606270 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -54,6 +54,7 @@ namespace smt { for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; n->m_args[i] = arg; + arg->m_is_shared = 2; SASSERT(n->get_arg(i) == arg); if (update_children_parent) arg->get_root()->m_parents.push_back(n); From 180ab727e73e591a0247aedd4997715f6deebd24 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 10 Oct 2023 07:32:07 -0700 Subject: [PATCH 234/428] fix a bug in unit nl prop Signed-off-by: Lev Nachmanson --- src/math/lp/monomial_bounds.cpp | 59 +++++++++++++++++++++------------ src/math/lp/monomial_bounds.h | 5 +-- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 46fa6db81..c79d3f8e4 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -305,8 +305,8 @@ namespace nla { void monomial_bounds::unit_propagate(monic & m) { if (m.is_propagated()) return; - - if (!is_linear(m)) { + lpvar w, fixed_to_zero; + if (!is_linear(m, w, fixed_to_zero)) { #if UNIT_PROPAGATE_BOUNDS propagate(m); #endif @@ -315,12 +315,16 @@ namespace nla { c().emons().set_propagated(m); - rational k = fixed_var_product(m); - lpvar w = non_fixed_var(m); - if (w == null_lpvar || k == 0) - propagate_fixed(m, k); - else - propagate_nonfixed(m, k, w); + if (fixed_to_zero != null_lpvar) { + propagate_fixed_to_zero(m, fixed_to_zero); + } + else { + rational k = fixed_var_product(m, w); + if (w == null_lpvar) + propagate_fixed(m, k); + else + propagate_nonfixed(m, k, w); + } ++c().lra.settings().stats().m_nla_propagate_eq; } @@ -332,13 +336,19 @@ namespace nla { exp.add_pair(d, mpq(1)); return exp; } + + void monomial_bounds::propagate_fixed_to_zero(monic const& m, lpvar fixed_to_zero) { + auto* dep = c().lra.get_bound_constraint_witnesses_for_column(fixed_to_zero); + TRACE("nla_solver", tout << "propagate fixed " << m << " = 0, fixed_to_zero = " << fixed_to_zero << "\n";); + c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, rational(0), dep); + + // propagate fixed equality + auto exp = get_explanation(dep); + c().add_fixed_equality(c().lra.column_to_reported_index(m.var()), rational(0), exp); + } void monomial_bounds::propagate_fixed(monic const& m, rational const& k) { auto* dep = explain_fixed(m, k); - if (!c().lra.is_base(m.var())) { - lp::impq val(k); - c().lra.set_value_for_nbasic_column(m.var(), val); - } TRACE("nla_solver", tout << "propagate fixed " << m << " = " << k << "\n";); c().lra.update_column_type_and_bound(m.var(), lp::lconstraint_kind::EQ, k, dep); @@ -385,24 +395,31 @@ namespace nla { } - bool monomial_bounds::is_linear(monic const& m) { - unsigned non_fixed = 0; + bool monomial_bounds::is_linear(monic const& m, lpvar& w, lpvar & fixed_to_zero) { + w = fixed_to_zero = null_lpvar; for (lpvar v : m) { - if (!c().var_is_fixed(v)) - ++non_fixed; - else if (c().val(v).is_zero()) + if (!c().var_is_fixed(v)) { + if (w != null_lpvar) + return false; + w = v; + } + else if (c().get_lower_bound(v).is_zero()) { + fixed_to_zero = v; return true; + } } - return non_fixed <= 1; + return true; } - rational monomial_bounds::fixed_var_product(monic const& m) { + rational monomial_bounds::fixed_var_product(monic const& m, lpvar w) { rational r(1); for (lpvar v : m) { // we have to use the column bounds here, because the column value may be outside the bounds - if (c().var_is_fixed(v)) - r *= c().lra.get_lower_bound(v).x; + if (v != w ){ + SASSERT(c().var_is_fixed(v)); + r *= c().lra.get_lower_bound(v).x; + } } return r; } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 6fa439e01..9e62e1520 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -24,6 +24,7 @@ namespace nla { bool propagate_value(dep_interval& range, lpvar v, unsigned power); void compute_product(unsigned start, monic const& m, scoped_dep_interval& i); bool propagate(monic const& m); + void propagate_fixed_to_zero(monic const& m, lpvar fixed_to_zero); void propagate_fixed(monic const& m, rational const& k); void propagate_nonfixed(monic const& m, rational const& k, lpvar w); u_dependency* explain_fixed(monic const& m, rational const& k); @@ -35,8 +36,8 @@ namespace nla { // monomial propagation void unit_propagate(monic & m); - bool is_linear(monic const& m); - rational fixed_var_product(monic const& m); + bool is_linear(monic const& m, lpvar& w, lpvar & fixed_to_zero); + rational fixed_var_product(monic const& m, lpvar w); lpvar non_fixed_var(monic const& m); public: monomial_bounds(core* core); From e8e636c3ec5a63fa0a3e7f3c4d94b943f9b9fccb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 19:24:21 +0900 Subject: [PATCH 235/428] fix #6936 --- src/sat/sat_parallel.cpp | 25 ++++++++----------------- src/util/rlimit.h | 7 ++++--- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index cdb13706f..9f531d19c 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -25,12 +25,7 @@ namespace sat { void parallel::vector_pool::next(unsigned& index) { SASSERT(index < m_size); unsigned n = index + 2 + get_length(index); - if (n >= m_size) { - index = 0; - } - else { - index = n; - } + index = (n >= m_size) ? 0 : n; } void parallel::vector_pool::reserve(unsigned num_threads, unsigned sz) { @@ -64,9 +59,8 @@ namespace sat { } void parallel::vector_pool::end_add_vector() { - if (m_tail >= m_size) { + if (m_tail >= m_size) m_tail = 0; - } } @@ -93,9 +87,9 @@ namespace sat { parallel::parallel(solver& s): m_num_clauses(0), m_consumer_ready(false), m_scoped_rlimit(s.rlimit()) {} parallel::~parallel() { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - dealloc(m_solvers[i]); - } + m_limits.reset(); + for (auto* s : m_solvers) + dealloc(s); } void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { @@ -106,9 +100,8 @@ namespace sat { for (unsigned i = 0; i < num_extra_solvers; ++i) { s.m_params.set_uint("random_seed", s.m_rand()); - if (i == 1 + num_threads/2) { + if (i == 1 + num_threads/2) s.m_params.set_sym("phase", symbol("random")); - } m_solvers[i] = alloc(sat::solver, s.m_params, m_limits[i]); m_solvers[i]->copy(s, true); m_solvers[i]->set_par(this, i); @@ -164,9 +157,8 @@ namespace sat { IF_VERBOSE(3, verbose_stream() << owner << ": share " << c << "\n";); lock_guard lock(m_mux); m_pool.begin_add_vector(owner, n); - for (unsigned i = 0; i < n; ++i) { + for (unsigned i = 0; i < n; ++i) m_pool.add_vector_elem(c[i].index()); - } m_pool.end_add_vector(); } @@ -220,9 +212,8 @@ namespace sat { if (m_priorities.empty()) return; - for (bool_var v = 0; v < m_priorities.size(); ++v) { + for (bool_var v = 0; v < m_priorities.size(); ++v) s.update_activity(v, m_priorities[v]); - } s.m_activity_inc = 128; #endif } diff --git a/src/util/rlimit.h b/src/util/rlimit.h index 9be53cd55..0abb06cb3 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -91,8 +91,9 @@ public: struct scoped_limits { reslimit& m_limit; - unsigned m_sz; - scoped_limits(reslimit& lim): m_limit(lim), m_sz(0) {} - ~scoped_limits() { for (unsigned i = 0; i < m_sz; ++i) m_limit.pop_child(); } + unsigned m_sz = 0; + scoped_limits(reslimit& lim): m_limit(lim) {} + ~scoped_limits() { reset(); } + void reset() { for (unsigned i = 0; i < m_sz; ++i) m_limit.pop_child(); m_sz = 0; } void push_child(reslimit* lim) { m_limit.push_child(lim); ++m_sz; } }; From f3e9712beb83b8f8194f93c17c34852c1c1237a9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 20:29:17 +0900 Subject: [PATCH 236/428] formatting --- src/sat/sat_parallel.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 9f531d19c..f7b54b998 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -5,7 +5,7 @@ Module Name: sat_parallel.cpp - Abstract: +Abstract: Utilities for parallel SAT solving. @@ -260,15 +260,13 @@ namespace sat { bool parallel::copy_solver(solver& s) { bool copied = false; - { - lock_guard lock(m_mux); - m_consumer_ready = true; - if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { - s.copy(*m_solver_copy, true); - copied = true; - m_num_clauses = s.m_clauses.size(); - } - } + lock_guard lock(m_mux); + m_consumer_ready = true; + if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { + s.copy(*m_solver_copy, true); + copied = true; + m_num_clauses = s.m_clauses.size(); + } return copied; } From 338d7b328352509cebb14ad0bf2176c74f8cdc2a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 20:29:39 +0900 Subject: [PATCH 237/428] remove unused variables --- src/math/lp/int_solver.h | 4 ---- src/smt/smt_clause_proof.cpp | 1 - 2 files changed, 5 deletions(-) diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index ab29ab7f4..1bd3f1c3d 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -44,10 +44,6 @@ class int_solver { int_solver& lia; lar_solver& lra; lar_core_solver& lrac; - 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() const { return true; } diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index bf27777c4..5cb3729ab 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -194,7 +194,6 @@ namespace smt { m_trail.push_back(info(st, v, p)); if (m_on_clause_eh) m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); - static unsigned s_count = 0; if (m_has_log) { init_pp_out(); From d04807e8c33900b1c86388319da6796afdc3772f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 13:43:38 -0700 Subject: [PATCH 238/428] merge Signed-off-by: Nikolaj Bjorner --- src/math/lp/int_solver.cpp | 7 +- src/math/lp/lia_move.h | 62 +++++++------ src/math/lp/monomial_bounds.cpp | 121 +++++++++++++++---------- src/math/lp/monomial_bounds.h | 1 + src/smt/params/smt_params_helper.pyg | 1 + src/smt/params/theory_arith_params.cpp | 2 + src/smt/params/theory_arith_params.h | 1 + src/smt/theory_lra.cpp | 12 +-- 8 files changed, 122 insertions(+), 85 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 1964262f3..e248a0081 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -172,7 +172,8 @@ namespace lp { lia_move int_solver::check(lp::explanation * e) { SASSERT(lra.ax_is_correct()); - if (!has_inf_int()) return lia_move::sat; + if (!has_inf_int()) + return lia_move::sat; m_t.clear(); m_k.reset(); @@ -181,7 +182,8 @@ namespace lp { m_upper = false; lia_move r = lia_move::undef; - if (m_gcd.should_apply()) r = m_gcd(); + if (m_gcd.should_apply()) + r = m_gcd(); check_return_helper pc(lra); @@ -298,7 +300,6 @@ namespace lp { return m_number_of_calls % settings().m_int_find_cube_period == 0; } - bool int_solver::should_gomory_cut() { return m_number_of_calls % settings().m_int_gomory_cut_period == 0; } diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index ca61d7b7a..12e3d8e35 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -19,34 +19,38 @@ Revision History: --*/ #pragma once namespace lp { -enum class lia_move { - sat, - branch, - cut, - conflict, - continue_with_check, - undef, - unsat -}; -inline std::string lia_move_to_string(lia_move m) { - switch (m) { - case lia_move::sat: - return "sat"; - case lia_move::branch: - return "branch"; - case lia_move::cut: - return "cut"; - case lia_move::conflict: - return "conflict"; - case lia_move::continue_with_check: - return "continue_with_check"; - case lia_move::undef: - return "undef"; - case lia_move::unsat: - return "unsat"; - default: - UNREACHABLE(); + enum class lia_move { + sat, + branch, + cut, + conflict, + continue_with_check, + undef, + unsat }; - return "strange"; -} + inline std::string lia_move_to_string(lia_move m) { + switch (m) { + case lia_move::sat: + return "sat"; + case lia_move::branch: + return "branch"; + case lia_move::cut: + return "cut"; + case lia_move::conflict: + return "conflict"; + case lia_move::continue_with_check: + return "continue_with_check"; + case lia_move::undef: + return "undef"; + case lia_move::unsat: + return "unsat"; + default: + UNREACHABLE(); + }; + return "strange"; + } + + inline std::ostream& operator<<(std::ostream& out, lia_move const& m) { + return out << lia_move_to_string(m); + } } diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index c79d3f8e4..b94dcbf57 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -58,38 +58,42 @@ namespace nla { auto const& upper = dep.upper(range); auto cmp = dep.upper_is_open(range) ? llc::LT : llc::LE; ++c().lra.settings().stats().m_nla_propagate_bounds; -#if UNIT_PROPAGATE_BOUNDS - auto* d = dep.get_upper_dep(range); - c().lra.update_column_type_and_bound(v, cmp, upper, d); -#else - lp::explanation ex; - dep.get_upper_dep(range, ex); - if (is_too_big(upper)) - return false; - new_lemma lemma(c(), "propagate value - upper bound of range is below value"); - lemma &= ex; - lemma |= ineq(v, cmp, upper); - TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); -#endif + if (c().params().arith_nl_internal_bounds()) { + auto* d = dep.get_upper_dep(range); + TRACE("arith", tout << "upper " << cmp << " " << upper << "\n"); + propagate_bound(v, cmp, upper, d); + } + else { + lp::explanation ex; + dep.get_upper_dep(range, ex); + if (is_too_big(upper)) + return false; + new_lemma lemma(c(), "propagate value - upper bound of range is below value"); + lemma &= ex; + lemma |= ineq(v, cmp, upper); + TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); + } return true; } else if (dep.is_above(range, val)) { auto const& lower = dep.lower(range); auto cmp = dep.lower_is_open(range) ? llc::GT : llc::GE; ++c().lra.settings().stats().m_nla_propagate_bounds; -#if UNIT_PROPAGATE_BOUNDS - auto* d = dep.get_lower_dep(range); - c().lra.update_column_type_and_bound(v, cmp, lower, d); -#else - lp::explanation ex; - dep.get_lower_dep(range, ex); - if (is_too_big(lower)) - return false; - new_lemma lemma(c(), "propagate value - lower bound of range is above value"); - lemma &= ex; - lemma |= ineq(v, cmp, lower); - TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); -#endif + if (c().params().arith_nl_internal_bounds()) { + auto* d = dep.get_lower_dep(range); + propagate_bound(v, cmp, lower, d); + TRACE("arith", tout << v << " " << cmp << " " << lower << "\n"); + } + else { + lp::explanation ex; + dep.get_lower_dep(range, ex); + if (is_too_big(lower)) + return false; + new_lemma lemma(c(), "propagate value - lower bound of range is above value"); + lemma &= ex; + lemma |= ineq(v, cmp, lower); + TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); + } return true; } else { @@ -97,6 +101,28 @@ namespace nla { } } + /** + * Ensure that bounds are integral when the variable is integer. + */ + void monomial_bounds::propagate_bound(lpvar v, lp::lconstraint_kind cmp, rational const& q, u_dependency* d) { + SASSERT(cmp != llc::EQ && cmp != llc::NE); + if (!c().var_is_int(v)) + c().lra.update_column_type_and_bound(v, cmp, q, d); + else if (q.is_int()) { + if (cmp == llc::GT) + c().lra.update_column_type_and_bound(v, llc::GE, q + 1, d); + else if(cmp == llc::LT) + c().lra.update_column_type_and_bound(v, llc::LE, q - 1, d); + else + c().lra.update_column_type_and_bound(v, cmp, q, d); + } + else if (cmp == llc::GE || cmp == llc::GT) + c().lra.update_column_type_and_bound(v, llc::GE, ceil(q), d); + else + c().lra.update_column_type_and_bound(v, llc::LE, floor(q), d); + } + + /** * val(v)^p should be in range. * if val(v)^p > upper(range) add @@ -129,28 +155,30 @@ namespace nla { if ((p % 2 == 1) || val_v.is_pos()) { ++c().lra.settings().stats().m_nla_propagate_bounds; auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; -#if UNIT_PROPAGATE_BOUNDS - auto* d = dep.get_upper_dep(); - c().lra.update_column_type_and_bound(v, le, r, d); -#else - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); - lemma &= ex; - lemma |= ineq(v, le, r); -#endif + if (c().params().arith_nl_internal_bounds()) { + auto* d = dep.get_upper_dep(range); + propagate_bound(v, le, r, d); + } + else { + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); + lemma &= ex; + lemma |= ineq(v, le, r); + } return true; } if (p % 2 == 0 && val_v.is_neg()) { ++c().lra.settings().stats().m_nla_propagate_bounds; SASSERT(!r.is_neg()); auto ge = dep.upper_is_open(range) ? llc::GT : llc::GE; -#if UNIT_PROPAGATE_BOUNDS - auto* d = dep.get_upper_dep(); - c().lra.update_column_type_and_bound(v, ge, -r, d); -#else - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below negative value"); - lemma &= ex; - lemma |= ineq(v, ge, -r); -#endif + if (c().params().arith_nl_internal_bounds()) { + auto* d = dep.get_upper_dep(range); + propagate_bound(v, ge, -r, d); + } + else { + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below negative value"); + lemma &= ex; + lemma |= ineq(v, ge, -r); + } return true; } } @@ -306,10 +334,11 @@ namespace nla { if (m.is_propagated()) return; lpvar w, fixed_to_zero; - if (!is_linear(m, w, fixed_to_zero)) { -#if UNIT_PROPAGATE_BOUNDS - propagate(m); -#endif + + if (!is_linear(m)) { + if (c().params().arith_nl_internal_bounds()) { + propagate(m); + } return; } diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 9e62e1520..554fb6109 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -17,6 +17,7 @@ namespace nla { class monomial_bounds : common { dep_intervals& dep; + void propagate_bound(lpvar v, lp::lconstraint_kind cmp, rational const& q, u_dependency* d); void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; bool propagate_down(monic const& m, lpvar u); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 9fcda7f64..6d14e7cc6 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -83,6 +83,7 @@ def_module_params(module_name='smt', ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), ('arith.nl.cross_nested', BOOL, True, 'enable cross-nested consistency checking'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), + ('arith.nl.internal_bounds', BOOL, False, 'use internal bounds propagation'), ('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 9bc830dd1..d1a6c6f11 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -39,6 +39,7 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_nl_arith_propagate_linear_monomials = p.arith_nl_propagate_linear_monomials(); m_nl_arith_optimize_bounds = p.arith_nl_optimize_bounds(); m_nl_arith_cross_nested = p.arith_nl_cross_nested(); + m_nl_arith_internal_bounds = p.arith_nl_internal_bounds(); arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); @@ -95,4 +96,5 @@ void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_nl_arith_propagate_linear_monomials); DISPLAY_PARAM(m_nl_arith_optimize_bounds); DISPLAY_PARAM(m_nl_arith_cross_nested); + DISPLAY_PARAM(m_nl_arith_internal_bounds); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index f78086300..c84cca23b 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -108,6 +108,7 @@ struct theory_arith_params { bool m_nl_arith_propagate_linear_monomials = true; bool m_nl_arith_optimize_bounds = true; bool m_nl_arith_cross_nested = true; + bool m_nl_arith_internal_bounds = false; theory_arith_params(params_ref const & p = params_ref()) { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index db4dba137..d38c44340 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2113,7 +2113,6 @@ public: bool propagate_core() { m_model_is_initialized = false; flush_bound_axioms(); - // disabled in master: propagate_nla(); if (ctx().inconsistent()) return true; @@ -3175,14 +3174,13 @@ public: ctx().display_detailed_literal(tout << ctx().get_assign_level(c.var()) << " " << c << " ", c) << "\n"; for (auto e : m_eqs) tout << pp(e.first, m) << " = " << pp(e.second, m) << "\n"; - tout << " ==> "; - tout << pp(x, m) << " = " << pp(y, m) << "\n"; + tout << " ==> " << pp(x, m) << " = " << pp(y, m) << "\n"; ); std::function fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); }; scoped_trace_stream _sts(th, fn); - //VERIFY(validate_eq(x, y)); + // VERIFY(validate_eq(x, y)); ctx().assign_eq(x, y, eq_justification(js)); } @@ -3291,11 +3289,11 @@ public: tout << "lemma scope: " << ctx().get_scope_level(); for (auto const& p : m_params) tout << " " << p; tout << "\n"; - display_evidence(tout, m_explanation); - display(tout << "is-conflict: " << is_conflict << "\n");); + display_evidence(tout, m_explanation);); for (auto ev : m_explanation) set_evidence(ev.ci(), m_core, m_eqs); + SASSERT(!m_core.empty() || !m_eqs.empty()); // SASSERT(validate_conflict(m_core, m_eqs)); if (is_conflict) { @@ -3517,7 +3515,7 @@ public: cancel_eh eh(m.limit()); scoped_timer timer(1000, &eh); bool result = l_true != nctx.check(); - CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, core.size(), core.data(), eqs.size(), eqs.data(), false_literal);); + CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, core.size(), core.data(), eqs.size(), eqs.data(), false_literal);); return result; } From 6445d01557556c35626895ecaa80f490344ad6fb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 13:42:02 -0700 Subject: [PATCH 239/428] normalize newlines for if --- src/qe/mbp/mbp_qel.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/qe/mbp/mbp_qel.cpp b/src/qe/mbp/mbp_qel.cpp index 11b651268..5f7c713ff 100644 --- a/src/qe/mbp/mbp_qel.cpp +++ b/src/qe/mbp/mbp_qel.cpp @@ -32,7 +32,7 @@ Revision History: namespace mbp { class mbp_qel::impl { - private: +private: ast_manager &m; array_util m_array_util; datatype_util m_dt_util; @@ -105,11 +105,12 @@ class mbp_qel::impl { mbp_tg_plugin *get_plugin(family_id fid) { for (auto p : m_plugins) - if (p->get_family_id() == fid) return p; + if (p->get_family_id() == fid) + return p; return nullptr; } - public: +public: impl(ast_manager &m, params_ref const &p) : m(m), m_array_util(m), m_dt_util(m), m_params(p), m_tg(m) {} @@ -119,7 +120,8 @@ class mbp_qel::impl { } void operator()(app_ref_vector &vars, expr_ref &fml, model &mdl) { - if (vars.empty()) return; + if (vars.empty()) + return; init(vars, fml, mdl); // Apply MBP rules till saturation @@ -177,8 +179,10 @@ class mbp_qel::impl { tout << "\n";); std::function non_core = [&](expr *e) { - if (is_app(e) && is_partial_eq(to_app(e))) return true; - if (m.is_ite(e) || m.is_or(e) || m.is_implies(e) || m.is_and(e) || m.is_distinct(e)) return true; + if (is_app(e) && is_partial_eq(to_app(e))) + return true; + if (m.is_ite(e) || m.is_or(e) || m.is_implies(e) || m.is_and(e) || m.is_distinct(e)) + return true; return red_vars.is_marked(e); }; @@ -203,9 +207,11 @@ class mbp_qel::impl { } std::function substituted = [&](expr *e) { - if (is_app(e) && is_partial_eq(to_app(e))) return true; - if (m.is_ite(e)) return true; - return red_vars.is_marked(e) || s_vars.is_marked(e); + return + (is_app(e) && is_partial_eq(to_app(e))) || + m.is_ite(e) || + red_vars.is_marked(e) || + s_vars.is_marked(e); }; // remove all substituted variables From 960a024d3d22388981e47c28d413d9a622cc069a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 13:54:00 -0700 Subject: [PATCH 240/428] fix build Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index b94dcbf57..fa634c219 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -335,10 +335,9 @@ namespace nla { return; lpvar w, fixed_to_zero; - if (!is_linear(m)) { - if (c().params().arith_nl_internal_bounds()) { + if (!is_linear(m, w, fixed_to_zero)) { + if (c().params().arith_nl_internal_bounds()) propagate(m); - } return; } From 01188462d50cbd4f3f2a84b4930ab23d2ee1dd4a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2023 16:24:05 -0700 Subject: [PATCH 241/428] build Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index fa634c219..0d6fb47e7 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -60,7 +60,6 @@ namespace nla { ++c().lra.settings().stats().m_nla_propagate_bounds; if (c().params().arith_nl_internal_bounds()) { auto* d = dep.get_upper_dep(range); - TRACE("arith", tout << "upper " << cmp << " " << upper << "\n"); propagate_bound(v, cmp, upper, d); } else { @@ -82,7 +81,6 @@ namespace nla { if (c().params().arith_nl_internal_bounds()) { auto* d = dep.get_lower_dep(range); propagate_bound(v, cmp, lower, d); - TRACE("arith", tout << v << " " << cmp << " " << lower << "\n"); } else { lp::explanation ex; From fcb03aa56c193c5d53baa4d65105e25aa3404c4a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 10 Oct 2023 17:19:40 +0100 Subject: [PATCH 242/428] minor code simplification --- src/smt/smt_conflict_resolution.cpp | 2 +- src/util/approx_set.h | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 2561fbb5a..793243473 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -753,7 +753,7 @@ namespace smt { } else { if (j != i) { - m_lemma[j] = m_lemma[i]; + m_lemma[j] = l; m_lemma_atoms.set(j, m_lemma_atoms.get(i)); } j++; diff --git a/src/util/approx_set.h b/src/util/approx_set.h index aa6f8d383..a1835be6f 100644 --- a/src/util/approx_set.h +++ b/src/util/approx_set.h @@ -41,7 +41,7 @@ static_assert(sizeof(unsigned) == 4, "unsigned are 4 bytes"); template class approx_set_tpl : private T2U_Proc { protected: - R m_set; + R m_set = approx_set_traits::zero; unsigned e2u(T const & e) const { return T2U_Proc::operator()(e); } @@ -52,24 +52,17 @@ protected: static approx_set_tpl r2s(R const & s) { approx_set_tpl r; r.m_set = s; return r; } public: - approx_set_tpl(): - m_set(approx_set_traits::zero) { - } + approx_set_tpl() = default; explicit approx_set_tpl(T const & e): m_set(e2s(e)) { } - approx_set_tpl(unsigned sz, T const * es): - m_set(approx_set_traits::zero) { + approx_set_tpl(unsigned sz, T const * es) { for (unsigned i = 0; i < sz; i++) insert(es[i]); } - approx_set_tpl(approx_set_tpl const & s): - m_set(s.m_set) { - } - void set(R s) { m_set = s; } R get() const { return m_set; } From ba6c23bbc5d2e080a75a159d82f8c5c934372c60 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Sat, 14 Oct 2023 04:06:15 -0400 Subject: [PATCH 243/428] bug fix #6934 (#6940) --- src/qe/mbp/mbp_basic_tg.cpp | 7 +++++-- src/qe/mbp/mbp_qel.cpp | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index 5eca04151..fa657d990 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -130,12 +130,15 @@ struct mbp_basic_tg::impl { for (auto a1 : *c) { for (auto a2 : *c) { if (a1 == a2) continue; - if (m_mdl.are_equal(a1, a2)) { + expr_ref e(m.mk_eq(a1, a2), m); + if (m_mdl.is_true(e)) { m_tg.add_eq(a1, a2); eq = true; break; - } else + } else { + SASSERT(m_mdl.is_false(e)); m_tg.add_deq(a1, a2); + } } } if (eq) diff --git a/src/qe/mbp/mbp_qel.cpp b/src/qe/mbp/mbp_qel.cpp index 5f7c713ff..5778419c1 100644 --- a/src/qe/mbp/mbp_qel.cpp +++ b/src/qe/mbp/mbp_qel.cpp @@ -181,7 +181,8 @@ public: std::function non_core = [&](expr *e) { if (is_app(e) && is_partial_eq(to_app(e))) return true; - if (m.is_ite(e) || m.is_or(e) || m.is_implies(e) || m.is_and(e) || m.is_distinct(e)) + if (m.is_ite(e) || m.is_or(e) || m.is_implies(e) || + m.is_distinct(e)) return true; return red_vars.is_marked(e); }; From 08af965b569328b15f2edcba35288a2efc97fb57 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2023 01:23:23 -0700 Subject: [PATCH 244/428] updates to monomial bounds --- src/math/lp/lar_core_solver.h | 1 + src/math/lp/lar_core_solver_def.h | 2 +- src/math/lp/lar_solver.cpp | 69 +++++------ src/math/lp/lar_solver.h | 11 +- src/math/lp/monomial_bounds.cpp | 196 +++++++++++++++++++----------- src/math/lp/monomial_bounds.h | 3 + src/math/lp/ul_pair.h | 6 +- src/smt/theory_lra.cpp | 6 +- 8 files changed, 174 insertions(+), 120 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index 4a5cb20b1..d58cbb9f7 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -123,6 +123,7 @@ public: m_stacked_simplex_strategy.pop(k); m_r_solver.m_settings.set_simplex_strategy(m_stacked_simplex_strategy); + m_infeasible_linear_combination.reset(); lp_assert(m_r_solver.basis_heading_is_correct()); } diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index b6743873f..e0ffed16e 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -49,7 +49,7 @@ void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) - m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); + m_infeasible_linear_combination.push_back({rc.coeff(), rc.var()}); } void lar_core_solver::fill_not_improvable_zero_sum() { diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 069709126..4c26aaff6 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -179,7 +179,7 @@ namespace lp { lp_status lar_solver::get_status() const { return m_status; } - void lar_solver::set_status(lp_status s) { + void lar_solver::set_status(lp_status s) { TRACE("lar_solver", tout << "setting status to " << s << "\n";); m_status = s; } @@ -224,9 +224,9 @@ namespace lp { } void lar_solver::push() { + m_trail.push_scope(); m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); - m_columns_to_ul_pairs.push(); m_crossed_bounds_column = null_lpvar; m_crossed_bounds_deps = nullptr; m_mpq_lar_core_solver.push(); @@ -260,16 +260,16 @@ namespace lp { TRACE("lar_solver", tout << "k = " << k << std::endl;); m_crossed_bounds_column = null_lpvar; m_crossed_bounds_deps = nullptr; - unsigned n = m_columns_to_ul_pairs.peek_size(k); + m_trail.pop_scope(k); + unsigned n = m_columns_to_ul_pairs.size(); m_var_register.shrink(n); - pop_tableau(n); + + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); + lp_assert(A_r().column_count() == n); - TRACE("lar_solver_details", - for (unsigned j = 0; j < n; j++) { - print_column_info(j, tout) << "\n"; - } - ); - m_columns_to_ul_pairs.pop(k); + TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) print_column_info(j, tout) << "\n";); m_mpq_lar_core_solver.pop(k); remove_non_fixed_from_fixed_var_table(); @@ -612,26 +612,23 @@ namespace lp { } + void lar_solver::set_upper_bound_witness(var_index j, u_dependency* dep) { - ul_pair ul = m_columns_to_ul_pairs[j]; - ul.upper_bound_witness() = dep; - m_columns_to_ul_pairs[j] = ul; + m_trail.push(vector_value_trail(m_columns_to_ul_pairs, j)); + m_columns_to_ul_pairs[j].upper_bound_witness() = dep; } void lar_solver::set_lower_bound_witness(var_index j, u_dependency* dep) { - ul_pair ul = m_columns_to_ul_pairs[j]; - ul.lower_bound_witness() = dep; - m_columns_to_ul_pairs[j] = ul; + m_trail.push(vector_value_trail(m_columns_to_ul_pairs, j)); + m_columns_to_ul_pairs[j].lower_bound_witness() = dep; } void lar_solver::register_monoid_in_map(std::unordered_map& coeffs, const mpq& a, unsigned j) { auto it = coeffs.find(j); - if (it == coeffs.end()) { + if (it == coeffs.end()) coeffs[j] = a; - } - else { + else it->second += a; - } } @@ -1269,7 +1266,7 @@ namespace lp { } bool lar_solver::column_represents_row_in_tableau(unsigned j) { - return m_columns_to_ul_pairs()[j].associated_with_row(); + return m_columns_to_ul_pairs[j].associated_with_row(); } void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { @@ -1371,20 +1368,6 @@ namespace lp { lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } - void lar_solver::pop_tableau(unsigned old_size) { - lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - - lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). - // At this moment m_column_names is already popped - - while (A_r().column_count() > old_size) - remove_last_column_from_tableau(); - lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - } void lar_solver::clean_inf_heap_of_r_solver_after_pop() { vector became_feas; @@ -1490,6 +1473,15 @@ namespace lp { return j; } + struct lar_solver::add_column : public trail { + lar_solver& s; + add_column(lar_solver& s) : s(s) {} + virtual void undo() { + s.remove_last_column_from_tableau(); + s.m_columns_to_ul_pairs.pop_back(); + } + }; + var_index lar_solver::add_var(unsigned ext_j, bool is_int) { TRACE("add_var", tout << "adding var " << ext_j << (is_int ? " int" : " nonint") << std::endl;); var_index local_j; @@ -1500,9 +1492,9 @@ namespace lp { lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); local_j = A_r().column_count(); m_columns_to_ul_pairs.push_back(ul_pair(false)); // not associated with a row - while (m_usage_in_terms.size() <= ext_j) { + m_trail.push(add_column(*this)); + while (m_usage_in_terms.size() <= ext_j) m_usage_in_terms.push_back(0); - } add_non_basic_var_to_core_fields(ext_j, is_int); lp_assert(sizes_are_correct()); return local_j; @@ -1653,6 +1645,7 @@ namespace lp { unsigned j = A_r().column_count(); ul_pair ul(true); // to mark this column as associated_with_row m_columns_to_ul_pairs.push_back(ul); + m_trail.push(add_column(*this)); add_basic_var_to_core_fields(); A_r().fill_last_row_with_pivoting(*term, @@ -2367,7 +2360,7 @@ namespace lp { SASSERT(m_crossed_bounds_deps == nullptr); set_status(lp_status::INFEASIBLE); m_crossed_bounds_column = j; - const auto& ul = this->m_columns_to_ul_pairs()[j]; + const auto& ul = this->m_columns_to_ul_pairs[j]; u_dependency* bdep = lower_bound? ul.lower_bound_witness() : ul.upper_bound_witness(); SASSERT(bdep != nullptr); m_crossed_bounds_deps = m_dependencies.mk_join(bdep, dep); diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 949cb0910..6c7b30664 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -42,6 +42,7 @@ #include "util/debug.h" #include "util/stacked_value.h" #include "util/vector.h" +#include "util/trail.h" namespace lp { @@ -74,6 +75,7 @@ class lar_solver : public column_namer { }; //////////////////// fields ////////////////////////// + trail_stack m_trail; lp_settings m_settings; lp_status m_status = lp_status::UNKNOWN; stacked_value m_simplex_strategy; @@ -85,7 +87,8 @@ class lar_solver : public column_namer { bool m_need_register_terms = false; var_register m_var_register; var_register m_term_register; - stacked_vector m_columns_to_ul_pairs; + struct add_column; + svector m_columns_to_ul_pairs; constraint_set m_constraints; // the set of column indices j such that bounds have changed for j indexed_uint_set m_columns_with_changed_bounds; @@ -240,7 +243,6 @@ class lar_solver : public column_namer { void remove_last_column_from_basis_tableau(unsigned j); void remove_last_column_from_tableau(); - void pop_tableau(unsigned old_size); 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; @@ -393,6 +395,7 @@ class lar_solver : public column_namer { 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); + unsigned num_scopes() const { return m_term_count.stack_size(); } 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(); @@ -503,7 +506,7 @@ class lar_solver : public column_namer { if (tv::is_term(j)) { j = m_var_register.external_to_local(j); } - return m_columns_to_ul_pairs()[j].upper_bound_witness(); + return m_columns_to_ul_pairs[j].upper_bound_witness(); } inline const impq& get_upper_bound(column_index j) const { @@ -591,7 +594,7 @@ class lar_solver : public column_namer { if (tv::is_term(j)) { j = m_var_register.external_to_local(j); } - return m_columns_to_ul_pairs()[j].lower_bound_witness(); + return m_columns_to_ul_pairs[j].lower_bound_witness(); } inline tv column2tv(column_index const& c) const { diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 0d6fb47e7..08ae6f249 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -12,8 +12,6 @@ #include "math/lp/nla_intervals.h" #include "math/lp/numeric_pair.h" -#define UNIT_PROPAGATE_BOUNDS 0 - namespace nla { monomial_bounds::monomial_bounds(core* c): @@ -21,16 +19,17 @@ namespace nla { dep(c->m_intervals.get_dep_intervals()) {} void monomial_bounds::propagate() { - for (lpvar v : c().m_to_refine) + for (lpvar v : c().m_to_refine) { propagate(c().emon(v)); + if (add_lemma()) + break; + } } - bool monomial_bounds::is_too_big(mpq const& q) const { return rational(q).bitsize() > 256; } - /** * Accumulate product of variables in monomial starting at position 'start' */ @@ -53,8 +52,9 @@ namespace nla { * a bounds axiom. */ bool monomial_bounds::propagate_value(dep_interval& range, lpvar v) { - auto val = c().val(v); - if (dep.is_below(range, val)) { + // auto val = c().val(v); + bool propagated = false; + if (should_propagate_upper(range, v, 1)) { auto const& upper = dep.upper(range); auto cmp = dep.upper_is_open(range) ? llc::LT : llc::LE; ++c().lra.settings().stats().m_nla_propagate_bounds; @@ -72,9 +72,9 @@ namespace nla { lemma |= ineq(v, cmp, upper); TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); } - return true; + propagated = true; } - else if (dep.is_above(range, val)) { + if (should_propagate_lower(range, v, 1)) { auto const& lower = dep.lower(range); auto cmp = dep.lower_is_open(range) ? llc::GT : llc::GE; ++c().lra.settings().stats().m_nla_propagate_bounds; @@ -92,11 +92,45 @@ namespace nla { lemma |= ineq(v, cmp, lower); TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); } - return true; + propagated = true; } - else { + return propagated; + } + + bool monomial_bounds::should_propagate_lower(dep_interval const& range, lpvar v, unsigned p) { + if (dep.lower_is_inf(range)) return false; - } + u_dependency* d = nullptr; + rational bound; + bool is_strict; + if (!c().has_lower_bound(v, d, bound, is_strict)) + return true; + auto const& lower = dep.lower(range); + if (p > 1) + bound = power(bound, p); + if (bound < lower) + return true; + if (bound > lower) + return false; + return !is_strict && dep.lower_is_open(range); + } + + bool monomial_bounds::should_propagate_upper(dep_interval const& range, lpvar v, unsigned p) { + if (dep.upper_is_inf(range)) + return false; + u_dependency* d = nullptr; + rational bound; + bool is_strict; + if (!c().has_upper_bound(v, d, bound, is_strict)) + return true; + auto const& upper = dep.upper(range); + if (p > 1) + bound = power(bound, p); + if (bound > upper) + return true; + if (bound < upper) + return false; + return !is_strict && dep.upper_is_open(range); } /** @@ -135,68 +169,86 @@ namespace nla { SASSERT(p > 0); if (p == 1) return propagate_value(range, v); - auto val_v = c().val(v); - auto val = power(val_v, p); rational r; - if (dep.is_below(range, val)) { + if (should_propagate_upper(range, v, p)) { // v.upper^p > range.upper + SASSERT(c().has_upper_bound(v)); lp::explanation ex; dep.get_upper_dep(range, ex); + // p even, range.upper < 0, v^p >= 0 -> infeasible if (p % 2 == 0 && rational(dep.upper(range)).is_neg()) { ++c().lra.settings().stats().m_nla_propagate_bounds; new_lemma lemma(c(), "range requires a non-negative upper bound"); lemma &= ex; return true; } - else if (rational(dep.upper(range)).root(p, r)) { - // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 - // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 - if ((p % 2 == 1) || val_v.is_pos()) { - ++c().lra.settings().stats().m_nla_propagate_bounds; - auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; - if (c().params().arith_nl_internal_bounds()) { - auto* d = dep.get_upper_dep(range); - propagate_bound(v, le, r, d); - } - else { - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); - lemma &= ex; - lemma |= ineq(v, le, r); - } - return true; - } - if (p % 2 == 0 && val_v.is_neg()) { - ++c().lra.settings().stats().m_nla_propagate_bounds; - SASSERT(!r.is_neg()); - auto ge = dep.upper_is_open(range) ? llc::GT : llc::GE; - if (c().params().arith_nl_internal_bounds()) { - auto* d = dep.get_upper_dep(range); - propagate_bound(v, ge, -r, d); - } - else { - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below negative value"); - lemma &= ex; - lemma |= ineq(v, ge, -r); - } - return true; - } - } - // TBD: add bounds as long as difference to val is above some epsilon. - } - else if (dep.is_above(range, val)) { - if (rational(dep.lower(range)).root(p, r)) { + // v.upper < 0, but v^p > range.upper -> infeasible. + if (p % 2 == 0 && c().get_upper_bound(v) < 0) { ++c().lra.settings().stats().m_nla_propagate_bounds; - lp::explanation ex; - dep.get_lower_dep(range, ex); - auto ge = dep.lower_is_open(range) ? llc::GT : llc::GE; - auto le = dep.lower_is_open(range) ? llc::LT : llc::LE; - new_lemma lemma(c(), "propagate value - root case - lower bound of range is above value"); + new_lemma lemma(c(), "range requires a non-negative upper bound"); lemma &= ex; - lemma |= ineq(v, ge, r); - if (p % 2 == 0) - lemma |= ineq(v, le, -r); + lemma.explain_existing_upper_bound(v); return true; } - // TBD: add bounds as long as difference to val is above some epsilon. + SASSERT(p % 2 == 1 || c().get_upper_bound(v) >= 0); + + if (rational(dep.upper(range)).root(p, r)) { + // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 + // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 + ++c().lra.settings().stats().m_nla_propagate_bounds; + auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; + if (c().params().arith_nl_internal_bounds()) { + auto* d = dep.get_upper_dep(range); + if (p % 2 == 0) + d = dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); + propagate_bound(v, le, r, d); + } + else { + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); + lemma &= ex; + if (p % 2 == 0) + lemma.explain_existing_upper_bound(v); + lemma |= ineq(v, le, r); + } + return true; + } + } + + if (should_propagate_lower(range, v, p)) { // v.lower^p < range.lower + // + // range.lower < 0 -> v.lower >= root(p, range.lower) + // range.lower >= 0, p odd -> v.lower >= root(p, range.lower) + // range.lower >= 0, p even, v.lower >= 0 -> v.lower >= root(p, range.lower) + // default: + // v.lower >= root(p, range.lower) || (p even & v.upper <= -root(p, range.lower)) + // + // pre-condition: p even -> range.lower >= 0 + // + if (rational(dep.lower(range)).root(p, r)) { + ++c().lra.settings().stats().m_nla_propagate_bounds; + auto ge = dep.lower_is_open(range) ? llc::GT : llc::GE; + auto le = dep.lower_is_open(range) ? llc::LT : llc::LE; + if (c().params().arith_nl_internal_bounds()) { + if (rational(dep.lower(range)).is_neg() || p % 2 == 1) { + auto* d = dep.get_lower_dep(range); + propagate_bound(v, ge, r, d); + return true; + } + if (c().get_lower_bound(v) >= 0) { + auto* d = dep.get_lower_dep(range); + d = dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); + propagate_bound(v, ge, r, d); + return true; + } + } + lp::explanation ex; + dep.get_lower_dep(range, ex); + new_lemma lemma(c(), "propagate value - root case - lower bound of range is above value"); + lemma &= ex; + lemma |= ineq(v, ge, r); + if (p % 2 == 0) + lemma |= ineq(v, le, -r); + return true; + } } return false; } @@ -316,28 +368,30 @@ namespace nla { continue; monic& m = c().emon(v); unit_propagate(m); - if (c().lra.get_status() == lp::lp_status::INFEASIBLE) { - lp::explanation exp; - c().lra.get_infeasibility_explanation(exp); - new_lemma lemma(c(), "propagate fixed - infeasible lra"); - lemma &= exp; + if (add_lemma()) break; - } if (c().m_conflicts > 0) break; } } + bool monomial_bounds::add_lemma() { + if (c().lra.get_status() != lp::lp_status::INFEASIBLE) + return false; + lp::explanation exp; + c().lra.get_infeasibility_explanation(exp); + new_lemma lemma(c(), "propagate fixed - infeasible lra"); + lemma &= exp; + return true; + } + void monomial_bounds::unit_propagate(monic & m) { if (m.is_propagated()) return; lpvar w, fixed_to_zero; - if (!is_linear(m, w, fixed_to_zero)) { - if (c().params().arith_nl_internal_bounds()) - propagate(m); + if (!is_linear(m, w, fixed_to_zero)) return; - } c().emons().set_propagated(m); diff --git a/src/math/lp/monomial_bounds.h b/src/math/lp/monomial_bounds.h index 554fb6109..19043e072 100644 --- a/src/math/lp/monomial_bounds.h +++ b/src/math/lp/monomial_bounds.h @@ -17,6 +17,8 @@ namespace nla { class monomial_bounds : common { dep_intervals& dep; + bool should_propagate_lower(dep_interval const& range, lpvar v, unsigned p); + bool should_propagate_upper(dep_interval const& range, lpvar v, unsigned p); void propagate_bound(lpvar v, lp::lconstraint_kind cmp, rational const& q, u_dependency* d); void var2interval(lpvar v, scoped_dep_interval& i); bool is_too_big(mpq const& q) const; @@ -34,6 +36,7 @@ namespace nla { void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const; bool is_free(lpvar v) const; bool is_zero(lpvar v) const; + bool add_lemma(); // monomial propagation void unit_propagate(monic & m); diff --git a/src/math/lp/ul_pair.h b/src/math/lp/ul_pair.h index 37fd6e9ed..354070281 100644 --- a/src/math/lp/ul_pair.h +++ b/src/math/lp/ul_pair.h @@ -44,7 +44,7 @@ inline bool compare(const std::pair & a, const std::paircheck(); - switch (r) { case l_false: add_lemmas(); @@ -3293,9 +3292,8 @@ public: for (auto ev : m_explanation) set_evidence(ev.ci(), m_core, m_eqs); - SASSERT(!m_core.empty() || !m_eqs.empty()); - // SASSERT(validate_conflict(m_core, m_eqs)); + // VERIFY(validate_conflict(m_core, m_eqs)); if (is_conflict) { ctx().set_conflict( ctx().mk_justification( @@ -3509,6 +3507,8 @@ public: bool validate_conflict(literal_vector const& core, svector const& eqs) { if (params().m_arith_mode != arith_solver_id::AS_NEW_ARITH) return true; + + VERIFY(!m_core.empty() || !m_eqs.empty()); scoped_arith_mode _sa(ctx().get_fparams()); context nctx(m, ctx().get_fparams(), ctx().get_params()); add_background(nctx); From d44d78f9d1d51f4d9743c4e48fbf96644834d195 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2023 01:32:58 -0700 Subject: [PATCH 245/428] remove temporary configuration parameter used for testing Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 75 ++++++++------------------ src/smt/params/smt_params_helper.pyg | 1 - src/smt/params/theory_arith_params.cpp | 2 - src/smt/params/theory_arith_params.h | 2 - 4 files changed, 21 insertions(+), 59 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 08ae6f249..2f2e524aa 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -58,40 +58,28 @@ namespace nla { auto const& upper = dep.upper(range); auto cmp = dep.upper_is_open(range) ? llc::LT : llc::LE; ++c().lra.settings().stats().m_nla_propagate_bounds; - if (c().params().arith_nl_internal_bounds()) { - auto* d = dep.get_upper_dep(range); - propagate_bound(v, cmp, upper, d); - } - else { - lp::explanation ex; - dep.get_upper_dep(range, ex); - if (is_too_big(upper)) - return false; - new_lemma lemma(c(), "propagate value - upper bound of range is below value"); - lemma &= ex; - lemma |= ineq(v, cmp, upper); - TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); - } + lp::explanation ex; + dep.get_upper_dep(range, ex); + if (is_too_big(upper)) + return false; + new_lemma lemma(c(), "propagate value - upper bound of range is below value"); + lemma &= ex; + lemma |= ineq(v, cmp, upper); + TRACE("nla_solver", dep.display(tout << c().val(v) << " > ", range) << "\n" << lemma << "\n";); propagated = true; } if (should_propagate_lower(range, v, 1)) { auto const& lower = dep.lower(range); auto cmp = dep.lower_is_open(range) ? llc::GT : llc::GE; ++c().lra.settings().stats().m_nla_propagate_bounds; - if (c().params().arith_nl_internal_bounds()) { - auto* d = dep.get_lower_dep(range); - propagate_bound(v, cmp, lower, d); - } - else { - lp::explanation ex; - dep.get_lower_dep(range, ex); - if (is_too_big(lower)) - return false; - new_lemma lemma(c(), "propagate value - lower bound of range is above value"); - lemma &= ex; - lemma |= ineq(v, cmp, lower); - TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); - } + lp::explanation ex; + dep.get_lower_dep(range, ex); + if (is_too_big(lower)) + return false; + new_lemma lemma(c(), "propagate value - lower bound of range is above value"); + lemma &= ex; + lemma |= ineq(v, cmp, lower); + TRACE("nla_solver", dep.display(tout << c().val(v) << " < ", range) << "\n" << lemma << "\n";); propagated = true; } return propagated; @@ -196,19 +184,11 @@ namespace nla { // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 ++c().lra.settings().stats().m_nla_propagate_bounds; auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; - if (c().params().arith_nl_internal_bounds()) { - auto* d = dep.get_upper_dep(range); - if (p % 2 == 0) - d = dep.mk_join(d, c().lra.get_column_upper_bound_witness(v)); - propagate_bound(v, le, r, d); - } - else { - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); - lemma &= ex; - if (p % 2 == 0) - lemma.explain_existing_upper_bound(v); - lemma |= ineq(v, le, r); - } + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); + lemma &= ex; + if (p % 2 == 0) + lemma.explain_existing_upper_bound(v); + lemma |= ineq(v, le, r); return true; } } @@ -227,19 +207,6 @@ namespace nla { ++c().lra.settings().stats().m_nla_propagate_bounds; auto ge = dep.lower_is_open(range) ? llc::GT : llc::GE; auto le = dep.lower_is_open(range) ? llc::LT : llc::LE; - if (c().params().arith_nl_internal_bounds()) { - if (rational(dep.lower(range)).is_neg() || p % 2 == 1) { - auto* d = dep.get_lower_dep(range); - propagate_bound(v, ge, r, d); - return true; - } - if (c().get_lower_bound(v) >= 0) { - auto* d = dep.get_lower_dep(range); - d = dep.mk_join(d, c().lra.get_column_lower_bound_witness(v)); - propagate_bound(v, ge, r, d); - return true; - } - } lp::explanation ex; dep.get_lower_dep(range, ex); new_lemma lemma(c(), "propagate value - root case - lower bound of range is above value"); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 6d14e7cc6..9fcda7f64 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -83,7 +83,6 @@ def_module_params(module_name='smt', ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), ('arith.nl.cross_nested', BOOL, True, 'enable cross-nested consistency checking'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), - ('arith.nl.internal_bounds', BOOL, False, 'use internal bounds propagation'), ('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index d1a6c6f11..9bc830dd1 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -39,7 +39,6 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_nl_arith_propagate_linear_monomials = p.arith_nl_propagate_linear_monomials(); m_nl_arith_optimize_bounds = p.arith_nl_optimize_bounds(); m_nl_arith_cross_nested = p.arith_nl_cross_nested(); - m_nl_arith_internal_bounds = p.arith_nl_internal_bounds(); arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); @@ -96,5 +95,4 @@ void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_nl_arith_propagate_linear_monomials); DISPLAY_PARAM(m_nl_arith_optimize_bounds); DISPLAY_PARAM(m_nl_arith_cross_nested); - DISPLAY_PARAM(m_nl_arith_internal_bounds); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index c84cca23b..f19544157 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -108,8 +108,6 @@ struct theory_arith_params { bool m_nl_arith_propagate_linear_monomials = true; bool m_nl_arith_optimize_bounds = true; bool m_nl_arith_cross_nested = true; - bool m_nl_arith_internal_bounds = false; - theory_arith_params(params_ref const & p = params_ref()) { updt_params(p); From 47f1c86f930b71efec93e5f787d51b82313c27d8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2023 02:38:49 -0700 Subject: [PATCH 246/428] fix regression Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 41 +++++++++++++++++++-------------- src/math/lp/lar_solver.h | 8 ++++--- src/math/lp/monomial_bounds.cpp | 26 ++++----------------- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 4c26aaff6..92f6b01ec 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -230,8 +230,6 @@ namespace lp { m_crossed_bounds_column = null_lpvar; m_crossed_bounds_deps = nullptr; m_mpq_lar_core_solver.push(); - m_term_count = m_terms.size(); - m_term_count.push(); m_constraints.push(); m_usage_in_terms.push(); m_dependencies.push_scope(); @@ -267,14 +265,11 @@ namespace lp { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) print_column_info(j, tout) << "\n";); m_mpq_lar_core_solver.pop(k); remove_non_fixed_from_fixed_var_table(); - clean_popped_elements(n, m_columns_with_changed_bounds); - clean_popped_elements(n, m_incorrect_columns); for (auto rid : m_row_bounds_to_replay) add_touched_row(rid); @@ -288,14 +283,6 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_constraints.pop(k); - m_term_count.pop(k); - for (unsigned i = m_term_count; i < m_terms.size(); i++) { - if (m_need_register_terms) - deregister_normalized_term(*m_terms[i]); - delete m_terms[i]; - } - m_term_register.shrink(m_term_count); - m_terms.resize(m_term_count); m_simplex_strategy.pop(k); m_settings.set_simplex_strategy(m_simplex_strategy); lp_assert(sizes_are_correct()); @@ -1473,12 +1460,30 @@ namespace lp { return j; } - struct lar_solver::add_column : public trail { + struct lar_solver::undo_add_column : public trail { lar_solver& s; - add_column(lar_solver& s) : s(s) {} + undo_add_column(lar_solver& s) : s(s) {} virtual void undo() { s.remove_last_column_from_tableau(); s.m_columns_to_ul_pairs.pop_back(); + unsigned j = s.m_columns_to_ul_pairs.size(); + if (s.m_columns_with_changed_bounds.contains(j)) + s.m_columns_with_changed_bounds.remove(j); + if (s.m_incorrect_columns.contains(j)) + s.m_incorrect_columns.remove(j); + } + }; + + struct lar_solver::undo_add_term : public trail { + lar_solver& s; + undo_add_term(lar_solver& s):s(s) {} + void undo() override { + auto* t = s.m_terms.back(); + if (s.m_need_register_terms) + s.deregister_normalized_term(*t); + delete t; + s.m_terms.pop_back(); + s.m_term_register.shrink(s.m_terms.size()); } }; @@ -1492,7 +1497,7 @@ namespace lp { lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); local_j = A_r().column_count(); m_columns_to_ul_pairs.push_back(ul_pair(false)); // not associated with a row - m_trail.push(add_column(*this)); + m_trail.push(undo_add_column(*this)); while (m_usage_in_terms.size() <= ext_j) m_usage_in_terms.push_back(0); add_non_basic_var_to_core_fields(ext_j, is_int); @@ -1575,8 +1580,10 @@ namespace lp { return false; } #endif + void lar_solver::push_term(lar_term* t) { m_terms.push_back(t); + m_trail.push(undo_add_term(*this)); } // terms @@ -1645,7 +1652,7 @@ namespace lp { unsigned j = A_r().column_count(); ul_pair ul(true); // to mark this column as associated_with_row m_columns_to_ul_pairs.push_back(ul); - m_trail.push(add_column(*this)); + m_trail.push(undo_add_column(*this)); add_basic_var_to_core_fields(); A_r().fill_last_row_with_pivoting(*term, diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 6c7b30664..06d7da5b6 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -87,7 +87,6 @@ class lar_solver : public column_namer { bool m_need_register_terms = false; var_register m_var_register; var_register m_term_register; - struct add_column; svector m_columns_to_ul_pairs; constraint_set m_constraints; // the set of column indices j such that bounds have changed for j @@ -103,7 +102,6 @@ class lar_solver : public column_namer { indexed_uint_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; std::unordered_map, term_hasher, term_comparer> @@ -119,6 +117,10 @@ class lar_solver : public column_namer { indexed_uint_set m_fixed_base_var_set; // end of fields + ////////////////// nested structs ///////////////////////// + struct undo_add_column; + struct undo_add_term; + ////////////////// methods //////////////////////////////// static bool valid_index(unsigned j) { return static_cast(j) >= 0; } @@ -395,7 +397,7 @@ class lar_solver : public column_namer { 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); - unsigned num_scopes() const { return m_term_count.stack_size(); } + unsigned num_scopes() const { return m_trail.get_num_scopes(); } 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(); diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 2f2e524aa..446bf4698 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -52,7 +52,7 @@ namespace nla { * a bounds axiom. */ bool monomial_bounds::propagate_value(dep_interval& range, lpvar v) { - // auto val = c().val(v); + bool propagated = false; if (should_propagate_upper(range, v, 1)) { auto const& upper = dep.upper(range); @@ -88,37 +88,21 @@ namespace nla { bool monomial_bounds::should_propagate_lower(dep_interval const& range, lpvar v, unsigned p) { if (dep.lower_is_inf(range)) return false; - u_dependency* d = nullptr; - rational bound; - bool is_strict; - if (!c().has_lower_bound(v, d, bound, is_strict)) - return true; + auto bound = c().val(v); auto const& lower = dep.lower(range); if (p > 1) bound = power(bound, p); - if (bound < lower) - return true; - if (bound > lower) - return false; - return !is_strict && dep.lower_is_open(range); + return bound < lower; } bool monomial_bounds::should_propagate_upper(dep_interval const& range, lpvar v, unsigned p) { if (dep.upper_is_inf(range)) return false; - u_dependency* d = nullptr; - rational bound; - bool is_strict; - if (!c().has_upper_bound(v, d, bound, is_strict)) - return true; + auto bound = c().val(v); auto const& upper = dep.upper(range); if (p > 1) bound = power(bound, p); - if (bound > upper) - return true; - if (bound < upper) - return false; - return !is_strict && dep.upper_is_open(range); + return bound > upper; } /** From a39d4adf5b67ab77966eac8146c6c3dbc406f7df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2023 13:45:42 -0700 Subject: [PATCH 247/428] build fixes Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 446bf4698..1acb56783 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -143,7 +143,6 @@ namespace nla { return propagate_value(range, v); rational r; if (should_propagate_upper(range, v, p)) { // v.upper^p > range.upper - SASSERT(c().has_upper_bound(v)); lp::explanation ex; dep.get_upper_dep(range, ex); // p even, range.upper < 0, v^p >= 0 -> infeasible @@ -154,16 +153,16 @@ namespace nla { return true; } // v.upper < 0, but v^p > range.upper -> infeasible. - if (p % 2 == 0 && c().get_upper_bound(v) < 0) { + if (p % 2 == 0 && c().has_upper_bound(v) && c().get_upper_bound(v) < 0) { ++c().lra.settings().stats().m_nla_propagate_bounds; new_lemma lemma(c(), "range requires a non-negative upper bound"); lemma &= ex; lemma.explain_existing_upper_bound(v); return true; } - SASSERT(p % 2 == 1 || c().get_upper_bound(v) >= 0); - if (rational(dep.upper(range)).root(p, r)) { + if (rational(dep.upper(range)).root(p, r) && (p % 2 == 1 || c().has_upper_bound(v))) { + SASSERT(p % 2 == 1 || c().get_upper_bound(v) >= 0); // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 ++c().lra.settings().stats().m_nla_propagate_bounds; From 5619ed05869f8e22cba4a49f3e00d9a4a63b5699 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2023 13:55:56 -0700 Subject: [PATCH 248/428] resurrect old bounds propagation Signed-off-by: Nikolaj Bjorner --- src/math/lp/monomial_bounds.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 1acb56783..07028e001 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -161,18 +161,28 @@ namespace nla { return true; } - if (rational(dep.upper(range)).root(p, r) && (p % 2 == 1 || c().has_upper_bound(v))) { - SASSERT(p % 2 == 1 || c().get_upper_bound(v) >= 0); + if (rational(dep.upper(range)).root(p, r)) { // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 // v = -2, [-1,+1]^2 < v^2 -> add bound v >= -1 - ++c().lra.settings().stats().m_nla_propagate_bounds; - auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; - new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); - lemma &= ex; - if (p % 2 == 0) - lemma.explain_existing_upper_bound(v); - lemma |= ineq(v, le, r); - return true; + + if ((p % 2 == 1) || c().val(v).is_pos()) { + ++c().lra.settings().stats().m_nla_propagate_bounds; + auto le = dep.upper_is_open(range) ? llc::LT : llc::LE; + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below value"); + lemma &= ex; + lemma |= ineq(v, le, r); + return true; + } + + if (p % 2 == 0 && c().val(v).is_neg()) { + ++c().lra.settings().stats().m_nla_propagate_bounds; + SASSERT(!r.is_neg()); + auto ge = dep.upper_is_open(range) ? llc::GT : llc::GE; + new_lemma lemma(c(), "propagate value - root case - upper bound of range is below negative value"); + lemma &= ex; + lemma |= ineq(v, ge, -r); + return true; + } } } From 5942dc24bd90fcad262e94fcba88aa48bfbbebe1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 11:41:25 -0700 Subject: [PATCH 249/428] #6523 --- src/ast/euf/euf_egraph.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 3683295d6..34781aad5 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -36,8 +36,10 @@ namespace euf { } m_expr2enode.setx(f->get_id(), n, nullptr); push_node(n); - for (unsigned i = 0; i < num_args; ++i) - set_cgc_enabled(args[i], true); + for (unsigned i = 0; i < num_args; ++i) { + set_cgc_enabled(args[i], true); + args[i]->get_root()->set_is_shared(l_undef); + } return n; } From 41b1f47d77353e261d411fd382357626bf1c6c88 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 12:15:28 -0700 Subject: [PATCH 250/428] #6523 deal with memory leak when there is an exception --- src/sat/smt/q_ematch.cpp | 5 ++--- src/smt/theory_lra.cpp | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index ec10426a7..83366dded 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -576,14 +576,13 @@ namespace q { void ematch::add(quantifier* _q) { TRACE("q", tout << "add " << mk_pp(_q, m) << "\n"); - clause* c = clausify(_q); + scoped_ptr c = clausify(_q); quantifier* q = c->q(); if (m_q2clauses.contains(q)) { - dealloc(c); return; } ensure_ground_enodes(*c); - m_clauses.push_back(c); + m_clauses.push_back(c.detach()); m_q2clauses.insert(q, c->index()); ctx.push(pop_clause(*this)); init_watch(*c); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index cd4ca38c7..284c34b54 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1648,6 +1648,7 @@ public: break; } + switch (check_nla()) { case FC_DONE: break; From b2efa592ce047d01a0023f548ab0fe299a1c9d55 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 12:17:08 -0700 Subject: [PATCH 251/428] #6523 deal with memory leak on exceptions --- src/sat/smt/q_ematch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 83366dded..5ddc3150b 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -486,7 +486,7 @@ namespace q { * basic clausifier, assumes q has been normalized. */ clause* ematch::clausify(quantifier* _q) { - clause* cl = alloc(clause, m, m_clauses.size()); + scoped_ptr cl = alloc(clause, m, m_clauses.size()); cl->m_literal = ctx.mk_literal(_q); quantifier_ref q(_q, m); q = m_qs.flatten(q); @@ -514,7 +514,7 @@ namespace q { unsigned generation = nq ? nq->generation() : ctx.generation(); cl->m_stat = m_qstat_gen(_q, generation); SASSERT(ctx.s().value(cl->m_literal) == l_true); - return cl; + return cl.detach(); } lit ematch::clausify_literal(expr* arg) { From 6553382ec8f03663813da1e7a0de337dd76a2f50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 12:30:24 -0700 Subject: [PATCH 252/428] remove extra assume-eqs --- src/smt/theory_lra.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 284c34b54..abbcea199 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1635,7 +1635,7 @@ public: switch (is_sat) { case l_true: TRACE("arith", display(tout)); - + switch (check_lia()) { case FC_DONE: break; @@ -1647,7 +1647,6 @@ public: st = FC_CONTINUE; break; } - switch (check_nla()) { case FC_DONE: @@ -1658,7 +1657,7 @@ public: TRACE("arith", tout << "check-nra giveup\n";); st = FC_GIVEUP; break; - } + } if (assume_eqs()) { ++m_stats.m_assume_eqs; @@ -1970,9 +1969,6 @@ public: } if (!check_idiv_bounds()) return FC_CONTINUE; - - if (assume_eqs()) - return FC_CONTINUE; return FC_DONE; } @@ -2027,7 +2023,7 @@ public: add_lemmas(); return FC_CONTINUE; case l_true: - return assume_eqs()? FC_CONTINUE: FC_DONE; + return FC_DONE; default: return FC_GIVEUP; } From 891ab8bac543d82a23cb0fd1f6d48b85f5c3acf1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 12:37:14 -0700 Subject: [PATCH 253/428] #6523 fixup looping --- src/sat/smt/arith_solver.cpp | 53 ++++++++++++++++++++++++++++-------- src/sat/smt/arith_solver.h | 4 ++- src/smt/theory_lra.cpp | 1 - 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 17ab03ee6..2921e5018 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -83,6 +83,7 @@ namespace arith { m_new_eq = false; flush_bound_axioms(); + propagate_nla(); unsigned qhead = m_asserted_qhead; while (m_asserted_qhead < m_asserted.size() && !s().inconsistent() && m.inc()) { @@ -301,6 +302,22 @@ namespace arith { m_explanation.add_pair(j, v); } + void solver::add_equality(lpvar j, rational const& k, lp::explanation const& exp) { + TRACE("arith", tout << "equality " << j << " " << k << "\n"); + theory_var v; + if (k == 1) + v = m_one_var; + else if (k == 0) + v = m_zero_var; + else if (!m_value2var.find(k, v)) + return; + theory_var w = lp().local_to_external(j); + if (w < 0) + return; + lpvar i = register_theory_var_in_lar_solver(v); + add_eq(i, j, exp, true); + } + bool solver::add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) { if (s().inconsistent()) return false; @@ -1414,14 +1431,6 @@ namespace arith { core.push_back(~mk_ineq_literal(ineq)); set_conflict_or_lemma(hint_type::nla_h, core, false); } - - void solver::assume_literals() { - for (auto const& ineq : m_nla->literals()) { - auto lit = mk_ineq_literal(ineq); - ctx.mark_relevant(lit); - s().set_phase(lit); - } - } sat::literal solver::mk_ineq_literal(nla::ineq const& ineq) { bool is_lower = true, sign = true, is_eq = false; @@ -1462,9 +1471,7 @@ namespace arith { lbool r = m_nla->check(); switch (r) { case l_false: - assume_literals(); - for (const nla::lemma& l : m_nla->lemmas()) - false_case_of_check_nla(l); + add_lemmas(); break; case l_true: if (assume_eqs()) @@ -1476,6 +1483,30 @@ namespace arith { return r; } + void solver::add_lemmas() { + for (auto const& ineq : m_nla->literals()) { + auto lit = mk_ineq_literal(ineq); + ctx.mark_relevant(lit); + s().set_phase(lit); + } + for (const nla::lemma& l : m_nla->lemmas()) + false_case_of_check_nla(l); + if (!propagate_eqs()) + return; + for (auto const& [v,k,e] : m_nla->fixed_equalities()) + add_equality(v, k, e); + for (auto const& [i,j,e] : m_nla->equalities()) + add_eq(i,j,e,false); + } + + void solver::propagate_nla() { + if (m_nla) { + m_nla->propagate(); + add_lemmas(); + lp().collect_more_rows_for_lp_propagation(); + } + } + void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) { auto& jst = euf::th_explain::from_index(idx); ctx.get_th_antecedents(l, jst, r, probing); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 801eb474c..b3d10ab52 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -407,6 +407,9 @@ namespace arith { bool check_delayed_eqs(); lbool check_lia(); lbool check_nla(); + void add_lemmas(); + void propagate_nla(); + void add_equality(lpvar v, rational const& k, lp::explanation const& exp); bool is_infeasible() const; nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const; @@ -463,7 +466,6 @@ namespace arith { void set_evidence(lp::constraint_index idx); void assign(literal lit, literal_vector const& core, svector const& eqs, euf::th_proof_hint const* pma); - void assume_literals(); sat::literal mk_ineq_literal(nla::ineq const& ineq); void false_case_of_check_nla(const nla::lemma& l); void dbg_finalize_model(model& mdl); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index abbcea199..9e8f57e2c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2173,7 +2173,6 @@ public: } void add_equality(lpvar j, rational const& k, lp::explanation const& exp) { - //verbose_stream() << "equality " << j << " " << k << "\n"; TRACE("arith", tout << "equality " << j << " " << k << "\n"); theory_var v; if (k == 1) From cafe3acff1d4aedf2b08a4700574fcb41b4e4355 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 12:41:34 -0700 Subject: [PATCH 254/428] delay detach Signed-off-by: Nikolaj Bjorner --- src/sat/smt/q_ematch.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 5ddc3150b..aa79bab5b 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -578,11 +578,10 @@ namespace q { TRACE("q", tout << "add " << mk_pp(_q, m) << "\n"); scoped_ptr c = clausify(_q); quantifier* q = c->q(); - if (m_q2clauses.contains(q)) { + if (m_q2clauses.contains(q)) return; - } ensure_ground_enodes(*c); - m_clauses.push_back(c.detach()); + m_clauses.push_back(c.get()); m_q2clauses.insert(q, c->index()); ctx.push(pop_clause(*this)); init_watch(*c); @@ -613,6 +612,7 @@ namespace q { if (!unary) j++; } + c.detach(); } From bdac86501d6e27b8032925d6f9be02c5501bff0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2023 20:33:48 -0700 Subject: [PATCH 255/428] add facility to check for missing propagations --- src/math/lp/nla_grobner.cpp | 42 +++++++++++++++++++---- src/math/lp/nla_grobner.h | 3 ++ src/math/lp/nra_solver.cpp | 66 +++++++++++++++++++++++++++++++++++-- src/math/lp/nra_solver.h | 7 +++- src/smt/theory_lra.cpp | 2 +- 5 files changed, 109 insertions(+), 11 deletions(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index f2fe8d9b2..c6d29ac05 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -61,6 +61,9 @@ namespace nla { } + // DEBUG_CODE(for (auto e : m_solver.equations()) check_missing_propagation(*e);); + + if (m_quota > 0) --m_quota; @@ -125,11 +128,11 @@ namespace nla { typedef lp::lar_term term; bool grobner::propagate_fixed(const dd::solver::equation& eq) { dd::pdd const& p = eq.poly(); - //IF_VERBOSE(0, verbose_stream() << p << "\n"); if (p.is_unary()) { unsigned v = p.var(); if (c().var_is_fixed(v)) return false; + ineq new_eq(v, llc::EQ, rational::zero()); if (c().ineq_holds(new_eq)) return false; @@ -147,12 +150,19 @@ namespace nla { rational d = lcm(denominator(a), denominator(b)); a *= d; b *= d; +#if 0 + c().lra.update_column_type_and_bound(v, lp::lconstraint_kind::EQ, b/a, eq.dep()); + lp::explanation exp; + explain(eq, exp); + c().add_fixed_equality(c().lra.column_to_reported_index(v), b/a, exp); +#else ineq new_eq(term(a, v), llc::EQ, b); if (c().ineq_holds(new_eq)) return false; new_lemma lemma(c(), "pdd-eq"); add_dependencies(lemma, eq); lemma |= new_eq; +#endif return true; } @@ -193,15 +203,19 @@ namespace nla { return true; } - - void grobner::add_dependencies(new_lemma& lemma, const dd::solver::equation& eq) { - lp::explanation ex; + void grobner::explain(const dd::solver::equation& eq, lp::explanation& exp) { u_dependency_manager dm; vector lv; dm.linearize(eq.dep(), lv); for (unsigned ci : lv) - ex.push_back(ci); - lemma &= ex; + exp.push_back(ci); + } + + + void grobner::add_dependencies(new_lemma& lemma, const dd::solver::equation& eq) { + lp::explanation exp; + explain(eq, exp); + lemma &= exp; } void grobner::configure() { @@ -280,6 +294,10 @@ namespace nla { } bool grobner::is_conflicting(const dd::solver::equation& e) { + for (auto j : e.poly().free_vars()) + if (lra.column_is_free(j)) + return false; + auto& di = c().m_intervals.get_dep_intervals(); dd::pdd_interval eval(di); eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) { @@ -549,4 +567,16 @@ namespace nla { tout << "\n"); } + void grobner::check_missing_propagation(const dd::solver::equation& e) { + vector eqs; + eqs.push_back(e.poly()); + lbool r = c().m_nra.check(eqs); + CTRACE("grobner", r == l_false, m_solver.display(tout << "missed conflict ", e);); + if (r != l_true) + return; + r = c().m_nra.check_tight(e.poly()); + CTRACE("grobner", r == l_false, m_solver.display(tout << "tight equality ", e);); + } + + } diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index c7d41413c..1c04c82e0 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -41,6 +41,9 @@ namespace nla { bool propagate_factorization(const dd::solver::equation& eq); void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq); + void explain(const dd::solver::equation& eq, lp::explanation& exp); + + void check_missing_propagation(const dd::solver::equation& eq); // setup void configure(); diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index dc4681559..182845d58 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -206,14 +206,62 @@ struct solver::imp { } } + if (r == l_true) + return r; + IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n"; m_nlsat->display(verbose_stream()); for (auto const& [v, w] : m_lp2nl) { auto& ls = m_nla_core.lra; if (ls.column_has_lower_bound(v)) - verbose_stream() << w << " >= " << ls.get_lower_bound(v) << "\n"; + verbose_stream() << "x" << w << " >= " << ls.get_lower_bound(v) << "\n"; if (ls.column_has_upper_bound(v)) - verbose_stream() << w << " <= " << ls.get_upper_bound(v) << "\n"; + verbose_stream() << "x" << w << " <= " << ls.get_upper_bound(v) << "\n"; + }); + + + return r; + } + + lbool check_tight(dd::pdd const& eq) { + m_zero = nullptr; + m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); + m_zero = alloc(scoped_anum, am()); + m_lp2nl.reset(); + m_term_set.reset(); + add_eq(eq); + for (auto const& [v, w] : m_lp2nl) { + auto& ls = m_nla_core.lra; + if (ls.column_has_lower_bound(v)) + add_strict_lb(ls.get_lower_bound(v), w); + if (ls.column_has_upper_bound(v)) + add_strict_ub(ls.get_upper_bound(v), w); + } + + lbool r = l_undef; + try { + r = m_nlsat->check(); + } + catch (z3_exception&) { + if (m_limit.is_canceled()) { + r = l_undef; + } + else { + throw; + } + } + + if (r == l_true) + return r; + + IF_VERBOSE(0, verbose_stream() << "check-nra tight " << r << "\n"; + m_nlsat->display(verbose_stream()); + for (auto const& [v, w] : m_lp2nl) { + auto& ls = m_nla_core.lra; + if (ls.column_has_lower_bound(v)) + verbose_stream() << "x" << w << " >= " << ls.get_lower_bound(v) << "\n"; + if (ls.column_has_upper_bound(v)) + verbose_stream() << "x" << w << " <= " << ls.get_upper_bound(v) << "\n"; }); @@ -235,6 +283,13 @@ struct solver::imp { m_nlsat->mk_clause(1, &lit, nullptr); } + void add_strict_lb(lp::impq const& b, unsigned w) { + add_bound(b.x, w, false, nlsat::atom::kind::GT); + } + void add_strict_ub(lp::impq const& b, unsigned w) { + add_bound(b.x, w, false, nlsat::atom::kind::LT); + } + void add_lb(lp::impq const& b, unsigned w) { add_bound(b.x, w, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT); } @@ -269,7 +324,8 @@ struct solver::imp { m_lp2nl.insert(v, w); } polynomial::polynomial_ref vp(pm.mk_polynomial(w, 1), pm); - return pm.add(lo, pm.mul(vp, hi)); + polynomial::polynomial_ref mp(pm.mul(vp, hi), pm); + return pm.add(lo, mp); } bool is_int(lp::var_index v) { @@ -361,6 +417,10 @@ lbool solver::check(vector const& eqs) { return m_imp->check(eqs); } +lbool solver::check_tight(dd::pdd const& eq) { + return m_imp->check_tight(eq); +} + bool solver::need_check() { return m_imp->need_check(); } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index b8863e44b..3f6335013 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -38,10 +38,15 @@ namespace nra { lbool check(); /** - \breif Check feasibility of equalities modulo bounds constraints on their variables. + \brief Check feasibility of equalities modulo bounds constraints on their variables. */ lbool check(vector const& eqs); + /** + \brief Check if equality is tight. + */ + lbool check_tight(const dd::pdd& eq); + /* \brief determine whether nra check is needed. */ diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 9e8f57e2c..b98cc002f 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2158,7 +2158,6 @@ public: if (m_nla) { m_nla->propagate(); add_lemmas(); - add_equalities(); lp().collect_more_rows_for_lp_propagation(); } } @@ -2193,6 +2192,7 @@ public: assume_literal(i); for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); + add_equalities(); } bool should_propagate() const { From 18fc6914d396241e928d43ba8a5c0d8531944b8b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2023 00:40:24 -0700 Subject: [PATCH 256/428] add facility to experiment with nla justified conflicts from Grobner equations Signed-off-by: Nikolaj Bjorner --- src/math/grobner/pdd_simplifier.cpp | 8 +++--- src/math/lp/nla_grobner.cpp | 40 +++++++++++++++++++++++------ src/math/lp/nla_grobner.h | 2 ++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/math/grobner/pdd_simplifier.cpp b/src/math/grobner/pdd_simplifier.cpp index 4a8140681..8673d87f3 100644 --- a/src/math/grobner/pdd_simplifier.cpp +++ b/src/math/grobner/pdd_simplifier.cpp @@ -89,7 +89,7 @@ namespace dd { bool simplifier::simplify_linear_step(bool binary) { TRACE("dd.solver", tout << "binary " << binary << "\n";); - IF_VERBOSE(2, verbose_stream() << "binary " << binary << "\n"); + IF_VERBOSE(3, verbose_stream() << "binary " << binary << "\n"); equation_vector linear; for (equation* e : s.m_to_simplify) { pdd p = e->poly(); @@ -184,7 +184,7 @@ namespace dd { */ bool simplifier::simplify_cc_step() { TRACE("dd.solver", tout << "cc\n";); - IF_VERBOSE(2, verbose_stream() << "cc\n"); + IF_VERBOSE(3, verbose_stream() << "cc\n"); u_map los; bool reduced = false; unsigned j = 0; @@ -217,7 +217,7 @@ namespace dd { */ bool simplifier::simplify_leaf_step() { TRACE("dd.solver", tout << "leaf\n";); - IF_VERBOSE(2, verbose_stream() << "leaf\n"); + IF_VERBOSE(3, verbose_stream() << "leaf\n"); use_list_t use_list = get_use_list(); equation_vector leaves; for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) { @@ -262,7 +262,7 @@ namespace dd { */ bool simplifier::simplify_elim_pure_step() { TRACE("dd.solver", tout << "pure\n";); - IF_VERBOSE(2, verbose_stream() << "pure\n"); + IF_VERBOSE(3, verbose_stream() << "pure\n"); use_list_t use_list = get_use_list(); unsigned j = 0; for (equation* e : s.m_to_simplify) { diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index c6d29ac05..af7373d02 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -36,7 +36,7 @@ namespace nla { if (m_quota == 0) m_quota = c().params().arith_nl_gr_q(); - if (m_quota == 1) + if (false && m_quota == 1) return; lp_settings().stats().m_grobner_calls++; @@ -316,7 +316,11 @@ namespace nla { c().m_intervals.display(tout << "j" << j << " ", a); tout << " "; } tout << "\n"); - + +#if 0 + if (add_nla_conflict(e)) + return true; +#endif return false; } eval.get_interval(e.poly(), i_wd); @@ -329,6 +333,10 @@ namespace nla { return true; } else { +#if 0 + if (add_nla_conflict(e)) + return true; +#endif TRACE("grobner", m_solver.display(tout << "no conflict ", e) << "\n"); return false; } @@ -567,14 +575,30 @@ namespace nla { tout << "\n"); } - void grobner::check_missing_propagation(const dd::solver::equation& e) { + bool grobner::is_nla_conflict(const dd::solver::equation& eq) { vector eqs; - eqs.push_back(e.poly()); - lbool r = c().m_nra.check(eqs); - CTRACE("grobner", r == l_false, m_solver.display(tout << "missed conflict ", e);); - if (r != l_true) + eqs.push_back(eq.poly()); + return l_false == c().m_nra.check(eqs); + } + + bool grobner::add_nla_conflict(const dd::solver::equation& eq) { + if (is_nla_conflict(eq)) { + new_lemma lemma(m_core,"nla-conflict"); + lp::explanation exp; + explain(eq, exp); + lemma &= exp; + return true; + } + return false; + } + + + void grobner::check_missing_propagation(const dd::solver::equation& e) { + bool is_confl = is_nla_conflict(e); + CTRACE("grobner", is_confl, m_solver.display(tout << "missed conflict ", e);); + if (is_confl) return; - r = c().m_nra.check_tight(e.poly()); + lbool r = c().m_nra.check_tight(e.poly()); CTRACE("grobner", r == l_false, m_solver.display(tout << "tight equality ", e);); } diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index 1c04c82e0..40fbb1ffc 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -43,6 +43,8 @@ namespace nla { void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq); void explain(const dd::solver::equation& eq, lp::explanation& exp); + bool is_nla_conflict(const dd::solver::equation& eq); + bool add_nla_conflict(const dd::solver::equation& eq); void check_missing_propagation(const dd::solver::equation& eq); // setup From ba881d9c9bec76d690dbe3a8d6eef9ecbe78106f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2023 00:40:43 -0700 Subject: [PATCH 257/428] add facility to experiment with nla justified conflicts from Grobner equations Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_grobner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index af7373d02..a7c7dd762 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -36,7 +36,7 @@ namespace nla { if (m_quota == 0) m_quota = c().params().arith_nl_gr_q(); - if (false && m_quota == 1) + if (m_quota == 1) return; lp_settings().stats().m_grobner_calls++; From f678861aef8a49d53100d9d45fb28d04e49f81d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2023 08:43:08 -0700 Subject: [PATCH 258/428] fix #6947 --- src/math/lp/monomial_bounds.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index 07028e001..e54f82682 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -152,14 +152,6 @@ namespace nla { lemma &= ex; return true; } - // v.upper < 0, but v^p > range.upper -> infeasible. - if (p % 2 == 0 && c().has_upper_bound(v) && c().get_upper_bound(v) < 0) { - ++c().lra.settings().stats().m_nla_propagate_bounds; - new_lemma lemma(c(), "range requires a non-negative upper bound"); - lemma &= ex; - lemma.explain_existing_upper_bound(v); - return true; - } if (rational(dep.upper(range)).root(p, r)) { // v = -2, [-4,-3]^3 < v^3 -> add bound v <= -3 From c9c5dbc347121791762cd3c1170cb691a3aa6831 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2023 09:27:22 -0700 Subject: [PATCH 259/428] #6523 --- src/math/lp/nla_core.cpp | 13 ++++++------- src/sat/smt/arith_solver.cpp | 10 +++++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 056df3b69..50cd31661 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -563,18 +563,15 @@ bool core::var_is_fixed(lpvar j) const { bool core::var_is_free(lpvar j) const { return lra.column_is_free(j); } - std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const { lra.print_term_as_indices(in.term(), out); - out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs(); - return out; + return out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs(); } std::ostream & core::print_var(lpvar j, std::ostream & out) const { - if (is_monic_var(j)) { + if (is_monic_var(j)) print_monic(m_emons[j], out); - } lra.print_column_info(j, out); signed_var jr = m_evars.find(j); @@ -1524,9 +1521,11 @@ void core::add_bounds() { lpvar i = m_to_refine[(k + r) % sz]; auto const& m = m_emons[i]; for (lpvar j : m.vars()) { - if (!var_is_free(j)) continue; + if (!var_is_free(j)) + continue; // split the free variable (j <= 0, or j > 0), and return - m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); + m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero())); + TRACE("nla_solver", print_ineq(m_literals.back(), tout) << "\n"); ++lp_settings().stats().m_nla_add_bounds; return; } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 2921e5018..ebfe93dfe 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1443,7 +1443,6 @@ namespace arith { case lp::NE: is_eq = true; sign = true; break; default: UNREACHABLE(); } - TRACE("arith", tout << "is_lower: " << is_lower << " sign " << sign << "\n";); // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); // then term is used instead of ineq.m_term sat::literal lit; @@ -1452,6 +1451,7 @@ namespace arith { else lit = ctx.expr2literal(mk_bound(ineq.term(), ineq.rs(), is_lower)); + TRACE("arith", tout << "is_lower: " << is_lower << " sign " << sign << " " << ctx.literal2expr(lit) << "\n";); return sign ? ~lit : lit; } @@ -1488,6 +1488,14 @@ namespace arith { auto lit = mk_ineq_literal(ineq); ctx.mark_relevant(lit); s().set_phase(lit); + // force trichotomy axiom for equality literals + if (ineq.cmp() == lp::EQ) { + nla::lemma l; + l.push_back(ineq); + l.push_back(nla::ineq(lp::LT, ineq.term(), ineq.rs())); + l.push_back(nla::ineq(lp::GT, ineq.term(), ineq.rs())); + false_case_of_check_nla(l); + } } for (const nla::lemma& l : m_nla->lemmas()) false_case_of_check_nla(l); From 3fa67777e5c7d4a4bf09e84a923a7ddca74696de Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Oct 2023 19:50:13 -0700 Subject: [PATCH 260/428] fix exception safety in pdd-solver --- src/math/dd/dd_pdd.cpp | 31 ++--- src/math/dd/dd_pdd.h | 3 +- src/math/grobner/pdd_simplifier.cpp | 187 +++++++++++++--------------- src/math/grobner/pdd_solver.cpp | 77 ++++++------ src/math/grobner/pdd_solver.h | 1 + 5 files changed, 147 insertions(+), 152 deletions(-) diff --git a/src/math/dd/dd_pdd.cpp b/src/math/dd/dd_pdd.cpp index 291af3b5c..970eb991f 100644 --- a/src/math/dd/dd_pdd.cpp +++ b/src/math/dd/dd_pdd.cpp @@ -62,6 +62,10 @@ namespace dd { init_nodes(level2var); } + void pdd_manager::set_max_num_nodes(unsigned n) { + m_max_num_nodes = n + m_level2var.size(); + } + void pdd_manager::init_nodes(unsigned_vector const& l2v) { // add dummy nodes for operations, and 0, 1 pdds. for (unsigned i = 0; i < pdd_no_op; ++i) { @@ -1352,9 +1356,8 @@ namespace dd { e->get_data().m_refcount = 0; } if (do_gc) { - if (m_nodes.size() > m_max_num_nodes) { - throw mem_out(); - } + if (m_nodes.size() > m_max_num_nodes) + throw mem_out(); alloc_free_nodes(m_nodes.size()/2); } SASSERT(e->get_data().m_lo == n.m_lo); @@ -1600,7 +1603,8 @@ namespace dd { for (unsigned i = m_nodes.size(); i-- > pdd_no_op; ) { if (!reachable[i]) { if (is_val(i)) { - if (m_freeze_value == val(i)) continue; + if (m_freeze_value == val(i)) + continue; m_free_values.push_back(m_mpq_table.find(val(i)).m_value_index); m_mpq_table.remove(val(i)); } @@ -1615,20 +1619,17 @@ namespace dd { ptr_vector to_delete, to_keep; for (auto* e : m_op_cache) { - if (e->m_result != null_pdd) { - to_delete.push_back(e); - } - else { - to_keep.push_back(e); - } + if (e->m_result != null_pdd) + to_delete.push_back(e); + else + to_keep.push_back(e); } m_op_cache.reset(); - for (op_entry* e : to_delete) { + for (op_entry* e : to_delete) m_alloc.deallocate(sizeof(*e), e); - } - for (op_entry* e : to_keep) { - m_op_cache.insert(e); - } + + for (op_entry* e : to_keep) + m_op_cache.insert(e); m_factor_cache.reset(); diff --git a/src/math/dd/dd_pdd.h b/src/math/dd/dd_pdd.h index 6dee7977f..f2547e962 100644 --- a/src/math/dd/dd_pdd.h +++ b/src/math/dd/dd_pdd.h @@ -324,8 +324,9 @@ namespace dd { semantics get_semantics() const { return m_semantics; } void reset(unsigned_vector const& level2var); - void set_max_num_nodes(unsigned n) { m_max_num_nodes = n; } + void set_max_num_nodes(unsigned n); unsigned_vector const& get_level2var() const { return m_level2var; } + unsigned num_nodes() const { return m_nodes.size() - m_free_nodes.size(); } pdd mk_var(unsigned i); pdd mk_val(rational const& r); diff --git a/src/math/grobner/pdd_simplifier.cpp b/src/math/grobner/pdd_simplifier.cpp index 8673d87f3..6364d9ae6 100644 --- a/src/math/grobner/pdd_simplifier.cpp +++ b/src/math/grobner/pdd_simplifier.cpp @@ -75,7 +75,7 @@ namespace dd { } } catch (pdd_manager::mem_out) { - IF_VERBOSE(2, verbose_stream() << "simplifier memout\n"); + IF_VERBOSE(1, verbose_stream() << "simplifier memout\n"); // done reduce DEBUG_CODE(s.invariant();); } @@ -94,7 +94,8 @@ namespace dd { for (equation* e : s.m_to_simplify) { pdd p = e->poly(); if (binary) { - if (p.is_binary()) linear.push_back(e); + if (p.is_binary()) + linear.push_back(e); } else if (p.is_linear()) { linear.push_back(e); @@ -112,29 +113,33 @@ namespace dd { use_list_t use_list = get_use_list(); compare_top_var ctv; std::stable_sort(linear.begin(), linear.end(), ctv); - equation_vector trivial; + struct trivial { + solver& s; + equation_vector elems; + trivial(solver& s) : s(s) {} + ~trivial () { + for (auto* e : elems) + s.del_equation(e); + } + }; + trivial trivial(s); unsigned j = 0; bool has_conflict = false; for (equation* src : linear) { - if (has_conflict) { - break; - } - if (s.is_trivial(*src)) { - continue; - } + if (has_conflict) + break; + if (s.is_trivial(*src)) + continue; unsigned v = src->poly().var(); equation_vector const& uses = use_list[v]; TRACE("dd.solver", s.display(tout << "uses of: ", *src) << "\n"; - for (equation* e : uses) { - s.display(tout, *e) << "\n"; - }); + for (equation* e : uses) s.display(tout, *e) << "\n";); bool changed_leading_term; bool all_reduced = true; for (equation* dst : uses) { - if (src == dst || s.is_trivial(*dst)) { - continue; - } + if (src == dst || s.is_trivial(*dst)) + continue; pdd q = dst->poly(); if (!src->poly().is_binary() && !q.is_linear()) { all_reduced = false; @@ -142,9 +147,8 @@ namespace dd { } remove_from_use(dst, use_list, v); s.simplify_using(*dst, *src, changed_leading_term); - if (s.is_trivial(*dst)) { - trivial.push_back(dst); - } + if (s.is_trivial(*dst)) + trivial.elems.push_back(dst); else if (s.is_conflict(dst)) { s.pop_equation(dst); s.set_conflict(dst); @@ -158,9 +162,8 @@ namespace dd { // SASSERT(!dst->poly().free_vars().contains(v)); add_to_use(dst, use_list); } - if (all_reduced) { - linear[j++] = src; - } + if (all_reduced) + linear[j++] = src; } if (!has_conflict) { linear.shrink(j); @@ -169,9 +172,7 @@ namespace dd { s.push_equation(solver::solved, src); } } - for (equation* e : trivial) { - s.del_equation(e); - } + DEBUG_CODE(s.invariant();); return j > 0 || has_conflict; } @@ -187,8 +188,9 @@ namespace dd { IF_VERBOSE(3, verbose_stream() << "cc\n"); u_map los; bool reduced = false; - unsigned j = 0; - for (equation* eq1 : s.m_to_simplify) { + solver::scoped_update sc(s.m_to_simplify); + for (; sc.i < sc.sz; ++sc.i) { + auto* eq1 = sc.get(); SASSERT(eq1->state() == solver::to_simplify); pdd p = eq1->poly(); equation* eq2 = los.insert_if_not_there(p.lo().index(), eq1); @@ -201,14 +203,11 @@ namespace dd { s.retire(eq1); continue; } - else if (s.check_conflict(*eq1)) { - continue; - } + else if (s.check_conflict(*eq1)) + continue; } - s.m_to_simplify[j] = eq1; - eq1->set_index(j++); + sc.nextj(); } - s.m_to_simplify.shrink(j); return reduced; } @@ -225,15 +224,12 @@ namespace dd { pdd p = e->poly(); if (p.is_val()) continue; - if (!p.hi().is_val()) { - continue; - } + if (!p.hi().is_val()) + continue; leaves.reset(); - for (equation* e2 : use_list[p.var()]) { - if (e != e2 && e2->poly().var_is_leaf(p.var())) { - leaves.push_back(e2); - } - } + for (equation* e2 : use_list[p.var()]) + if (e != e2 && e2->poly().var_is_leaf(p.var())) + leaves.push_back(e2); for (equation* e2 : leaves) { bool changed_leading_term; remove_from_use(e2, use_list); @@ -263,23 +259,20 @@ namespace dd { bool simplifier::simplify_elim_pure_step() { TRACE("dd.solver", tout << "pure\n";); IF_VERBOSE(3, verbose_stream() << "pure\n"); - use_list_t use_list = get_use_list(); - unsigned j = 0; - for (equation* e : s.m_to_simplify) { + use_list_t use_list = get_use_list(); + solver::scoped_update sc(s.m_to_simplify); + bool has_solved = false; + for (; sc.i < sc.sz; ++sc.i) { + equation* e = sc.get(); pdd p = e->poly(); if (!p.is_val() && p.hi().is_val() && use_list[p.var()].size() == 1) { s.push_equation(solver::solved, e); + has_solved = true; } - else { - s.m_to_simplify[j] = e; - e->set_index(j++); - } + else + sc.nextj(); } - if (j != s.m_to_simplify.size()) { - s.m_to_simplify.shrink(j); - return true; - } - return false; + return has_solved; } /** @@ -288,63 +281,59 @@ namespace dd { */ bool simplifier::simplify_elim_dual_step() { use_list_t use_list = get_use_list(); - unsigned j = 0; bool reduced = false; - for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) { - equation* e = s.m_to_simplify[i]; - pdd p = e->poly(); - // check that e is linear in top variable. - if (e->state() != solver::to_simplify) { - reduced = true; - } - else if (!s.done() && !s.is_trivial(*e) && p.hi().is_val() && use_list[p.var()].size() == 2) { - for (equation* e2 : use_list[p.var()]) { - if (e2 == e) continue; - bool changed_leading_term; - - remove_from_use(e2, use_list); - s.simplify_using(*e2, *e, changed_leading_term); - if (s.is_conflict(e2)) { - s.pop_equation(e2); - s.set_conflict(e2); - } - // when e2 is trivial, leading term is changed - SASSERT(!s.is_trivial(*e2) || changed_leading_term); - if (changed_leading_term) { - s.pop_equation(e2); - s.push_equation(solver::to_simplify, e2); - } - add_to_use(e2, use_list); - break; + { + solver::scoped_update sc(s.m_to_simplify); + for (; sc.i < sc.sz; ++sc.i) { + equation* e = sc.get(); + pdd p = e->poly(); + // check that e is linear in top variable. + if (e->state() != solver::to_simplify) { + reduced = true; } - reduced = true; - s.push_equation(solver::solved, e); - } - else { - s.m_to_simplify[j] = e; - e->set_index(j++); + else if (!s.done() && !s.is_trivial(*e) && p.hi().is_val() && use_list[p.var()].size() == 2) { + for (equation* e2 : use_list[p.var()]) { + if (e2 == e) + continue; + bool changed_leading_term; + + remove_from_use(e2, use_list); + s.simplify_using(*e2, *e, changed_leading_term); + if (s.is_conflict(e2)) { + s.pop_equation(e2); + s.set_conflict(e2); + } + // when e2 is trivial, leading term is changed + SASSERT(!s.is_trivial(*e2) || changed_leading_term); + if (changed_leading_term) { + s.pop_equation(e2); + s.push_equation(solver::to_simplify, e2); + } + add_to_use(e2, use_list); + break; + } + reduced = true; + s.push_equation(solver::solved, e); + } + else + sc.nextj(); } } if (reduced) { // clean up elements in s.m_to_simplify // they may have moved. - s.m_to_simplify.shrink(j); - j = 0; - for (equation* e : s.m_to_simplify) { - if (s.is_trivial(*e)) { - s.retire(e); - } - else if (e->state() == solver::to_simplify) { - s.m_to_simplify[j] = e; - e->set_index(j++); - } + solver::scoped_update sc(s.m_to_simplify); + for (; sc.i < sc.sz; ++sc.i) { + equation* e = sc.get(); + if (s.is_trivial(*e)) + s.retire(e); + else if (e->state() == solver::to_simplify) + sc.nextj(); } - s.m_to_simplify.shrink(j); return true; } - else { - return false; - } + else + return false; } void simplifier::add_to_use(equation* e, use_list_t& use_list) { diff --git a/src/math/grobner/pdd_solver.cpp b/src/math/grobner/pdd_solver.cpp index e689c78a9..3f41d07cf 100644 --- a/src/math/grobner/pdd_solver.cpp +++ b/src/math/grobner/pdd_solver.cpp @@ -90,11 +90,9 @@ namespace dd { } void solver::saturate() { - simplify(); - if (done()) { - return; - } - init_saturate(); + if (done()) + return; + init_saturate(); TRACE("dd.solver", display(tout);); try { while (!done() && step()) { @@ -105,7 +103,7 @@ namespace dd { DEBUG_CODE(invariant();); } catch (pdd_manager::mem_out) { - IF_VERBOSE(2, verbose_stream() << "mem-out\n"); + IF_VERBOSE(1, verbose_stream() << "mem-out saturate\n"); // don't reduce further } } @@ -124,7 +122,7 @@ namespace dd { solver::scoped_process::~scoped_process() { if (e) { - pdd p = e->poly(); + pdd const& p = e->poly(); SASSERT(!p.is_val()); g.push_equation(processed, e); } @@ -137,9 +135,8 @@ namespace dd { void solver::superpose(equation const & eq) { - for (equation* target : m_processed) { - superpose(eq, *target); - } + for (equation* target : m_processed) + superpose(eq, *target); } /* @@ -166,32 +163,28 @@ namespace dd { TRACE("dd.solver", display(tout << "simplification result: ", eq);); } + void solver::well_formed() { + auto& set = m_to_simplify; + for (unsigned k = 0; k < set.size(); ++k) + for (unsigned l = k + 1; l < set.size(); ++l) { + if (!set[l] || !set[k] || set[k] != set[l]) + continue; + verbose_stream() << k << " " << l << " " << set[k] << "\n"; + for (auto* s : set) + verbose_stream() << s->idx() << "\n"; + VERIFY(set[k] != set[l]); + } + } /* Use the given equation to simplify equations in set */ - void solver::simplify_using(equation_vector& set, std::function& simplifier) { - struct scoped_update { - equation_vector& set; - unsigned i, j, sz; - scoped_update(equation_vector& set): set(set), i(0), j(0), sz(set.size()) {} - void nextj() { - set[j] = set[i]; - set[i]->set_index(j++); - } - ~scoped_update() { - for (; i < sz; ++i) - nextj(); - set.shrink(j); - } - }; - + void solver::simplify_using(equation_vector& set, std::function& simplifier) { scoped_update sr(set); for (; sr.i < sr.sz; ++sr.i) { equation& target = *set[sr.i]; bool changed_leading_term = false; bool simplified = true; simplified = !done() && simplifier(target, changed_leading_term); - if (simplified && is_trivial(target)) retire(&target); @@ -286,21 +279,32 @@ namespace dd { m_stats.m_compute_steps++; IF_VERBOSE(3, if (m_stats.m_compute_steps % 100 == 0) verbose_stream() << "compute steps = " << m_stats.m_compute_steps << "\n";); equation* e = pick_next(); - if (!e) return false; + if (!e) + return false; scoped_process sd(*this, e); equation& eq = *e; SASSERT(eq.state() == to_simplify); simplify_using(eq, m_processed); - if (is_trivial(eq)) { sd.e = nullptr; retire(e); return true; } - if (check_conflict(eq)) { sd.e = nullptr; return false; } + if (is_trivial(eq)) { + sd.e = nullptr; + retire(e); + return true; + } + if (check_conflict(eq)) { + sd.e = nullptr; + return false; + } m_too_complex = false; simplify_using(m_processed, eq); - if (done()) return false; + if (done()) + return false; TRACE("dd.solver", display(tout << "eq = ", eq);); superpose(eq); simplify_using(m_to_simplify, eq); - if (done()) return false; - if (!m_too_complex) sd.done(); + if (done()) + return false; + if (!m_too_complex) + sd.done(); return true; } @@ -345,9 +349,9 @@ namespace dd { } void solver::reset() { - for (equation* e : m_solved) dealloc(e); - for (equation* e : m_to_simplify) dealloc(e); - for (equation* e : m_processed) dealloc(e); + for (equation* e : m_solved) dealloc(e); + for (equation* e : m_to_simplify) dealloc(e); + for (equation* e : m_processed) dealloc(e); m_subst.reset(); m_solved.reset(); m_processed.reset(); @@ -445,7 +449,6 @@ namespace dd { #endif } - void solver::pop_equation(equation& eq) { equation_vector& v = get_queue(eq); unsigned idx = eq.idx(); diff --git a/src/math/grobner/pdd_solver.h b/src/math/grobner/pdd_solver.h index bc20c21b4..9ad3faee2 100644 --- a/src/math/grobner/pdd_solver.h +++ b/src/math/grobner/pdd_solver.h @@ -108,6 +108,7 @@ public: private: typedef ptr_vector equation_vector; + for (; i < sz; ++i) typedef std::function print_dep_t; pdd_manager& m; From 0a1cc4c054722b80f8cb682c5d20a947a0b22d0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Oct 2023 19:50:34 -0700 Subject: [PATCH 261/428] fix exception safety in pdd-solver --- src/math/grobner/pdd_solver.h | 71 +++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/math/grobner/pdd_solver.h b/src/math/grobner/pdd_solver.h index 9ad3faee2..872fef5fd 100644 --- a/src/math/grobner/pdd_solver.h +++ b/src/math/grobner/pdd_solver.h @@ -49,30 +49,17 @@ public: }; struct config { - unsigned m_eqs_threshold; - unsigned m_expr_size_limit; - unsigned m_expr_degree_limit; - unsigned m_max_steps; - unsigned m_max_simplified; - unsigned m_random_seed; - bool m_enable_exlin; - unsigned m_eqs_growth; - unsigned m_expr_size_growth; - unsigned m_expr_degree_growth; - unsigned m_number_of_conflicts_to_report; - config() : - m_eqs_threshold(UINT_MAX), - m_expr_size_limit(UINT_MAX), - m_expr_degree_limit(UINT_MAX), - m_max_steps(UINT_MAX), - m_max_simplified(UINT_MAX), - m_random_seed(0), - m_enable_exlin(false), - m_eqs_growth(10), - m_expr_size_growth(10), - m_expr_degree_growth(5), - m_number_of_conflicts_to_report(1) - {} + unsigned m_eqs_threshold = UINT_MAX; + unsigned m_expr_size_limit = UINT_MAX; + unsigned m_expr_degree_limit = UINT_MAX; + unsigned m_max_steps = UINT_MAX; + unsigned m_max_simplified = UINT_MAX; + unsigned m_random_seed = 0; + bool m_enable_exlin = false; + unsigned m_eqs_growth = 10; + unsigned m_expr_size_growth = 10; + unsigned m_expr_degree_growth = 5; + unsigned m_number_of_conflicts_to_report = 1; }; enum eq_state { @@ -82,18 +69,14 @@ public: }; class equation { - eq_state m_state; - unsigned m_idx; //!< unique index + eq_state m_state = to_simplify; + unsigned m_idx = 0; //!< unique index pdd m_poly; //!< polynomial in pdd form u_dependency * m_dep; //!< justification for the equality public: equation(pdd const& p, u_dependency* d): - m_state(to_simplify), - m_idx(0), m_poly(p), - m_dep(d) - { - + m_dep(d) { } const pdd& poly() const { return m_poly; } @@ -105,10 +88,33 @@ public: void set_state(eq_state st) { m_state = st; } void set_index(unsigned idx) { m_idx = idx; } }; -private: typedef ptr_vector equation_vector; + + struct scoped_update { + equation_vector& set; + unsigned i = 0; + unsigned j = 0; + unsigned sz; + scoped_update(equation_vector& set) : + set(set), sz(set.size()) { + } + ~scoped_update() { for (; i < sz; ++i) + nextj(); + set.shrink(j); + } + equation* get() { return set[i]; } + + void nextj() { + set[j] = set[i]; + set[i]->set_index(j++); + } + }; + +private: + + typedef std::function print_dep_t; pdd_manager& m; @@ -192,6 +198,7 @@ private: void push_equation(eq_state st, equation& eq); void push_equation(eq_state st, equation* eq) { push_equation(st, *eq); } + void well_formed(); void invariant() const; struct scoped_process { solver& g; From 37fe9cc764b7c98788813a071b82a241b46b9ad7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Oct 2023 21:19:57 -0700 Subject: [PATCH 262/428] add Horner saturation to Grobner conflict detection - throttle Grobner - add (disabled) propagate_linear_equation to prepare for additional propagation. - add validation code is_nla_conflict/add_nla_conflict to establish missed conflicts --- src/math/lp/nla_core.h | 4 +- src/math/lp/nla_grobner.cpp | 172 ++++++++++++++++++++++++++++++------ src/math/lp/nla_grobner.h | 29 +++--- 3 files changed, 167 insertions(+), 38 deletions(-) diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 3ff33a879..f99f640bf 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -169,8 +169,8 @@ public: return params().arith_nl_horner() && lp_settings().stats().m_nla_calls % params().arith_nl_horner_frequency() == 0; } - bool need_run_grobner() const { - return params().arith_nl_grobner() && lp_settings().stats().m_nla_calls % params().arith_nl_grobner_frequency() == 0; + bool need_run_grobner() const { + return params().arith_nl_grobner(); } void set_active_vars_weights(nex_creator&); diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index a7c7dd762..b3ad6173e 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -13,7 +13,6 @@ Author: #include "util/uint_set.h" #include "math/lp/nla_core.h" #include "math/lp/factorization_factory_imp.h" -#include "math/lp/nex.h" #include "math/grobner/pdd_solver.h" #include "math/dd/pdd_interval.h" #include "math/dd/pdd_eval.h" @@ -33,17 +32,33 @@ namespace nla { } void grobner::operator()() { + + if (lra.column_count() > 5000) + return; + if (m_quota == 0) m_quota = c().params().arith_nl_gr_q(); - if (m_quota == 1) + if (m_quota == 1) { + m_delay_base++; + m_delay = m_delay_base; + m_quota = c().params().arith_nl_gr_q(); + } + + if (m_delay > 0) { + --m_delay; return; + } + lp_settings().stats().m_grobner_calls++; find_nl_cluster(); configure(); m_solver.saturate(); + if (m_delay_base > 0) + --m_delay_base; + if (is_conflicting()) return; @@ -56,6 +71,9 @@ namespace nla { if (propagate_factorization()) return; + + if (false && propagate_linear_equations()) + return; } catch (...) { @@ -63,15 +81,14 @@ namespace nla { // DEBUG_CODE(for (auto e : m_solver.equations()) check_missing_propagation(*e);); - + ++m_delay_base; if (m_quota > 0) --m_quota; - IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << m_quota << "\n"); + IF_VERBOSE(3, verbose_stream() << "grobner miss, quota " << m_quota << "\n"); IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream())); - #if 0 // diagnostics: did we miss something vector eqs; @@ -82,18 +99,16 @@ namespace nla { } bool grobner::is_conflicting() { - unsigned conflicts = 0; - for (auto eq : m_solver.equations()) - if (is_conflicting(*eq) && ++conflicts >= m_solver.number_of_conflicts_to_report()) - break; - - if (conflicts > 0) - lp_settings().stats().m_grobner_conflicts++; - - TRACE("grobner", m_solver.display(tout)); - IF_VERBOSE(2, if (conflicts > 0) verbose_stream() << "grobner conflict\n"); - - return conflicts > 0; + bool is_conflict = false; + for (auto eq : m_solver.equations()) { + if (is_conflicting(*eq)) { + lp_settings().stats().m_grobner_conflicts++; + TRACE("grobner", m_solver.display(tout)); + IF_VERBOSE(3, verbose_stream() << "grobner conflict\n"); + return true; + } + } + return false; } bool grobner::propagate_bounds() { @@ -293,22 +308,29 @@ namespace nla { return out; } - bool grobner::is_conflicting(const dd::solver::equation& e) { - for (auto j : e.poly().free_vars()) - if (lra.column_is_free(j)) - return false; + bool grobner::equation_is_true(dd::solver::equation const& eq) { + if (any_of(eq.poly().free_vars(), [&](unsigned j) { return lra.column_is_free(j); })) + return true; + dd::pdd_eval eval; + eval.var2val() = [&](unsigned j){ return val(j); }; + return eval(eq.poly()) == 0; + } + + bool grobner::is_conflicting(const dd::solver::equation& e) { + if (equation_is_true(e)) + return false; auto& di = c().m_intervals.get_dep_intervals(); - dd::pdd_interval eval(di); - eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) { + dd::pdd_interval evali(di); + evali.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) { if (deps) c().m_intervals.set_var_interval(j, a); else c().m_intervals.set_var_interval(j, a); }; scoped_dep_interval i(di), i_wd(di); - eval.get_interval(e.poly(), i); + evali.get_interval(e.poly(), i); if (!di.separated_from_zero(i)) { TRACE("grobner", m_solver.display(tout << "not separated from 0 ", e) << "\n"; - eval.get_interval_distributed(e.poly(), i); + evali.get_interval_distributed(e.poly(), i); tout << "separated from 0: " << di.separated_from_zero(i) << "\n"; for (auto j : e.poly().free_vars()) { scoped_dep_interval a(di); @@ -316,14 +338,17 @@ namespace nla { c().m_intervals.display(tout << "j" << j << " ", a); tout << " "; } tout << "\n"); + + if (add_horner_conflict(e)) + return true; #if 0 if (add_nla_conflict(e)) return true; #endif return false; } - eval.get_interval(e.poly(), i_wd); + evali.get_interval(e.poly(), i_wd); std::function f = [this](const lp::explanation& e) { new_lemma lemma(m_core, "pdd"); lemma &= e; @@ -356,6 +381,57 @@ namespace nla { return false; } + bool grobner::propagate_linear_equations() { + unsigned changed = 0; + for (auto eq : m_solver.equations()) + if (propagate_linear_equations(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) + return true; + return changed > 0; + } + + bool grobner::propagate_linear_equations(dd::solver::equation const& e) { + if (equation_is_true(e)) + return false; + if (!e.poly().is_linear()) + return false; + + rational lc(1); + for (auto const& [coeff, vars] : e.poly()) + lc = lcm(denominator(coeff), lc); + auto q = e.poly(); + + +#if 0 + // TODO: instead add a row to lra solver, make sure that make_feasible + // gets invoked (for example, bail out of final check). + vector> coeffs; + while (!q.is_val()) { + coeffs.push_back({lc*q.hi().val(), q.var()}); + q = q.lo(); + } + + lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); + term_index = c().lra.map_term_index_to_column_index(term_index); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, -lc*q.val(), e.dep()); + +#else + + new_lemma lemma(m_core,"nla-linear"); + lp::explanation exp; + explain(e, exp); + lemma &= exp; + term t; + while (!q.is_val()) { + t.add_monomial(lc*q.hi().val(), q.var()); + q = q.lo(); + } + lemma |= ineq(t, llc::EQ, -lc*q.val()); +#endif + + return true; + } + + void grobner::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector & q) { if (c().active_var_set_contains(j)) return; @@ -581,6 +657,50 @@ namespace nla { return l_false == c().m_nra.check(eqs); } + bool grobner::add_horner_conflict(const dd::solver::equation& eq) { + nex_creator& nc = m_nex_creator; + nc.pop(0); + nex_creator::sum_factory sum(nc); + unsigned row_index = 0; + u_map var2nex; + for (auto v : eq.poly().free_vars()) + var2nex.insert(v, nc.mk_var(v)); + unsigned mx = 0; + for (auto v : eq.poly().free_vars()) + mx = std::max(v, mx); + nc.set_number_of_vars(mx + 1); + for (auto const& [coeff, vars] : eq.poly()) { + switch (vars.size()) { + case 0: + sum += nc.mk_scalar(coeff); + break; + case 1: + sum += nc.mk_mul(coeff, var2nex[vars[0]]); + break; + default: + nc.m_mk_mul.reset(); + nc.m_mk_mul *= coeff; + for (auto v : vars) + nc.m_mk_mul *= var2nex[v]; + sum += nc.m_mk_mul.mk(); + break; + } + } + nex* e = nc.simplify(sum.mk()); + if (e->get_degree() < 2 || !e->is_sum()) + return false; + + auto dep = eq.dep(); + cross_nested cn( + [this, dep](const nex* n) { bool r = c().m_intervals.check_nex(n, dep); TRACE("grobner", tout << "check " << r << " " << *n << "\n"); return r; }, + [this](unsigned j) { return c().var_is_fixed(j); }, + [this]() { return c().random(); }, nc); + cn.run(to_sum(e)); + bool ret = cn.done(); + TRACE("grobner", tout << "Horner " << ret << " " << eq.poly() << "\n"); + return ret; + } + bool grobner::add_nla_conflict(const dd::solver::equation& eq) { if (is_nla_conflict(eq)) { new_lemma lemma(m_core,"nla-conflict"); diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index 40fbb1ffc..dc5a188a5 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -24,28 +24,37 @@ namespace nla { lp::lar_solver& lra; indexed_uint_set m_rows; unsigned m_quota = 0; + unsigned m_delay_base = 0; + unsigned m_delay = 0; lp::lp_settings& lp_settings(); // solving bool is_conflicting(); - bool is_conflicting(const dd::solver::equation& eq); + bool is_conflicting(dd::solver::equation const& eq); bool propagate_bounds(); - bool propagate_bounds(const dd::solver::equation& eq); + bool propagate_bounds(dd::solver::equation const& eq); bool propagate_eqs(); - bool propagate_fixed(const dd::solver::equation& eq); + bool propagate_fixed(dd::solver::equation const& eq); bool propagate_factorization(); - bool propagate_factorization(const dd::solver::equation& eq); - - void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq); - void explain(const dd::solver::equation& eq, lp::explanation& exp); + bool propagate_factorization(dd::solver::equation const& eq); - bool is_nla_conflict(const dd::solver::equation& eq); - bool add_nla_conflict(const dd::solver::equation& eq); - void check_missing_propagation(const dd::solver::equation& eq); + + bool propagate_linear_equations(); + bool propagate_linear_equations(dd::solver::equation const& eq); + + void add_dependencies(new_lemma& lemma, dd::solver::equation const& eq); + void explain(dd::solver::equation const& eq, lp::explanation& exp); + + bool add_horner_conflict(dd::solver::equation const& eq); + bool is_nla_conflict(dd::solver::equation const& eq); + bool add_nla_conflict(dd::solver::equation const& eq); + void check_missing_propagation(dd::solver::equation const& eq); + + bool equation_is_true(dd::solver::equation const& eq); // setup void configure(); From 11ab583232c52d71a30311c3b7836beedb442835 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Oct 2023 10:34:31 -0700 Subject: [PATCH 263/428] fix #6956 --- src/smt/smt_consequences.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index cac8d40b1..fe2bd7149 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -274,6 +274,12 @@ namespace smt { expr_ref_vector& conseq, expr_ref_vector& unfixed) { + for (expr* a : assumptions0) + if (!m.is_bool(a)) { + std::string msg = std::string("assumption ") + mk_pp(a, m) + std::string(" is not Boolean"); + warning_msg(msg.c_str()); + throw default_exception(msg.c_str()); + } m_antecedents.reset(); m_antecedents.insert(true_literal.var(), index_set()); pop_to_base_lvl(); From 8c00181815369de87b45bbe015b5606ec7b9b5f2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Oct 2023 10:41:24 -0700 Subject: [PATCH 264/428] fix #6955 --- src/qe/qsat.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index fe4c4945c..6472a83c2 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -568,25 +568,21 @@ namespace qe { m_solver = nullptr; } - void assert_expr(expr *e) { - if (!m.is_true(e)) - m_solver->assert_expr(e); - } + void assert_expr(expr* e) { + if (!m.is_true(e)) + m_solver->assert_expr(e); + } void assert_blocking_fml(expr* e) { - if (m.is_true(e)) return; - if (m_last_assert) { - if (e == m_last_assert) { - verbose_stream() << "Asserting this expression twice in a row:\n " << m_last_assert << "\n"; - SASSERT(false); - std::exit(3); + if (m.is_true(e)) + return; + if (m_last_assert && e == m_last_assert && !m.is_false(e)) { + IF_VERBOSE(0, verbose_stream() << "Asserting this expression twice in a row:\n " << m_last_assert << "\n"); + UNREACHABLE(); } - - } - m_last_assert = e; - + m_last_assert = e; m_solver->assert_expr(e); } - + void get_core(expr_ref_vector& core) { core.reset(); m_solver->get_unsat_core(core); From 97058b0d5d8e9f3138479b5f77250f7477c29222 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Oct 2023 16:08:35 -0700 Subject: [PATCH 265/428] allow for propagations the trigger make-feasible check --- src/math/lp/nla_core.cpp | 12 +++++------- src/math/lp/nla_core.h | 2 ++ src/math/lp/nla_grobner.cpp | 6 ++---- src/math/lp/nla_solver.h | 1 + src/smt/theory_lra.cpp | 23 +++++++++++++---------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 50cd31661..978348816 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -819,6 +819,7 @@ void core::clear() { m_fixed_equalities.clear(); m_equalities.clear(); m_conflicts = 0; + m_check_feasible = false; } void core::init_search() { @@ -1192,11 +1193,7 @@ void core::negate_relation(new_lemma& lemma, unsigned j, const rational& a) { } bool core::conflict_found() const { - for (const auto & l : m_lemmas) { - if (l.is_conflict()) - return true; - } - return false; + return any_of(m_lemmas, [&](const auto& l) { return l.is_conflict(); }); } bool core::done() const { @@ -1555,7 +1552,7 @@ lbool core::check() { bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty(); }; + auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1573,6 +1570,7 @@ lbool core::check() { {1, check2}, {1, check3} }; check_weighted(3, checks); + if (!m_lemmas.empty() || !m_literals.empty()) return l_false; } @@ -1610,7 +1608,7 @@ lbool core::check() { lp_settings().stats().m_nra_calls++; } - if (ret == l_undef && !m_lemmas.empty() && m_reslim.inc()) + if (ret == l_undef && !no_effect() && m_reslim.inc()) ret = l_false; lp_settings().stats().m_nla_lemmas += m_lemmas.size(); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index f99f640bf..bd2b19a9a 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -87,6 +87,7 @@ class core { intervals m_intervals; monomial_bounds m_monomial_bounds; unsigned m_conflicts; + bool m_check_feasible = false; horner m_horner; grobner m_grobner; emonics m_emons; @@ -424,6 +425,7 @@ public: vector const& literals() const { return m_literals; } vector const& equalities() const { return m_equalities; } vector const& fixed_equalities() const { return m_fixed_equalities; } + bool check_feasible() const { return m_check_feasible; } void add_fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e) { m_fixed_equalities.push_back({v, k, e}); } void add_equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e) { m_equalities.push_back({i, j, e}); } diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index b3ad6173e..be7c4d519 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -401,9 +401,7 @@ namespace nla { auto q = e.poly(); -#if 0 - // TODO: instead add a row to lra solver, make sure that make_feasible - // gets invoked (for example, bail out of final check). +#if 1 vector> coeffs; while (!q.is_val()) { coeffs.push_back({lc*q.hi().val(), q.var()}); @@ -413,7 +411,7 @@ namespace nla { lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); term_index = c().lra.map_term_index_to_column_index(term_index); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, -lc*q.val(), e.dep()); - + c().m_check_feasible = true; #else new_lemma lemma(m_core,"nla-linear"); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index c508e68d0..10cc8e006 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -51,5 +51,6 @@ namespace nla { vector const& literals() const; vector const& fixed_equalities() const; vector const& equalities() const; + bool check_feasible() const { return m_core->check_feasible(); } }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index b98cc002f..0fabe7ecd 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2162,15 +2162,6 @@ public: } } - void add_equalities() { - if (!propagate_eqs()) - return; - for (auto const& [v,k,e] : m_nla->fixed_equalities()) - add_equality(v, k, e); - for (auto const& [i,j,e] : m_nla->equalities()) - add_eq(i,j,e,false); - } - void add_equality(lpvar j, rational const& k, lp::explanation const& exp) { TRACE("arith", tout << "equality " << j << " " << k << "\n"); theory_var v; @@ -2188,11 +2179,23 @@ public: } void add_lemmas() { + if (m_nla->check_feasible()) { + auto is_sat = make_feasible(); + if (l_false == is_sat) { + get_infeasibility_explanation_and_set_conflict(); + return; + } + } for (const nla::ineq& i : m_nla->literals()) assume_literal(i); for (const nla::lemma & l : m_nla->lemmas()) false_case_of_check_nla(l); - add_equalities(); + if (!propagate_eqs()) + return; + for (auto const& [v, k, e] : m_nla->fixed_equalities()) + add_equality(v, k, e); + for (auto const& [i, j, e] : m_nla->equalities()) + add_eq(i, j, e, false); } bool should_propagate() const { From 53ce18ef347d8367634777c882f4d325bb489aee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2023 19:57:06 -0700 Subject: [PATCH 266/428] update backoff for bounded_nla --- src/math/lp/nla_core.cpp | 26 ++++++++++++++------------ src/math/lp/nla_core.h | 4 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 978348816..66409231c 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -40,7 +40,7 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_use_nra_model(false), m_nra(s, m_nra_lim, *this) { - m_nlsat_delay = lp_settings().nlsat_delay(); + // m_nlsat_delay_bound = lp_settings().nlsat_delay(); lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { for (lpvar j : columns_with_changed_bounds) { if (is_monic_var(j)) @@ -1550,7 +1550,7 @@ lbool core::check() { bool run_grobner = need_run_grobner(); bool run_horner = need_run_horner(); bool run_bounded_nlsat = should_run_bounded_nlsat(); - bool run_bounds = params().arith_nl_branching(); + bool run_bounds = params().arith_nl_branching(); auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; }; @@ -1574,6 +1574,9 @@ lbool core::check() { if (!m_lemmas.empty() || !m_literals.empty()) return l_false; } + + if (no_effect() && run_bounded_nlsat) + ret = bounded_nlsat(); if (no_effect()) m_basics.basic_lemma(true); @@ -1584,8 +1587,6 @@ lbool core::check() { if (no_effect()) m_divisions.check(); - if (no_effect() && run_bounded_nlsat) - ret = bounded_nlsat(); if (no_effect() && ret == l_undef) { std::function check1 = [&]() { m_order.order_lemma(); }; @@ -1623,9 +1624,9 @@ lbool core::check() { bool core::should_run_bounded_nlsat() { if (!params().arith_nl_nra()) return false; - if (m_nlsat_delay > m_nlsat_fails) - ++m_nlsat_fails; - return m_nlsat_delay <= m_nlsat_fails; + if (m_nlsat_delay > 0) + --m_nlsat_delay; + return m_nlsat_delay < 5; } lbool core::bounded_nlsat() { @@ -1643,11 +1644,12 @@ lbool core::bounded_nlsat() { m_nra.updt_params(p); lp_settings().stats().m_nra_calls++; if (ret == l_undef) - ++m_nlsat_delay; - else { - m_nlsat_fails = 0; - m_nlsat_delay /= 2; - } + ++m_nlsat_delay_bound; + else if (m_nlsat_delay_bound > 0) + m_nlsat_delay_bound /= 2; + + m_nlsat_delay = m_nlsat_delay_bound; + if (ret == l_true) clear(); return ret; diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index bd2b19a9a..4bb7e11ee 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -60,8 +60,8 @@ class core { friend class nra::solver; friend class divisions; - unsigned m_nlsat_delay = 50; - unsigned m_nlsat_fails = 0; + unsigned m_nlsat_delay = 0; + unsigned m_nlsat_delay_bound = 0; bool should_run_bounded_nlsat(); lbool bounded_nlsat(); From c9d298e57fbf1ca06a1dfb41e307a40fc80fefe7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2023 19:57:41 -0700 Subject: [PATCH 267/428] enable propagate-linear-equations and extend to monomials --- src/math/lp/nla_grobner.cpp | 68 ++++++++++++++++++++----------------- src/math/lp/nla_grobner.h | 1 + 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index be7c4d519..f3367a4ea 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -72,7 +72,7 @@ namespace nla { if (propagate_factorization()) return; - if (false && propagate_linear_equations()) + if (propagate_linear_equations()) return; } catch (...) { @@ -81,6 +81,8 @@ namespace nla { // DEBUG_CODE(for (auto e : m_solver.equations()) check_missing_propagation(*e);); + // for (auto e : m_solver.equations()) check_missing_propagation(*e); + ++m_delay_base; if (m_quota > 0) --m_quota; @@ -383,6 +385,9 @@ namespace nla { bool grobner::propagate_linear_equations() { unsigned changed = 0; + m_mon2var.clear(); + for (auto const& m : c().emons()) + m_mon2var[m.vars()] = m.var(); for (auto eq : m_solver.equations()) if (propagate_linear_equations(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) return true; @@ -392,40 +397,40 @@ namespace nla { bool grobner::propagate_linear_equations(dd::solver::equation const& e) { if (equation_is_true(e)) return false; - if (!e.poly().is_linear()) + rational value(0); + for (auto const& [coeff, vars] : e.poly()) { + if (vars.empty()) + value += coeff; + else if (vars.size() == 1) + value += coeff*val(vars[0]); + else if (m_mon2var.find(vars) == m_mon2var.end()) + return false; + else + value += coeff*val(m_mon2var.find(vars)->second); + } + if (value == 0) return false; rational lc(1); - for (auto const& [coeff, vars] : e.poly()) + for (auto const& [coeff, vars] : e.poly()) lc = lcm(denominator(coeff), lc); - auto q = e.poly(); - -#if 1 - vector> coeffs; - while (!q.is_val()) { - coeffs.push_back({lc*q.hi().val(), q.var()}); - q = q.lo(); - } + vector> coeffs; + rational offset(0); + + for (auto const& [coeff, vars] : e.poly()) { + if (vars.size() == 0) + offset -= lc*coeff; + else if (vars.size() == 1) + coeffs.push_back({lc*coeff, vars[0]}); + else + coeffs.push_back({lc*coeff, m_mon2var.find(vars)->second}); + } lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); term_index = c().lra.map_term_index_to_column_index(term_index); - c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, -lc*q.val(), e.dep()); + c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, offset, e.dep()); c().m_check_feasible = true; -#else - - new_lemma lemma(m_core,"nla-linear"); - lp::explanation exp; - explain(e, exp); - lemma &= exp; - term t; - while (!q.is_val()) { - t.add_monomial(lc*q.hi().val(), q.var()); - q = q.lo(); - } - lemma |= ineq(t, llc::EQ, -lc*q.val()); -#endif - return true; } @@ -690,12 +695,11 @@ namespace nla { auto dep = eq.dep(); cross_nested cn( - [this, dep](const nex* n) { bool r = c().m_intervals.check_nex(n, dep); TRACE("grobner", tout << "check " << r << " " << *n << "\n"); return r; }, + [this, dep](const nex* n) { return c().m_intervals.check_nex(n, dep); }, [this](unsigned j) { return c().var_is_fixed(j); }, [this]() { return c().random(); }, nc); cn.run(to_sum(e)); bool ret = cn.done(); - TRACE("grobner", tout << "Horner " << ret << " " << eq.poly() << "\n"); return ret; } @@ -714,10 +718,12 @@ namespace nla { void grobner::check_missing_propagation(const dd::solver::equation& e) { bool is_confl = is_nla_conflict(e); CTRACE("grobner", is_confl, m_solver.display(tout << "missed conflict ", e);); - if (is_confl) + if (is_confl) { + IF_VERBOSE(2, verbose_stream() << "missed conflict\n"); return; - lbool r = c().m_nra.check_tight(e.poly()); - CTRACE("grobner", r == l_false, m_solver.display(tout << "tight equality ", e);); + } + //lbool r = c().m_nra.check_tight(e.poly()); + //CTRACE("grobner", r == l_false, m_solver.display(tout << "tight equality ", e);); } diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index dc5a188a5..cbf0cf109 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -26,6 +26,7 @@ namespace nla { unsigned m_quota = 0; unsigned m_delay_base = 0; unsigned m_delay = 0; + std::unordered_map m_mon2var; lp::lp_settings& lp_settings(); From 4e21e126a8bef9e1035d67430593700d61a2683b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2023 19:58:07 -0700 Subject: [PATCH 268/428] update add_lemmas to use check-feasible --- src/sat/smt/arith_solver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index ebfe93dfe..cbccf7fa8 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1484,6 +1484,13 @@ namespace arith { } void solver::add_lemmas() { + if (m_nla->check_feasible()) { + auto is_sat = make_feasible(); + if (l_false == is_sat) { + get_infeasibility_explanation_and_set_conflict(); + return; + } + } for (auto const& ineq : m_nla->literals()) { auto lit = mk_ineq_literal(ineq); ctx.mark_relevant(lit); From 8fac89cdcc5291eb8234ba49fcc758499808951f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2023 19:58:39 -0700 Subject: [PATCH 269/428] enable more simplification in case inequality triggers a change. --- src/ast/simplifiers/dependent_expr_state.h | 7 +++++- src/ast/simplifiers/then_simplifier.h | 27 ++++++++++++++++++++-- src/solver/simplifier_solver.cpp | 16 ++++++++++--- src/solver/solver_preprocess.cpp | 13 ++++++++++- src/tactic/dependent_expr_state_tactic.h | 7 ++++++ 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h index b4fe4e9d4..165cbbce0 100644 --- a/src/ast/simplifiers/dependent_expr_state.h +++ b/src/ast/simplifiers/dependent_expr_state.h @@ -74,6 +74,8 @@ public: virtual bool inconsistent() = 0; virtual model_reconstruction_trail& model_trail() = 0; virtual void flatten_suffix() {} + virtual bool updated() = 0; + virtual void reset_updated() = 0; trail_stack m_trail; void push() { @@ -109,6 +111,9 @@ public: virtual void add(dependent_expr const& j) { throw default_exception("unexpected addition"); } virtual bool inconsistent() { return false; } virtual model_reconstruction_trail& model_trail() { throw default_exception("unexpected access to model reconstruction"); } + virtual bool updated() { return false; } + virtual void reset_updated() {} + }; inline std::ostream& operator<<(std::ostream& out, dependent_expr_state& st) { @@ -147,7 +152,7 @@ protected: index_set indices() { return index_set(*this); } proof* mp(proof* a, proof* b) { return (a && b) ? m.mk_modus_ponens(a, b) : nullptr; } - + proof* tr(proof* a, proof* b) { return m.mk_transitivity(a, b); } public: dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {} virtual ~dependent_expr_simplifier() {} diff --git a/src/ast/simplifiers/then_simplifier.h b/src/ast/simplifiers/then_simplifier.h index 6ee8b9412..e5a7ca104 100644 --- a/src/ast/simplifiers/then_simplifier.h +++ b/src/ast/simplifiers/then_simplifier.h @@ -51,6 +51,10 @@ class then_simplifier : public dependent_expr_simplifier { } }; +protected: + + bool m_bail_on_no_change = false; + public: then_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): @@ -72,9 +76,17 @@ public: break; s->reset_statistics(); collect_stats _cs(*s); - s->reduce(); - m_fmls.flatten_suffix(); + m_fmls.reset_updated(); + try { + s->reduce(); + m_fmls.flatten_suffix(); + } + catch (rewriter_exception &) { + break; + } TRACE("simplifier", tout << s->name() << "\n" << m_fmls); + if (m_bail_on_no_change && !m_fmls.updated()) + break; } } @@ -108,3 +120,14 @@ public: s->pop(n); } }; + +class if_change_simplifier : public then_simplifier { +public: + if_change_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + then_simplifier(m, p, fmls) { + m_bail_on_no_change = true; + } + + char const* name() const override { return "if-change-then"; } + +}; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index bac75f1e5..712c42f45 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -36,6 +36,7 @@ class simplifier_solver : public solver { struct dep_expr_state : public dependent_expr_state { simplifier_solver& s; model_reconstruction_trail m_reconstruction_trail; + bool m_updated = false; dep_expr_state(simplifier_solver& s) :dependent_expr_state(s.m), s(s), m_reconstruction_trail(s.m, m_trail) {} ~dep_expr_state() override {} virtual unsigned qtail() const override { return s.m_fmls.size(); } @@ -43,10 +44,13 @@ class simplifier_solver : public solver { void update(unsigned i, dependent_expr const& j) override { SASSERT(j.fml()); check_false(j.fml()); - s.m_fmls[i] = j; + s.m_fmls[i] = j; + m_updated = true; } - void add(dependent_expr const& j) override { check_false(j.fml()); s.m_fmls.push_back(j); } + void add(dependent_expr const& j) override { m_updated = true; check_false(j.fml()); s.m_fmls.push_back(j); } bool inconsistent() override { return s.m_inconsistent; } + bool updated() override { return m_updated; } + void reset_updated() override { m_updated = false; } model_reconstruction_trail& model_trail() override { return m_reconstruction_trail; } std::ostream& display(std::ostream& out) const override { unsigned i = 0; @@ -77,7 +81,7 @@ class simplifier_solver : public solver { expr_mark seen; unsigned j = qhead(); for (unsigned i = qhead(); i < qtail(); ++i) { - expr* f = s.m_fmls[i].fml(); + expr* f = s.m_fmls[i].fml(), *g = nullptr; if (seen.is_marked(f)) continue; seen.mark(f, true); @@ -89,6 +93,12 @@ class simplifier_solver : public solver { add(dependent_expr(s.m, arg, nullptr, d)); continue; } + if (s.m.is_not(f, g) && s.m.is_or(g)) { + auto* d = s.m_fmls[i].dep(); + for (expr* arg : *to_app(g)) + add(dependent_expr(s.m, mk_not(s.m, arg), nullptr, d)); + continue; + } if (i != j) s.m_fmls[j] = s.m_fmls[i]; ++j; diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 9cac4b835..1ac7bb8b4 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -47,6 +47,17 @@ Notes: void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st) { + auto mk_bound_simplifier = [&]() { + auto* s1 = alloc(bound_simplifier, m, p, st); + auto* s2 = alloc(then_simplifier, m, p, st); + s2->add_simplifier(alloc(rewriter_simplifier, m, p, st)); + s2->add_simplifier(alloc(propagate_values, m, p, st)); + s2->add_simplifier(alloc(euf::solve_eqs, m, st)); + auto* r = alloc(if_change_simplifier, m, p, st); + r->add_simplifier(s1); + r->add_simplifier(s2); + return r; + }; smt_params smtp(p); s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); if (smtp.m_propagate_values) s.add_simplifier(alloc(propagate_values, m, p, st)); @@ -60,7 +71,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, de if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); - if (smtp.m_bound_simplifier) s.add_simplifier(alloc(bound_simplifier, m, p, st)); + if (smtp.m_bound_simplifier) s.add_simplifier(mk_bound_simplifier()); if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); diff --git a/src/tactic/dependent_expr_state_tactic.h b/src/tactic/dependent_expr_state_tactic.h index 347e147fb..b5ea4d6c1 100644 --- a/src/tactic/dependent_expr_state_tactic.h +++ b/src/tactic/dependent_expr_state_tactic.h @@ -33,6 +33,7 @@ private: expr_ref_vector m_frozen; scoped_ptr m_simp; scoped_ptr m_model_trail; + bool m_updated = false; void init() { if (!m_simp) { @@ -75,6 +76,7 @@ public: void update(unsigned i, dependent_expr const& j) override { if (inconsistent()) return; + m_updated = true; auto [f, p, d] = j(); m_goal->update(i, f, p, d); } @@ -82,6 +84,7 @@ public: void add(dependent_expr const& j) override { if (inconsistent()) return; + m_updated = true; auto [f, p, d] = j(); m_goal->assert_expr(f, p, d); } @@ -96,6 +99,10 @@ public: char const* name() const override { return m_simp ? m_simp->name() : "null"; } + bool updated() override { return m_updated; } + + void reset_updated() override { m_updated = false; } + void updt_params(params_ref const& p) override { m_params.append(p); init(); From aa703160cef2e4b78326961be16e2a6b7e34bdd9 Mon Sep 17 00:00:00 2001 From: itehax <124720521+itehax@users.noreply.github.com> Date: Tue, 24 Oct 2023 00:28:02 +0200 Subject: [PATCH 270/428] Update README.md (#6960) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39b0a3e78..230b3eeeb 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,7 @@ to Z3's C API. For more information, see [MachineArithmetic/README.md](https://g * [.NET API](https://z3prover.github.io/api/html/namespace_microsoft_1_1_z3.html) * [Java API](https://z3prover.github.io/api/html/namespacecom_1_1microsoft_1_1z3.html) * [Python API](https://z3prover.github.io/api/html/namespacez3py.html) (also available in [pydoc format](https://z3prover.github.io/api/html/z3.html)) +* [Rust](https://github.com/prove-rs/z3.rs) * C * OCaml * [Julia](https://github.com/ahumenberger/Z3.jl) From 8b040490697b79d93756aad69eea7291f2046983 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:29:42 -0700 Subject: [PATCH 271/428] Bump @babel/traverse from 7.19.4 to 7.23.2 in /src/api/js (#6954) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.19.4 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/api/js/package-lock.json | 164 ++++++++++++++++++++++++++++------- 1 file changed, 133 insertions(+), 31 deletions(-) diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index a46cae951..5f1d63bd0 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -1,7 +1,8 @@ { "name": "z3-solver", - "requires": true, + "version": "0.1.0", "lockfileVersion": 1, + "requires": true, "dependencies": { "@ampproject/remapping": { "version": "2.2.0", @@ -109,25 +110,6 @@ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, "@babel/helper-module-imports": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", @@ -361,21 +343,141 @@ } }, "@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "requires": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + } + }, + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/types": { From f07c46a396b743d6cb7573a143a1e65685ed0617 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:24:29 +0100 Subject: [PATCH 272/428] Bump actions/setup-node from 3 to 4 (#6961) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/wasm-release.yml | 2 +- .github/workflows/wasm.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index defcd8fea..805cd3517 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4 - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 9e321947e..4e4f7aa4d 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4 - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" From d5fe4b0d78ef109582e9562adb9a67d33a8871ea Mon Sep 17 00:00:00 2001 From: rsetaluri Date: Tue, 24 Oct 2023 13:19:44 -0700 Subject: [PATCH 273/428] Update script to use importlib_resources (#6949) To avoid a deprecation warning, this change updates scripts/update_api.py to use 'importlib_resources' instead of 'pkg_resources'. See https://setuptools.pypa.io/en/latest/pkg_resources.html and https://importlib-resources.readthedocs.io/en/latest/migration.html for more information. --- scripts/update_api.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index c68597ea2..78bfb6b7a 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1827,17 +1827,25 @@ def write_core_py_preamble(core_py): core_py.write( """ # Automatically generated file +import atexit import sys, os +import contextlib import ctypes -import pkg_resources +import importlib_resources from .z3types import * from .z3consts import * +_file_manager = contextlib.ExitStack() +atexit.register(_file_manager.close) _ext = 'dll' if sys.platform in ('win32', 'cygwin') else 'dylib' if sys.platform == 'darwin' else 'so' _lib = None +_z3_lib_resource = importlib_resources.files('z3', 'lib') +_z3_lib_resource_path = _file_manager.enter_context( + importlib_resources.as_file(_z3_lib_resource) +) _default_dirs = ['.', os.path.dirname(os.path.abspath(__file__)), - pkg_resources.resource_filename('z3', 'lib'), + _z3_lib_resource_path, os.path.join(sys.prefix, 'lib'), None] _all_dirs = [] From 0859be5649222241b385f53f1592d3d2000537f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 09:06:57 -0700 Subject: [PATCH 274/428] #6953 Signed-off-by: Nikolaj Bjorner --- .../rewriter/bit_blaster/bit_blaster_rewriter.cpp | 12 +++++++++++- src/sat/smt/arith_solver.cpp | 7 ------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index 801ebf4b5..68e3825c9 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -21,6 +21,7 @@ Notes: #include "ast/rewriter/bit_blaster/bit_blaster_tpl_def.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/bool_rewriter.h" +#include "ast/rewriter/th_rewriter.h" #include "util/ref_util.h" #include "ast/ast_smt2_pp.h" @@ -549,10 +550,19 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); case OP_INT2BV: case OP_BV2INT: return BR_FAILED; - default: + default: TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); + { + expr_ref r(m().mk_app(f, num, args), m()); + result = r; + th_rewriter rw(m()); + rw(result); + if (!is_app(result) || to_app(result)->get_decl() != f) + return BR_REWRITE_FULL; + } throw_unsupported(f); + } } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index cbccf7fa8..196895216 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -996,9 +996,6 @@ namespace arith { TRACE("arith", ctx.display(tout);); - if (!check_delayed_eqs()) - return sat::check_result::CR_CONTINUE; - switch (check_lia()) { case l_true: break; @@ -1022,10 +1019,6 @@ namespace arith { break; } - if (delayed_assume_eqs()) { - ++m_stats.m_assume_eqs; - return sat::check_result::CR_CONTINUE; - } if (assume_eqs()) { ++m_stats.m_assume_eqs; return sat::check_result::CR_CONTINUE; From 7b490543ca72626e5b26dc164d91cabc2368e61c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 09:59:54 -0700 Subject: [PATCH 275/428] add missing simplification; handle nit #6952 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 9 +++++++++ src/math/polynomial/rpolynomial.cpp | 24 +++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 90ba5bb3a..f8c4c9811 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1245,6 +1245,15 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu result = str().mk_substr(a1, m_autil.mk_add(b1, b), m_autil.mk_sub(c1, b)); return BR_REWRITE3; } + rational r1, r2; + if (str().is_extract(a, a1, b1, c1) && + m_autil.is_numeral(b1, r1) && r1.is_unsigned() && + m_autil.is_numeral(c1, r2) && r2.is_unsigned() && + constantPos && constantLen && + r1 == 0 && r2 >= pos + len) { + result = str().mk_substr(a1, b, c); + return BR_REWRITE1; + } if (str().is_extract(a, a1, b1, c1) && is_prefix(a1, b1, c1) && is_prefix(a, b, c)) { diff --git a/src/math/polynomial/rpolynomial.cpp b/src/math/polynomial/rpolynomial.cpp index 0afd22a75..dd1b82994 100644 --- a/src/math/polynomial/rpolynomial.cpp +++ b/src/math/polynomial/rpolynomial.cpp @@ -658,9 +658,7 @@ namespace rpolynomial { void display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) { var x = p->max_var(); bool first = true; - unsigned i = p->size(); - while (i > 0) { - --i; + for (unsigned i = p->size(); i-- > 0; ) { poly_or_num * pn = p->arg(i); if (pn == nullptr) continue; @@ -697,23 +695,19 @@ namespace rpolynomial { display(out, to_poly(pn), proc, use_star); } else { - bool add_paren = false; - if (i > 0) - add_paren = !is_monomial(to_poly(pn)); + bool add_paren = !is_monomial(to_poly(pn)); if (add_paren) out << "("; display(out, to_poly(pn), proc, use_star); if (add_paren) out << ")"; - if (i > 0) { - if (use_star) - out << "*"; - else - out << " "; - proc(out, x); - if (i > 1) - out << "^" << i; - } + if (use_star) + out << "*"; + else + out << " "; + proc(out, x); + if (i > 1) + out << "^" << i; } } } From 236afeb8cb0257c916b3d2b0291bb9f3807665bb Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Thu, 26 Oct 2023 00:07:03 +0700 Subject: [PATCH 276/428] docs: More intra-doc linking, bit of formatting. (#6963) --- src/api/z3_api.h | 4 +- src/api/z3_fpa.h | 256 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 2 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 9a93611b4..894bd60dc 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5594,14 +5594,14 @@ extern "C" { void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a); /** - \brief Increment the reference counter of the given Z3_func_interp object. + \brief Increment the reference counter of the given \c Z3_func_interp object. def_API('Z3_func_interp_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f); /** - \brief Decrement the reference counter of the given Z3_func_interp object. + \brief Decrement the reference counter of the given \c Z3_func_interp object. def_API('Z3_func_interp_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index 4ab0d9177..9c4b22153 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -32,6 +32,12 @@ extern "C" { \param c logical context + \sa Z3_mk_fpa_round_nearest_ties_to_away or Z3_mk_fpa_rna + \sa Z3_mk_fpa_round_nearest_ties_to_even or Z3_mk_fpa_rne + \sa Z3_mk_fpa_round_toward_negative or Z3_mk_fpa_rtn + \sa Z3_mk_fpa_round_toward_positive or Z3_mk_fpa_rtp + \sa Z3_mk_fpa_round_toward_zero or Z3_mk_fpa_rtz + def_API('Z3_mk_fpa_rounding_mode_sort', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c); @@ -39,8 +45,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. + This is the same as #Z3_mk_fpa_rne. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_round_nearest_ties_to_away + \sa Z3_mk_fpa_round_toward_negative + \sa Z3_mk_fpa_round_toward_positive + \sa Z3_mk_fpa_round_toward_zero + def_API('Z3_mk_fpa_round_nearest_ties_to_even', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c); @@ -48,8 +62,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. + This is the same as #Z3_mk_fpa_round_nearest_ties_to_even. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_rna + \sa Z3_mk_fpa_rtn + \sa Z3_mk_fpa_rtp + \sa Z3_mk_fpa_rtz + def_API('Z3_mk_fpa_rne', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c); @@ -57,8 +79,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. + This is the same as #Z3_mk_fpa_rna. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_round_nearest_ties_to_even + \sa Z3_mk_fpa_round_toward_negative + \sa Z3_mk_fpa_round_toward_positive + \sa Z3_mk_fpa_round_toward_zero + def_API('Z3_mk_fpa_round_nearest_ties_to_away', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c); @@ -66,8 +96,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. + This is the same as #Z3_mk_fpa_round_nearest_ties_to_away. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_rne + \sa Z3_mk_fpa_rtn + \sa Z3_mk_fpa_rtp + \sa Z3_mk_fpa_rtz + def_API('Z3_mk_fpa_rna', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c); @@ -75,8 +113,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. + This is the same as #Z3_mk_fpa_rtp. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_round_nearest_ties_to_away + \sa Z3_mk_fpa_round_nearest_ties_to_even + \sa Z3_mk_fpa_round_toward_negative + \sa Z3_mk_fpa_round_toward_zero + def_API('Z3_mk_fpa_round_toward_positive', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c); @@ -84,8 +130,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. + This is the same as #Z3_mk_fpa_round_toward_positive. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_rna + \sa Z3_mk_fpa_rne + \sa Z3_mk_fpa_rtn + \sa Z3_mk_fpa_rtz + def_API('Z3_mk_fpa_rtp', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c); @@ -93,8 +147,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. + This is the same as #Z3_mk_fpa_rtn. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_round_nearest_ties_to_away + \sa Z3_mk_fpa_round_nearest_ties_to_even + \sa Z3_mk_fpa_round_toward_positive + \sa Z3_mk_fpa_round_toward_zero + def_API('Z3_mk_fpa_round_toward_negative', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c); @@ -102,8 +164,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. + This is the same as #Z3_mk_fpa_round_toward_negative. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_rna + \sa Z3_mk_fpa_rne + \sa Z3_mk_fpa_rtp + \sa Z3_mk_fpa_rtz + def_API('Z3_mk_fpa_rtn', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c); @@ -111,8 +181,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. + This is the same as #Z3_mk_fpa_rtz. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_round_nearest_ties_to_away + \sa Z3_mk_fpa_round_nearest_ties_to_even + \sa Z3_mk_fpa_round_toward_negative + \sa Z3_mk_fpa_round_toward_positive + def_API('Z3_mk_fpa_round_toward_zero', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c); @@ -120,8 +198,16 @@ extern "C" { /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. + This is the same as #Z3_mk_fpa_round_toward_zero. + \param c logical context + \sa Z3_mk_fpa_rounding_mode_sort + \sa Z3_mk_fpa_rna + \sa Z3_mk_fpa_rne + \sa Z3_mk_fpa_rtn + \sa Z3_mk_fpa_rtp + def_API('Z3_mk_fpa_rtz', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c); @@ -135,6 +221,11 @@ extern "C" { \remark \c ebits must be larger than 1 and \c sbits must be larger than 2. + \sa Z3_mk_fpa_sort_half or Z3_mk_fpa_sort_16 + \sa Z3_mk_fpa_sort_single or Z3_mk_fpa_sort_32 + \sa Z3_mk_fpa_sort_double or Z3_mk_fpa_sort_64 + \sa Z3_mk_fpa_sort_quadruple or Z3_mk_fpa_sort_128 + def_API('Z3_mk_fpa_sort', SORT, (_in(CONTEXT), _in(UINT), _in(UINT))) */ Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits); @@ -142,8 +233,15 @@ extern "C" { /** \brief Create the half-precision (16-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_16. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_single + \sa Z3_mk_fpa_sort_double + \sa Z3_mk_fpa_sort_quadruple + def_API('Z3_mk_fpa_sort_half', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c); @@ -151,8 +249,15 @@ extern "C" { /** \brief Create the half-precision (16-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_half. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_32 + \sa Z3_mk_fpa_sort_64 + \sa Z3_mk_fpa_sort_128 + def_API('Z3_mk_fpa_sort_16', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c); @@ -160,8 +265,15 @@ extern "C" { /** \brief Create the single-precision (32-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_32. + \param c logical context. + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_half + \sa Z3_mk_fpa_sort_double + \sa Z3_mk_fpa_sort_quadruple + def_API('Z3_mk_fpa_sort_single', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c); @@ -169,8 +281,15 @@ extern "C" { /** \brief Create the single-precision (32-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_single. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_16 + \sa Z3_mk_fpa_sort_64 + \sa Z3_mk_fpa_sort_128 + def_API('Z3_mk_fpa_sort_32', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c); @@ -178,8 +297,15 @@ extern "C" { /** \brief Create the double-precision (64-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_64. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_half + \sa Z3_mk_fpa_sort_single + \sa Z3_mk_fpa_sort_quadruple + def_API('Z3_mk_fpa_sort_double', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c); @@ -187,8 +313,15 @@ extern "C" { /** \brief Create the double-precision (64-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_double. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_16 + \sa Z3_mk_fpa_sort_32 + \sa Z3_mk_fpa_sort_128 + def_API('Z3_mk_fpa_sort_64', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c); @@ -196,8 +329,15 @@ extern "C" { /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_128. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_half + \sa Z3_mk_fpa_sort_single + \sa Z3_mk_fpa_sort_double + def_API('Z3_mk_fpa_sort_quadruple', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c); @@ -205,8 +345,15 @@ extern "C" { /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. + This is the same as #Z3_mk_fpa_sort_quadruple. + \param c logical context + \sa Z3_mk_fpa_sort + \sa Z3_mk_fpa_sort_16 + \sa Z3_mk_fpa_sort_32 + \sa Z3_mk_fpa_sort_64 + def_API('Z3_mk_fpa_sort_128', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c); @@ -218,6 +365,7 @@ extern "C" { \param s target sort \sa Z3_mk_fpa_inf + \sa Z3_mk_fpa_is_nan \sa Z3_mk_fpa_zero def_API('Z3_mk_fpa_nan', AST, (_in(CONTEXT),_in(SORT))) @@ -233,6 +381,7 @@ extern "C" { When \c negative is \c true, -oo will be generated instead of +oo. + \sa Z3_mk_fpa_is_infinite \sa Z3_mk_fpa_nan \sa Z3_mk_fpa_zero @@ -250,6 +399,7 @@ extern "C" { When \c negative is \c true, -zero will be generated instead of +zero. \sa Z3_mk_fpa_inf + \sa Z3_mk_fpa_is_zero \sa Z3_mk_fpa_nan def_API('Z3_mk_fpa_zero', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) @@ -397,6 +547,10 @@ extern "C" { \param c logical context \param t term of FloatingPoint sort + \sa Z3_mk_fpa_is_negative + \sa Z3_mk_fpa_is_positive + \sa Z3_mk_fpa_neg + def_API('Z3_mk_fpa_abs', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t); @@ -407,6 +561,10 @@ extern "C" { \param c logical context \param t term of FloatingPoint sort + \sa Z3_mk_fpa_abs + \sa Z3_mk_fpa_is_negative + \sa Z3_mk_fpa_is_positive + def_API('Z3_mk_fpa_neg', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t); @@ -533,6 +691,8 @@ extern "C" { \c t1, \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_max + def_API('Z3_mk_fpa_min', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -546,6 +706,8 @@ extern "C" { \c t1, \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_min + def_API('Z3_mk_fpa_max', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -559,6 +721,11 @@ extern "C" { \c t1 and \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_eq + \sa Z3_mk_fpa_geq + \sa Z3_mk_fpa_gt + \sa Z3_mk_fpa_lt + def_API('Z3_mk_fpa_leq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -572,6 +739,11 @@ extern "C" { \c t1 and \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_eq + \sa Z3_mk_fpa_geq + \sa Z3_mk_fpa_gt + \sa Z3_mk_fpa_leq + def_API('Z3_mk_fpa_lt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -585,6 +757,11 @@ extern "C" { \c t1 and \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_eq + \sa Z3_mk_fpa_gt + \sa Z3_mk_fpa_leq + \sa Z3_mk_fpa_lt + def_API('Z3_mk_fpa_geq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -598,6 +775,11 @@ extern "C" { \c t1 and \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_eq + \sa Z3_mk_fpa_geq + \sa Z3_mk_fpa_leq + \sa Z3_mk_fpa_lt + def_API('Z3_mk_fpa_gt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -613,6 +795,11 @@ extern "C" { \c t1 and \c t2 must have the same FloatingPoint sort. + \sa Z3_mk_fpa_geq + \sa Z3_mk_fpa_gt + \sa Z3_mk_fpa_leq + \sa Z3_mk_fpa_lt + def_API('Z3_mk_fpa_eq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2); @@ -625,6 +812,11 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_is_infinite + \sa Z3_mk_fpa_is_nan + \sa Z3_mk_fpa_is_subnormal + \sa Z3_mk_fpa_is_zero + def_API('Z3_mk_fpa_is_normal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t); @@ -637,6 +829,11 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_is_infinite + \sa Z3_mk_fpa_is_nan + \sa Z3_mk_fpa_is_normal + \sa Z3_mk_fpa_is_zero + def_API('Z3_mk_fpa_is_subnormal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t); @@ -649,6 +846,12 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_is_infinite + \sa Z3_mk_fpa_is_nan + \sa Z3_mk_fpa_is_normal + \sa Z3_mk_fpa_is_subnormal + \sa Z3_mk_fpa_zero + def_API('Z3_mk_fpa_is_zero', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t); @@ -661,6 +864,12 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_inf + \sa Z3_mk_fpa_is_nan + \sa Z3_mk_fpa_is_normal + \sa Z3_mk_fpa_is_subnormal + \sa Z3_mk_fpa_is_zero + def_API('Z3_mk_fpa_is_infinite', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t); @@ -673,6 +882,12 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_is_infinite + \sa Z3_mk_fpa_is_normal + \sa Z3_mk_fpa_is_subnormal + \sa Z3_mk_fpa_is_zero + \sa Z3_mk_fpa_nan + def_API('Z3_mk_fpa_is_nan', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t); @@ -685,6 +900,10 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_abs + \sa Z3_mk_fpa_is_positive + \sa Z3_mk_fpa_neg + def_API('Z3_mk_fpa_is_negative', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t); @@ -697,6 +916,10 @@ extern "C" { \c t must have FloatingPoint sort. + \sa Z3_mk_fpa_abs + \sa Z3_mk_fpa_is_negative + \sa Z3_mk_fpa_neg + def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t); @@ -848,6 +1071,8 @@ extern "C" { \param c logical context \param s FloatingPoint sort + \sa Z3_fpa_get_sbits + def_API('Z3_fpa_get_ebits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s); @@ -858,6 +1083,8 @@ extern "C" { \param c logical context \param s FloatingPoint sort + \sa Z3_fpa_get_ebits + def_API('Z3_fpa_get_sbits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s); @@ -868,6 +1095,11 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_inf + \sa Z3_fpa_is_numeral_normal + \sa Z3_fpa_is_numeral_subnormal + \sa Z3_fpa_is_numeral_zero + def_API('Z3_fpa_is_numeral_nan', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t); @@ -878,6 +1110,11 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_nan + \sa Z3_fpa_is_numeral_normal + \sa Z3_fpa_is_numeral_subnormal + \sa Z3_fpa_is_numeral_zero + def_API('Z3_fpa_is_numeral_inf', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_inf(Z3_context c, Z3_ast t); @@ -888,6 +1125,11 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_inf + \sa Z3_fpa_is_numeral_nan + \sa Z3_fpa_is_numeral_normal + \sa Z3_fpa_is_numeral_subnormal + def_API('Z3_fpa_is_numeral_zero', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_zero(Z3_context c, Z3_ast t); @@ -898,6 +1140,11 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_inf + \sa Z3_fpa_is_numeral_nan + \sa Z3_fpa_is_numeral_subnormal + \sa Z3_fpa_is_numeral_zero + def_API('Z3_fpa_is_numeral_normal', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_normal(Z3_context c, Z3_ast t); @@ -908,6 +1155,11 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_inf + \sa Z3_fpa_is_numeral_nan + \sa Z3_fpa_is_numeral_normal + \sa Z3_fpa_is_numeral_zero + def_API('Z3_fpa_is_numeral_subnormal', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_subnormal(Z3_context c, Z3_ast t); @@ -918,6 +1170,8 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_negative + def_API('Z3_fpa_is_numeral_positive', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_positive(Z3_context c, Z3_ast t); @@ -928,6 +1182,8 @@ extern "C" { \param c logical context \param t a floating-point numeral + \sa Z3_fpa_is_numeral_positive + def_API('Z3_fpa_is_numeral_negative', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_negative(Z3_context c, Z3_ast t); From 55775bdc2054cc235208683e7db2bc4e0119c3ee Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 13:15:15 -0700 Subject: [PATCH 277/428] incremnet log level for debug output on cancelation 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 66409231c..91c03cd13 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1616,7 +1616,7 @@ lbool core::check() { TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";); - IF_VERBOSE(2, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());}); + IF_VERBOSE(5, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());}); CTRACE("nla_solver", ret == l_undef, tout << "Monomials\n"; print_monics(tout);); return ret; } From e2db2b864bab5c3871e8da0ac2c36c2a61b1c11d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 15:09:21 -0700 Subject: [PATCH 278/428] add hook for in-processing simplification for NLA Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 29 ++++------------------------- src/math/lp/nla_core.h | 4 ++-- src/math/lp/nla_solver.h | 1 + src/smt/theory_lra.cpp | 2 ++ 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 91c03cd13..a68ea1738 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1556,9 +1556,6 @@ lbool core::check() { if (no_effect()) m_monomial_bounds.propagate(); - - if (no_effect() && improve_bounds()) - return l_false; { std::function check1 = [&]() { if (no_effect() && run_horner) m_horner.horner_lemmas(); }; @@ -1793,28 +1790,6 @@ void core::set_use_nra_model(bool m) { void core::collect_statistics(::statistics & st) { } - -bool core::improve_bounds() { - return false; - - uint_set seen; - bool bounds_improved = false; - auto insert = [&](lpvar v) { - if (seen.contains(v)) - return; - seen.insert(v); - if (lra.improve_bound(v, false)) - bounds_improved = true, lp_settings().stats().m_nla_bounds_improvements++; - if (lra.improve_bound(v, true)) - bounds_improved = true, lp_settings().stats().m_nla_bounds_improvements++; - }; - for (auto & m : m_emons) { - insert(m.var()); - for (auto v : m.vars()) - insert(v); - } - return bounds_improved; -} void core::propagate() { clear(); @@ -1822,6 +1797,10 @@ void core::propagate() { m_monics_with_changed_bounds.reset(); } + void core::simplify() { + // in-processing simplifiation can go here, such as bounds improvements. + } + } // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 4bb7e11ee..25156e8b6 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -104,8 +104,6 @@ class core { void check_weighted(unsigned sz, std::pair>* checks); void add_bounds(); - // try to improve bounds for variables in monomials. - bool improve_bounds(); public: // constructor @@ -386,6 +384,8 @@ public: bool no_lemmas_hold() const; void propagate(); + + void simplify(); lbool test_check(); lpvar map_to_root(lpvar) const; diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 10cc8e006..9e650c127 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -38,6 +38,7 @@ namespace nla { bool need_check(); lbool check(); void propagate(); + void simplify() { m_core->simplify(); } lbool check_power(lpvar r, lpvar x, lpvar y); bool is_monic_var(lpvar) const; bool influences_nl_var(lpvar) const; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0fabe7ecd..4de1e732b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1091,6 +1091,8 @@ public: void restart_eh() { m_arith_eq_adapter.restart_eh(); + if (m_nla) + m_nla->simplify(); } void relevant_eh(app* n) { From 20c54048f79d9fb57650ca390c44be5a917e390a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 16:19:16 -0700 Subject: [PATCH 279/428] use cone of influence reduction before calling nlsat. --- src/math/lp/nla_core.cpp | 9 +- src/math/lp/nla_grobner.cpp | 49 ++-- src/math/lp/nla_grobner.h | 4 +- src/math/lp/nra_solver.cpp | 390 +++++++++++++++++++-------- src/math/lp/nra_solver.h | 7 +- src/smt/params/smt_params_helper.pyg | 2 +- 6 files changed, 323 insertions(+), 138 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index a68ea1738..d0862a6ff 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1549,7 +1549,6 @@ lbool core::check() { lbool ret = l_undef; bool run_grobner = need_run_grobner(); bool run_horner = need_run_horner(); - bool run_bounded_nlsat = should_run_bounded_nlsat(); bool run_bounds = params().arith_nl_branching(); auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; }; @@ -1572,7 +1571,7 @@ lbool core::check() { return l_false; } - if (no_effect() && run_bounded_nlsat) + if (no_effect() && should_run_bounded_nlsat()) ret = bounded_nlsat(); if (no_effect()) @@ -1582,8 +1581,7 @@ lbool core::check() { m_basics.basic_lemma(false); if (no_effect()) - m_divisions.check(); - + m_divisions.check(); if (no_effect() && ret == l_undef) { std::function check1 = [&]() { m_order.order_lemma(); }; @@ -1610,7 +1608,6 @@ lbool core::check() { ret = l_false; lp_settings().stats().m_nla_lemmas += m_lemmas.size(); - TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";); IF_VERBOSE(5, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());}); @@ -1623,7 +1620,7 @@ bool core::should_run_bounded_nlsat() { return false; if (m_nlsat_delay > 0) --m_nlsat_delay; - return m_nlsat_delay < 5; + return m_nlsat_delay < 2; } lbool core::bounded_nlsat() { diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index f3367a4ea..2abb6ef4e 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -50,10 +50,10 @@ namespace nla { return; } - lp_settings().stats().m_grobner_calls++; find_nl_cluster(); - configure(); + if (!configure()) + return; m_solver.saturate(); if (m_delay_base > 0) @@ -89,15 +89,14 @@ namespace nla { IF_VERBOSE(3, verbose_stream() << "grobner miss, quota " << m_quota << "\n"); IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream())); + } - -#if 0 - // diagnostics: did we miss something - vector eqs; - for (auto eq : m_solver.equations()) - eqs.push_back(eq->poly()); - c().m_nra.check(eqs); -#endif + dd::solver::equation_vector const& grobner::core_equations(bool all_eqs) { + flet _add_all(m_add_all_eqs, all_eqs); + find_nl_cluster(); + if (!configure()) + throw dd::pdd_manager::mem_out(); + return m_solver.equations(); } bool grobner::is_conflicting() { @@ -220,7 +219,7 @@ namespace nla { return true; } - void grobner::explain(const dd::solver::equation& eq, lp::explanation& exp) { + void grobner::explain(dd::solver::equation const& eq, lp::explanation& exp) { u_dependency_manager dm; vector lv; dm.linearize(eq.dep(), lv); @@ -235,7 +234,7 @@ namespace nla { lemma &= exp; } - void grobner::configure() { + bool grobner::configure() { m_solver.reset(); try { set_level2var(); @@ -253,12 +252,12 @@ namespace nla { add_fixed_monic(j); } } - catch (...) { + catch (dd::pdd_manager::mem_out) { IF_VERBOSE(2, verbose_stream() << "pdd throw\n"); - return; + return false; } TRACE("grobner", m_solver.display(tout)); - + #if 0 IF_VERBOSE(2, m_pdd_grobner.display(verbose_stream())); dd::pdd_eval eval(m_pdd_manager); @@ -282,6 +281,8 @@ namespace nla { m_solver.set(cfg); m_solver.adjust_cfg(); m_pdd_manager.set_max_num_nodes(10000); // or something proportional to the number of initial nodes. + + return true; } std::ostream& grobner::diagnose_pdd_miss(std::ostream& out) { @@ -389,8 +390,8 @@ namespace nla { for (auto const& m : c().emons()) m_mon2var[m.vars()] = m.var(); for (auto eq : m_solver.equations()) - if (propagate_linear_equations(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) - return true; + if (propagate_linear_equations(*eq)) + ++changed; return changed > 0; } @@ -430,7 +431,7 @@ namespace nla { lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); term_index = c().lra.map_term_index_to_column_index(term_index); c().lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::EQ, offset, e.dep()); - c().m_check_feasible = true; + c().m_check_feasible = true; return true; } @@ -455,11 +456,16 @@ namespace nla { continue; m_rows.insert(row); unsigned k = lra.get_base_column_in_row(row); - if (lra.column_is_free(k) && k != j) + // grobner bassis does not know about integer constraints + if (lra.column_is_free(k) && !m_add_all_eqs && k != j) + continue; + // a free column over the reals can be assigned + if (lra.column_is_free(k) && k != j && !lra.var_is_int(k)) continue; CTRACE("grobner", matrix.m_rows[row].size() > c().params().arith_nl_grobner_row_length_limit(), - tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";); - if (matrix.m_rows[row].size() > c().params().arith_nl_horner_row_length_limit()) + tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";); + // limits overhead of grobner equations, unless this is for extracting a complete COI of the non-satisfied subset. + if (!m_add_all_eqs && matrix.m_rows[row].size() > c().params().arith_nl_horner_row_length_limit()) continue; for (auto& rc : matrix.m_rows[row]) add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q); @@ -585,7 +591,6 @@ namespace nla { add_eq(sum, dep); } - void grobner::find_nl_cluster() { prepare_rows_and_active_vars(); svector q; diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index cbf0cf109..bf5ea8dcf 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -26,6 +26,7 @@ namespace nla { unsigned m_quota = 0; unsigned m_delay_base = 0; unsigned m_delay = 0; + bool m_add_all_eqs = false; std::unordered_map m_mon2var; lp::lp_settings& lp_settings(); @@ -58,7 +59,7 @@ namespace nla { bool equation_is_true(dd::solver::equation const& eq); // setup - void configure(); + bool configure(); void set_level2var(); void find_nl_cluster(); void prepare_rows_and_active_vars(); @@ -76,5 +77,6 @@ namespace nla { public: grobner(core *core); void operator()(); + dd::solver::equation_vector const& core_equations(bool all_eqs); }; } diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 182845d58..49ce5a9c2 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -19,17 +19,17 @@ typedef nla::mon_eq mon_eq; typedef nla::variable_map_type variable_map_type; struct solver::imp { - lp::lar_solver& s; + lp::lar_solver& lra; reslimit& m_limit; params_ref m_params; u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables - indexed_uint_set m_term_set; + indexed_uint_set m_term_set; scoped_ptr m_nlsat; - scoped_ptr m_zero; - mutable variable_map_type m_variable_values; // current model - nla::core& m_nla_core; + scoped_ptr m_values; // values provided by LRA solver + nla::core& m_nla_core; + imp(lp::lar_solver& s, reslimit& lim, params_ref const& p, nla::core& nla_core): - s(s), + lra(s), m_limit(lim), m_params(p), m_nla_core(nla_core) {} @@ -38,6 +38,68 @@ struct solver::imp { return m_nla_core.m_to_refine.size() != 0; } + indexed_uint_set m_mon_set, m_constraint_set; + + struct occurs { + unsigned_vector constraints; + unsigned_vector monics; + }; + + void init_cone_of_influence() { + indexed_uint_set visited; + unsigned_vector todo; + vector var2occurs; + m_term_set.reset(); + m_mon_set.reset(); + m_constraint_set.reset(); + + for (auto ci : lra.constraints().indices()) { + auto const& c = lra.constraints()[ci]; + for (auto const& [coeff, v] : c.coeffs()) { + var2occurs.reserve(v + 1); + var2occurs[v].constraints.push_back(ci); + } + } + + for (auto const& m : m_nla_core.emons()) { + for (auto v : m.vars()) { + var2occurs.reserve(v + 1); + var2occurs[v].monics.push_back(m.var()); + } + } + + for (auto const& m : m_nla_core.m_to_refine) + todo.push_back(m); + + for (unsigned i = 0; i < todo.size(); ++i) { + auto v = todo[i]; + if (visited.contains(v)) + continue; + visited.insert(v); + var2occurs.reserve(v + 1); + for (auto ci : var2occurs[v].constraints) { + m_constraint_set.insert(ci); + auto const& c = lra.constraints()[ci]; + for (auto const& [coeff, w] : c.coeffs()) + todo.push_back(w); + } + for (auto w : var2occurs[v].monics) + todo.push_back(w); + + if (lra.column_corresponds_to_term(v)) { + m_term_set.insert(v); + lp::tv ti = lp::tv::raw(lra.column_to_reported_index(v)); + for (auto kv : lra.get_term(ti)) + todo.push_back(kv.column().index()); + } + + if (m_nla_core.is_monic_var(v)) { + m_mon_set.insert(v); + for (auto w : m_nla_core.emons()[v]) + todo.push_back(w); + } + } + } /** \brief one-shot nlsat check. @@ -52,24 +114,25 @@ struct solver::imp { */ lbool check() { SASSERT(need_check()); - m_zero = nullptr; + m_values = nullptr; m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_zero = alloc(scoped_anum, am()); + m_values = alloc(scoped_anum_vector, am()); m_term_set.reset(); m_lp2nl.reset(); vector core; + init_cone_of_influence(); // add linear inequalities from lra_solver - for (lp::constraint_index ci : s.constraints().indices()) { + for (auto ci : m_constraint_set) add_constraint(ci); - } - + // add polynomial definitions. - for (auto const& m : m_nla_core.emons()) - add_monic_eq(m); + for (auto const& m : m_mon_set) + add_monic_eq(m_nla_core.emons()[m]); + + // add term definitions. for (unsigned i : m_term_set) add_term(i); - // TBD: add variable bounds? lbool r = l_undef; try { @@ -86,12 +149,27 @@ struct solver::imp { TRACE("nra", m_nlsat->display(tout << r << "\n"); display(tout); - for (auto kv : m_lp2nl) - tout << "j" << kv.m_key << " := x" << kv.m_value << "\n"; + for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n"; ); switch (r) { case l_true: m_nla_core.set_use_nra_model(true); + lra.init_model(); + for (lp::constraint_index ci : lra.constraints().indices()) + if (!check_constraint(ci)) { + IF_VERBOSE(0, verbose_stream() << "constraint " << ci << " violated\n"; + lra.constraints().display(verbose_stream())); + UNREACHABLE(); + return l_undef; + } + for (auto const& m : m_nla_core.emons()) { + if (!check_monic(m)) { + IF_VERBOSE(0, verbose_stream() << "monic " << m << " violated\n"; + lra.constraints().display(verbose_stream())); + UNREACHABLE(); + return l_undef; + } + } break; case l_false: { lp::explanation ex; @@ -112,12 +190,31 @@ struct solver::imp { return r; } + void add_monic_eq_bound(mon_eq const& m) { + if (!lra.column_has_lower_bound(m.var()) && + !lra.column_has_upper_bound(m.var())) + return; + polynomial::manager& pm = m_nlsat->pm(); + svector vars; + for (auto v : m.vars()) + vars.push_back(lp2nl(v)); + auto v = m.var(); + polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.data()), pm); + polynomial::monomial * mls[1] = { m1 }; + polynomial::scoped_numeral_vector coeffs(pm.m()); + coeffs.push_back(mpz(1)); + polynomial::polynomial_ref p(pm.mk_polynomial(1, coeffs.data(), mls), pm); + if (lra.column_has_lower_bound(v)) + add_lb(lra.get_lower_bound(v), p, lra.get_column_lower_bound_witness(v)); + if (lra.column_has_upper_bound(v)) + add_ub(lra.get_upper_bound(v), p, lra.get_column_upper_bound_witness(v)); + } + void add_monic_eq(mon_eq const& m) { polynomial::manager& pm = m_nlsat->pm(); svector vars; - for (auto v : m.vars()) { + for (auto v : m.vars()) vars.push_back(lp2nl(v)); - } polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.data()), pm); polynomial::monomial_ref m2(pm.mk_monomial(lp2nl(m.var()), 1), pm); polynomial::monomial * mls[2] = { m1, m2 }; @@ -132,7 +229,7 @@ struct solver::imp { } void add_constraint(unsigned idx) { - auto& c = s.constraints()[idx]; + auto& c = lra.constraints()[idx]; auto& pm = m_nlsat->pm(); auto k = c.kind(); auto rhs = c.rhs(); @@ -140,9 +237,9 @@ struct solver::imp { auto sz = lhs.size(); svector vars; rational den = denominator(rhs); - for (auto kv : lhs) { - vars.push_back(lp2nl(kv.second)); - den = lcm(den, denominator(kv.first)); + for (auto [coeff, v] : lhs) { + vars.push_back(lp2nl(v)); + den = lcm(den, denominator(coeff)); } vector coeffs; for (auto kv : lhs) { @@ -176,21 +273,128 @@ struct solver::imp { m_nlsat->mk_clause(1, &lit, a); } + bool check_monic(mon_eq const& m) { + scoped_anum val1(am()), val2(am()); + am().set(val1, value(m.var())); + am().set(val2, rational::one().to_mpq()); + for (auto v : m.vars()) + am().mul(val2, value(v), val2); + return am().eq(val1, val2); + } + + bool check_constraint(unsigned idx) { + auto& c = lra.constraints()[idx]; + auto& pm = m_nlsat->pm(); + auto k = c.kind(); + auto offset = -c.rhs(); + auto lhs = c.coeffs(); + auto sz = lhs.size(); + + scoped_anum val(am()), mon(am()); + am().set(val, offset.to_mpq()); + for (auto [coeff, v] : lhs) { + am().set(mon, coeff.to_mpq()); + am().mul(mon, value(v), mon); + am().add(val, mon, val); + } + am().set(mon, rational::zero().to_mpq()); + switch (k) { + case lp::lconstraint_kind::LE: + return am().le(val, mon); + case lp::lconstraint_kind::GE: + return am().ge(val, mon); + case lp::lconstraint_kind::LT: + return am().lt(val, mon); + case lp::lconstraint_kind::GT: + return am().gt(val, mon); + case lp::lconstraint_kind::EQ: + return am().eq(val, mon); + default: + UNREACHABLE(); + } + return false; + } + + lbool check(dd::solver::equation_vector const& eqs) { + m_values = nullptr; + m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); + m_values = alloc(scoped_anum_vector, am()); + m_lp2nl.reset(); + m_term_set.reset(); + for (auto const& eq : eqs) + add_eq(*eq); + for (auto const& m : m_nla_core.emons()) + if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) + add_monic_eq_bound(m); + for (unsigned i : m_term_set) + add_term(i); + for (auto const& [v, w] : m_lp2nl) { + if (lra.column_has_lower_bound(v)) + add_lb(lra.get_lower_bound(v), w, lra.get_column_lower_bound_witness(v)); + if (lra.column_has_upper_bound(v)) + add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v)); + } + + lbool r = l_undef; + try { + r = m_nlsat->check(); + } + catch (z3_exception&) { + if (m_limit.is_canceled()) { + r = l_undef; + } + else { + throw; + } + } + + switch (r) { + case l_true: + m_nla_core.set_use_nra_model(true); + lra.init_model(); + for (lp::constraint_index ci : lra.constraints().indices()) + if (!check_constraint(ci)) + return l_undef; + for (auto const& m : m_nla_core.emons()) { + if (!check_monic(m)) + return l_undef; + break; + case l_false: { + lp::explanation ex; + vector core; + m_nlsat->get_core(core); + u_dependency_manager dm; + vector lv; + for (auto c : core) + dm.linearize(static_cast(c), lv); + for (auto ci : lv) + ex.push_back(ci); + nla::new_lemma lemma(m_nla_core, __FUNCTION__); + lemma &= ex; + break; + } + case l_undef: + break; + } + + return r; + } lbool check(vector const& eqs) { - m_zero = nullptr; + m_values = nullptr; m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_zero = alloc(scoped_anum, am()); + m_values = alloc(scoped_anum_vector, am()); m_lp2nl.reset(); m_term_set.reset(); for (auto const& eq : eqs) add_eq(eq); + for (auto const& m : m_nla_core.emons()) + add_monic_eq(m); for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.lra; - if (ls.column_has_lower_bound(v)) - add_lb(ls.get_lower_bound(v), w); - if (ls.column_has_upper_bound(v)) - add_ub(ls.get_upper_bound(v), w); + if (lra.column_has_lower_bound(v)) + add_lb(lra.get_lower_bound(v), w); + if (lra.column_has_upper_bound(v)) + add_ub(lra.get_upper_bound(v), w); } lbool r = l_undef; @@ -212,63 +416,21 @@ struct solver::imp { IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n"; m_nlsat->display(verbose_stream()); for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.lra; - if (ls.column_has_lower_bound(v)) - verbose_stream() << "x" << w << " >= " << ls.get_lower_bound(v) << "\n"; - if (ls.column_has_upper_bound(v)) - verbose_stream() << "x" << w << " <= " << ls.get_upper_bound(v) << "\n"; + if (lra.column_has_lower_bound(v)) + verbose_stream() << "x" << w << " >= " << lra.get_lower_bound(v) << "\n"; + if (lra.column_has_upper_bound(v)) + verbose_stream() << "x" << w << " <= " << lra.get_upper_bound(v) << "\n"; }); return r; } - lbool check_tight(dd::pdd const& eq) { - m_zero = nullptr; - m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_zero = alloc(scoped_anum, am()); - m_lp2nl.reset(); - m_term_set.reset(); - add_eq(eq); - for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.lra; - if (ls.column_has_lower_bound(v)) - add_strict_lb(ls.get_lower_bound(v), w); - if (ls.column_has_upper_bound(v)) - add_strict_ub(ls.get_upper_bound(v), w); - } - - lbool r = l_undef; - try { - r = m_nlsat->check(); - } - catch (z3_exception&) { - if (m_limit.is_canceled()) { - r = l_undef; - } - else { - throw; - } - } - - if (r == l_true) - return r; - - IF_VERBOSE(0, verbose_stream() << "check-nra tight " << r << "\n"; - m_nlsat->display(verbose_stream()); - for (auto const& [v, w] : m_lp2nl) { - auto& ls = m_nla_core.lra; - if (ls.column_has_lower_bound(v)) - verbose_stream() << "x" << w << " >= " << ls.get_lower_bound(v) << "\n"; - if (ls.column_has_upper_bound(v)) - verbose_stream() << "x" << w << " <= " << ls.get_upper_bound(v) << "\n"; - }); - - - return r; + void add_eq(dd::solver::equation const& eq) { + add_eq(eq.poly(), eq.dep()); } - void add_eq(dd::pdd const& eq) { + void add_eq(dd::pdd const& eq, nlsat::assumption a = nullptr) { dd::pdd normeq = eq; rational lc(1); for (auto const& [c, m] : eq) @@ -280,28 +442,35 @@ struct solver::imp { bool is_even[1] = { false }; polynomial::polynomial* ps[1] = { p }; nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); - m_nlsat->mk_clause(1, &lit, nullptr); + m_nlsat->mk_clause(1, &lit, a); } - void add_strict_lb(lp::impq const& b, unsigned w) { - add_bound(b.x, w, false, nlsat::atom::kind::GT); - } - void add_strict_ub(lp::impq const& b, unsigned w) { - add_bound(b.x, w, false, nlsat::atom::kind::LT); + + void add_lb(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { + polynomial::manager& pm = m_nlsat->pm(); + polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); + add_lb(b, p, a); } - void add_lb(lp::impq const& b, unsigned w) { - add_bound(b.x, w, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT); + void add_ub(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { + polynomial::manager& pm = m_nlsat->pm(); + polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); + add_ub(b, p, a); } - void add_ub(lp::impq const& b, unsigned w) { - add_bound(b.x, w, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT); + + void add_lb(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { + add_bound(b.x, p, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT, a); + } + + void add_ub(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { + add_bound(b.x, p, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT, a); } // w - bound < 0 // w - bound > 0 - void add_bound(lp::mpq const& bound, unsigned w, bool neg, nlsat::atom::kind k) { + + void add_bound(lp::mpq const& bound, polynomial::polynomial* p1, bool neg, nlsat::atom::kind k, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); - polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm); polynomial::polynomial_ref p2(pm.mk_const(bound), pm); polynomial::polynomial_ref p(pm.sub(p1, p2), pm); polynomial::polynomial* ps[1] = { p }; @@ -309,7 +478,13 @@ struct solver::imp { nlsat::literal lit = m_nlsat->mk_ineq_literal(k, 1, ps, is_even); if (neg) lit.neg(); - m_nlsat->mk_clause(1, &lit, nullptr); + m_nlsat->mk_clause(1, &lit, a); + } + + void add_bound(lp::mpq const& bound, unsigned w, bool neg, nlsat::atom::kind k, nlsat::assumption a = nullptr) { + polynomial::manager& pm = m_nlsat->pm(); + polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm); + add_bound(bound, p1, neg, k, a); } polynomial::polynomial* pdd2polynomial(dd::pdd const& p) { @@ -320,7 +495,7 @@ struct solver::imp { polynomial::polynomial_ref hi(pdd2polynomial(p.hi()), pm); unsigned w, v = p.var(); if (!m_lp2nl.find(v, w)) { - w = m_nlsat->mk_var(false); + w = m_nlsat->mk_var(is_int(v)); m_lp2nl.insert(v, w); } polynomial::polynomial_ref vp(pm.mk_polynomial(w, 1), pm); @@ -329,16 +504,15 @@ struct solver::imp { } bool is_int(lp::var_index v) { - return s.var_is_int(v); + return lra.var_is_int(v); } - polynomial::var lp2nl(lp::var_index v) { polynomial::var r; if (!m_lp2nl.find(v, r)) { r = m_nlsat->mk_var(is_int(v)); m_lp2nl.insert(v, r); - if (!m_term_set.contains(v) && s.column_corresponds_to_term(v)) { + if (!m_term_set.contains(v) && lra.column_corresponds_to_term(v)) { m_term_set.insert(v); } } @@ -346,9 +520,9 @@ struct solver::imp { } // void add_term(unsigned term_column) { - lp::tv ti = lp::tv::raw(s.column_to_reported_index(term_column)); - const lp::lar_term& t = s.get_term(ti); - // code that creates a polynomial equality between the linear coefficients and + lp::tv ti = lp::tv::raw(lra.column_to_reported_index(term_column)); + const lp::lar_term& t = lra.get_term(ti); + // code that creates a polynomial equality between the linear coefficients and // variable representing the term. svector vars; rational den(1); @@ -367,16 +541,22 @@ struct solver::imp { polynomial::polynomial_ref p(pm.mk_linear(coeffs.size(), coeffs.data(), vars.data(), rational(0)), pm); polynomial::polynomial* ps[1] = { p }; bool is_even[1] = { false }; - nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); m_nlsat->mk_clause(1, &lit, nullptr); } - nlsat::anum const& value(lp::var_index v) const { + nlsat::anum const& value(lp::var_index v) { polynomial::var pv; if (m_lp2nl.find(v, pv)) return m_nlsat->value(pv); - else - return *m_zero; + else { + for (unsigned w = m_values->size(); w <= v; ++w) { + scoped_anum a(am()); + am().set(a, m_nla_core.val(w).to_mpq()); + m_values->push_back(a); + } + return (*m_values)[v]; + } } nlsat::anum_manager& am() { @@ -417,8 +597,8 @@ lbool solver::check(vector const& eqs) { return m_imp->check(eqs); } -lbool solver::check_tight(dd::pdd const& eq) { - return m_imp->check_tight(eq); +lbool solver::check(dd::solver::equation_vector const& eqs) { + return m_imp->check(eqs); } bool solver::need_check() { @@ -429,7 +609,7 @@ std::ostream& solver::display(std::ostream& out) const { return m_imp->display(out); } -nlsat::anum const& solver::value(lp::var_index v) const { +nlsat::anum const& solver::value(lp::var_index v) { return m_imp->value(v); } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index 3f6335013..0b52a5b1d 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -9,6 +9,7 @@ #include "util/rlimit.h" #include "util/params.h" #include "nlsat/nlsat_solver.h" +#include "math/grobner/pdd_solver.h" #include "math/dd/dd_pdd.h" namespace lp { @@ -43,9 +44,9 @@ namespace nra { lbool check(vector const& eqs); /** - \brief Check if equality is tight. + \brief Check feasibility with respect to a set of reduced constraints. */ - lbool check_tight(const dd::pdd& eq); + lbool check(dd::solver::equation_vector const& eqs); /* \brief determine whether nra check is needed. @@ -55,7 +56,7 @@ namespace nra { /* \brief Access model. */ - nlsat::anum const& value(lp::var_index v) const; + nlsat::anum const& value(lp::var_index v); nlsat::anum_manager& am(); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 9fcda7f64..300bef1fb 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -78,7 +78,7 @@ def_module_params(module_name='smt', ('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'), ('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'), ('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'), - ('arith.nl.delay', UINT, 500, 'number of calls to final check before invoking bounded nlsat check'), + ('arith.nl.delay', UINT, 10, 'number of calls to final check before invoking bounded nlsat check'), ('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'), ('arith.nl.optimize_bounds', BOOL, True, 'enable bounds optimization'), ('arith.nl.cross_nested', BOOL, True, 'enable cross-nested consistency checking'), From 4434cee5df67251c3402ff881de48739e0f0756e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 16:38:18 -0700 Subject: [PATCH 280/428] merge --- src/math/lp/nra_solver.cpp | 159 +++++++++++++++++++------------------ src/math/lp/nra_solver.h | 2 - 2 files changed, 80 insertions(+), 81 deletions(-) diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 49ce5a9c2..158679e00 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -18,6 +18,7 @@ namespace nra { typedef nla::mon_eq mon_eq; typedef nla::variable_map_type variable_map_type; + struct solver::imp { lp::lar_solver& lra; reslimit& m_limit; @@ -68,12 +69,12 @@ struct solver::imp { } } - for (auto const& m : m_nla_core.m_to_refine) - todo.push_back(m); + for (auto const& m : m_nla_core.m_to_refine) + todo.push_back(m); for (unsigned i = 0; i < todo.size(); ++i) { auto v = todo[i]; - if (visited.contains(v)) + if (visited.contains(v)) continue; visited.insert(v); var2occurs.reserve(v + 1); @@ -82,22 +83,22 @@ struct solver::imp { auto const& c = lra.constraints()[ci]; for (auto const& [coeff, w] : c.coeffs()) todo.push_back(w); - } + } for (auto w : var2occurs[v].monics) todo.push_back(w); if (lra.column_corresponds_to_term(v)) { m_term_set.insert(v); lp::tv ti = lp::tv::raw(lra.column_to_reported_index(v)); - for (auto kv : lra.get_term(ti)) - todo.push_back(kv.column().index()); + for (auto kv : lra.get_term(ti)) + todo.push_back(kv.column().index()); } if (m_nla_core.is_monic_var(v)) { m_mon_set.insert(v); for (auto w : m_nla_core.emons()[v]) todo.push_back(w); - } + } } } @@ -112,7 +113,7 @@ struct solver::imp { TBD: use partial model from lra_solver to prime the state of nlsat_solver. TBD: explore more incremental ways of applying nlsat (using assumptions) */ - lbool check() { + lbool check() { SASSERT(need_check()); m_values = nullptr; m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); @@ -125,18 +126,18 @@ struct solver::imp { // add linear inequalities from lra_solver for (auto ci : m_constraint_set) add_constraint(ci); - + // add polynomial definitions. - for (auto const& m : m_mon_set) - add_monic_eq(m_nla_core.emons()[m]); + for (auto const& m : m_mon_set) + add_monic_eq(m_nla_core.emons()[m]); // add term definitions. - for (unsigned i : m_term_set) + for (unsigned i : m_term_set) add_term(i); lbool r = l_undef; try { - r = m_nlsat->check(); + r = m_nlsat->check(); } catch (z3_exception&) { if (m_limit.is_canceled()) { @@ -146,30 +147,31 @@ struct solver::imp { throw; } } - TRACE("nra", +#if 0 + TRACE("nra", m_nlsat->display(tout << r << "\n"); - display(tout); - for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n"; - ); + display(tout); + for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n";); +#endif switch (r) { - case l_true: + case l_true: m_nla_core.set_use_nra_model(true); - lra.init_model(); - for (lp::constraint_index ci : lra.constraints().indices()) - if (!check_constraint(ci)) { - IF_VERBOSE(0, verbose_stream() << "constraint " << ci << " violated\n"; - lra.constraints().display(verbose_stream())); - UNREACHABLE(); - return l_undef; - } - for (auto const& m : m_nla_core.emons()) { - if (!check_monic(m)) { - IF_VERBOSE(0, verbose_stream() << "monic " << m << " violated\n"; - lra.constraints().display(verbose_stream())); - UNREACHABLE(); - return l_undef; - } + lra.init_model(); + for (lp::constraint_index ci : lra.constraints().indices()) + if (!check_constraint(ci)) { + IF_VERBOSE(0, verbose_stream() << "constraint " << ci << " violated\n"; + lra.constraints().display(verbose_stream())); + UNREACHABLE(); + return l_undef; } + for (auto const& m : m_nla_core.emons()) { + if (!check_monic(m)) { + IF_VERBOSE(0, verbose_stream() << "monic " << m << " violated\n"; + lra.constraints().display(verbose_stream())); + UNREACHABLE(); + return l_undef; + } + } break; case l_false: { lp::explanation ex; @@ -186,9 +188,9 @@ struct solver::imp { } case l_undef: break; - } + } return r; - } + } void add_monic_eq_bound(mon_eq const& m) { if (!lra.column_has_lower_bound(m.var()) && @@ -322,22 +324,22 @@ struct solver::imp { m_lp2nl.reset(); m_term_set.reset(); for (auto const& eq : eqs) - add_eq(*eq); - for (auto const& m : m_nla_core.emons()) - if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) - add_monic_eq_bound(m); - for (unsigned i : m_term_set) - add_term(i); + add_eq(*eq); + for (auto const& m : m_nla_core.emons()) + if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) + add_monic_eq_bound(m); + for (unsigned i : m_term_set) + add_term(i); for (auto const& [v, w] : m_lp2nl) { - if (lra.column_has_lower_bound(v)) - add_lb(lra.get_lower_bound(v), w, lra.get_column_lower_bound_witness(v)); - if (lra.column_has_upper_bound(v)) - add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v)); + if (lra.column_has_lower_bound(v)) + add_lb(lra.get_lower_bound(v), w, lra.get_column_lower_bound_witness(v)); + if (lra.column_has_upper_bound(v)) + add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v)); } lbool r = l_undef; try { - r = m_nlsat->check(); + r = m_nlsat->check(); } catch (z3_exception&) { if (m_limit.is_canceled()) { @@ -349,13 +351,13 @@ struct solver::imp { } switch (r) { - case l_true: + case l_true: m_nla_core.set_use_nra_model(true); lra.init_model(); - for (lp::constraint_index ci : lra.constraints().indices()) + for (lp::constraint_index ci : lra.constraints().indices()) if (!check_constraint(ci)) - return l_undef; - for (auto const& m : m_nla_core.emons()) { + return l_undef; + for (auto const& m : m_nla_core.emons()) if (!check_monic(m)) return l_undef; break; @@ -365,7 +367,7 @@ struct solver::imp { m_nlsat->get_core(core); u_dependency_manager dm; vector lv; - for (auto c : core) + for (auto c : core) dm.linearize(static_cast(c), lv); for (auto ci : lv) ex.push_back(ci); @@ -375,8 +377,7 @@ struct solver::imp { } case l_undef: break; - } - + } return r; } @@ -388,18 +389,18 @@ struct solver::imp { m_term_set.reset(); for (auto const& eq : eqs) add_eq(eq); - for (auto const& m : m_nla_core.emons()) - add_monic_eq(m); + for (auto const& m : m_nla_core.emons()) + add_monic_eq(m); for (auto const& [v, w] : m_lp2nl) { if (lra.column_has_lower_bound(v)) add_lb(lra.get_lower_bound(v), w); if (lra.column_has_upper_bound(v)) add_ub(lra.get_upper_bound(v), w); } - + lbool r = l_undef; try { - r = m_nlsat->check(); + r = m_nlsat->check(); } catch (z3_exception&) { if (m_limit.is_canceled()) { @@ -412,7 +413,7 @@ struct solver::imp { if (r == l_true) return r; - + IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n"; m_nlsat->display(verbose_stream()); for (auto const& [v, w] : m_lp2nl) { @@ -420,38 +421,36 @@ struct solver::imp { verbose_stream() << "x" << w << " >= " << lra.get_lower_bound(v) << "\n"; if (lra.column_has_upper_bound(v)) verbose_stream() << "x" << w << " <= " << lra.get_upper_bound(v) << "\n"; - }); - - + }); + return r; } - + void add_eq(dd::solver::equation const& eq) { add_eq(eq.poly(), eq.dep()); } - + void add_eq(dd::pdd const& eq, nlsat::assumption a = nullptr) { dd::pdd normeq = eq; rational lc(1); - for (auto const& [c, m] : eq) + for (auto const& [c, m] : eq) lc = lcm(denominator(c), lc); if (lc != 1) normeq *= lc; polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pdd2polynomial(normeq), pm); - bool is_even[1] = { false }; - polynomial::polynomial* ps[1] = { p }; - nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + bool is_even[1] = {false}; + polynomial::polynomial* ps[1] = {p}; + nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); m_nlsat->mk_clause(1, &lit, a); } - void add_lb(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); add_lb(b, p, a); } - + void add_ub(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); @@ -473,8 +472,8 @@ struct solver::imp { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p2(pm.mk_const(bound), pm); polynomial::polynomial_ref p(pm.sub(p1, p2), pm); - polynomial::polynomial* ps[1] = { p }; - bool is_even[1] = { false }; + polynomial::polynomial* ps[1] = {p}; + bool is_even[1] = {false}; nlsat::literal lit = m_nlsat->mk_ineq_literal(k, 1, ps, is_even); if (neg) lit.neg(); @@ -486,10 +485,10 @@ struct solver::imp { polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm); add_bound(bound, p1, neg, k, a); } - + polynomial::polynomial* pdd2polynomial(dd::pdd const& p) { polynomial::manager& pm = m_nlsat->pm(); - if (p.is_val()) + if (p.is_val()) return pm.mk_const(p.val()); polynomial::polynomial_ref lo(pdd2polynomial(p.lo()), pm); polynomial::polynomial_ref hi(pdd2polynomial(p.hi()), pm); @@ -502,7 +501,9 @@ struct solver::imp { polynomial::polynomial_ref mp(pm.mul(vp, hi), pm); return pm.add(lo, mp); } - + + + bool is_int(lp::var_index v) { return lra.var_is_int(v); } @@ -521,7 +522,7 @@ struct solver::imp { // void add_term(unsigned term_column) { lp::tv ti = lp::tv::raw(lra.column_to_reported_index(term_column)); - const lp::lar_term& t = lra.get_term(ti); + const lp::lar_term& t = lra.get_term(ti); // code that creates a polynomial equality between the linear coefficients and // variable representing the term. svector vars; @@ -531,7 +532,7 @@ struct solver::imp { den = lcm(den, denominator(kv.coeff())); } vars.push_back(lp2nl(term_column)); - + vector coeffs; for (auto kv : t) { coeffs.push_back(den * kv.coeff()); @@ -539,9 +540,9 @@ struct solver::imp { coeffs.push_back(-den); polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pm.mk_linear(coeffs.size(), coeffs.data(), vars.data(), rational(0)), pm); - polynomial::polynomial* ps[1] = { p }; - bool is_even[1] = { false }; - nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + polynomial::polynomial* ps[1] = {p}; + bool is_even[1] = {false}; + nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); m_nlsat->mk_clause(1, &lit, nullptr); } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index 0b52a5b1d..db1311dad 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -19,8 +19,6 @@ namespace lp { namespace nra { - - class solver { struct imp; imp* m_imp; From 702744f1397b46497e858fe2efbac89df7b1668f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 16:57:31 -0700 Subject: [PATCH 281/428] fix build Signed-off-by: Nikolaj Bjorner --- src/math/lp/nra_solver.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 158679e00..d7c1ac5ba 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -207,9 +207,9 @@ struct solver::imp { coeffs.push_back(mpz(1)); polynomial::polynomial_ref p(pm.mk_polynomial(1, coeffs.data(), mls), pm); if (lra.column_has_lower_bound(v)) - add_lb(lra.get_lower_bound(v), p, lra.get_column_lower_bound_witness(v)); + add_lb_p(lra.get_lower_bound(v), p, lra.get_column_lower_bound_witness(v)); if (lra.column_has_upper_bound(v)) - add_ub(lra.get_upper_bound(v), p, lra.get_column_upper_bound_witness(v)); + add_ub_p(lra.get_upper_bound(v), p, lra.get_column_upper_bound_witness(v)); } void add_monic_eq(mon_eq const& m) { @@ -448,20 +448,20 @@ struct solver::imp { void add_lb(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); - add_lb(b, p, a); + add_lb_p(b, p, a); } void add_ub(lp::impq const& b, unsigned w, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); - add_ub(b, p, a); + add_ub_p(b, p, a); } - void add_lb(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { + void add_lb_p(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { add_bound(b.x, p, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT, a); } - void add_ub(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { + void add_ub_p(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { add_bound(b.x, p, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT, a); } From 76f9e1d2b34524a583d156dc52d251119fceb73d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Oct 2023 17:31:19 -0700 Subject: [PATCH 282/428] fix build Signed-off-by: Nikolaj Bjorner --- src/math/lp/nra_solver.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index d7c1ac5ba..5bf983f15 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -458,17 +458,17 @@ struct solver::imp { } void add_lb_p(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { - add_bound(b.x, p, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT, a); + add_bound_p(b.x, p, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT, a); } void add_ub_p(lp::impq const& b, polynomial::polynomial* p, nlsat::assumption a = nullptr) { - add_bound(b.x, p, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT, a); + add_bound_p(b.x, p, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT, a); } // w - bound < 0 // w - bound > 0 - void add_bound(lp::mpq const& bound, polynomial::polynomial* p1, bool neg, nlsat::atom::kind k, nlsat::assumption a = nullptr) { + void add_bound_p(lp::mpq const& bound, polynomial::polynomial* p1, bool neg, nlsat::atom::kind k, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); polynomial::polynomial_ref p2(pm.mk_const(bound), pm); polynomial::polynomial_ref p(pm.sub(p1, p2), pm); @@ -482,8 +482,8 @@ struct solver::imp { void add_bound(lp::mpq const& bound, unsigned w, bool neg, nlsat::atom::kind k, nlsat::assumption a = nullptr) { polynomial::manager& pm = m_nlsat->pm(); - polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm); - add_bound(bound, p1, neg, k, a); + polynomial::polynomial_ref p(pm.mk_polynomial(w), pm); + add_bound_p(bound, p, neg, k, a); } polynomial::polynomial* pdd2polynomial(dd::pdd const& p) { From 5622fd13622a24731ed8750a7ebda3d710a3e25b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Oct 2023 03:26:41 -0700 Subject: [PATCH 283/428] initialize delay bound Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index d0862a6ff..2ec4a3a10 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -40,8 +40,8 @@ core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) : m_use_nra_model(false), m_nra(s, m_nra_lim, *this) { - // m_nlsat_delay_bound = lp_settings().nlsat_delay(); - lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { + m_nlsat_delay_bound = lp_settings().nlsat_delay(); + lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) { for (lpvar j : columns_with_changed_bounds) { if (is_monic_var(j)) m_monics_with_changed_bounds.insert(j); @@ -1567,11 +1567,11 @@ lbool core::check() { {1, check3} }; check_weighted(3, checks); - if (!m_lemmas.empty() || !m_literals.empty()) + if (!m_lemmas.empty() || !m_literals.empty() || m_check_feasible) return l_false; } - if (no_effect() && should_run_bounded_nlsat()) + if (no_effect() && should_run_bounded_nlsat()) ret = bounded_nlsat(); if (no_effect()) From 0b8d7b755d90557086b460af15caba406488b91d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Oct 2023 03:48:50 -0700 Subject: [PATCH 284/428] useful string rewrites Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index f8c4c9811..95fa39b1d 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -828,6 +828,7 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) { br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { zstring b; + rational r; m_es.reset(); str().get_concat(a, m_es); unsigned len = 0; @@ -867,6 +868,13 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { result = str().mk_length(z); return BR_REWRITE1; } + // len(extract(x, 0, z)) = min(z, len(x)) + if (str().is_extract(a, x, y, z) && + m_autil.is_numeral(y, r) && r.is_zero()) { + expr* len_x = str().mk_length(x); + result = m().mk_ite(m_autil.mk_le(len_x, z), len_x, z); + return BR_REWRITE2; + } #if 0 expr* s = nullptr, *offset = nullptr, *length = nullptr; if (str().is_extract(a, s, offset, length)) { @@ -1209,6 +1217,11 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu constantPos &= pos.is_unsigned(); constantLen &= len.is_unsigned(); + if (constantPos && constantLen && len == 1) { + result = str().mk_at(a, b); + return BR_REWRITE1; + } + if (constantPos && constantLen && constantBase) { unsigned _pos = pos.get_unsigned(); unsigned _len = len.get_unsigned(); @@ -1548,9 +1561,17 @@ bool seq_rewriter::reduce_by_char(expr_ref& r, expr* ch, unsigned depth) { */ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { zstring c; - rational r; + rational r, offset_r, len_r; + expr* offset, *a1, *len; expr_ref_vector lens(m()); sort* sort_a = a->get_sort(); + if (str().is_extract(a, a1, offset, len) && + m_autil.is_numeral(offset, offset_r) && offset_r.is_zero() && + m_autil.is_numeral(len, len_r) && m_autil.is_numeral(b, r) && + r < len_r) { + result = str().mk_at(a1, b); + return BR_REWRITE1; + } if (!get_lengths(b, lens, r)) { return BR_FAILED; } From 93427f1175783a541eff60d3b1cacb8cde052d76 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Oct 2023 08:48:58 -0700 Subject: [PATCH 285/428] regression test 2447 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 95fa39b1d..4f94b89d2 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -870,10 +870,13 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { } // len(extract(x, 0, z)) = min(z, len(x)) if (str().is_extract(a, x, y, z) && - m_autil.is_numeral(y, r) && r.is_zero()) { + m_autil.is_numeral(y, r) && r.is_zero() && + m_autil.is_numeral(z, r) && r >= 0) { expr* len_x = str().mk_length(x); + expr* zero = m_autil.mk_int(0); result = m().mk_ite(m_autil.mk_le(len_x, z), len_x, z); - return BR_REWRITE2; + // result = m().mk_ite(m_autil.mk_le(z, zero), zero, result); + return BR_REWRITE_FULL; } #if 0 expr* s = nullptr, *offset = nullptr, *length = nullptr; From f6c9ead10c0cf904925d7495f5c08dae66643b7a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Oct 2023 13:17:20 -0700 Subject: [PATCH 286/428] #6964 Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 78bfb6b7a..5cd53e16f 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1839,7 +1839,7 @@ _file_manager = contextlib.ExitStack() atexit.register(_file_manager.close) _ext = 'dll' if sys.platform in ('win32', 'cygwin') else 'dylib' if sys.platform == 'darwin' else 'so' _lib = None -_z3_lib_resource = importlib_resources.files('z3', 'lib') +_z3_lib_resource = importlib_resources.files('z3').joinpath('lib') _z3_lib_resource_path = _file_manager.enter_context( importlib_resources.as_file(_z3_lib_resource) ) From 52d16a11f9a56b6f650e61f0bd8d5455df08f0e1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Oct 2023 16:15:36 -0700 Subject: [PATCH 287/428] deal with non-termination in new arithmetic solver Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_axioms.cpp | 8 ++++---- src/sat/smt/arith_solver.cpp | 19 ++++++++++++++----- src/sat/smt/arith_solver.h | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/sat/smt/arith_axioms.cpp b/src/sat/smt/arith_axioms.cpp index d17154361..173ae28c8 100644 --- a/src/sat/smt/arith_axioms.cpp +++ b/src/sat/smt/arith_axioms.cpp @@ -386,12 +386,12 @@ namespace arith { ctx.push(push_back_vector>>(m_delayed_eqs)); } - void solver::mk_diseq_axiom(euf::th_eq const& e) { - if (is_bool(e.v1())) + void solver::mk_diseq_axiom(theory_var v1, theory_var v2) { + if (is_bool(v1)) return; force_push(); - expr* e1 = var2expr(e.v1()); - expr* e2 = var2expr(e.v2()); + expr* e1 = var2expr(v1); + expr* e2 = var2expr(v2); if (e1->get_id() > e2->get_id()) std::swap(e1, e2); if (m.are_distinct(e1, e2)) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 196895216..32b935b9a 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -896,6 +896,9 @@ namespace arith { } bool solver::assume_eqs() { + if (delayed_assume_eqs()) + return true; + TRACE("arith", display(tout);); random_update(); m_model_eqs.reset(); @@ -944,8 +947,15 @@ namespace arith { continue; literal eq = eq_internalize(n1, n2); ctx.mark_relevant(eq); - if (s().value(eq) != l_true) + switch (s().value(eq)) { + case l_true: + break; + case l_undef: return true; + case l_false: + mk_diseq_axiom(v1, v2); + return true; + } } return false; } @@ -1018,13 +1028,14 @@ namespace arith { st = sat::check_result::CR_GIVEUP; break; } - + if (assume_eqs()) { ++m_stats.m_assume_eqs; return sat::check_result::CR_CONTINUE; } if (!check_delayed_eqs()) return sat::check_result::CR_CONTINUE; + if (ctx.get_config().m_arith_ignore_int && int_undef) return sat::check_result::CR_GIVEUP; if (m_not_handled != nullptr) { @@ -1106,7 +1117,7 @@ namespace arith { if (p.second) new_eq_eh(e); else if (is_eq(e.v1(), e.v2())) { - mk_diseq_axiom(e); + mk_diseq_axiom(e.v1(), e.v2()); found_diseq = true; break; } @@ -1467,8 +1478,6 @@ namespace arith { add_lemmas(); break; case l_true: - if (assume_eqs()) - return l_false; break; case l_undef: break; diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index b3d10ab52..f3e8d1407 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -355,7 +355,7 @@ namespace arith { literal is_bound_implied(lp::lconstraint_kind k, rational const& value, api_bound const& b) const; void assert_bound(bool is_true, api_bound& b); void mk_eq_axiom(bool is_eq, euf::th_eq const& eq); - void mk_diseq_axiom(euf::th_eq const& eq); + void mk_diseq_axiom(theory_var v1, theory_var v2); void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r); api_bound* mk_var_bound(sat::literal lit, theory_var v, lp_api::bound_kind bk, rational const& bound); lp::lconstraint_kind bound2constraint_kind(bool is_int, lp_api::bound_kind bk, bool is_true); From e7c17e68b8281996b2d3d150de958dbceae21ec8 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Sat, 28 Oct 2023 21:46:43 +0200 Subject: [PATCH 288/428] Fixed next_split call in pop (#6966) * Give users ability to see if propagation failed * Skip propagations in the new core if they are already satisfied * Fix registration in final * Don't make it too complicated... * Fixed next_split when called in pop Made delay_units available even without quantifiers * Missing push calls before "decide"-callback --- src/smt/smt_context.cpp | 1 - src/smt/theory_user_propagator.cpp | 69 +++++++++++++++++------------- src/smt/theory_user_propagator.h | 3 +- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 577123ec9..6d90653ba 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4122,7 +4122,6 @@ namespace smt { // Moreover, I backtrack only one level. bool delay_forced_restart = m_fparams.m_delay_units && - internalized_quantifiers() && num_lits == 1 && conflict_lvl > m_search_lvl + 1 && !m.proofs_enabled() && diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index 6735272a1..84a6799fb 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -62,12 +62,12 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { enode* n = ensure_enode ? this->ensure_enode(e) : ctx.get_enode(e); if (is_attached_to_var(n)) return; - + theory_var v = mk_var(n); m_var2expr.reserve(v + 1); m_var2expr[v] = term; m_expr2var.setx(term->get_id(), v, null_theory_var); - + if (m.is_bool(e) && !ctx.b_internalized(e)) { bool_var bv = ctx.mk_bool_var(e); ctx.set_var_theory(bv, get_id()); @@ -79,7 +79,6 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) { literal_vector explain; if (ctx.is_fixed(n, r, explain)) m_prop.push_back(prop_info(explain, v, r)); - } bool theory_user_propagator::propagate_cb( @@ -109,14 +108,19 @@ void theory_user_propagator::register_cb(expr* e) { bool theory_user_propagator::next_split_cb(expr* e, unsigned idx, lbool phase) { if (e == nullptr) { // clear - m_next_split_var = null_bool_var; + m_next_split_var = nullptr; + return true; + } + if (!ctx.e_internalized(e)) { + // We may not eagerly internalize it (might crash when done in pop) => delay + m_next_split_var = e; return true; } - ensure_enode(e); bool_var b = enode_to_bool(ctx.get_enode(e), idx); if (b == null_bool_var || ctx.get_assignment(b) != l_undef) return false; - m_next_split_var = b; + m_next_split_var = e; + m_next_split_idx = idx; m_next_split_phase = phase; return true; } @@ -150,7 +154,7 @@ final_check_status theory_user_propagator::final_check_eh() { m_final_eh(m_user_context, this); } catch (...) { - throw default_exception("Exception thrown in \"final\"-callback"); + throw default_exception("Exception thrown in \"final\"-callback"); } CTRACE("user_propagate", can_propagate(), tout << "can propagate\n"); propagate(); @@ -170,11 +174,11 @@ void theory_user_propagator::new_fixed_eh(theory_var v, expr* value, unsigned nu ctx.push_trail(insert_map(m_fixed, v)); m_id2justification.setx(v, literal_vector(num_lits, jlits), literal_vector()); try { - m_fixed_eh(m_user_context, this, var2expr(v), value); - } - catch (...) { + m_fixed_eh(m_user_context, this, var2expr(v), value); + } + catch (...) { throw default_exception("Exception thrown in \"fixed\"-callback"); - } + } } bool_var theory_user_propagator::enode_to_bool(enode* n, unsigned idx) { @@ -191,18 +195,18 @@ bool_var theory_user_propagator::enode_to_bool(enode* n, unsigned idx) { void theory_user_propagator::decide(bool_var& var, bool& is_pos) { if (!m_decide_eh) return; - + const bool_var_data& d = ctx.get_bdata(var); - - if (!d.is_enode() && !d.is_theory_atom()) + + if (!d.is_enode() && !d.is_theory_atom()) return; - - enode* original_enode = nullptr; + + enode* original_enode = nullptr; unsigned original_bit = 0; bv_util bv(m); theory* th = nullptr; theory_var v = null_theory_var; - + // get the associated theory if (!d.is_enode()) { // it might be a value that does not have an enode @@ -216,7 +220,7 @@ void theory_user_propagator::decide(bool_var& var, bool& is_pos) { th = ctx.get_theory(d.get_theory()); } } - + if (v == null_theory_var && !th) return; @@ -225,7 +229,7 @@ void theory_user_propagator::decide(bool_var& var, bool& is_pos) { if (v == null_theory_var) { // it is not a registered boolean value but it is a bitvector - auto registered_bv = ((theory_bv *) th)->get_bv_with_theory(var, get_family_id()); + auto registered_bv = ((theory_bv*) th)->get_bv_with_theory(var, get_family_id()); if (!registered_bv.first) // there is no registered bv associated with the bit return; @@ -237,6 +241,7 @@ void theory_user_propagator::decide(bool_var& var, bool& is_pos) { // call the registered callback unsigned new_bit = original_bit; + force_push(); expr *e = var2expr(v); m_decide_eh(m_user_context, this, e, new_bit, is_pos); @@ -252,12 +257,16 @@ void theory_user_propagator::decide(bool_var& var, bool& is_pos) { } bool theory_user_propagator::get_case_split(bool_var& var, bool& is_pos) { - if (m_next_split_var == null_bool_var) + if (m_next_split_var == nullptr) return false; - - var = m_next_split_var; + ensure_enode(m_next_split_var); + bool_var b = enode_to_bool(ctx.get_enode(m_next_split_var), m_next_split_idx); + if (b == null_bool_var || ctx.get_assignment(b) != l_undef) + return false; + var = b; is_pos = ctx.guess(var, m_next_split_phase); - m_next_split_var = null_bool_var; + m_next_split_var = nullptr; + m_next_split_idx = 0; m_next_split_phase = l_undef; return true; } @@ -289,11 +298,11 @@ bool theory_user_propagator::can_propagate() { void theory_user_propagator::propagate_consequence(prop_info const& prop) { justification* js; - m_lits.reset(); + m_lits.reset(); m_eqs.reset(); - for (expr* id : prop.m_ids) + for (expr* id: prop.m_ids) m_lits.append(m_id2justification[expr2var(id)]); - for (auto const& [a,b] : prop.m_eqs) + for (auto const& [a, b]: prop.m_eqs) if (a != b) m_eqs.push_back(enode_pair(get_enode(expr2var(a)), get_enode(expr2var(b)))); DEBUG_CODE(for (auto const& [a, b] : m_eqs) VERIFY(a->get_root() == b->get_root());); @@ -328,7 +337,7 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { lit = mk_literal(fn); } else - lit = mk_literal(prop.m_conseq); + lit = mk_literal(prop.m_conseq); ctx.mark_as_relevant(lit); #if 0 @@ -339,8 +348,8 @@ void theory_user_propagator::propagate_consequence(prop_info const& prop) { ctx.assign(lit, js); #endif - -#if 1 + +#if 1 m_lits.push_back(lit); ctx.mk_th_lemma(get_id(), m_lits); #endif @@ -358,7 +367,7 @@ void theory_user_propagator::propagate() { return; TRACE("user_propagate", tout << "propagating queue head: " << m_qhead << " prop queue: " << m_prop.size() << "\n"); force_push(); - + unsigned qhead = m_to_add_qhead; if (qhead < m_to_add.size()) { for (; qhead < m_to_add.size(); ++qhead) diff --git a/src/smt/theory_user_propagator.h b/src/smt/theory_user_propagator.h index 2f045b0ba..4b365ef7a 100644 --- a/src/smt/theory_user_propagator.h +++ b/src/smt/theory_user_propagator.h @@ -83,7 +83,8 @@ namespace smt { expr_ref_vector m_to_add; unsigned_vector m_to_add_lim; unsigned m_to_add_qhead = 0; - bool_var m_next_split_var = null_bool_var; + expr* m_next_split_var = nullptr; + unsigned m_next_split_idx = 0; lbool m_next_split_phase = l_undef; expr* var2expr(theory_var v) { return m_var2expr.get(v); } From bd8e5eee4b78587bf45ea9c12691914869fb4378 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 10:21:24 -0700 Subject: [PATCH 289/428] add simplification experiment (disabled) for tracking, some reshuffling of equation/fixed_equation structs Signed-off-by: Nikolaj Bjorner --- src/math/lp/explanation.h | 14 ++ src/math/lp/int_solver.cpp | 231 ++++++++++++++++++++++++++++++ src/math/lp/int_solver.h | 6 +- src/math/lp/lp_types.h | 1 + src/math/lp/monomial_bounds.cpp | 4 +- src/math/lp/nla_core.cpp | 13 +- src/math/lp/nla_core.h | 9 +- src/math/lp/nla_solver.cpp | 8 +- src/math/lp/nla_solver.h | 5 +- src/math/lp/nla_types.h | 12 -- src/sat/smt/arith_diagnostics.cpp | 1 - src/smt/theory_lra.cpp | 21 ++- 12 files changed, 286 insertions(+), 39 deletions(-) diff --git a/src/math/lp/explanation.h b/src/math/lp/explanation.h index b7f721a30..960c5fb4a 100644 --- a/src/math/lp/explanation.h +++ b/src/math/lp/explanation.h @@ -121,4 +121,18 @@ public: } }; + + struct equality { + lp::lpvar i, j; + lp::explanation e; + equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e):i(i),j(j),e(e) {} + }; + + struct fixed_equality { + lp::lpvar v; + rational k; + lp::explanation e; + fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e):v(v),k(k),e(e) {} + }; + } diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index e248a0081..35a082da9 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -683,5 +683,236 @@ namespace lp { return result; } + void int_solver::simplify(std::function& is_root) { + +#if 0 + + // in-processing simplification can go here, such as bounds improvements. + + if (!lra.is_feasible()) { + lra.find_feasible_solution(); + if (!lra.is_feasible()) + return; + } + + stopwatch sw; + explanation exp1, exp2; + + // + // tighten integer bounds + // It is a weak method as it ownly strengthens bounds if + // variables are already at one of the to-be discovered bounds. + // + sw.start(); + unsigned changes = 0; + auto const& constraints = lra.constraints(); + auto print_var = [this](lpvar j) { + if (lra.column_corresponds_to_term(j)) { + std::stringstream strm; + lra.print_column_info(j, strm); + return strm.str(); + } + else + return std::string("j") + std::to_string(j); + }; + unsigned start = random(); + unsigned num_checks = 0; + for (lpvar j0 = 0; j0 < lra.column_count(); ++j0) { + lpvar j = (j0 + start) % lra.column_count(); + + if (num_checks > 1000) + break; + if (is_fixed(j)) + continue; + if (!lra.column_is_int(j)) + continue; + rational value = get_value(j).x; + bool tight_lower = false, tight_upper = false; + u_dependency* dep; + + if (!value.is_int()) + continue; + + bool at_up = at_upper(j); + + if (!at_lower(j)) { + ++num_checks; + lra.push(); + auto k = lp::lconstraint_kind::LE; + lra.update_column_type_and_bound(j, k, (value - 1).to_mpq(), nullptr); + lra.find_feasible_solution(); + if (!lra.is_feasible()) { + tight_upper = true; + ++changes; + lra.get_infeasibility_explanation(exp1); +#if 0 + display_column(std::cout, j); + std::cout << print_var(j) << " >= " << value << "\n"; + unsigned i = 0; + for (auto p : exp1) { + std::cout << "(" << p.ci() << ")"; + constraints.display(std::cout, print_var, p.ci()); + if (++i < exp1.size()) + std::cout << " "; + } +#endif + } + lra.pop(1); + if (tight_upper) { + dep = nullptr; + for (auto& cc : exp1) + dep = lra.join_deps(dep, constraints[cc.ci()].dep()); + lra.update_column_type_and_bound(j, lp::lconstraint_kind::GE, value.to_mpq(), dep); + } + } + + if (!at_up) { + ++num_checks; + lra.push(); + auto k = lp::lconstraint_kind::GE; + lra.update_column_type_and_bound(j, k, (value + 1).to_mpq(), nullptr); + lra.find_feasible_solution(); + if (!lra.is_feasible()) { + tight_lower = true; + ++changes; + lra.get_infeasibility_explanation(exp1); +#if 0 + display_column(std::cout, j); + std::cout << print_var(j) << " <= " << value << "\n"; + unsigned i = 0; + for (auto p : exp1) { + std::cout << "(" << p.ci() << ")"; + constraints.display(std::cout, print_var, p.ci()); + if (++i < exp1.size()) + std::cout << " "; + } +#endif + } + lra.pop(1); + if (tight_lower) { + dep = nullptr; + for (auto& cc : exp1) + dep = lra.join_deps(dep, constraints[cc.ci()].dep()); + lra.update_column_type_and_bound(j, lp::lconstraint_kind::LE, value.to_mpq(), dep); + } + } + } + sw.stop(); + std::cout << "changes " << changes << " columns " << lra.column_count() << " time: " << sw.get_seconds() << "\n"; + std::cout.flush(); + + // + // identify equalities + // + + m_equalities.reset(); + map value2roots; + + vector> coeffs; + coeffs.push_back({-rational::one(), 0}); + coeffs.push_back({rational::one(), 0}); + + num_checks = 0; + + // make sure values are sampled with respect to the same state of the Simplex. + vector values; + for (lpvar j = 0; j < lra.column_count(); ++j) + values.push_back(get_value(j).x); + + sw.reset(); + sw.start(); + start = random(); + for (lpvar j0 = 0; j0 < lra.column_count(); ++j0) { + lpvar j = (j0 + start) % lra.column_count(); + if (is_fixed(j)) + continue; + if (!lra.column_is_int(j)) + continue; + if (!is_root(j)) + continue; + rational value = values[j]; + if (!value2roots.contains(value)) { + unsigned_vector vec; + vec.push_back(j); + value2roots.insert(value, vec); + continue; + } + auto& roots = value2roots.find(value); + bool has_eq = false; + // + // Super inefficient check. There are better ways. + // 1. call into equality finder: + // the cheap equality finder can also be used. + // 2. value sweeping: + // update partitions of values based on feasible tableaus + // instead of having just the values vector use the values + // collected when the find_feasible_solution succeeds with + // a new assignment. + // 3. a more expensive equality finder: + // use the tableau to extract equalities from tight rows. + // If x = y is implied, there is a set of rows that link x and y + // and such that the variables are at their bounds. + // 4. retain information between calls: + // If simplification is invoked at the same backtracking level (or above) + // form the previous call and it is established that x <= y (but not x == y), then no need to + // recheck the inequality x <= y. + for (auto k : roots) { + bool le = false, ge = false; + u_dependency* dep = nullptr; + lra.push(); + coeffs[0].second = j; + coeffs[1].second = k; + lp::lpvar term_index = lra.add_term(coeffs, UINT_MAX); + term_index = lra.map_term_index_to_column_index(term_index); + lra.push(); + lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::GE, mpq(1), nullptr); + lra.find_feasible_solution(); + if (!lra.is_feasible()) { + lra.get_infeasibility_explanation(exp1); + le = true; + } + lra.pop(1); + ++num_checks; + if (le) { + lra.push(); + lra.update_column_type_and_bound(term_index, lp::lconstraint_kind::LE, mpq(-1), nullptr); + lra.find_feasible_solution(); + if (!lra.is_feasible()) { + lra.get_infeasibility_explanation(exp2); + exp1.add_expl(exp2); + ge = true; + } + lra.pop(1); + ++num_checks; + } + lra.pop(1); + if (le && ge) { + has_eq = true; + m_equalities.push_back({j, k, exp1}); + break; + } + // artificial throttle. + if (num_checks > 10000) + break; + } + if (!has_eq) + roots.push_back(j); + + // artificial throttle. + if (num_checks > 10000) + break; + } + + sw.stop(); + std::cout << "equalities " << m_equalities.size() << " num checks " << num_checks << " time: " << sw.get_seconds() << "\n"; + std::cout.flush(); + + // + // Cuts? Eg. for 0-1 variables or bounded integers? + // + +#endif + } + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 1bd3f1c3d..f1faedf35 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -67,6 +67,8 @@ class int_solver { bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise hnf_cutter m_hnf_cutter; unsigned m_hnf_cut_period; + + vector m_equalities; public: int_solver(lar_solver& lp); @@ -84,7 +86,9 @@ public: const impq & get_value(unsigned j) const; bool at_lower(unsigned j) const; bool at_upper(unsigned j) const; - + void simplify(std::function& is_root); + vector const& equalities() const { return m_equalities; } + private: // lia_move patch_nbasic_columns(); bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); diff --git a/src/math/lp/lp_types.h b/src/math/lp/lp_types.h index c69dbe10b..f770fe956 100644 --- a/src/math/lp/lp_types.h +++ b/src/math/lp/lp_types.h @@ -92,6 +92,7 @@ public: }; + } inline std::ostream& operator<<(std::ostream& out, lp::tv const& t) { diff --git a/src/math/lp/monomial_bounds.cpp b/src/math/lp/monomial_bounds.cpp index e54f82682..9c99fa8c3 100644 --- a/src/math/lp/monomial_bounds.cpp +++ b/src/math/lp/monomial_bounds.cpp @@ -391,8 +391,8 @@ namespace nla { void monomial_bounds::propagate_nonfixed(monic const& m, rational const& k, lpvar w) { vector> coeffs; - coeffs.push_back(std::make_pair(-k, w)); - coeffs.push_back(std::make_pair(rational::one(), m.var())); + coeffs.push_back({-k, w}); + coeffs.push_back({rational::one(), m.var()}); lp::lpvar term_index = c().lra.add_term(coeffs, UINT_MAX); auto* dep = explain_fixed(m, k); term_index = c().lra.map_term_index_to_column_index(term_index); diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 2ec4a3a10..d9fc43057 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -17,7 +17,7 @@ Author: #include "math/grobner/pdd_solver.h" #include "math/dd/pdd_interval.h" #include "math/dd/pdd_eval.h" -namespace nla { +using namespace nla; typedef lp::lar_term term; @@ -1785,20 +1785,17 @@ void core::set_use_nra_model(bool m) { } } -void core::collect_statistics(::statistics & st) { -} - void core::propagate() { clear(); m_monomial_bounds.unit_propagate(); m_monics_with_changed_bounds.reset(); } - void core::simplify() { - // in-processing simplifiation can go here, such as bounds improvements. - } +void core::simplify() { + // in-processing simplifiation can go here, such as bounds improvements. + +} -} // end of nla diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 25156e8b6..29effd4e4 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -74,8 +74,8 @@ class core { std::function m_relevant; vector m_lemmas; vector m_literals; - vector m_equalities; - vector m_fixed_equalities; + vector m_equalities; + vector m_fixed_equalities; indexed_uint_set m_to_refine; indexed_uint_set m_monics_with_changed_bounds; tangents m_tangents; @@ -420,11 +420,10 @@ public: bool has_real(const monic& m) const; void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } - void collect_statistics(::statistics&); vector const& lemmas() const { return m_lemmas; } vector const& literals() const { return m_literals; } - vector const& equalities() const { return m_equalities; } - vector const& fixed_equalities() const { return m_fixed_equalities; } + vector const& equalities() const { return m_equalities; } + vector const& fixed_equalities() const { return m_fixed_equalities; } bool check_feasible() const { return m_check_feasible; } void add_fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e) { m_fixed_equalities.push_back({v, k, e}); } diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index f4d09810e..4b501a39e 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -88,10 +88,6 @@ namespace nla { return m_core->m_nra.value(v); } - void solver::collect_statistics(::statistics & st) { - m_core->collect_statistics(st); - } - // ensure r = x^y, add abstraction/refinement lemmas lbool solver::check_power(lpvar r, lpvar x, lpvar y) { return m_core->check_power(r, x, y); @@ -109,11 +105,11 @@ namespace nla { return m_core->literals(); } - vector const& solver::equalities() const { + vector const& solver::equalities() const { return m_core->equalities(); } - vector const& solver::fixed_equalities() const { + vector const& solver::fixed_equalities() const { return m_core->fixed_equalities(); } diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 9e650c127..7da05c3e4 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -47,11 +47,10 @@ namespace nla { core& get_core(); nlsat::anum_manager& am(); nlsat::anum const& am_value(lp::var_index v) const; - void collect_statistics(::statistics & st); vector const& lemmas() const; vector const& literals() const; - vector const& fixed_equalities() const; - vector const& equalities() const; + vector const& fixed_equalities() const; + vector const& equalities() const; bool check_feasible() const { return m_core->check_feasible(); } }; } diff --git a/src/math/lp/nla_types.h b/src/math/lp/nla_types.h index 3930a62a9..cb62c5a30 100644 --- a/src/math/lp/nla_types.h +++ b/src/math/lp/nla_types.h @@ -25,18 +25,6 @@ namespace nla { typedef lp::var_index lpvar; const lpvar null_lpvar = UINT_MAX; - struct equality { - lp::lpvar i, j; - lp::explanation e; - equality(lp::lpvar i, lp::lpvar j, lp::explanation const& e):i(i),j(j),e(e) {} - }; - - struct fixed_equality { - lp::lpvar v; - rational k; - lp::explanation e; - fixed_equality(lp::lpvar v, rational const& k, lp::explanation const& e):v(v),k(k),e(e) {} - }; inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); } diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index e9d989545..47e3ee551 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -90,7 +90,6 @@ namespace arith { void solver::collect_statistics(statistics& st) const { m_stats.collect_statistics(st); lp().settings().stats().collect_statistics(st); - if (m_nla) m_nla->collect_statistics(st); } void solver::explain_assumptions(lp::explanation const& e) { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 4de1e732b..fffa85d44 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1091,6 +1091,26 @@ public: void restart_eh() { m_arith_eq_adapter.restart_eh(); +#if 0 + // experiment + if (m_lia) { + std::function is_root = [&](unsigned j) { + theory_var v = lp().local_to_external(j); + if (v < 0) + return false; + auto* n = get_enode(v); + if (!th.is_relevant_and_shared(n)) + return false; + if (n->is_root()) + return true; + theory_var w = n->get_root()->get_th_var(get_id()); + return w == v; + }; + m_lia->simplify(is_root); + for (auto const& [i, j, e] : m_lia->equalities()) + add_eq(i, j, e, false); + } +#endif if (m_nla) m_nla->simplify(); } @@ -3843,7 +3863,6 @@ public: m_arith_eq_adapter.collect_statistics(st); m_stats.collect_statistics(st); lp().settings().stats().collect_statistics(st); - if (m_nla) m_nla->collect_statistics(st); } /* From 9d57bdd2ef2843ef6d1f8ff216c06854741fcc08 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 30 Oct 2023 00:29:42 +0000 Subject: [PATCH 290/428] Assorted fixes for floats (#6968) * Improve 4be26eb5430672196df96b0fe0faa25c4bb085ea * Add-on to 0f4f32c5d020e4d63fa84a64f3136f6ad84aefb2 * Fix mk_numeral * Fix corner-case in fp.div * Fixes for corner-cases in mk_to_fp_(un)signed * Fix out-of-range results in mpf_manager::fma * Further adjustments for fp.to_fp_(un)signed * fp.to_fp from real can't be NaN * fp.to_fp from reals: add bounds * Fix NaN encodings in theory_fpa. * Fix fp.fma rounding with tiny floats * Fix literal creation order in theory_fpa --- src/ast/fpa/bv2fpa_converter.cpp | 4 +- src/ast/fpa/fpa2bv_converter.cpp | 162 +++++++++++++++++++------------ src/ast/fpa/fpa2bv_converter.h | 1 + src/smt/theory_fpa.cpp | 34 ++++++- src/smt/theory_fpa.h | 3 +- src/util/mpf.cpp | 48 ++++----- 6 files changed, 162 insertions(+), 90 deletions(-) diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 00e9d71c3..cc2abf01c 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -324,7 +324,7 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * expr_ref else_value(m.mk_app(to_bv_i, dom.size(), dom.data()), m); result->set_else(else_value); } - else if (m_fpa_util.is_to_real(f)) { + else if (m_fpa_util.is_to_real(f)) { SASSERT(dom.size() == 1); func_decl_ref to_real_i(m.mk_func_decl(fid, OP_FPA_TO_REAL_I, 0, nullptr, dom.size(), dom.data()), m); expr_ref else_value(m.mk_app(to_real_i, dom.size(), dom.data()), m); @@ -508,7 +508,7 @@ void bv2fpa_converter::convert_uf2bvuf(model_core * mc, model_core * target_mode } } - TRACE("bv2fpa", tout << "Target model: " << *target_model; ); + TRACE("bv2fpa", tout << "Target model: " << *target_model << std::endl; ); } void bv2fpa_converter::display(std::ostream & out) { diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index ab13e751a..f866e3c23 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -147,36 +147,11 @@ void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * a void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 0); - sort* s = f->get_range(); - if (f->get_num_parameters() == 1) { - SASSERT(f->get_parameter(0).is_external()); - unsigned p_id = f->get_parameter(0).get_ext_id(); - mpf const& v = m_plugin->get_value(p_id); - mk_numeral(s, v, result); - return; - } - scoped_mpf v(m_mpf_manager); - unsigned ebits = m_util.get_ebits(s), sbits = m_util.get_sbits(s); - switch (f->get_decl_kind()) { - case OP_FPA_PLUS_INF: - m_util.fm().mk_pinf(ebits, sbits, v); - break; - case OP_FPA_MINUS_INF: - m_util.fm().mk_ninf(ebits, sbits, v); - break; - case OP_FPA_NAN: - m_util.fm().mk_nan(ebits, sbits, v); - break; - case OP_FPA_PLUS_ZERO: - m_util.fm().mk_pzero(ebits, sbits, v); - break; - case OP_FPA_MINUS_ZERO: - m_util.fm().mk_nzero(ebits, sbits, v); - break; - default: - UNREACHABLE(); - } - mk_numeral(s, v, result); + scoped_mpf v(m_mpf_manager); + expr_ref a(m); + a = m.mk_app(f, num, args); + m_util.is_numeral(a, v); + mk_numeral(f->get_range(), v, result); } void fpa2bv_converter::mk_numeral(sort * s, mpf const & v, expr_ref & result) { @@ -941,8 +916,8 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); - expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m), c8(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m), v9(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); @@ -998,6 +973,9 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits)); b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig); + dbg_decouple("fpa2bv_div_a_sig_ext", a_sig_ext); + dbg_decouple("fpa2bv_div_b_sig_ext", b_sig_ext); + expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); @@ -1017,14 +995,21 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & expr_ref quotient(m); // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, a_sig_ext, b_sig_ext); - dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); - expr_ref sticky(m); + expr_ref sticky(m), upper(m), upper_reduced(m), too_large(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); + upper = m_bv_util.mk_extract(sbits + sbits + extra_bits-1, extra_bits+sbits+2, quotient); + upper_reduced = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, upper.get()); + too_large = m.mk_eq(upper_reduced, m_bv_util.mk_numeral(1, 1)); + c8 = too_large; + mk_ite(signs_xor, ninf, pinf, v8); + dbg_decouple("fpa2bv_div_res_sig_p4", res_sig); + dbg_decouple("fpa2bv_div_upper", upper); + dbg_decouple("fpa2bv_div_too_large", too_large); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); @@ -1042,10 +1027,14 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); - round(s, rm, res_sgn, res_sig, res_exp, v8); + dbg_decouple("fpa2bv_div_res_sig", res_sig); + dbg_decouple("fpa2bv_div_res_exp", res_exp); + + round(s, rm, res_sgn, res_sig, res_exp, v9); // And finally, we tie them together. - mk_ite(c7, v7, v8, result); + mk_ite(c8, v8, v9, result); + mk_ite(c7, v7, result, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); @@ -2809,8 +2798,46 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * expr * e = m.mk_eq(m_util.mk_to_real(result), x); m_extra_assertions.push_back(e); - // x = 0 -> result = 0+ - m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_eq(result, m_util.mk_pzero(result->get_sort())))); + + expr_ref r_is_nan(m); + mk_is_nan(result, r_is_nan); + m_extra_assertions.push_back(m.mk_not(r_is_nan)); + + rational min_real, max_real; + const mpz& max_exp_z = m_mpf_manager.m_powers2.m1(ebits-1); + SASSERT(m_mpz_manager.is_uint(max_exp_z)); + unsigned max_exp = m_mpz_manager.get_uint(max_exp_z); + rational max_sig = m_mpf_manager.m_powers2.m1(sbits) / m_mpf_manager.m_powers2(sbits-1); + max_real = max_sig * rational(m_mpf_manager.m_powers2(max_exp)); + TRACE("fpa2bv_to_real", tout << "max exp: " << max_exp << " max real: " << max_real << std::endl;); + + expr_ref r_is_pinf(m), r_is_ninf(m); + mk_is_pinf(result, r_is_pinf); + mk_is_ninf(result, r_is_ninf); + + expr_ref e_max_real(m), e_max_real_neg(m); + e_max_real = m_arith_util.mk_numeral(max_real, false); + e_max_real_neg = m_arith_util.mk_numeral(-max_real, false); + + expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m); + mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_nta); + mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_nte); + mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_tp); + mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_tn); + mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_tz); + + expr_ref implies_gt_max_real(m), implies_lt_min_real(m); + implies_gt_max_real = m.mk_implies(r_is_pinf, m.mk_and(rm_tp, m_arith_util.mk_gt(x, e_max_real))); + implies_lt_min_real = m.mk_implies(r_is_ninf, m.mk_and(rm_tn, m_arith_util.mk_lt(x, e_max_real_neg))); + + m_extra_assertions.push_back(implies_gt_max_real); + m_extra_assertions.push_back(implies_lt_min_real); + + // x = 0 -> result = +0/-0 + expr_ref pzero(m), nzero(m); + mk_pzero(result->get_sort(), pzero); + mk_nzero(result->get_sort(), nzero); + m_extra_assertions.push_back(m.mk_implies(m.mk_eq(x, zero), m.mk_or(m.mk_eq(result, pzero), m.mk_eq(result, nzero)))); } SASSERT(is_well_sorted(m, result)); @@ -2854,19 +2881,13 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, e.to_mpq().numerator(), q.to_mpq()); m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, e.to_mpq().numerator(), q.to_mpq()); - app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); - a_nte = m_plugin->mk_numeral(nte); - a_nta = m_plugin->mk_numeral(nta); - a_tp = m_plugin->mk_numeral(tp); - a_tn = m_plugin->mk_numeral(tn); - a_tz = m_plugin->mk_numeral(tz); - expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); - mk_numeral(a_nte->get_decl(), 0, nullptr, bv_nte); - mk_numeral(a_nta->get_decl(), 0, nullptr, bv_nta); - mk_numeral(a_tp->get_decl(), 0, nullptr, bv_tp); - mk_numeral(a_tn->get_decl(), 0, nullptr, bv_tn); - mk_numeral(a_tz->get_decl(), 0, nullptr, bv_tz); + sort *s = f->get_range(); + mk_numeral(s, nte, bv_nte); + mk_numeral(s, nta, bv_nta); + mk_numeral(s, tp, bv_tp); + mk_numeral(s, tn, bv_tn); + mk_numeral(s, tz, bv_tz); expr_ref c1(m), c2(m), c3(m), c4(m); c1 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); @@ -3003,30 +3024,42 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const unsigned bv_sz = m_bv_util.get_bv_size(x); SASSERT(m_bv_util.get_bv_size(rm) == 3); + expr_ref rm_is_to_neg(m); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + expr_ref bv1_1(m), bv0_sz(m); bv1_1 = m_bv_util.mk_numeral(1, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); - expr_ref is_zero(m), pzero(m); + expr_ref is_zero(m), pzero(m), nzero(m); is_zero = m.mk_eq(x, bv0_sz); mk_pzero(f, pzero); + mk_nzero(f, nzero); - // Special case: x == 0 -> p/n zero + // Special case: x == 0 -> +zero expr_ref c1(m), v1(m); c1 = is_zero; - v1 = pzero; + v1 = pzero; // No -zero? zeros_consistent_4.smt2 requires +zero. // Special case: x != 0 - expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); + expr_ref sign_bit(m), exp_too_large(m), sig_4(m), exp_2(m), rest(m); expr_ref is_neg(m), x_abs(m), neg_x(m); - is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); - is_neg = m.mk_eq(is_neg_bit, bv1_1); - neg_x = m_bv_util.mk_bv_neg(x); // overflow problem? + sign_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); + rest = m_bv_util.mk_extract(bv_sz - 2, 0, x); + dbg_decouple("fpa2bv_to_fp_signed_rest", rest); + is_neg = m.mk_eq(sign_bit, bv1_1); + neg_x = m_bv_util.mk_bv_neg(x); x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) // bv_sz-2 is the "1.0" bit for the rounder. + expr_ref is_max_neg(m); + is_max_neg = m.mk_and(is_neg, m.mk_eq(rest, m_bv_util.mk_numeral(0, bv_sz-1))); + dbg_decouple("fpa2bv_to_fp_signed_is_max_neg", is_max_neg); + + x_abs = m.mk_ite(is_max_neg, m_bv_util.mk_concat(bv1_1, m_bv_util.mk_numeral(0, bv_sz-1)), x_abs); + dbg_decouple("fpa2bv_to_fp_signed_x_abs", x_abs); expr_ref lz(m); mk_leading_zeros(x_abs, bv_sz, lz); @@ -3061,6 +3094,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed + s_exp = m.mk_ite(is_max_neg, m_bv_util.mk_bv_sub(s_exp, m_bv_util.mk_numeral(1, bv_sz)), s_exp); SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); dbg_decouple("fpa2bv_to_fp_signed_s_exp", s_exp); @@ -3093,7 +3127,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large); expr_ref sgn(m), sig(m), exp(m); - sgn = is_neg_bit; + sgn = sign_bit; sig = sig_4; exp = exp_2; @@ -3132,6 +3166,9 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con rm = to_app(args[0])->get_arg(0); x = args[1]; + expr_ref rm_is_to_neg(m); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + dbg_decouple("fpa2bv_to_fp_unsigned_x", x); unsigned ebits = m_util.get_ebits(f->get_range()); @@ -3143,14 +3180,15 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con bv0_1 = m_bv_util.mk_numeral(0, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); - expr_ref is_zero(m), pzero(m); + expr_ref is_zero(m), pzero(m), nzero(m); is_zero = m.mk_eq(x, bv0_sz); mk_pzero(f, pzero); + mk_nzero(f, nzero); - // Special case: x == 0 -> p/n zero + // Special case: x == 0 -> +zero expr_ref c1(m), v1(m); c1 = is_zero; - v1 = pzero; + v1 = pzero; // No nzero? // Special case: x != 0 expr_ref exp_too_large(m), sig_4(m), exp_2(m); @@ -3194,7 +3232,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); // the remaining bits are 0 if ebits is large enough. - exp_too_large = m.mk_false(); // This is always in range. + exp_too_large = m.mk_false(); // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. // exp < bv_sz (+sign bit which is [0]) diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index 19315129a..e237c0dcd 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -208,6 +208,7 @@ protected: private: void mk_nan(sort * s, expr_ref & result); + void mk_nzero(sort * s, expr_ref & result); void mk_pzero(sort * s, expr_ref & result); void mk_zero(sort * s, expr_ref & sgn, expr_ref & result); diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index 2ecc17c45..f99b86684 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -220,7 +220,7 @@ namespace smt { TRACE("t_fpa_detail", tout << "asserting " << mk_ismt2_pp(e, m) << "\n";); if (m.has_trace_stream()) log_axiom_instantiation(e); ctx.internalize(e, false); - if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; + if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); @@ -239,11 +239,11 @@ namespace smt { if (ctx.b_internalized(atom)) return true; - ctx.internalize(atom->get_args(), atom->get_num_args(), false); - literal l(ctx.mk_bool_var(atom)); ctx.set_var_theory(l.var(), get_id()); + ctx.internalize(atom->get_args(), atom->get_num_args(), false); + expr_ref bv_atom(m_rw.convert_atom(m_th_rw, atom)); expr_ref bv_atom_w_side_c(m), atom_eq(m); bv_atom_w_side_c = m.mk_and(bv_atom, mk_side_conditions()); @@ -253,6 +253,18 @@ namespace smt { return true; } + void theory_fpa::mk_bv_nan(sort * s, expr_ref & result) { + SASSERT(m_fpa_util.is_float(s)); + unsigned sbits = m_fpa_util.get_sbits(s); + unsigned ebits = m_fpa_util.get_ebits(s); + expr_ref exp(m), sgn(m), sig(m); + exp = m_bv_util.mk_numeral(m_fpa_util.fm().m_powers2.m1(ebits), ebits); + sgn = m_bv_util.mk_numeral(0, 1); + sig = m_bv_util.mk_numeral(1, sbits - 1); + expr * args[3] = {sgn, exp, sig}; + result = m_bv_util.mk_concat(3, args); + } + bool theory_fpa::internalize_term(app * term) { TRACE("t_fpa_internalize", tout << "internalizing term: " << mk_ismt2_pp(term, m) << "\n";); SASSERT(term->get_family_id() == get_family_id()); @@ -286,6 +298,22 @@ namespace smt { default: /* ignore */; } + expr * owner = e->get_expr(); + sort * s = owner->get_sort(); + if (m_fpa_util.is_float(s)) + { + TRACE("t_fpa", tout << "extra nan constraint for: " << mk_ismt2_pp(owner, m) << "\n";); + expr_ref wrapped(m), is_nan(m), bv_nan(m); + app_ref impl(m); + wrapped = m_converter.wrap(owner); + is_nan = m_fpa_util.mk_is_nan(owner); + mk_bv_nan(s, bv_nan); + impl = m.mk_or(m.mk_and(is_nan, m.mk_eq(wrapped, bv_nan)), + m.mk_and(m.mk_not(is_nan), m.mk_not(m.mk_eq(wrapped, bv_nan)))); + assert_cnstr(impl); + assert_cnstr(mk_side_conditions()); + } + if (!ctx.relevancy()) relevant_eh(term); } diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 9aa70d9bf..88927998e 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -121,10 +121,11 @@ namespace smt { void attach_new_th_var(enode * n); void assert_cnstr(expr * e); - enode* ensure_enode(expr* e); enode* get_root(expr* a) { return ensure_enode(a)->get_root(); } app* get_ite_value(expr* e); + + void mk_bv_nan(sort * s, expr_ref & result); }; }; diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index d2f30e708..605b12aab 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -913,34 +913,38 @@ void mpf_manager::fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf co TRACE("mpf_dbg", tout << "R*= " << to_string_binary(res, 2, 0) << " (renormalized, delta=" << renorm_delta << ")" << std::endl;); - if (exp(res) <= mk_max_exp(x.ebits)) - { - set(o, x.ebits, x.sbits, res.sign(), res.exponent(), mpz(0)); + set(o, x.ebits, x.sbits, res.sign(), res.exponent(), mpz(0)); - if (x.sbits >= 4) { - m_mpz_manager.machine_div_rem(res.significand(), m_powers2(x.sbits - 4 + 3), o.significand, sticky_rem); - renorm_sticky |= !m_mpz_manager.is_zero(sticky_rem); - } - else { - m_mpz_manager.mul2k(res.significand(), 4 - x.sbits + 3, o.significand); - } - - if (renorm_sticky && m_mpz_manager.is_even(o.significand)) - m_mpz_manager.inc(o.significand); - - TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl; - tout << "R = " << to_string_binary(o, 1, 3) << std::endl;); - - if (m_mpz_manager.is_zero(o.significand)) - mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); - else - round(rm, o); + if (x.sbits >= 4) { + m_mpz_manager.machine_div_rem(res.significand(), m_powers2(x.sbits - 4 + 3), o.significand, sticky_rem); + renorm_sticky |= !m_mpz_manager.is_zero(sticky_rem); } else { - mk_inf(x.ebits, x.sbits, res.sign(), o); + m_mpz_manager.mul2k(res.significand(), 4 - x.sbits + 3, o.significand); + o.exponent -= 4 - x.sbits + 3; } + + if (renorm_sticky && m_mpz_manager.is_even(o.significand)) + m_mpz_manager.inc(o.significand); + + TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl; + tout << "R = " << to_string_binary(o, 1, 3) << std::endl;); + + unsigned max_size = o.sbits+4; + unsigned sig_size = m_mpz_manager.bitsize(o.significand); + if (sig_size > max_size) { + unsigned d = sig_size - max_size; + m_mpz_manager.machine_div2k(o.significand, d); + o.exponent += d; + } + + if (m_mpz_manager.is_zero(o.significand)) + mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); + else + round(rm, o); } + TRACE("mpf_dbg", tout << "FMA = " << to_string(o) << std::endl;); } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { From 589024aa1c13a7db71e496c7cec6c00fccfe9684 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 17:43:14 -0700 Subject: [PATCH 291/428] fix #6969 Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_grobner.cpp | 12 ++++++++++-- src/smt/theory_lra.cpp | 17 ++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 2abb6ef4e..0cd6b1859 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -199,14 +199,22 @@ namespace nla { // IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n"); term t; + rational lc(1); + auto ql = q; + while (!ql.is_val()) { + lc = lcm(lc, denominator(ql.hi().val())); + ql = q.lo(); + } + lc = lcm(denominator(ql.val()), lc); + while (!q.is_val()) { - t.add_monomial(q.hi().val(), q.var()); + t.add_monomial(lc*q.hi().val(), q.var()); q = q.lo(); } vector ineqs; for (auto v : vars) ineqs.push_back(ineq(v, llc::EQ, rational::zero())); - ineqs.push_back(ineq(t, llc::EQ, -q.val())); + ineqs.push_back(ineq(t, llc::EQ, -lc*q.val())); for (auto const& i : ineqs) if (c().ineq_holds(i)) return false; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index fffa85d44..132f51693 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -589,11 +589,15 @@ class theory_lra::imp { void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); - ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); +// static unsigned num_bin = 0; +// std::cout << "binary " << (num_bin++) << "\n"; + ctx().mk_th_lemma(get_id(), l1, l2, num_params, params); } void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_smt2(tout, 3, lits); tout << "\n";); + static unsigned num_ter = 0; + std::cout << "ternary " << (num_ter++) << "\n"; ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); } @@ -3342,7 +3346,7 @@ public: // The lemmas can come in batches // and the same literal can appear in several lemmas in a batch: it becomes l_true // in earlier processing, but it was not so when the lemma was produced - ctx().mk_th_axiom(get_id(), m_core.size(), m_core.data()); + ctx().mk_th_lemma(get_id(), m_core.size(), m_core.data()); } } @@ -3703,17 +3707,16 @@ public: app_ref coeffs2app(u_map const& coeffs, rational const& offset, bool is_int) { expr_ref_vector args(m); - for (auto const& kv : coeffs) { - theory_var w = kv.m_key; + for (auto const& [w, coeff] : coeffs) { expr* o = get_enode(w)->get_expr(); - if (kv.m_value.is_zero()) { + if (coeff.is_zero()) { // continue } - else if (kv.m_value.is_one()) { + else if (coeff.is_one()) { args.push_back(o); } else { - args.push_back(a.mk_mul(a.mk_numeral(kv.m_value, is_int), o)); + args.push_back(a.mk_mul(a.mk_numeral(coeff, is_int), o)); } } if (!offset.is_zero()) { From 372696096944940bc35a12a27f186be0bfe76b60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 17:43:53 -0700 Subject: [PATCH 292/428] fix #6969 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 132f51693..df59537b5 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -589,15 +589,11 @@ class theory_lra::imp { void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); -// static unsigned num_bin = 0; -// std::cout << "binary " << (num_bin++) << "\n"; ctx().mk_th_lemma(get_id(), l1, l2, num_params, params); } void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_smt2(tout, 3, lits); tout << "\n";); - static unsigned num_ter = 0; - std::cout << "ternary " << (num_ter++) << "\n"; ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); } From a957a5673dde8a316bba35e28bc323752a9ad3b6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 18:48:48 -0700 Subject: [PATCH 293/428] remove experiment with theory lemmas Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index df59537b5..6acd4952e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -589,7 +589,7 @@ class theory_lra::imp { void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); - ctx().mk_th_lemma(get_id(), l1, l2, num_params, params); + ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); } void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { @@ -3342,7 +3342,7 @@ public: // The lemmas can come in batches // and the same literal can appear in several lemmas in a batch: it becomes l_true // in earlier processing, but it was not so when the lemma was produced - ctx().mk_th_lemma(get_id(), m_core.size(), m_core.data()); + ctx().mk_th_axiom(get_id(), m_core.size(), m_core.data()); } } From 160971df60d9c264a09c624fad041e66163ef651 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 19:10:46 -0700 Subject: [PATCH 294/428] fix #6969, again Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_grobner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 0cd6b1859..21d46cccc 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -203,7 +203,7 @@ namespace nla { auto ql = q; while (!ql.is_val()) { lc = lcm(lc, denominator(ql.hi().val())); - ql = q.lo(); + ql = ql.lo(); } lc = lcm(denominator(ql.val()), lc); From 938a89e197549d67f7e804fe1d395a03037b4a3e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 29 Oct 2023 19:45:14 -0700 Subject: [PATCH 295/428] prepare for local version of Gomory cuts Signed-off-by: Nikolaj Bjorner --- src/math/lp/int_solver.cpp | 34 ++++++++++++++++++++++++++++++++++ src/math/lp/int_solver.h | 2 +- src/smt/theory_lra.cpp | 4 ++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 35a082da9..6e0c90679 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -194,7 +194,11 @@ namespace lp { if (r == lia_move::undef && m_patcher.should_apply()) r = m_patcher(); if (r == lia_move::undef && should_find_cube()) r = int_cube(*this)(); if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); +#if 1 if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this)(); +#else + if (r == lia_move::undef && should_gomory_cut()) r = local_gomory(); +#endif if (r == lia_move::undef) r = int_branch(*this)(); return r; } @@ -914,5 +918,35 @@ namespace lp { #endif } + lia_move int_solver::local_gomory() { + for (unsigned i = 0; i < 4; ++i) { + + m_ex->clear(); + m_t.clear(); + m_k.reset(); + auto r = gomory(*this)(); + IF_VERBOSE(3, verbose_stream() << i << " " << r << "\n"); + if (r != lia_move::cut) + return r; + u_dependency* dep = nullptr; + for (auto c : *m_ex) + dep = lra.join_deps(lra.dep_manager().mk_leaf(c.ci()), dep); + lp::lpvar term_index = lra.add_term(get_term().coeffs_as_vector(), UINT_MAX); + term_index = lra.map_term_index_to_column_index(term_index); + lra.update_column_type_and_bound(term_index, is_upper() ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE, get_offset(), dep); + lra.find_feasible_solution(); + if (!lra.is_feasible()) { + lra.get_infeasibility_explanation(*m_ex); + return lia_move::conflict; + } + } + m_ex->clear(); + m_t.clear(); + m_k.reset(); + + return lia_move::undef; + } + + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index f1faedf35..82425669d 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -110,6 +110,7 @@ private: bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; bool cut_indices_are_columns() const; + lia_move local_gomory(); public: std::ostream& display_column(std::ostream & out, unsigned j) const; @@ -128,7 +129,6 @@ public: bool is_term(unsigned j) const; unsigned column_count() const; bool all_columns_are_bounded() const; - void find_feasible_solution(); lia_move hnf_cut(); int select_int_infeasible_var(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 6acd4952e..7856485d6 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2416,6 +2416,10 @@ public: return false; theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form + if (uv == null_theory_var) + return false; + if (vv == null_theory_var) + return false; enode* n1 = get_enode(uv); enode* n2 = get_enode(vv); From 996b844cde66726ff3f4f2e95e1712df035031da Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Mon, 30 Oct 2023 20:30:23 +0100 Subject: [PATCH 296/428] Fixed parsing of | and \ (#6975) * Give users ability to see if propagation failed * Skip propagations in the new core if they are already satisfied * Fix registration in final * Don't make it too complicated... * Fixed next_split when called in pop Made delay_units available even without quantifiers * Missing push calls before "decide"-callback * Fixed parsing of | and \ * Unit-test for parsing bug --- src/parsers/smt2/smt2scanner.cpp | 9 ++++- src/test/smt2print_parse.cpp | 67 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index 2fb45db3b..8beb782c1 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -106,8 +106,13 @@ namespace smt2 { TRACE("scanner", tout << "new quoted symbol: " << m_id << "\n";); return SYMBOL_TOKEN; } - escape = (c == '\\'); - m_string.push_back(c); + else if (c != '|' && c != '\\' && escape) { + m_string.push_back('\\'); + } + + escape = (c == '\\') && !escape; + if (!escape) + m_string.push_back(c); next(); } } diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index da4badf3e..543b437e2 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -8,6 +8,7 @@ Copyright (c) 2015 Microsoft Corporation // for SMT-LIB2. #include "api/z3.h" +#include "util/debug.h" #include void test_print(Z3_context ctx, Z3_ast_vector av) { @@ -159,6 +160,70 @@ void test_repeated_eval() { Z3_del_context(ctx); } +void test_name(Z3_string spec, Z3_string expected_name) { + Z3_context ctx = Z3_mk_context(nullptr); + Z3_set_error_handler(ctx, setError); + std::cout << "spec:\n" << spec << "\n"; + is_error = false; + + Z3_ast_vector a = + Z3_parse_smtlib2_string(ctx, + spec, + 0, + nullptr, + nullptr, + 0, + nullptr, + nullptr); + + std::cout << "done parsing\n"; + ENSURE(is_error == (expected_name == nullptr)); + if (is_error) { + Z3_del_context(ctx); + return; + } + Z3_ast_vector_inc_ref(ctx, a); + + ENSURE(Z3_ast_vector_size(ctx, a) == 1) + Z3_ast c = Z3_ast_vector_get(ctx, a, 0); + Z3_inc_ref(ctx, c); + Z3_app app = Z3_to_app(ctx, c); + Z3_func_decl decl = Z3_get_app_decl(ctx, app); + Z3_symbol symbol = Z3_get_decl_name(ctx, decl); + Z3_string name = Z3_get_symbol_string(ctx, symbol); + bool success = strcmp(name, expected_name) == 0; + Z3_dec_ref(ctx, c); + Z3_ast_vector_dec_ref(ctx, a); + Z3_del_context(ctx); + ENSURE(success); +} + +void test_symbol_escape() { + +#define SYMBOL_ASSERTION(N) \ + "(declare-const " N " Bool)\n" \ + "(assert " N ")\n" \ + "(check-sat)\n" + + std::cout << "testing Z3_eval_smtlib2_string\n"; + + try { + test_name(SYMBOL_ASSERTION("|a|"), "a"); + test_name(SYMBOL_ASSERTION("|a\\|"), nullptr); + test_name(SYMBOL_ASSERTION("|a\\||"), "a|"); + test_name(SYMBOL_ASSERTION("|a\\\\|"), "a\\"); + test_name(SYMBOL_ASSERTION("|a\\\\||"), nullptr); + test_name(SYMBOL_ASSERTION("|a\\a|"), "a\\a"); + test_name(SYMBOL_ASSERTION("|a\\a"), nullptr); + } + catch(...) { + std::cout << "Error: uncaught exception\n"; + throw; + } + + std::cout << "done evaluating\n"; +} + void tst_smt2print_parse() { // test basic datatypes @@ -225,4 +290,6 @@ void tst_smt2print_parse() { test_repeated_eval(); + test_symbol_escape(); + } From f97dd3402880963fe5b19ccbf144c3cce4e07cd7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Oct 2023 14:54:04 -0700 Subject: [PATCH 297/428] tests Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 2 +- src/math/lp/int_solver.cpp | 26 ++++++++++++++++++++++---- src/math/lp/nla_basics_lemmas.cpp | 2 +- src/math/lp/nla_grobner.cpp | 2 -- src/math/lp/nra_solver.cpp | 2 -- src/smt/theory_lra.cpp | 2 +- 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 4f94b89d2..12ab1cbb1 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -873,8 +873,8 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { m_autil.is_numeral(y, r) && r.is_zero() && m_autil.is_numeral(z, r) && r >= 0) { expr* len_x = str().mk_length(x); - expr* zero = m_autil.mk_int(0); result = m().mk_ite(m_autil.mk_le(len_x, z), len_x, z); + // expr* zero = m_autil.mk_int(0); // result = m().mk_ite(m_autil.mk_le(z, zero), zero, result); return BR_REWRITE_FULL; } diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 6e0c90679..a16fdea4c 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -689,7 +689,8 @@ namespace lp { void int_solver::simplify(std::function& is_root) { -#if 0 + return; +#if 1 // in-processing simplification can go here, such as bounds improvements. @@ -699,6 +700,20 @@ namespace lp { return; } + +#endif + +#if 1 + lp::explanation exp; + m_ex = &exp; + m_t.clear(); + m_k.reset(); + + if (has_inf_int()) + local_gomory(); +#endif + +#if 0 stopwatch sw; explanation exp1, exp2; @@ -919,8 +934,7 @@ namespace lp { } lia_move int_solver::local_gomory() { - for (unsigned i = 0; i < 4; ++i) { - + for (unsigned i = 0; i < 2 && has_inf_int() && !settings().get_cancel_flag(); ++i) { m_ex->clear(); m_t.clear(); m_k.reset(); @@ -939,11 +953,15 @@ namespace lp { lra.get_infeasibility_explanation(*m_ex); return lia_move::conflict; } + //r = m_patcher(); + //if (r != lia_move::undef) + // return r; } m_ex->clear(); m_t.clear(); m_k.reset(); - + if (!has_inf_int()) + return lia_move::sat; return lia_move::undef; } diff --git a/src/math/lp/nla_basics_lemmas.cpp b/src/math/lp/nla_basics_lemmas.cpp index 191e59a79..705247aa1 100644 --- a/src/math/lp/nla_basics_lemmas.cpp +++ b/src/math/lp/nla_basics_lemmas.cpp @@ -331,7 +331,7 @@ bool basics::basic_lemma_for_mon_neutral_derived(const monic& rm, const factoriz for (auto fc : f) { lpvar j = var(fc); all_int &= c().var_is_int(j); - if (j == null_lpvar && abs(val(j)) == abs_mv && + if (u == null_lpvar && abs(val(j)) == abs_mv && c().vars_are_equiv(j, mon_var) && (mon_var_is_sep_from_zero || c().var_is_separated_from_zero(j))) u = j; diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 21d46cccc..92c02ab80 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -100,7 +100,6 @@ namespace nla { } bool grobner::is_conflicting() { - bool is_conflict = false; for (auto eq : m_solver.equations()) { if (is_conflicting(*eq)) { lp_settings().stats().m_grobner_conflicts++; @@ -677,7 +676,6 @@ namespace nla { nex_creator& nc = m_nex_creator; nc.pop(0); nex_creator::sum_factory sum(nc); - unsigned row_index = 0; u_map var2nex; for (auto v : eq.poly().free_vars()) var2nex.insert(v, nc.mk_var(v)); diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 5bf983f15..74d6f7187 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -286,11 +286,9 @@ struct solver::imp { bool check_constraint(unsigned idx) { auto& c = lra.constraints()[idx]; - auto& pm = m_nlsat->pm(); auto k = c.kind(); auto offset = -c.rhs(); auto lhs = c.coeffs(); - auto sz = lhs.size(); scoped_anum val(am()), mon(am()); am().set(val, offset.to_mpq()); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 7856485d6..90e6ddce0 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1091,7 +1091,7 @@ public: void restart_eh() { m_arith_eq_adapter.restart_eh(); -#if 0 +#if 1 // experiment if (m_lia) { std::function is_root = [&](unsigned j) { From fe6f38a160e916d1523c2db41baf9d6d50ae01c8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Oct 2023 15:31:56 -0700 Subject: [PATCH 298/428] #6951, fix build Signed-off-by: Nikolaj Bjorner --- src/test/smt2print_parse.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 543b437e2..21e2235d9 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -10,6 +10,7 @@ Copyright (c) 2015 Microsoft Corporation #include "api/z3.h" #include "util/debug.h" #include +#include void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); From 91c2139b5dfa043af0a1d50a5591e662f1c1b6fb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Oct 2023 17:56:44 -0700 Subject: [PATCH 299/428] just use std::string Signed-off-by: Nikolaj Bjorner --- src/test/smt2print_parse.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 21e2235d9..76b169a4a 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -10,7 +10,6 @@ Copyright (c) 2015 Microsoft Corporation #include "api/z3.h" #include "util/debug.h" #include -#include void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); @@ -192,7 +191,7 @@ void test_name(Z3_string spec, Z3_string expected_name) { Z3_func_decl decl = Z3_get_app_decl(ctx, app); Z3_symbol symbol = Z3_get_decl_name(ctx, decl); Z3_string name = Z3_get_symbol_string(ctx, symbol); - bool success = strcmp(name, expected_name) == 0; + bool success = std::string(name) == std::string(expected_name); Z3_dec_ref(ctx, c); Z3_ast_vector_dec_ref(ctx, a); Z3_del_context(ctx); From 3af2b36cd73ad5377a9e0c76c013a057da4bc793 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 31 Oct 2023 15:48:06 +0000 Subject: [PATCH 300/428] Add Z3_solver_interrupt to OCaml API (#6976) --- src/api/ml/z3.ml | 26 +++++---- src/api/ml/z3.mli | 143 +++++++++++++++++++++++++--------------------- 2 files changed, 95 insertions(+), 74 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 4d195a0b1..a5b61c8dc 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -26,7 +26,7 @@ struct let (major, minor, build, revision) = Z3native.get_version () let full_version : string = Z3native.get_full_version() - + let to_string = string_of_int major ^ "." ^ string_of_int minor ^ "." ^ @@ -45,12 +45,12 @@ let mk_list f n = let check_int32 v = v = Int32.to_int (Int32.of_int v) -let mk_int_expr ctx v ty = +let mk_int_expr ctx v ty = if not (check_int32 v) then Z3native.mk_numeral ctx (string_of_int v) ty else Z3native.mk_int ctx v ty - + let mk_context (settings:(string * string) list) = let cfg = Z3native.mk_config () in let f e = Z3native.set_param_value cfg (fst e) (snd e) in @@ -62,6 +62,9 @@ let mk_context (settings:(string * string) list) = Z3native.enable_concurrent_dec_ref res; res +let interrupt (ctx:context) = + Z3native.interrupt ctx + module Symbol = struct type symbol = Z3native.symbol @@ -721,7 +724,7 @@ struct let mk_exists = _internal_mk_quantifier ~universal:false let mk_exists_const = _internal_mk_quantifier_const ~universal:false let mk_lambda_const ctx bound body = Z3native.mk_lambda_const ctx (List.length bound) bound body - let mk_lambda ctx bound body = + let mk_lambda ctx bound body = let names = List.map (fun (x,_) -> x) bound in let sorts = List.map (fun (_,y) -> y) bound in Z3native.mk_lambda ctx (List.length bound) sorts names body @@ -917,10 +920,10 @@ struct let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) = mk_sort ctx (Symbol.mk_string ctx name) constructors - + let mk_sort_ref (ctx: context) (name:Symbol.symbol) = Z3native.mk_datatype_sort ctx name - + let mk_sort_ref_s (ctx: context) (name: string) = mk_sort_ref ctx (Symbol.mk_string ctx name) @@ -1249,7 +1252,7 @@ end module Seq = struct let mk_seq_sort = Z3native.mk_seq_sort - let is_seq_sort = Z3native.is_seq_sort + let is_seq_sort = Z3native.is_seq_sort let mk_re_sort = Z3native.mk_re_sort let is_re_sort = Z3native.is_re_sort let mk_string_sort = Z3native.mk_string_sort @@ -1264,7 +1267,7 @@ struct let mk_seq_concat ctx args = Z3native.mk_seq_concat ctx (List.length args) args let mk_seq_prefix = Z3native.mk_seq_prefix let mk_seq_suffix = Z3native.mk_seq_suffix - let mk_seq_contains = Z3native.mk_seq_contains + let mk_seq_contains = Z3native.mk_seq_contains let mk_seq_extract = Z3native.mk_seq_extract let mk_seq_replace = Z3native.mk_seq_replace let mk_seq_at = Z3native.mk_seq_at @@ -1889,9 +1892,9 @@ struct | _ -> UNKNOWN let get_model x = - try + try let q = Z3native.solver_get_model (gc x) x in - if Z3native.is_null_model q then None else Some q + if Z3native.is_null_model q then None else Some q with | _ -> None let get_proof x = @@ -1916,6 +1919,9 @@ struct let add_simplifier = Z3native.solver_add_simplifier let translate x = Z3native.solver_translate (gc x) x let to_string x = Z3native.solver_to_string (gc x) x + + let interrupt (ctx:context) (s:solver) = + Z3native.solver_interrupt ctx s end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 006ee6051..e16abe717 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -48,6 +48,12 @@ type context *) val mk_context : (string * string) list -> context +(** Interrupt the execution of a Z3 procedure. + + This procedure can be used to interrupt: solvers, simplifiers and tactics. + Note: Tactic.interrupt is an alias for this. *) +val interrupt: context -> unit + (** Interaction logging for Z3 Interaction logs are used to record calls into the API into a text file. The text file can be replayed using z3. It has to be the same version of z3 @@ -1068,13 +1074,13 @@ sig if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. *) val mk_constructor_s : context -> string -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor - + (* Create a forward reference to a recursive datatype being declared. The forward reference can be used in a nested occurrence: the range of an array or as element sort of a sequence. The forward reference should only be used when used in an accessor for a recursive datatype that gets declared. *) val mk_sort_ref : context -> Symbol.symbol -> Sort.sort - + (* [mk_sort_ref_s ctx s] is [mk_sort_ref ctx (Symbol.mk_string ctx s)] *) val mk_sort_ref_s : context -> string -> Sort.sort @@ -1653,8 +1659,8 @@ sig - The \c ceiling of [t1/t2] if \c t2 is different from zero, and [t1*t2 < 0]. - If [t2] is zero, then the result is is not uniquely specified. - It can be set to any value that satisfies the constraints + If [t2] is zero, then the result is is not uniquely specified. + It can be set to any value that satisfies the constraints where signed division is used. The arguments must have the same bit-vector sort. *) val mk_sdiv : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1662,8 +1668,8 @@ sig (** Unsigned remainder. It is defined as [t1 - (t1 /u t2) * t2], where [/u] represents unsigned division. - If [t2] is zero, then the result is not uniquely specified. - It can be set to any value that satisfies the constraints + If [t2] is zero, then the result is not uniquely specified. + It can be set to any value that satisfies the constraints where unsigned remainder is used. The arguments must have the same bit-vector sort. *) val mk_urem : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1673,16 +1679,16 @@ sig It is defined as [t1 - (t1 /s t2) * t2], where [/s] represents signed division. The most significant bit (sign) of the result is equal to the most significant bit of \c t1. - If [t2] is zero, then the result is not uniquely specified. - It can be set to any value that satisfies the constraints + If [t2] is zero, then the result is not uniquely specified. + It can be set to any value that satisfies the constraints where signed remainder is used. The arguments must have the same bit-vector sort. *) val mk_srem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed remainder (sign follows divisor). - If [t2] is zero, then the result is not uniquely specified. - It can be set to any value that satisfies the constraints + If [t2] is zero, then the result is not uniquely specified. + It can be set to any value that satisfies the constraints where two's complement signed remainder is used. The arguments must have the same bit-vector sort. *) val mk_smod : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1864,7 +1870,7 @@ sig end (** Sequences, Strings and Regular Expressions **) -module Seq : +module Seq : sig (** create a sequence sort *) val mk_seq_sort : context -> Sort.sort -> Sort.sort @@ -1872,9 +1878,9 @@ sig (** test if sort is a sequence sort *) val is_seq_sort : context -> Sort.sort -> bool - (** create regular expression sorts over sequences of the argument sort *) + (** create regular expression sorts over sequences of the argument sort *) val mk_re_sort : context -> Sort.sort -> Sort.sort - + (** test if sort is a regular expression sort *) val is_re_sort : context -> Sort.sort -> bool @@ -1885,7 +1891,7 @@ sig val mk_char_sort : context -> Sort.sort (** test if sort is a string sort (a sequence of 8-bit bit-vectors) *) - val is_string_sort : context -> Sort.sort -> bool + val is_string_sort : context -> Sort.sort -> bool (** test if sort is a char sort *) val is_char_sort : context -> Sort.sort -> bool @@ -1894,51 +1900,51 @@ sig val mk_string : context -> string -> Expr.expr (** test if expression is a string *) - val is_string : context -> Expr.expr -> bool + val is_string : context -> Expr.expr -> bool (** retrieve string from string Expr.expr *) - val get_string : context -> Expr.expr -> string + val get_string : context -> Expr.expr -> string (** the empty sequence over base sort *) - val mk_seq_empty : context -> Sort.sort -> Expr.expr + val mk_seq_empty : context -> Sort.sort -> Expr.expr (** a unit sequence *) - val mk_seq_unit : context -> Expr.expr -> Expr.expr + val mk_seq_unit : context -> Expr.expr -> Expr.expr (** sequence concatenation *) - val mk_seq_concat : context -> Expr.expr list -> Expr.expr + val mk_seq_concat : context -> Expr.expr list -> Expr.expr (** predicate if the first argument is a prefix of the second *) - val mk_seq_prefix : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_prefix : context -> Expr.expr -> Expr.expr -> Expr.expr (** predicate if the first argument is a suffix of the second *) - val mk_seq_suffix : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_suffix : context -> Expr.expr -> Expr.expr -> Expr.expr (** predicate if the first argument contains the second *) - val mk_seq_contains : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_contains : context -> Expr.expr -> Expr.expr -> Expr.expr (** extract sub-sequence starting at index given by second argument and of length provided by third argument *) - val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** replace first occurrence of second argument by third *) - val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** a unit sequence at index provided by second argument *) - val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr (** length of a sequence *) - val mk_seq_length : context -> Expr.expr -> Expr.expr - - (** [mk_seq_nth ctx s index] retrieves from [s] the element at position [index]. + val mk_seq_length : context -> Expr.expr -> Expr.expr + + (** [mk_seq_nth ctx s index] retrieves from [s] the element at position [index]. The function is under-specified if the index is out of bounds. *) val mk_seq_nth : context -> Expr.expr -> Expr.expr -> Expr.expr (** index of the first occurrence of the second argument in the first *) - val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** [mk_seq_last_index ctx s substr] occurence of [substr] in the sequence [s] *) val mk_seq_last_index : context -> Expr.expr -> Expr.expr -> Expr.expr - + (** retrieve integer expression encoded in string *) val mk_str_to_int : context -> Expr.expr -> Expr.expr @@ -1950,7 +1956,7 @@ sig val mk_str_lt : context -> Expr.expr -> Expr.expr -> Expr.expr (** convert an integer expression to a string *) - val mk_int_to_str : context -> Expr.expr -> Expr.expr + val mk_int_to_str : context -> Expr.expr -> Expr.expr (** [mk_string_to_code ctx s] convert a unit length string [s] to integer code *) val mk_string_to_code : context -> Expr.expr -> Expr.expr @@ -1965,43 +1971,43 @@ sig val mk_sbv_to_str : context -> Expr.expr -> Expr.expr (** create regular expression that accepts the argument sequence *) - val mk_seq_to_re : context -> Expr.expr -> Expr.expr + val mk_seq_to_re : context -> Expr.expr -> Expr.expr (** regular expression membership predicate *) - val mk_seq_in_re : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_seq_in_re : context -> Expr.expr -> Expr.expr -> Expr.expr (** regular expression plus *) - val mk_re_plus : context -> Expr.expr -> Expr.expr + val mk_re_plus : context -> Expr.expr -> Expr.expr (** regular expression star *) - val mk_re_star : context -> Expr.expr -> Expr.expr + val mk_re_star : context -> Expr.expr -> Expr.expr (** optional regular expression *) - val mk_re_option : context -> Expr.expr -> Expr.expr + val mk_re_option : context -> Expr.expr -> Expr.expr (** union of regular expressions *) - val mk_re_union : context -> Expr.expr list -> Expr.expr + val mk_re_union : context -> Expr.expr list -> Expr.expr (** concatenation of regular expressions *) - val mk_re_concat : context -> Expr.expr list -> Expr.expr - + val mk_re_concat : context -> Expr.expr list -> Expr.expr + (** regular expression for the range between two characters *) - val mk_re_range : context -> Expr.expr -> Expr.expr -> Expr.expr + val mk_re_range : context -> Expr.expr -> Expr.expr -> Expr.expr (** bounded loop regular expression *) - val mk_re_loop : context -> Expr.expr -> int -> int -> Expr.expr - + val mk_re_loop : context -> Expr.expr -> int -> int -> Expr.expr + (** intersection of regular expressions *) val mk_re_intersect : context -> Expr.expr list -> Expr.expr (** the regular expression complement *) - val mk_re_complement : context -> Expr.expr -> Expr.expr + val mk_re_complement : context -> Expr.expr -> Expr.expr (** the regular expression that accepts no sequences *) - val mk_re_empty : context -> Sort.sort -> Expr.expr + val mk_re_empty : context -> Sort.sort -> Expr.expr (** the regular expression that accepts all sequences *) - val mk_re_full : context -> Sort.sort -> Expr.expr + val mk_re_full : context -> Sort.sort -> Expr.expr (** [mk_char ctx i] converts an integer to a character *) val mk_char : context -> int -> Expr.expr @@ -2339,7 +2345,7 @@ sig (** Retrieves the sign of a floating-point literal. *) val get_numeral_sign : context -> Expr.expr -> bool * int - (** Return the sign of a floating-point numeral as a bit-vector expression. + (** Return the sign of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector sign, so they are invalid arguments. *) val get_numeral_sign_bv : context -> Expr.expr -> Expr.expr @@ -2349,11 +2355,11 @@ sig (** Return the exponent value of a floating-point numeral as a signed integer *) val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int64 - (** Return the exponent of a floating-point numeral as a bit-vector expression. + (** Return the exponent of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector exponent, so they are invalid arguments. *) val get_numeral_exponent_bv : context -> Expr.expr -> bool -> Expr.expr - (** Return the significand value of a floating-point numeral as a bit-vector expression. + (** Return the significand value of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector significand, so they are invalid arguments. *) val get_numeral_significand_bv : context -> Expr.expr -> Expr.expr @@ -2386,7 +2392,7 @@ sig (** Indicates whether a floating-point numeral is negative. *) val is_numeral_negative : context -> Expr.expr -> bool - + (** Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. *) val mk_to_ieee_bv : context -> Expr.expr -> Expr.expr @@ -3260,7 +3266,7 @@ sig (** Assert multiple constraints (cs) into the solver, and track them (in the unsat) core using the Boolean constants in ps. - + This API is an alternative to {!check} with assumptions for extracting unsat cores. Both APIs can be used in the same solver. The unsat core will contain a combination of the Boolean variables provided using {!assert_and_track} and the Boolean literals @@ -3269,10 +3275,10 @@ sig (** Assert a constraint (c) into the solver, and track it (in the unsat) core using the Boolean constant p. - - This API is an alternative to {!check} with assumptions for extracting unsat cores. - Both APIs can be used in the same solver. The unsat core will contain a combination - of the Boolean variables provided using {!assert_and_track} and the Boolean literals + + This API is an alternative to {!check} with assumptions for extracting unsat cores. + Both APIs can be used in the same solver. The unsat core will contain a combination + of the Boolean variables provided using {!assert_and_track} and the Boolean literals provided using {!check} with assumptions. *) val assert_and_track : solver -> Expr.expr -> Expr.expr -> unit @@ -3342,6 +3348,15 @@ sig (** A string representation of the solver. *) val to_string : solver -> string + + (** Solver local interrupt. + + Normally you should use Z3_interrupt to cancel solvers because only + one solver is enabled concurrently per context. + However, per GitHub issue #1006, there are use cases where + it is more convenient to cancel a specific solver. Solvers + that are not selected for interrupts are left alone.*) + val interrupt: context -> solver -> unit end (** Fixedpoint solving *) @@ -3496,23 +3511,23 @@ sig val get_statistics : optimize -> Statistics.statistics (** Parse an SMT-LIB2 file with assertions, soft constraints and optimization - objectives. Add the parsed constraints and objectives to the optimization + objectives. Add the parsed constraints and objectives to the optimization context. *) val from_file : optimize -> string -> unit - (** Parse an SMT-LIB2 string with assertions, soft constraints and optimization - objectives. Add the parsed constraints and objectives to the optimization + (** Parse an SMT-LIB2 string with assertions, soft constraints and optimization + objectives. Add the parsed constraints and objectives to the optimization context. *) val from_string : optimize -> string -> unit - - (** Return the set of asserted formulas on the optimization context. *) + + (** Return the set of asserted formulas on the optimization context. *) val get_assertions : optimize -> Expr.expr list - (** Return objectives on the optimization context. If the objective function - is a max-sat objective it is returned as a Pseudo-Boolean (minimization) - sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...). If the objective - function is entered as a maximization objective, then return the - corresponding minimization objective. In this way the resulting + (** Return objectives on the optimization context. If the objective function + is a max-sat objective it is returned as a Pseudo-Boolean (minimization) + sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...). If the objective + function is entered as a maximization objective, then return the + corresponding minimization objective. In this way the resulting objective function is always returned as a minimization objective. *) val get_objectives : optimize -> Expr.expr list end From ea915e5b37ab43e4259b0e21c039d0a0dcac961e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Nov 2023 03:35:54 -0700 Subject: [PATCH 301/428] #6971 clear m_a1, m_a2 before calls that may affect model. --- src/sat/smt/arith_solver.cpp | 1 + src/smt/theory_lra.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 32b935b9a..74cce593d 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1518,6 +1518,7 @@ namespace arith { void solver::propagate_nla() { if (m_nla) { + m_a1 = nullptr; m_a2 = nullptr; m_nla->propagate(); add_lemmas(); lp().collect_more_rows_for_lp_propagation(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 90e6ddce0..d6c14311e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2178,6 +2178,8 @@ public: void propagate_nla() { if (m_nla) { + m_a1 = nullptr; + m_a2 = nullptr; m_nla->propagate(); add_lemmas(); lp().collect_more_rows_for_lp_propagation(); @@ -3385,7 +3387,7 @@ public: } nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const { - SASSERT(use_nra_model()); + SASSERT(m_nla && m_nla->use_nra_model()); auto t = get_tv(v); if (t.is_term()) { From 49a071988cec7aae927acd2084f7da79b89d70a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 Nov 2023 03:52:20 -0700 Subject: [PATCH 302/428] remove temporary algebraic numbers from upper layers, move to owner module --- src/math/lp/nla_solver.cpp | 11 ++++++ src/math/lp/nla_solver.h | 2 ++ src/math/lp/nra_solver.cpp | 68 ++++++++++++++++++++++-------------- src/math/lp/nra_solver.h | 5 +++ src/sat/smt/arith_solver.cpp | 17 +++------ src/sat/smt/arith_solver.h | 1 - src/smt/theory_lra.cpp | 23 ++++-------- 7 files changed, 70 insertions(+), 57 deletions(-) diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 4b501a39e..5ed9b4538 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -88,6 +88,17 @@ namespace nla { return m_core->m_nra.value(v); } + scoped_anum& solver::tmp1() { + SASSERT(use_nra_model()); + return m_core->m_nra.tmp1(); + } + + scoped_anum& solver::tmp2() { + SASSERT(use_nra_model()); + return m_core->m_nra.tmp1(); + } + + // ensure r = x^y, add abstraction/refinement lemmas lbool solver::check_power(lpvar r, lpvar x, lpvar y) { return m_core->check_power(r, x, y); diff --git a/src/math/lp/nla_solver.h b/src/math/lp/nla_solver.h index 7da05c3e4..1fbafdf6b 100644 --- a/src/math/lp/nla_solver.h +++ b/src/math/lp/nla_solver.h @@ -47,6 +47,8 @@ namespace nla { core& get_core(); nlsat::anum_manager& am(); nlsat::anum const& am_value(lp::var_index v) const; + scoped_anum& tmp1(); + scoped_anum& tmp2(); vector const& lemmas() const; vector const& literals() const; vector const& fixed_equalities() const; diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 74d6f7187..5661f2e89 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -27,6 +27,7 @@ struct solver::imp { indexed_uint_set m_term_set; scoped_ptr m_nlsat; scoped_ptr m_values; // values provided by LRA solver + scoped_ptr m_tmp1, m_tmp2; nla::core& m_nla_core; imp(lp::lar_solver& s, reslimit& lim, params_ref const& p, nla::core& nla_core): @@ -102,6 +103,15 @@ struct solver::imp { } } + void reset() { + m_values = nullptr; + m_tmp1 = nullptr; m_tmp2 = nullptr; + m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); + m_values = alloc(scoped_anum_vector, am()); + m_term_set.reset(); + m_lp2nl.reset(); + } + /** \brief one-shot nlsat check. A one shot checker is the least functionality that can @@ -115,11 +125,7 @@ struct solver::imp { */ lbool check() { SASSERT(need_check()); - m_values = nullptr; - m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_values = alloc(scoped_anum_vector, am()); - m_term_set.reset(); - m_lp2nl.reset(); + reset(); vector core; init_cone_of_influence(); @@ -316,28 +322,24 @@ struct solver::imp { } lbool check(dd::solver::equation_vector const& eqs) { - m_values = nullptr; - m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_values = alloc(scoped_anum_vector, am()); - m_lp2nl.reset(); - m_term_set.reset(); + reset(); for (auto const& eq : eqs) - add_eq(*eq); + add_eq(*eq); for (auto const& m : m_nla_core.emons()) - if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) - add_monic_eq_bound(m); + if (any_of(m.vars(), [&](lp::lpvar v) { return m_lp2nl.contains(v); })) + add_monic_eq_bound(m); for (unsigned i : m_term_set) - add_term(i); + add_term(i); for (auto const& [v, w] : m_lp2nl) { - if (lra.column_has_lower_bound(v)) - add_lb(lra.get_lower_bound(v), w, lra.get_column_lower_bound_witness(v)); - if (lra.column_has_upper_bound(v)) - add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v)); + if (lra.column_has_lower_bound(v)) + add_lb(lra.get_lower_bound(v), w, lra.get_column_lower_bound_witness(v)); + if (lra.column_has_upper_bound(v)) + add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v)); } - + lbool r = l_undef; try { - r = m_nlsat->check(); + r = m_nlsat->check(); } catch (z3_exception&) { if (m_limit.is_canceled()) { @@ -347,7 +349,7 @@ struct solver::imp { throw; } } - + switch (r) { case l_true: m_nla_core.set_use_nra_model(true); @@ -380,11 +382,7 @@ struct solver::imp { } lbool check(vector const& eqs) { - m_values = nullptr; - m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); - m_values = alloc(scoped_anum_vector, am()); - m_lp2nl.reset(); - m_term_set.reset(); + reset(); for (auto const& eq : eqs) add_eq(eq); for (auto const& m : m_nla_core.emons()) @@ -562,6 +560,19 @@ struct solver::imp { return m_nlsat->am(); } + scoped_anum& tmp1() { + if (!m_tmp1) + m_tmp1 = alloc(scoped_anum, am()); + return *m_tmp1; + } + + scoped_anum& tmp2() { + if (!m_tmp2) + m_tmp2 = alloc(scoped_anum, am()); + return *m_tmp2; + } + + void updt_params(params_ref& p) { m_params.append(p); } @@ -616,6 +627,11 @@ nlsat::anum_manager& solver::am() { return m_imp->am(); } +scoped_anum& solver::tmp1() { return m_imp->tmp1(); } + +scoped_anum& solver::tmp2() { return m_imp->tmp2(); } + + void solver::updt_params(params_ref& p) { m_imp->updt_params(p); } diff --git a/src/math/lp/nra_solver.h b/src/math/lp/nra_solver.h index db1311dad..747b4cee3 100644 --- a/src/math/lp/nra_solver.h +++ b/src/math/lp/nra_solver.h @@ -6,6 +6,7 @@ #pragma once #include "util/vector.h" #include "math/lp/lp_settings.h" +#include "math/polynomial/algebraic_numbers.h" #include "util/rlimit.h" #include "util/params.h" #include "nlsat/nlsat_solver.h" @@ -58,6 +59,10 @@ namespace nra { nlsat::anum_manager& am(); + scoped_anum& tmp1(); + + scoped_anum& tmp2(); + void updt_params(params_ref& p); /* diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 74cce593d..cb2ac53ba 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -619,11 +619,11 @@ namespace arith { value = n->get_root()->get_expr(); } else if (use_nra_model() && lp().external_to_local(v) != lp::null_lpvar) { - anum const& an = nl_value(v, *m_a1); + anum const& an = nl_value(v, m_nla->tmp1()); if (a.is_int(o) && !m_nla->am().is_int(an)) value = a.mk_numeral(rational::zero(), a.is_int(o)); else - value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o)); + value = a.mk_numeral(m_nla->am(), nl_value(v, m_nla->tmp1()), a.is_int(o)); } else if (v != euf::null_theory_var) { rational r = get_value(v); @@ -961,19 +961,12 @@ namespace arith { } bool solver::use_nra_model() { - if (m_nla && m_nla->use_nra_model()) { - if (!m_a1) { - m_a1 = alloc(scoped_anum, m_nla->am()); - m_a2 = alloc(scoped_anum, m_nla->am()); - } - return true; - } - return false; + return m_nla && m_nla->use_nra_model(); } bool solver::is_eq(theory_var v1, theory_var v2) { if (use_nra_model()) { - return m_nla->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); + return m_nla->am().eq(nl_value(v1, m_nla->tmp1()), nl_value(v2, m_nla->tmp2())); } else { return get_ivalue(v1) == get_ivalue(v2); @@ -1471,7 +1464,6 @@ namespace arith { if (!m_nla->need_check()) return l_true; - m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nla->check(); switch (r) { case l_false: @@ -1518,7 +1510,6 @@ namespace arith { void solver::propagate_nla() { if (m_nla) { - m_a1 = nullptr; m_a2 = nullptr; m_nla->propagate(); add_lemmas(); lp().collect_more_rows_for_lp_propagation(); diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index f3e8d1407..ddaaa6164 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -233,7 +233,6 @@ namespace arith { // non-linear arithmetic scoped_ptr m_nla; - scoped_ptr m_a1, m_a2; // integer arithmetic scoped_ptr m_lia; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d6c14311e..936efc459 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -174,7 +174,6 @@ class theory_lra::imp { // non-linear arithmetic scoped_ptr m_nla; - mutable scoped_ptr m_a1, m_a2; // integer arithmetic scoped_ptr m_lia; @@ -192,14 +191,7 @@ class theory_lra::imp { }; bool use_nra_model() const { - if (m_nla && m_nla->use_nra_model()) { - if (!m_a1) { - m_a1 = alloc(scoped_anum, m_nla->am()); - m_a2 = alloc(scoped_anum, m_nla->am()); - } - return true; - } - return false; + return m_nla && m_nla->use_nra_model(); } struct var_value_hash { @@ -1604,7 +1596,7 @@ public: bool is_eq(theory_var v1, theory_var v2) { if (use_nra_model()) - return m_nla->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); + return m_nla->am().eq(nl_value(v1, m_nla->tmp1()), nl_value(v2, m_nla->tmp2())); else return get_ivalue(v1) == get_ivalue(v2); } @@ -2038,7 +2030,6 @@ public: } final_check_status check_nla_continue() { - m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nla->check(); switch (r) { case l_false: @@ -2178,8 +2169,6 @@ public: void propagate_nla() { if (m_nla) { - m_a1 = nullptr; - m_a2 = nullptr; m_nla->propagate(); add_lemmas(); lp().collect_more_rows_for_lp_propagation(); @@ -3387,7 +3376,7 @@ public: } nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const { - SASSERT(m_nla && m_nla->use_nra_model()); + SASSERT(use_nra_model()); auto t = get_tv(v); if (t.is_term()) { @@ -3431,11 +3420,11 @@ public: theory_var v = n->get_th_var(get_id()); expr* o = n->get_expr(); if (use_nra_model() && lp().external_to_local(v) != lp::null_lpvar) { - anum const& an = nl_value(v, *m_a1); + anum const& an = nl_value(v, m_nla->tmp1()); if (a.is_int(o) && !m_nla->am().is_int(an)) { return alloc(expr_wrapper_proc, a.mk_numeral(rational::zero(), a.is_int(o))); } - return alloc(expr_wrapper_proc, a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o))); + return alloc(expr_wrapper_proc, a.mk_numeral(m_nla->am(), nl_value(v, m_nla->tmp1()), a.is_int(o))); } else { rational r = get_value(v); @@ -3818,7 +3807,7 @@ public: if (!ctx().is_relevant(get_enode(v))) out << "irr: "; out << "v" << v << " "; if (t.is_null()) out << "null"; else out << (t.is_term() ? "t":"j") << vi; - if (use_nra_model() && is_registered_var(v)) m_nla->am().display(out << " = ", nl_value(v, *m_a1)); + if (use_nra_model() && is_registered_var(v)) m_nla->am().display(out << " = ", nl_value(v, m_nla->tmp1())); else if (can_get_value(v)) out << " = " << get_value(v); if (is_int(v)) out << ", int"; if (ctx().is_shared(get_enode(v))) out << ", shared"; From ebd4d1a300c857bc48ce045ed23cab56580421cb Mon Sep 17 00:00:00 2001 From: AE1020 <68134252+AE1020@users.noreply.github.com> Date: Thu, 2 Nov 2023 05:58:09 -0400 Subject: [PATCH 303/428] WARNINGS_AS_ERRORS is ON/OFF, not TRUE/FALSE (#6979) --- README-CMake.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-CMake.md b/README-CMake.md index b50167b48..e28ffd4fd 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -303,7 +303,7 @@ The following useful options can be passed to CMake whilst configuring. * ``Z3_ENABLE_CFI`` - BOOL. If set to ``TRUE`` will enable Control Flow Integrity security checks. This is only supported by MSVC and Clang and will fail on other compilers. This requires Z3_LINK_TIME_OPTIMIZATION to also be enabled. * ``Z3_API_LOG_SYNC`` - BOOL. If set to ``TRUE`` will enable experimental API log sync feature. -* ``WARNINGS_AS_ERRORS`` - STRING. If set to ``TRUE`` compiler warnings will be treated as errors. If set to ``False`` compiler warnings will not be treated as errors. +* ``WARNINGS_AS_ERRORS`` - STRING. If set to ``ON`` compiler warnings will be treated as errors. If set to ``OFF`` compiler warnings will not be treated as errors. If set to ``SERIOUS_ONLY`` a subset of compiler warnings will be treated as errors. * ``Z3_C_EXAMPLES_FORCE_CXX_LINKER`` - BOOL. If set to ``TRUE`` the C API examples will request that the C++ linker is used rather than the C linker. * ``Z3_BUILD_EXECUTABLE`` - BOOL. If set to ``TRUE`` build the z3 executable. Defaults to ``TRUE`` unless z3 is being built as a submodule in which case it defaults to ``FALSE``. From ca6cb0af95ae15c063a33b3dd53a121586011501 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 1 Nov 2023 17:15:51 -0700 Subject: [PATCH 304/428] add changes in lp with validate_bound and maximize_term Signed-off-by: Lev Nachmanson --- src/math/lp/lar_core_solver.h | 2 +- src/math/lp/lar_solver.cpp | 373 ++++++++++++------ src/math/lp/lar_solver.h | 27 +- src/math/lp/lp_core_solver_base.cpp | 4 - src/math/lp/lp_core_solver_base.h | 10 +- src/math/lp/lp_core_solver_base_def.h | 78 ++-- src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/lp_primal_core_solver_def.h | 58 +-- .../lp/lp_primal_core_solver_tableau_def.h | 22 +- src/math/lp/lp_settings.h | 2 +- src/math/lp/nla_core.cpp | 3 + src/smt/theory_lra.cpp | 3 + 12 files changed, 368 insertions(+), 216 deletions(-) diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index d58cbb9f7..05f33b94b 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -122,7 +122,7 @@ public: m_r_solver.m_d.resize(m_r_A.column_count()); m_stacked_simplex_strategy.pop(k); - m_r_solver.m_settings.set_simplex_strategy(m_stacked_simplex_strategy); + m_r_solver.m_settings.simplex_strategy() = m_stacked_simplex_strategy; m_infeasible_linear_combination.reset(); lp_assert(m_r_solver.basis_heading_is_correct()); } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 92f6b01ec..d7d3a684a 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -192,23 +192,22 @@ namespace lp { stats().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); - auto strategy_was = settings().simplex_strategy(); - settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); + flet f(settings().simplex_strategy(), simplex_strategy_enum::tableau_rows); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); - settings().set_simplex_strategy(strategy_was); return ret; } lp_status lar_solver::solve() { - if (m_status == lp_status::INFEASIBLE) + if (m_status == lp_status::INFEASIBLE || m_status == lp_status::CANCELLED) return m_status; solve_with_core_solver(); - if (m_status != lp_status::INFEASIBLE) { - if (m_settings.bound_propagation()) - detect_rows_with_changed_bounds(); - } + if (m_status == lp_status::INFEASIBLE || m_status == lp_status::CANCELLED) + return m_status; + + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); clear_columns_with_changed_bounds(); return m_status; @@ -284,17 +283,20 @@ namespace lp { m_constraints.pop(k); m_simplex_strategy.pop(k); - m_settings.set_simplex_strategy(m_simplex_strategy); + m_settings.simplex_strategy() = m_simplex_strategy; lp_assert(sizes_are_correct()); lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); m_dependencies.pop_scope(k); + // init the nbasis sorting + require_nbasis_sort(); set_status(lp_status::UNKNOWN); } bool lar_solver::maximize_term_on_tableau(const lar_term& term, impq& term_max) { + flet f(m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only, false); if (settings().simplex_strategy() == simplex_strategy_enum::undecided) decide_on_strategy_and_adjust_initial_state(); @@ -303,7 +305,7 @@ namespace lp { lp_status st = m_mpq_lar_core_solver.m_r_solver.get_status(); TRACE("lar_solver", tout << st << "\n";); SASSERT(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()); - if (st == lp_status::UNBOUNDED) { + if (st == lp_status::UNBOUNDED || st == lp_status::CANCELLED) { return false; } else { @@ -312,38 +314,85 @@ namespace lp { } } - bool lar_solver::improve_bound(lpvar j, bool improve_lower_bound) { - lar_term term = get_term_to_maximize(j); - if (improve_lower_bound) - term.negate(); - impq bound; - if (!maximize_term_on_corrected_r_solver(term, bound)) - return false; - - return false; - // TODO - if (improve_lower_bound) { - bound.neg(); - if (column_has_lower_bound(j) && bound.x == column_lower_bound(j).x) - return false; - SASSERT(!column_has_lower_bound(j) || column_lower_bound(j).x < bound.x); - - // TODO - explain new lower bound. - // Seems the relevant information is in the "costs" that are used when - // setting the optimization objecive. The costs are cleared after a call so - // maybe have some way of extracting bound dependencies from the costs. - u_dependency* dep = nullptr; - update_column_type_and_bound(j, bound.y > 0 ? lconstraint_kind::GT : lconstraint_kind::GE, bound.x, dep); - } - else { - if (column_has_upper_bound(j) && bound.x == column_upper_bound(j).x) - return false; - SASSERT(!column_has_upper_bound(j) || column_upper_bound(j).x > bound.x); - // similar for upper bounds - u_dependency* dep = nullptr; - update_column_type_and_bound(j, bound.y < 0 ? lconstraint_kind::LT : lconstraint_kind::LE, bound.x, dep); + // get dependencies of the corresponding bounds from max_coeffs + u_dependency* lar_solver::get_dependencies_of_maximum(const vector>& max_coeffs) { + const auto& s = this->m_mpq_lar_core_solver.m_r_solver; + // The linear combinations of d_j*x[j] = the term that got maximized, where (d_j, j) is in max_coeffs + // Every j with positive coeff is at its upper bound, + // and every j with negative coeff is at its lower bound: so the sum cannot be increased. + // All variables j in the sum are non-basic. + u_dependency* dep = nullptr; + for (const auto & [d_j, j]: max_coeffs) { + SASSERT (!d_j.is_zero()); + + TRACE("lar_solver_improve_bounds", tout << "d[" << j << "] = " << d_j << "\n"; + s.print_column_info(j, tout);); + const ul_pair& ul = m_columns_to_ul_pairs[j]; + u_dependency * bound_dep; + if (d_j.is_pos()) + bound_dep = ul.upper_bound_witness(); + else + bound_dep = ul.lower_bound_witness(); + TRACE("lar_solver_improve_bounds", { + svector cs; + m_dependencies.linearize(bound_dep, cs); + for (auto c : cs) + m_constraints.display(tout, c) << "\n"; + }); + SASSERT(bound_dep != nullptr); + dep = m_dependencies.mk_join(dep, bound_dep); } - return true; + return dep; + } + // returns nullptr if the bound is not improved, otherwise returns the witness of the bound + u_dependency* lar_solver::find_improved_bound(lpvar j, bool lower_bound, mpq& bound) { + + SASSERT(is_feasible()); + if (lower_bound && column_has_lower_bound(j) && get_column_value(j) == column_lower_bound(j)) + return nullptr; // cannot do better + if (!lower_bound && column_has_upper_bound(j) && get_column_value(j) == column_upper_bound(j)) + return nullptr; // cannot do better + + + lar_term term = get_term_to_maximize(j); + if (lower_bound) + term.negate(); + vector> max_coeffs; + TRACE("lar_solver_improve_bounds", tout << "j = " << j << ", "; print_term(term, tout << "term to maximize\n");); + impq term_max; + if (!maximize_term_on_feasible_r_solver(term, term_max, &max_coeffs)) + return nullptr; + // term_max is equal to the sum of m_d[j]*x[j] over all non basic j. + // For the sum to be at the maximum all non basic variables should be at their bounds: if (m_d[j] > 0) x[j] = u[j], otherwise x[j] = l[j]. At upper bounds we have u[j].y <= 0, and at lower bounds we have l[j].y >= 0, therefore for the sum term_max.y <= 0. + SASSERT(!term_max.y.is_pos()); + + // To keep it simpler we ignore possible improvements from non-strict to strict bounds. + bound = term_max.x; + if (lower_bound) { + bound.neg(); + if (column_is_int(j)) + bound = ceil(bound); + + if (column_has_lower_bound(j) && column_is_int(j) && bound <= column_lower_bound(j).x) + return nullptr; + + TRACE("lar_solver_improve_bounds", + tout << "setting lower bound for " << j << " to " << bound << "\n"; + if (column_has_lower_bound(j)) tout << "bound was = " << column_lower_bound(j) << "\n";); + } + else { + if (column_is_int(j)) + bound = floor(bound); + + if (column_has_upper_bound(j)) { + if (bound >= column_upper_bound(j).x) + return nullptr; + } + TRACE("lar_solver_improve_bounds", + tout << "setting upper bound for " << j << " to " << bound << "\n"; + if (column_has_upper_bound(j)) tout << "bound was = " << column_upper_bound(j) << "\n";;); + } + return get_dependencies_of_maximum(max_coeffs); } bool lar_solver::costs_are_zeros_for_r_solver() const { @@ -361,36 +410,29 @@ 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_heap(); // hijack this set that should be empty right now - lp_assert(jset.empty()); - + auto& d = rslv.m_d; + auto& costs = rslv.m_costs; for (lar_term::ival p : term) { unsigned j = p.column(); - rslv.m_costs[j] = zero_of_type(); + costs[j] = zero_of_type(); int i = rslv.m_basis_heading[j]; - if (i < 0) - jset.insert(j); - else { + if (i < 0) + d[j] = zero_of_type(); + else for (const auto& rc : A_r().m_rows[i]) - jset.insert(rc.var()); - } + d[rc.var()] = zero_of_type(); } - for (unsigned j : jset) - rslv.m_d[j] = zero_of_type(); - - jset.clear(); - lp_assert(reduced_costs_are_zeroes_for_r_solver()); lp_assert(costs_are_zeros_for_r_solver()); } void lar_solver::prepare_costs_for_r_solver(const lar_term& term) { TRACE("lar_solver", print_term(term, tout << "prepare: ") << "\n";); - move_non_basic_columns_to_bounds(); auto& rslv = m_mpq_lar_core_solver.m_r_solver; lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); + move_non_basic_columns_to_bounds(); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); for (lar_term::ival p : term) { unsigned j = p.column(); @@ -400,7 +442,8 @@ namespace lp { else rslv.update_reduced_cost_for_basic_column_cost_change(-p.coeff(), j); } - rslv.m_costs_backup = rslv.m_costs; + if (settings().backup_costs) + rslv.m_costs_backup = rslv.m_costs; lp_assert(rslv.reduced_costs_are_correct_tableau()); } @@ -469,38 +512,33 @@ namespace lp { change_basic_columns_dependend_on_a_given_nb_column(j, delta); } - - bool lar_solver::maximize_term_on_corrected_r_solver(lar_term& term, - impq& term_max) { + bool lar_solver::maximize_term_on_feasible_r_solver(lar_term& term, + impq& term_max, vector>* max_coeffs = nullptr) { settings().backup_costs = false; bool ret = false; - TRACE("lar_solver", print_term(term, tout << "maximize: ") << "\n" << constraints() << ", strategy = " << (int)settings().simplex_strategy() << "\n";); - switch (settings().simplex_strategy()) { - - case simplex_strategy_enum::tableau_rows: - settings().set_simplex_strategy(simplex_strategy_enum::tableau_costs); - prepare_costs_for_r_solver(term); - ret = maximize_term_on_tableau(term, term_max); - settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); - set_costs_to_zero(term); - m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); - return ret; - - case simplex_strategy_enum::tableau_costs: - prepare_costs_for_r_solver(term); - ret = maximize_term_on_tableau(term, term_max); - set_costs_to_zero(term); - m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); - return ret; - - - default: - UNREACHABLE(); // wrong mode + TRACE("lar_solver", print_term(term, tout << "maximize: ") << "\n" + << constraints() << ", strategy = " << (int)settings().simplex_strategy() << "\n";); + if (settings().simplex_strategy() != simplex_strategy_enum::tableau_costs) + require_nbasis_sort(); + flet f(settings().simplex_strategy(), simplex_strategy_enum::tableau_costs); + prepare_costs_for_r_solver(term); + ret = maximize_term_on_tableau(term, term_max); + if (ret && max_coeffs != nullptr) { + for (unsigned j = 0; j < column_count(); j++) { + const mpq& d_j = m_mpq_lar_core_solver.m_r_solver.m_d[j]; + if (d_j.is_zero()) + continue; + max_coeffs->push_back(std::make_pair(d_j, j)); + TRACE("lar_solver", tout<<"m_d["<get_status()) { case lp_status::OPTIMAL: case lp_status::FEASIBLE: - case lp_status::UNBOUNDED: + case lp_status::UNBOUNDED: + SASSERT(m_mpq_lar_core_solver.m_r_solver.inf_heap().size() == 0); return true; default: return false; @@ -1093,7 +1120,7 @@ namespace lp { mpq lar_solver::get_value(column_index const& j) const { SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE); - SASSERT(m_columns_with_changed_bounds.empty()); + VERIFY(m_columns_with_changed_bounds.empty()); numeric_pair const& rp = get_column_value(j); return from_model_in_impq_to_mpq(rp); } @@ -1129,9 +1156,12 @@ namespace lp { return; mpq delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1)); for (unsigned j = 0; j < number_of_vars(); j++) { - auto& r = m_mpq_lar_core_solver.m_r_x[j]; - if (!r.y.is_zero()) - r = impq(r.x + delta * r.y); + auto& v = m_mpq_lar_core_solver.m_r_x[j]; + if (!v.y.is_zero()) { + v = impq(v.x + delta * v.y); + TRACE("lar_solver_feas", tout << "x[" << j << "] = " << v << "\n";); + SASSERT(!column_is_int(j) || v.is_int()); + } } } @@ -1546,6 +1576,7 @@ namespace lp { else { m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); m_mpq_lar_core_solver.m_r_nbasis.push_back(j); + require_nbasis_sort(); } } @@ -1818,7 +1849,98 @@ namespace lp { update_column_type_and_bound(j, kind, right_side, dep); } + + bool lar_solver::validate_bound(lpvar j, lconstraint_kind kind, const mpq& rs, u_dependency* dep) { + if (m_validate_blocker) return true; + + lar_solver solver; + solver.m_validate_blocker = true; + TRACE("lar_solver_validate", tout << "j = " << j << " " << lconstraint_kind_string(kind) << " " << rs << std::endl;); + add_dep_constraints_to_solver(solver, dep); + if (solver.external_to_local(j) == null_lpvar) { + return false; // we have to mention j in the dep + } + if (kind != EQ) { + add_bound_negation_to_solver(solver, j, kind, rs); + solver.find_feasible_solution(); + return solver.get_status() == lp_status::INFEASIBLE; + } + else { + solver.push(); + add_bound_negation_to_solver(solver, j, LE, rs); + solver.find_feasible_solution(); + if (solver.get_status() != lp_status::INFEASIBLE) + return false; + solver.pop(); + add_bound_negation_to_solver(solver, j, GE, rs); + solver.find_feasible_solution(); + return solver.get_status() == lp_status::INFEASIBLE; + } + } + + void lar_solver::add_dep_constraints_to_solver(lar_solver& ls, u_dependency* dep) { + auto constraints = flatten(dep); + for (auto c : constraints) + add_constraint_to_validate(ls, c); + } + void lar_solver::add_bound_negation_to_solver(lar_solver& ls, lpvar j, lconstraint_kind kind, const mpq& right_side) { + j = ls.external_to_local(j); + switch (kind) { + case LE: + ls.add_var_bound(j, GT, right_side); + break; + case LT: + ls.add_var_bound(j, GE, right_side); + break; + case GE: + ls.add_var_bound(j, LT, right_side); + break; + case GT: + ls.add_var_bound(j, LE, right_side); + break; + default: + UNREACHABLE(); + break; + } + } + void lar_solver::add_constraint_to_validate(lar_solver& ls, constraint_index ci) { + auto const& c = m_constraints[ci]; + TRACE("lar_solver_validate", tout << "adding constr with column = "<< c.column() << "\n"; m_constraints.display(tout, c); tout << std::endl;); + vector> coeffs; + for (auto p : c.coeffs()) { + lpvar jext = p.second; + lpvar j = ls.external_to_local(jext); + if (j == null_lpvar) { + ls.add_var(jext, column_is_int(jext)); + j = ls.external_to_local(jext); + } + coeffs.push_back(std::make_pair(p.first, j)); + } + + lpvar column_ext = c.column(); + unsigned j = ls.external_to_local(column_ext); + var_index tv; + if (j == UINT_MAX) { + tv = ls.add_term(coeffs, column_ext); + } + else { + tv = ls.add_term(coeffs, null_lpvar); + } + ls.add_var_bound(tv, c.kind(), c.rhs()); + } void lar_solver::update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { + SASSERT(validate_bound(j, kind, right_side, dep)); + TRACE( + "lar_solver_feas", + tout << "j" << j << " " << lconstraint_kind_string(kind) << " " << right_side << std::endl; + if (dep) { + tout << "dep:\n"; + auto cs = flatten(dep); + for (auto c : cs) { + constraints().display(tout, c); + tout << std::endl; + } + }); if (column_has_upper_bound(j)) update_column_type_and_bound_with_ub(j, kind, right_side, dep); else @@ -1826,12 +1948,12 @@ namespace lp { if (is_base(j) && column_is_fixed(j)) m_fixed_base_var_set.insert(j); - 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;); + 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;); } void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) { - m_columns_with_changed_bounds.insert(j); - TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); + m_columns_with_changed_bounds.insert(j); + TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";); } void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j, @@ -1873,11 +1995,22 @@ namespace lp { void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); - m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? + m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; adjust_initial_state(); } + struct scoped_backup { + lar_solver& m_s; + scoped_backup(lar_solver& s) : m_s(s) { + m_s.backup_x(); + } + ~scoped_backup() { + m_s.restore_x(); + } + }; + + void lar_solver::adjust_initial_state() { switch (m_settings.simplex_strategy()) { case simplex_strategy_enum::tableau_rows: @@ -2038,7 +2171,7 @@ namespace lp { UNREACHABLE(); } } - // clang-format off + void lar_solver::update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { 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); @@ -2091,7 +2224,7 @@ 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, u_dependency* dep) { lp_assert(!column_has_lower_bound(j) && !column_has_upper_bound(j)); @@ -2128,7 +2261,7 @@ namespace lp { } 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)); } @@ -2378,7 +2511,19 @@ namespace lp { for (auto j : m_columns_with_changed_bounds) detect_rows_with_changed_bounds_for_column(j); } - + std::ostream& lar_solver::print_explanation( + std::ostream& out, const explanation& exp, + std::function var_str) const { + out << "expl: "; + unsigned i = 0; + for (auto p : exp) { + out << "(" << p.ci() << ")"; + constraints().display(out, var_str, p.ci()); + if (++i < exp.size()) + out << " "; + } + return out; + } } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 06d7da5b6..2bb039715 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -155,12 +155,20 @@ class lar_solver : public column_namer { lpvar& crossed_bounds_column() { return m_crossed_bounds_column; } - private: + private: + bool validate_bound(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + void add_dep_constraints_to_solver(lar_solver& ls, u_dependency* dep); + void add_bound_negation_to_solver(lar_solver& ls, lpvar j, lconstraint_kind kind, const mpq& right_side); + void add_constraint_to_validate(lar_solver& ls, constraint_index ci); + bool m_validate_blocker = false; void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&); void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci); public: - void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); - private: + bool validate_blocker() const { return m_validate_blocker; } + bool & validate_blocker() { return m_validate_blocker; } + void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); + private: + void require_nbasis_sort() { m_mpq_lar_core_solver.m_r_solver.m_nbasis_sort_counter = 0; } void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep); @@ -203,7 +211,9 @@ class lar_solver : public column_namer { 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); + bool maximize_term_on_feasible_r_solver(lar_term& term, impq& term_max, vector>* max_coeffs); + u_dependency* get_dependencies_of_maximum(const vector>& max_coeffs); + void pop_core_solver_params(); void pop_core_solver_params(unsigned k); void set_upper_bound_witness(var_index j, u_dependency* ci); @@ -263,7 +273,12 @@ class lar_solver : public column_namer { mutable std::unordered_set m_set_of_different_singles; mutable mpq m_delta; - public: + public: + u_dependency* find_improved_bound(lpvar j, bool is_lower, mpq& bound); + + std::ostream& print_explanation( + std::ostream& out, const explanation& exp, + std::function var_str = [](lpvar j) { return std::string("j") + T_to_string(j); }) const; // this function just looks at the status bool is_feasible() const; @@ -302,8 +317,6 @@ class lar_solver : public column_namer { lp_status maximize_term(unsigned j_or_term, impq& term_max); - bool improve_bound(lpvar j, bool is_lower); - inline core_solver_pretty_printer pp(std::ostream& out) const { return core_solver_pretty_printer(m_mpq_lar_core_solver.m_r_solver, out); } diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index b14231d09..e91fc15fc 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,10 +23,8 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); @@ -35,7 +33,6 @@ template lp::lp_core_solver_base >::lp_core_s vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector >&, const vector >&); -template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( @@ -50,7 +47,6 @@ template lp::lp_core_solver_base::lp_core_solver_base( const vector&, const vector&, const vector&); -template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 2c0993d71..099c692c6 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -19,6 +19,7 @@ Revision History: --*/ #pragma once #include +#include #include "util/vector.h" #include #include "math/lp/lp_utils.h" @@ -88,7 +89,7 @@ public: const vector & m_column_types; const vector & m_lower_bounds; const vector & m_upper_bounds; - unsigned m_basis_sort_counter; + unsigned m_nbasis_sort_counter; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; // these rows are changed by adding to them a multiple of the pivot row @@ -165,10 +166,6 @@ public: void print_statistics(char const* str, X cost, std::ostream & message_stream); - bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream); - - bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream); - bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream); unsigned total_iterations() const { return m_total_iterations; } @@ -277,7 +274,7 @@ public: bool non_basis_has_no_doubles() const; bool basis_is_correctly_represented_in_heading() const ; - bool non_basis_is_correctly_represented_in_heading() const ; + bool non_basis_is_correctly_represented_in_heading(std::list*) const ; bool basis_heading_is_correct() const; @@ -416,6 +413,7 @@ public: TRACE("lp_core", tout << "inf col "; print_column_info(j, tout) << "\n";); return false; } + return true; } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index b4a53e872..da48eb560 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -56,7 +56,7 @@ lp_core_solver_base(static_matrix & A, m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_basis_sort_counter(0), + m_nbasis_sort_counter(0), m_tracing_basis_changes(false), m_touched_rows(nullptr), m_look_for_feasible_solution_only(false) { @@ -133,37 +133,6 @@ print_statistics(char const* str, X cost, std::ostream & out) { << ", nonzeros = " << m_A.number_of_non_zeroes() << std::endl; } -template bool lp_core_solver_base:: -print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & str) { - unsigned total_iterations = inc_total_iterations(); - if (m_settings.report_frequency != 0) { - if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { - print_statistics("", X(), str); - } - } - return time_is_over(); -} - -template bool lp_core_solver_base:: -print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & out) { - unsigned total_iterations = inc_total_iterations(); - if (m_settings.report_frequency != 0) - if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { - print_statistics(str, get_cost(), out); - } - return time_is_over(); -} - -template bool lp_core_solver_base:: -print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & out) { - unsigned total_iterations = inc_total_iterations(); - if (m_settings.report_frequency != 0) - if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { - print_statistics("", cost, out); - } - return time_is_over(); -} - template bool lp_core_solver_base:: column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { @@ -194,18 +163,6 @@ d_is_not_positive(unsigned j) const { return m_d[j] <= numeric_traits::zero(); } - -template bool lp_core_solver_base:: -time_is_over() { - if (m_settings.get_cancel_flag()) { - m_status = lp_status::TIME_EXHAUSTED; - return true; - } - else { - return false; - } -} - template void lp_core_solver_base:: rs_minus_Anx(vector & rs) { unsigned row = m_m(); @@ -360,7 +317,7 @@ basis_is_correctly_represented_in_heading() const { return true; } template bool lp_core_solver_base:: -non_basis_is_correctly_represented_in_heading() const { +non_basis_is_correctly_represented_in_heading(std::list* non_basis_list) const { for (unsigned i = 0; i < m_nbasis.size(); i++) if (m_basis_heading[m_nbasis[i]] != - static_cast(i) - 1) return false; @@ -368,7 +325,34 @@ non_basis_is_correctly_represented_in_heading() const { for (unsigned j = 0; j < m_A.column_count(); j++) if (m_basis_heading[j] >= 0) lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); - + + if (non_basis_list == nullptr) return true; + + std::unordered_set nbasis_set(this->m_nbasis.size()); + for (unsigned j : this->m_nbasis) + nbasis_set.insert(j); + + if (non_basis_list->size() != nbasis_set.size()) { + TRACE("lp_core", tout << "non_basis_list.size() = " << non_basis_list->size() << ", nbasis_set.size() = " << nbasis_set.size() << "\n";); + return false; + } + for (auto it = non_basis_list->begin(); it != non_basis_list->end(); it++) { + if (nbasis_set.find(*it) == nbasis_set.end()) { + TRACE("lp_core", tout << "column " << *it << " is in m_non_basis_list but not in m_nbasis\n";); + return false; + } + } + + // check for duplicates in m_non_basis_list + nbasis_set.clear(); + for (auto it = non_basis_list->begin(); it != non_basis_list->end(); it++) { + if (nbasis_set.find(*it) != nbasis_set.end()) { + TRACE("lp_core", tout << "column " << *it << " is in m_non_basis_list twice\n";); + return false; + } + nbasis_set.insert(*it); + } + return true; } @@ -390,7 +374,7 @@ template bool lp_core_solver_base:: if (!basis_is_correctly_represented_in_heading()) return false; - if (!non_basis_is_correctly_represented_in_heading()) + if (!non_basis_is_correctly_represented_in_heading(nullptr)) return false; return true; diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index f5d7314db..a239f1472 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -592,7 +592,7 @@ namespace lp { theta = zero_of_type(); } } - + bool correctly_moved_to_bounds(lpvar) const; bool column_is_benefitial_for_entering_basis(unsigned j) const; void init_infeasibility_costs(); void print_column(unsigned j, std::ostream &out); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index e18a5ef05..6ee265e39 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -40,49 +40,57 @@ void lp_primal_core_solver::sort_non_basis() { if (ca != 0 && cb == 0) return true; return ca < cb; }); - - m_non_basis_list.clear(); - // reinit m_basis_heading - for (unsigned j = 0; j < this->m_nbasis.size(); j++) { - unsigned col = this->m_nbasis[j]; - this->m_basis_heading[col] = - static_cast(j) - 1; - m_non_basis_list.push_back(col); + m_non_basis_list.resize(this->m_nbasis.size()); + // initialize m_non_basis_list from m_nbasis by using an iterator on m_non_basis_list + auto it = m_non_basis_list.begin(); + unsigned j = 0; + for (; j < this->m_nbasis.size(); j++, ++it) { + unsigned col = *it = this->m_nbasis[j]; + this->m_basis_heading[col] = -static_cast(j) - 1; } } +template +bool lp_primal_core_solver::correctly_moved_to_bounds(unsigned j) const { + switch (this->m_column_types[j]) { + case column_type::fixed: + return this->m_x[j] == this->m_lower_bounds[j]; + case column_type::boxed: + return this->m_x[j] == this->m_lower_bounds[j] || this->m_x[j] == this->m_upper_bounds[j]; + case column_type::lower_bound: + return this->m_x[j] == this->m_lower_bounds[j]; + case column_type::upper_bound: + return this->m_x[j] == this->m_upper_bounds[j]; + case column_type::free_column: + return true; + default: + UNREACHABLE(); + return false; + } +} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { const T& dj = this->m_d[j]; - TRACE("lar_solver", tout << "dj=" << dj << "\n";); + if (dj.is_zero()) return false; + TRACE("lar_solver", tout << "d[" << j <<"] = " << dj << "\n";); + SASSERT(correctly_moved_to_bounds(j)); switch (this->m_column_types[j]) { case column_type::fixed: break; case column_type::free_column: - if (!is_zero(dj)) - return true; - break; + return true; case column_type::lower_bound: if (dj > zero_of_type()) return true; - if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){ - return true; - } break; case column_type::upper_bound: if (dj < zero_of_type()) return true; - if (dj > 0 && this->m_x[j] < this->m_upper_bounds[j]) { - return true; - } break; case column_type::boxed: - if (dj > zero_of_type()) { - if (this->m_x[j] < this->m_upper_bounds[j]) - return true; - break; - } else if (dj < zero_of_type()) { - if (this->m_x[j] > this->m_lower_bounds[j]) - return true; - } + if (dj > zero_of_type() && this->m_x[j] == this->m_lower_bounds[j]) + return true; + if (dj < zero_of_type() && this->m_x[j] == this->m_upper_bounds[j]) + return true; break; default: UNREACHABLE(); 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..e557766bc 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -44,19 +44,22 @@ template void lp_primal_core_solver::advance_on_e advance_on_entering_and_leaving_tableau(entering, leaving, t); } + template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) + if (this->m_nbasis_sort_counter == 0) { + sort_non_basis(); + this->m_nbasis_sort_counter = 20; + } + else { + this->m_nbasis_sort_counter--; + SASSERT(non_basis_is_correctly_represented_in_heading(&m_non_basis_list)); + } unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); if (number_of_benefitial_columns_to_go_over == 0) return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } - else { - this->m_basis_sort_counter--; - } + unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size std::list::iterator entering_iter = m_non_basis_list.end(); unsigned n = 0; @@ -98,7 +101,8 @@ unsigned lp_primal_core_solver::solve() { } do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over( "feas t", * this->m_settings.get_message_ostream())) { + if (this->m_settings.get_cancel_flag()) { + this->set_status(lp_status::CANCELLED); return this->total_iterations(); } if (this->m_settings.use_tableau_rows()) { @@ -256,8 +260,6 @@ template int lp_primal_core_solver::find_leaving_ } template void lp_primal_core_solver::init_run_tableau() { lp_assert(basis_columns_are_set_correctly()); - this->m_basis_sort_counter = 0; // to initiate the sort of the basis - // this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; lp_assert(this->inf_heap_is_correct()); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 362d8966a..a616066c6 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -262,7 +262,7 @@ public: // the method of lar solver to use simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } - void set_simplex_strategy(simplex_strategy_enum s) { m_simplex_strategy = s; } + simplex_strategy_enum & simplex_strategy() { return m_simplex_strategy; } bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } #ifdef Z3DEBUG diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index d9fc43057..635103121 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1786,6 +1786,9 @@ void core::set_use_nra_model(bool m) { } void core::propagate() { +#if Z3DEBUG + flet f(lra.validate_blocker(), true); +#endif clear(); m_monomial_bounds.unit_propagate(); m_monics_with_changed_bounds.reset(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 936efc459..7fd31ae8c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2030,6 +2030,9 @@ public: } final_check_status check_nla_continue() { +#if Z3DEBUG + flet f(lp().validate_blocker(), true); +#endif lbool r = m_nla->check(); switch (r) { case l_false: From bdf1fcf5c126b64944ab8f4b28e70658700dd0df Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 2 Nov 2023 07:15:17 -0700 Subject: [PATCH 305/428] remove an assert --- src/math/lp/lp_primal_core_solver_tableau_def.h | 1 - 1 file changed, 1 deletion(-) 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 e557766bc..a700dbd29 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -53,7 +53,6 @@ template void lp_primal_core_solver::advance_on_e } else { this->m_nbasis_sort_counter--; - SASSERT(non_basis_is_correctly_represented_in_heading(&m_non_basis_list)); } unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); From 08d3a82ce01bfd594d375354f318ee2a354deea1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 2 Nov 2023 11:09:01 -0700 Subject: [PATCH 306/428] simplify the jump on entering --- src/math/lp/lp_primal_core_solver.h | 2 +- src/math/lp/lp_primal_core_solver_def.h | 47 ++++--------------- .../lp/lp_primal_core_solver_tableau_def.h | 5 +- 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index a239f1472..d34569032 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -231,7 +231,7 @@ namespace lp { 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(unsigned entering, X &t); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X &t); diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 6ee265e39..f14d268a3 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -98,48 +98,17 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign } return false; } - -template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, - const X & theta, - X & t, - bool & unlimited) { - switch(this->m_column_types[entering]){ - case column_type::boxed: - if (m_sign_of_entering_delta > 0) { - t = this->m_upper_bounds[entering] - this->m_x[entering]; - if (unlimited || t <= theta){ - lp_assert(t >= zero_of_type()); - return true; - } - } else { // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_lower_bounds[entering]; - if (unlimited || t <= theta) { - lp_assert(t >= zero_of_type()); - return true; - } - } +// we assume that the columns are at their bounds +template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, X & theta) { + if (this->m_column_types[entering] != column_type::boxed) return false; - case column_type::upper_bound: - if (m_sign_of_entering_delta > 0) { - t = this->m_upper_bounds[entering] - this->m_x[entering]; - if (unlimited || t <= theta){ - lp_assert(t >= zero_of_type()); - return true; - } - } - return false; - case column_type::lower_bound: - if (m_sign_of_entering_delta < 0) { - t = this->m_x[entering] - this->m_lower_bounds[entering]; - if (unlimited || t <= theta) { - lp_assert(t >= zero_of_type()); - return true; - } - } - return false; - default:return false; + X t = this->m_upper_bounds[entering] - this->m_lower_bounds[entering]; + if (t <= theta) { + theta = t; + return true; } return false; + } template bool lp_primal_core_solver:: 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 a700dbd29..437d27ae8 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -246,10 +246,7 @@ template int lp_primal_core_solver::find_leaving_ } } - ratio = t; - unlimited = false; - if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { - t = ratio; + if (try_jump_to_another_bound_on_entering(entering, t)) { return entering; } if (m_leaving_candidates.size() == 1) From 14312ef8a33991aa4ac460225fc5fe42e7afc2b1 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 2 Nov 2023 15:34:41 -0700 Subject: [PATCH 307/428] remove some warnings with clang Signed-off-by: Lev Nachmanson --- src/math/grobner/pdd_solver.cpp | 3 +-- src/math/lp/lar_solver.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/math/grobner/pdd_solver.cpp b/src/math/grobner/pdd_solver.cpp index 3f41d07cf..10f1eb88f 100644 --- a/src/math/grobner/pdd_solver.cpp +++ b/src/math/grobner/pdd_solver.cpp @@ -122,8 +122,7 @@ namespace dd { solver::scoped_process::~scoped_process() { if (e) { - pdd const& p = e->poly(); - SASSERT(!p.is_val()); + SASSERT(!e->poly().is_val()); g.push_equation(processed, e); } } diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index d7d3a684a..5795e1665 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -316,7 +316,6 @@ namespace lp { // get dependencies of the corresponding bounds from max_coeffs u_dependency* lar_solver::get_dependencies_of_maximum(const vector>& max_coeffs) { - const auto& s = this->m_mpq_lar_core_solver.m_r_solver; // The linear combinations of d_j*x[j] = the term that got maximized, where (d_j, j) is in max_coeffs // Every j with positive coeff is at its upper bound, // and every j with negative coeff is at its lower bound: so the sum cannot be increased. @@ -326,7 +325,7 @@ namespace lp { SASSERT (!d_j.is_zero()); TRACE("lar_solver_improve_bounds", tout << "d[" << j << "] = " << d_j << "\n"; - s.print_column_info(j, tout);); + this->m_mpq_lar_core_solver.m_r_solver.print_column_info(j, tout);); const ul_pair& ul = m_columns_to_ul_pairs[j]; u_dependency * bound_dep; if (d_j.is_pos()) From eed02b6d86cb149af89a860cdf92ce25fc880902 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Nov 2023 20:26:57 +0100 Subject: [PATCH 308/428] improved logging, use C++11 for loops instead of iterators --- src/smt/theory_arith_int.h | 80 +++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index e89b81674..da1ee6bc7 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -146,13 +146,10 @@ namespace smt { */ template theory_var theory_arith::find_infeasible_int_base_var() { - theory_var v = find_bounded_infeasible_int_base_var(); - if (v != null_theory_var) { - TRACE("find_infeasible_int_base_var", display_var(tout, v);); - return v; - } + theory_var r = find_bounded_infeasible_int_base_var(); + CTRACE("find_infeasible_int_base_var", r != null_theory_var, display_var(tout << "bounded infeasible", r);); + unsigned n = 0; - theory_var r = null_theory_var; #define SELECT_VAR(VAR) if (r == null_theory_var) { n = 1; r = VAR; } else { n++; SASSERT(n >= 2); if (m_random() % n == 0) r = VAR; } @@ -172,6 +169,7 @@ namespace smt { } } } + CTRACE("find_infeasible_int_base_var", r != null_theory_var, tout << "found small value v" << r << "\n"); } if (r == null_theory_var) { @@ -181,6 +179,8 @@ namespace smt { SELECT_VAR(v); } } + CTRACE("find_infeasible_int_base_var", r != null_theory_var, tout << "found base v" << r << "\n"); + } if (r == null_theory_var) { @@ -191,6 +191,7 @@ namespace smt { SELECT_VAR(v); } } + CTRACE("find_infeasible_int_base_var", r != null_theory_var, tout << "found quasi base v" << r << "\n"); } CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); @@ -438,15 +439,13 @@ namespace smt { bool theory_arith::is_gomory_cut_target(row const & r) { TRACE("gomory_cut", r.display(tout);); theory_var b = r.get_base_var(); - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { + for (auto& e : r) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). - if (!it->is_dead() && it->m_var != b && (!at_bound(it->m_var) || !get_value(it->m_var).is_rational())) { + if (!e.is_dead() && e.m_var != b && (!at_bound(e.m_var) || !get_value(e.m_var).is_rational())) { TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; - display_var(tout, it->m_var); - tout << "at_bound: " << at_bound(it->m_var) << "\n"; - tout << "infinitesimal: " << !get_value(it->m_var).is_rational() << "\n";); + display_var(tout, e.m_var); + tout << "at_bound: " << at_bound(e.m_var) << "\n"; + tout << "infinitesimal: " << !get_value(e.m_var).is_rational() << "\n";); return false; } } @@ -541,12 +540,10 @@ namespace smt { numeral lcm_den(1); unsigned num_ints = 0; - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead() && it->m_var != x_i) { - theory_var x_j = it->m_var; - numeral a_ij = it->m_coeff; + for (row_entry const& e : r) { + if (!e.is_dead() && e.m_var != x_i) { + theory_var x_j = e.m_var; + numeral a_ij = e.m_coeff; a_ij.neg(); // make the used format compatible with the format used in: Integrating Simplex with DPLL(T) if (is_real(x_j)) { numeral new_a_ij; @@ -709,38 +706,36 @@ namespace smt { numeral gcds(0); numeral least_coeff(0); bool least_coeff_is_bounded = false; - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - if (is_fixed(it->m_var)) { - // WARNING: it is not safe to use get_value(it->m_var) here, since - // get_value(it->m_var) may not satisfy it->m_var bounds at this point. - numeral aux = lcm_den * it->m_coeff; - consts += aux * lower_bound(it->m_var).get_rational(); + for (row_entry const& e : r) { + if (!e.is_dead()) { + if (is_fixed(e.m_var)) { + // WARNING: it is not safe to use get_value(e.m_var) here, since + // get_value(e.m_var) may not satisfy e.m_var bounds at this point. + numeral aux = lcm_den * e.m_coeff; + consts += aux * lower_bound(e.m_var).get_rational(); } - else if (is_real(it->m_var)) { + else if (is_real(e.m_var)) { return true; } else if (gcds.is_zero()) { - gcds = abs(lcm_den * it->m_coeff); + gcds = abs(lcm_den * e.m_coeff); least_coeff = gcds; - least_coeff_is_bounded = is_bounded(it->m_var); + least_coeff_is_bounded = is_bounded(e.m_var); } else { - numeral aux = abs(lcm_den * it->m_coeff); + numeral aux = abs(lcm_den * e.m_coeff); gcds = gcd(gcds, aux); if (aux < least_coeff) { least_coeff = aux; - least_coeff_is_bounded = is_bounded(it->m_var); + least_coeff_is_bounded = is_bounded(e.m_var); } else if (least_coeff_is_bounded && aux == least_coeff) { - least_coeff_is_bounded = is_bounded(it->m_var); + least_coeff_is_bounded = is_bounded(e.m_var); } } SASSERT(gcds.is_int()); SASSERT(least_coeff.is_int()); - TRACE("gcd_test_bug", tout << "coeff: " << it->m_coeff << ", gcds: " << gcds + TRACE("gcd_test_bug", tout << "coeff: " << e.m_coeff << ", gcds: " << gcds << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); } } @@ -790,14 +785,11 @@ namespace smt { antecedents ante(*this); - - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead() && !is_fixed(it->m_var)) { - theory_var v = it->m_var; + for (auto const& e : r) { + if (!e.is_dead() && !is_fixed(e.m_var)) { + theory_var v = e.m_var; SASSERT(!is_real(v)); - numeral ncoeff = lcm_den * it->m_coeff; + numeral ncoeff = lcm_den * e.m_coeff; SASSERT(ncoeff.is_int()); numeral abs_ncoeff = abs(ncoeff); if (abs_ncoeff == least_coeff) { @@ -814,8 +806,8 @@ namespace smt { // u += ncoeff * lower_bound(v).get_rational(); u.addmul(ncoeff, lower_bound(v).get_rational()); } - lower(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); - upper(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); + lower(v)->push_justification(ante, e.m_coeff, coeffs_enabled()); + upper(v)->push_justification(ante, e.m_coeff, coeffs_enabled()); } else if (gcds.is_zero()) { gcds = abs_ncoeff; From e86eae27e644370254e9ec2a9f6b9ff5434f682a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Nov 2023 12:19:43 +0100 Subject: [PATCH 309/428] #6523 and other heap-use-after-free error --- src/sat/smt/arith_solver.cpp | 10 +++++----- src/smt/theory_lra.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index cb2ac53ba..cefbbbf54 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -1042,7 +1042,10 @@ namespace arith { SASSERT(m_nla); SASSERT(m_nla->use_nra_model()); auto t = get_tv(v); - if (t.is_term()) { + if (!t.is_term()) { + m_nla->am().set(r, m_nla->am_value(t.id())); + } + else { m_todo_terms.push_back(std::make_pair(t, rational::one())); TRACE("nl_value", tout << "v" << v << " " << t.to_string() << "\n";); TRACE("nl_value", tout << "v" << v << " := w" << t.to_string() << "\n"; @@ -1072,11 +1075,8 @@ namespace arith { } } } - return r; - } - else { - return m_nla->am_value(t.id()); } + return r; } lbool solver::make_feasible() { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 7fd31ae8c..2491d403e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1954,7 +1954,7 @@ public: } // The call mk_bound() can set the m_infeasible_column in lar_solver // so the explanation is safer to take before this call. - app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); + expr_ref b(mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()), m); if (m.has_trace_stream()) { th.log_axiom_instantiation(b); m.trace_stream() << "[end-of-instance]\n"; @@ -3381,7 +3381,9 @@ public: nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const { SASSERT(use_nra_model()); auto t = get_tv(v); - if (t.is_term()) { + if (!t.is_term()) + m_nla->am().set(r, m_nla->am_value(t.id())); + else { m_todo_terms.push_back(std::make_pair(t, rational::one())); TRACE("nl_value", tout << "v" << v << " " << t.to_string() << "\n";); @@ -3412,11 +3414,9 @@ public: } } } - return r; - } - else { - return m_nla->am_value(t.id()); } + return r; + } model_value_proc * mk_value(enode * n, model_generator & mg) { From 2d1f4d5d93e348af5f7c94756c4d215bd51f49b0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 10:53:15 +0100 Subject: [PATCH 310/428] remove verbose logging --- src/smt/theory_arith_int.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index da1ee6bc7..4746040ce 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -437,17 +437,11 @@ namespace smt { */ template bool theory_arith::is_gomory_cut_target(row const & r) { - TRACE("gomory_cut", r.display(tout);); theory_var b = r.get_base_var(); for (auto& e : r) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). - if (!e.is_dead() && e.m_var != b && (!at_bound(e.m_var) || !get_value(e.m_var).is_rational())) { - TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; - display_var(tout, e.m_var); - tout << "at_bound: " << at_bound(e.m_var) << "\n"; - tout << "infinitesimal: " << !get_value(e.m_var).is_rational() << "\n";); + if (!e.is_dead() && e.m_var != b && (!at_bound(e.m_var) || !get_value(e.m_var).is_rational())) return false; - } } return true; } From 77dab53e9e480567b0fc5e655940fb0d6cca485d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 10:53:38 +0100 Subject: [PATCH 311/428] track number of Gomory cuts --- src/math/lp/lp_settings.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index a616066c6..a0cb485b5 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -115,6 +115,7 @@ struct statistics { unsigned m_hnf_cutter_calls; unsigned m_hnf_cuts; unsigned m_nla_calls; + unsigned m_gomory_cuts; unsigned m_nla_add_bounds; unsigned m_nla_propagate_bounds; unsigned m_nla_propagate_eq; @@ -143,6 +144,7 @@ struct statistics { st.update("arith-patches-success", m_patches_success); st.update("arith-hnf-calls", m_hnf_cutter_calls); st.update("arith-hnf-cuts", m_hnf_cuts); + st.update("arith-gomory-cuts", m_gomory_cuts); st.update("arith-horner-calls", m_horner_calls); st.update("arith-horner-conflicts", m_horner_conflicts); st.update("arith-horner-cross-nested-forms", m_cross_nested_forms); From fb957601376865f9c8bad97df175dabb7d01a39b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 10:53:55 +0100 Subject: [PATCH 312/428] remove template --- src/math/lp/lar_term.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index fc73f949f..5ce1ff2fc 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -52,8 +52,7 @@ public: unsigned size() const { return static_cast(m_coeffs.size()); } - template - const T & coeffs() const { + u_map const & coeffs() const { return m_coeffs; } @@ -97,9 +96,8 @@ public: vector> coeffs_as_vector() const { vector> ret; - for (const auto & p : m_coeffs) { - ret.push_back(std::make_pair(p.m_value, p.m_key)); - } + for (const auto & [c, v] : m_coeffs) + ret.push_back({v, c}); return ret; } From 9f0b3cdc2520bd089fe1ef4ef81a093960a20273 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 10:54:15 +0100 Subject: [PATCH 313/428] Add utility to expand sub-terms --- src/math/lp/lar_solver.cpp | 26 ++++++++++++++++++++++---- src/math/lp/lar_solver.h | 7 ++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 5795e1665..dcdc12249 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -160,9 +160,8 @@ namespace lp { if (tv::is_term(j)) return j; unsigned ext_var_or_term = m_var_register.local_to_external(j); - if (tv::is_term(ext_var_or_term)) { + if (tv::is_term(ext_var_or_term)) j = ext_var_or_term; - } return j; } @@ -2450,8 +2449,7 @@ namespace lp { // a_j.first gives the normalised coefficient, // a_j.second givis the column bool lar_solver::fetch_normalized_term_column(const lar_term& c, std::pair& a_j) const { - TRACE("lar_solver_terms", tout << "looking for term "; - print_term_as_indices(c, tout) << "\n";); + TRACE("lar_solver_terms", print_term_as_indices(c, tout << "looking for term ") << "\n";); lp_assert(c.is_normalized()); auto it = m_normalized_terms_to_columns.find(c); if (it != m_normalized_terms_to_columns.end()) { @@ -2463,6 +2461,26 @@ namespace lp { return false; } + lar_term lar_solver::unfold_nested_subterms(lar_term const& term) { + lar_term result; + vector> todo; + for (auto const & [j,c] : term.coeffs()) + todo.push_back({j, c}); + while (!todo.empty()) { + auto [j, c] = todo.back(); + todo.pop_back(); + auto tv = column2tv(j); + if (tv.is_term()) { + for (auto const& [j, c2] : get_term(tv).coeffs()) + todo.push_back({j, c*c2}); + } + else + result.add_monomial(c, j); + } + return result; + } + + std::pair lar_solver::add_equality(lpvar j, lpvar k) { vector> coeffs; if (tv::is_term(j)) diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 2bb039715..d01a42680 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -205,8 +205,7 @@ class lar_solver : public column_namer { static void clean_popped_elements_for_heap(unsigned n, lpvar_heap& set); static void clean_popped_elements(unsigned n, indexed_uint_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); @@ -273,7 +272,7 @@ class lar_solver : public column_namer { mutable std::unordered_set m_set_of_different_singles; mutable mpq m_delta; - public: +public: u_dependency* find_improved_bound(lpvar j, bool is_lower, mpq& bound); std::ostream& print_explanation( @@ -430,6 +429,8 @@ class lar_solver : public column_namer { return get_term(column2tv(to_column_index(j))); } + lar_term unfold_nested_subterms(lar_term const& term); + inline unsigned row_count() const { return A_r().row_count(); } bool var_is_registered(var_index vj) const; void clear_inf_heap() { From 3d99ed9dd4e790a8bd90475fbde85e7275712fe6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 19:57:30 +0100 Subject: [PATCH 314/428] Gomory cut / branch and bound improvements Improve fairness of cut generation by switching to find_infeasible_int_var with cascading priorities, allow stronger cuts by inlining terms. --- src/math/lp/gomory.cpp | 123 +++++++++++++++++------ src/math/lp/int_branch.cpp | 4 +- src/math/lp/int_solver.cpp | 195 ++++++++++++++++++++++++------------- src/math/lp/int_solver.h | 7 +- src/smt/theory_lra.cpp | 1 - 5 files changed, 230 insertions(+), 100 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index e4267cbd4..61325ef01 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -31,7 +31,7 @@ class create_cut { explanation* m_ex; // the conflict explanation unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value const row_strip& m_row; - const int_solver& lia; + int_solver& lia; mpq m_lcm_den; mpq m_f; mpq m_one_minus_f; @@ -133,26 +133,50 @@ class create_cut { return lia_move::conflict; } + void divd(mpq& r, mpq const& d) { + r /= d; + if (!r.is_int()) + r = ceil(r); + } + + bool can_divide_by(vector> const& p, mpq const& d) { + mpq lhs(0), rhs(m_k); + mpq max_c(abs(m_k)); + for (auto const& [c, v] : p) { + auto c1 = c; + max_c = std::max(max_c, abs(c1)); + divd(c1, d); + if (c1 == 0) + return false; + VERIFY(lia.get_value(v).y == 0); + lhs += c1 * lia.get_value(v).x; + } + if (max_c == 1) + return false; + divd(rhs, d); + return lhs < rhs; + } + void adjust_term_and_k_for_some_ints_case_gomory() { lp_assert(!m_t.is_empty()); // k = 1 + sum of m_t at bounds - auto pol = m_t.coeffs_as_vector(); + lar_term t = lia.lra.unfold_nested_subterms(m_t); + auto pol = t.coeffs_as_vector(); m_t.clear(); if (pol.size() == 1) { TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); - unsigned v = pol[0].second; + auto const& [a, v] = pol[0]; lp_assert(is_int(v)); - const mpq& a = pol[0].first; if (a.is_pos()) { // we have av >= k - m_k /= a; - if (!m_k.is_int()) - m_k = ceil(m_k); + divd(m_k, a); m_t.add_monomial(mpq(1), v); } else { - m_k /= -a; - if (!m_k.is_int()) - m_k = ceil(m_k); + // av >= k + // a/-a*v >= k / - a + // -v >= k / - a + // -v >= ceil(k / -a) + divd(m_k, -a); m_t.add_monomial(-mpq(1), v); } } @@ -162,17 +186,48 @@ class create_cut { TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); if (!m_lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. - for (auto & pi: pol) { - pi.first *= m_lcm_den; - SASSERT(!is_int(pi.second) || pi.first.is_int()); + for (auto & [c,v]: pol) { + c *= m_lcm_den; + SASSERT(!is_int(v) || c.is_int()); } m_k *= m_lcm_den; } - for (const auto & pi: pol) - m_t.add_monomial(pi.first, pi.second); + + // gcd reduction is loss-less: + mpq g(1); + for (const auto & [c, v] : pol) + g = gcd(g, c); + + if (g != 1) { + for (auto & [c, v] : pol) + c /= g; + divd(m_k, g); + } + +#if 0 + // TODO: create self-contained rounding mode to weaken cuts + // whose cofficients are considered too large + // (larger than bounds from the input) + mpq min_c = abs(m_k); + for (const auto & [c, v] : pol) + min_c = std::min(min_c, abs(c)); + + if (min_c > 1 && can_divide_by(pol, min_c)) { + for (auto& [c, v] : pol) + divd(c, min_c); + divd(m_k, min_c); + } +#endif + + for (const auto & [c, v]: pol) + m_t.add_monomial(c, v); + VERIFY(m_t.size() > 0); } + TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); lp_assert(m_k.is_int()); + + } std::string var_name(unsigned j) const { @@ -191,10 +246,9 @@ class create_cut { template void dump_coeff(std::ostream & out, const T& c) const { - out << "( * "; + out << "(* "; dump_coeff_val(out, c.coeff()); - auto t = lia.lra.column2tv(c.column()); - out << " " << var_name(t.id()) << ")"; + out << " " << var_name(c.column().index()) << ")"; } std::ostream& dump_row_coefficients(std::ostream & out) const { @@ -208,7 +262,7 @@ class create_cut { void dump_the_row(std::ostream& out) const { out << "; the row, excluding fixed vars\n"; - out << "(assert ( = ( +"; + out << "(assert (= (+"; dump_row_coefficients(out) << ") 0))\n"; } @@ -259,12 +313,12 @@ class create_cut { return dump_term_coefficients(out << "(+ ") << ")"; } - std::ostream& dump_term_le_k(std::ostream & out) const { - return dump_term_sum(out << "(<= ") << " " << m_k << ")"; + std::ostream& dump_term_ge_k(std::ostream & out) const { + return dump_term_sum(out << "(>= ") << " " << m_k << ")"; } void dump_the_cut_assert(std::ostream & out) const { - dump_term_le_k(out << "(assert (not ") << "))\n"; + dump_term_ge_k(out << "(assert (not ") << "))\n"; } void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const { @@ -310,7 +364,6 @@ public: unsigned j = p.var(); if (j == m_inf_col) { lp_assert(p.coeff() == one_of_type()); - TRACE("gomory_cut_detail", tout << "seeing basic var\n";); continue; } @@ -341,13 +394,21 @@ public: return report_conflict_from_gomory_cut(); if (some_int_columns) adjust_term_and_k_for_some_ints_case_gomory(); - TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); + if (!lia.current_solution_is_inf_on_cut()) { + m_ex->clear(); + m_t.clear(); + m_k = 1; + return lia_move::undef; + } lp_assert(lia.current_solution_is_inf_on_cut()); // checks that indices are columns - TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t.coeffs_as_vector(), tout << "gomory cut:"); tout << " <= " << m_k << std::endl;); + TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t.coeffs_as_vector(), tout << "gomory cut: "); tout << " >= " << m_k << std::endl;); + TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout); + lia.lra.display(tout)); + lia.settings().stats().m_gomory_cuts++; return lia_move::cut; } - create_cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& lia) : + create_cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row, int_solver& lia) : m_t(t), m_k(k), m_ex(ex), @@ -385,22 +446,26 @@ int gomory::find_basic_var() { int result = -1; unsigned min_row_size = UINT_MAX; -#if 0 - result = lia.select_int_infeasible_var(); +#if 1 + result = lia.select_int_infeasible_var(true); if (result == -1) return result; + TRACE("gomory_cut", tout << "row: " << result << "\n"); const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); if (is_gomory_cut_target(row)) return result; result = -1; + + UNREACHABLE(); #endif for (unsigned j : lra.r_basis()) { if (!lia.column_is_int_inf(j)) continue; const row_strip& row = lra.get_row(lia.row_of_basic_column(j)); + TRACE("gomory_cut", tout << "try j" << j << "\n"); if (!is_gomory_cut_target(row)) continue; IF_VERBOSE(20, lia.display_row_info(verbose_stream(), lia.row_of_basic_column(j))); @@ -417,7 +482,6 @@ int gomory::find_basic_var() { } lia_move gomory::operator()() { - lra.move_non_basic_columns_to_bounds(); int j = find_basic_var(); if (j == -1) return lia_move::undef; @@ -426,6 +490,7 @@ lia_move gomory::operator()() { SASSERT(lra.row_is_correct(r)); SASSERT(is_gomory_cut_target(row)); lia.m_upper = false; + lia.m_cut_vars.push_back(j); return cut(lia.m_t, lia.m_k, lia.m_ex, j, row); } diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index d6262a4d0..2137dfd82 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -52,8 +52,8 @@ lia_move int_branch::create_branch_on_column(int j) { int int_branch::find_inf_int_base_column() { -#if 0 - return lia.select_int_infeasible_var(); +#if 1 + return lia.select_int_infeasible_var(false); #endif int result = -1; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index a16fdea4c..602bb0a39 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -180,6 +180,8 @@ namespace lp { m_ex = e; m_ex->clear(); m_upper = false; + m_cut_vars.reset(); + lia_move r = lia_move::undef; if (m_gcd.should_apply()) @@ -193,12 +195,15 @@ namespace lp { ++m_number_of_calls; if (r == lia_move::undef && m_patcher.should_apply()) r = m_patcher(); if (r == lia_move::undef && should_find_cube()) r = int_cube(*this)(); + if (r == lia_move::undef) lra.move_non_basic_columns_to_bounds(); if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); -#if 1 + m_cut_vars.reset(); +#if 0 if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this)(); #else - if (r == lia_move::undef && should_gomory_cut()) r = local_gomory(); + if (r == lia_move::undef && should_gomory_cut()) r = local_gomory(2); #endif + m_cut_vars.reset(); if (r == lia_move::undef) r = int_branch(*this)(); return r; } @@ -626,71 +631,85 @@ namespace lp { } - int int_solver::select_int_infeasible_var() { - int result = -1; + int int_solver::select_int_infeasible_var(bool check_bounded) { + int r_small_box = -1; + int r_small_value = -1; + int r_any_value = -1; + unsigned n_small_box = 1; + unsigned n_small_value = 1; + unsigned n_any_value = 1; mpq range; mpq new_range; mpq small_value(1024); - unsigned n = 0; lar_core_solver & lcs = lra.m_mpq_lar_core_solver; - unsigned prev_usage = 0; // to quiet down the compile + unsigned prev_usage = 0; - enum state { small_box, is_small_value, any_value, not_found }; - state st = not_found; + auto check_bounded_fn = [&](unsigned j) { + if (!check_bounded) + return true; + auto const& row = lra.get_row(row_of_basic_column(j)); + for (const auto & p : row) { + unsigned j = p.var(); + if (!is_base(j) && (!at_bound(j) || !is_zero(get_value(j).y))) + return false; + } + return true; + }; + auto add_column = [&](bool improved, int& result, unsigned& n, unsigned j) { + if (result == -1) + result = j; + else if (improved && ((random() % (++n)) == 0)) + result = j; + }; + for (unsigned j : lra.r_basis()) { if (!column_is_int_inf(j)) continue; + if (!check_bounded_fn(j)) + continue; + if (m_cut_vars.contains(j)) + continue; + + SASSERT(!is_fixed(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) { - n = 0; - st = small_box; - } - if (n == 0 || new_range < range) { - result = j; + + bool improved = new_range <= range || r_small_box == -1; + if (improved) range = new_range; - n = 1; - } - else if (new_range == range && (random() % (++n) == 0)) { - result = j; - } + add_column(improved, r_small_box, n_small_box, j); continue; } - if (st == small_box) - continue; impq const& value = get_value(j); if (abs(value.x) < small_value || (has_upper(j) && small_value > upper_bound(j).x - value.x) || (has_lower(j) && small_value > value.x - lower_bound(j).x)) { - if (st != is_small_value) { - n = 0; - st = is_small_value; - } - if (random() % (++n) == 0) - result = j; - } - if (st == is_small_value) + TRACE("gomory_cut", tout << "small j" << j << "\n"); + add_column(true, r_small_value, n_small_value, j); continue; - SASSERT(st == not_found || st == any_value); - st = any_value; - if (n == 0 || usage > prev_usage) { - result = j; + } + TRACE("gomory_cut", tout << "any j" << j << "\n"); + add_column(usage >= prev_usage, r_any_value, n_any_value, j); + if (usage > prev_usage) prev_usage = usage; - n = 1; - } - else if (usage > 0 && usage == prev_usage && (random() % (++n) == 0)) - result = j; } - - return result; + + if (r_small_box != -1 && (random() % 3 != 0)) + return r_small_box; + if (r_small_value != -1 && (random() % 3) != 0) + return r_small_value; + if (r_any_value != -1) + return r_any_value; + if (r_small_box != -1) + return r_small_box; + return r_small_value; } void int_solver::simplify(std::function& is_root) { return; -#if 1 // in-processing simplification can go here, such as bounds improvements. @@ -701,17 +720,13 @@ namespace lp { } -#endif - -#if 1 lp::explanation exp; m_ex = &exp; m_t.clear(); m_k.reset(); if (has_inf_int()) - local_gomory(); -#endif + local_gomory(5); #if 0 stopwatch sw; @@ -933,35 +948,85 @@ namespace lp { #endif } - lia_move int_solver::local_gomory() { - for (unsigned i = 0; i < 2 && has_inf_int() && !settings().get_cancel_flag(); ++i) { + lia_move int_solver::local_gomory(unsigned num_cuts) { + + struct ex { explanation m_ex; lar_term m_term; mpq m_k; bool m_is_upper; }; + vector cuts; + for (unsigned i = 0; i < num_cuts && has_inf_int() && !settings().get_cancel_flag(); ++i) { m_ex->clear(); m_t.clear(); m_k.reset(); auto r = gomory(*this)(); - IF_VERBOSE(3, verbose_stream() << i << " " << r << "\n"); - if (r != lia_move::cut) - return r; - u_dependency* dep = nullptr; - for (auto c : *m_ex) - dep = lra.join_deps(lra.dep_manager().mk_leaf(c.ci()), dep); - lp::lpvar term_index = lra.add_term(get_term().coeffs_as_vector(), UINT_MAX); - term_index = lra.map_term_index_to_column_index(term_index); - lra.update_column_type_and_bound(term_index, is_upper() ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE, get_offset(), dep); - lra.find_feasible_solution(); - if (!lra.is_feasible()) { - lra.get_infeasibility_explanation(*m_ex); - return lia_move::conflict; - } - //r = m_patcher(); - //if (r != lia_move::undef) - // return r; + if (r != lia_move::cut) + break; + cuts.push_back({ *m_ex, m_t, m_k, is_upper() }); } + m_cut_vars.reset(); + + auto is_small_cut = [&](ex const& cut) { + return all_of(cut.m_term, [&](auto ci) { return ci.coeff().is_small(); }); + }; + + auto add_cut = [&](ex const& cut) { + u_dependency* dep = nullptr; + for (auto c : cut.m_ex) + dep = lra.join_deps(lra.dep_manager().mk_leaf(c.ci()), dep); + lp::lpvar term_index = lra.add_term(cut.m_term.coeffs_as_vector(), UINT_MAX); + term_index = lra.map_term_index_to_column_index(term_index); + lra.update_column_type_and_bound(term_index, + cut.m_is_upper ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE, + cut.m_k, dep); + }; + + auto _check_feasible = [&](void) { + auto st = lra.find_feasible_solution(); + if (!lra.is_feasible()) { + lra.get_infeasibility_explanation(*m_ex); + return false; + } + return true; + }; + + bool has_small = false, has_large = false; + + for (auto const& cut : cuts) { + if (!is_small_cut(cut)) { + has_large = true; + continue; + } + has_small = true; + add_cut(cut); + } + + if (has_large) { + lra.push(); + + for (auto const& cut : cuts) + if (!is_small_cut(cut)) + add_cut(cut); + + bool feas = _check_feasible(); + lra.pop(1); + + if (!feas) + return lia_move::conflict; + + } + + if (!_check_feasible()) + return lia_move::conflict; + + m_ex->clear(); m_t.clear(); m_k.reset(); if (!has_inf_int()) return lia_move::sat; + + if (has_small || has_large) + return lia_move::continue_with_check; + + lra.move_non_basic_columns_to_bounds(); return lia_move::undef; } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 82425669d..5a7d253d8 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -63,10 +63,11 @@ class int_solver { unsigned m_number_of_calls; lar_term m_t; // the term to return in the cut mpq m_k; // the right side of the cut + bool m_upper; // cut is an upper bound explanation *m_ex; // the conflict explanation - bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise hnf_cutter m_hnf_cutter; unsigned m_hnf_cut_period; + unsigned_vector m_cut_vars; // variables that should not be selected for cuts vector m_equalities; public: @@ -110,7 +111,7 @@ private: bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; bool cut_indices_are_columns() const; - lia_move local_gomory(); + lia_move local_gomory(unsigned num_cuts); public: std::ostream& display_column(std::ostream & out, unsigned j) const; @@ -131,7 +132,7 @@ public: bool all_columns_are_bounded() const; lia_move hnf_cut(); - int select_int_infeasible_var(); + int select_int_infeasible_var(bool check_bounded); }; } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 2491d403e..c74661d6b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1678,7 +1678,6 @@ public: return FC_CONTINUE; } - for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; From e6385f8c858e6ff2b7e55d7f97f0d9767d8e3ba2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Nov 2023 20:49:26 +0100 Subject: [PATCH 315/428] disable bound validation in debug mode Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index dcdc12249..aca888d18 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1927,7 +1927,7 @@ namespace lp { ls.add_var_bound(tv, c.kind(), c.rhs()); } void lar_solver::update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) { - SASSERT(validate_bound(j, kind, right_side, dep)); + // SASSERT(validate_bound(j, kind, right_side, dep)); TRACE( "lar_solver_feas", tout << "j" << j << " " << lconstraint_kind_string(kind) << " " << right_side << std::endl; From bd4d580b17a15fc29256963e8d52d7d5449d6742 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Nov 2023 13:49:30 +0100 Subject: [PATCH 316/428] fix #6986 --- src/math/lp/gomory.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 61325ef01..cc752c931 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -32,7 +32,7 @@ class create_cut { unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value const row_strip& m_row; int_solver& lia; - mpq m_lcm_den; + mpq m_lcm_den = { mpq(1) }; mpq m_f; mpq m_one_minus_f; mpq m_fj; @@ -82,8 +82,7 @@ class create_cut { push_explanation(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); - m_lcm_den = lcm(m_lcm_den, denominator(new_a)); - TRACE("gomory_cut_detail", tout << "new_a = " << new_a << ", k = " << m_k << ", lcm_den = " << m_lcm_den << "\n";); + TRACE("gomory_cut_detail", tout << "new_a = " << new_a << ", k = " << m_k << "\n";); #if SMALL_CUTS // if (numerator(new_a).is_big()) throw found_big(); if (numerator(new_a) > m_big_number) @@ -181,7 +180,9 @@ class create_cut { } } else { - m_lcm_den = lcm(m_lcm_den, denominator(m_k)); + m_lcm_den = denominator(m_k); + for (auto const& [c, v] : pol) + m_lcm_den = lcm(m_lcm_den, denominator(c)); lp_assert(m_lcm_den.is_pos()); TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); if (!m_lcm_den.is_one()) { @@ -192,6 +193,21 @@ class create_cut { } m_k *= m_lcm_den; } +#if 0 + unsigned j = 0, i = 0; + for (auto & [c, v] : pol) { + if (lia.is_fixed(v)) { + push_explanation(column_lower_bound_constraint(v)); + push_explanation(column_upper_bound_constraint(v)); + m_k -= c; + IF_VERBOSE(0, verbose_stream() << "got fixed " << v << "\n"); + } + else + pol[j++] = pol[i]; + ++i; + } + pol.shrink(j); +#endif // gcd reduction is loss-less: mpq g(1); @@ -219,8 +235,8 @@ class create_cut { } #endif - for (const auto & [c, v]: pol) - m_t.add_monomial(c, v); + for (const auto & [c, v]: pol) + m_t.add_monomial(c, v); VERIFY(m_t.size() > 0); } @@ -343,7 +359,6 @@ public: // gomory will be t >= k and the current solution has a property t < k m_k = 1; m_t.clear(); - mpq m_lcm_den(1); bool some_int_columns = false; mpq m_f = fractional_part(get_value(m_inf_col)); TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", "; @@ -415,7 +430,6 @@ public: m_inf_col(basic_inf_int_j), m_row(row), lia(lia), - m_lcm_den(1), m_f(fractional_part(get_value(basic_inf_int_j).x)), m_one_minus_f(1 - m_f) {} From 0556059bdfb697dd6fd3ba9e0855136f2b45b162 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Nov 2023 13:50:48 +0100 Subject: [PATCH 317/428] change to expr_ref to allow trying simplification --- src/smt/theory_lra.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c74661d6b..10ec08247 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1730,13 +1730,13 @@ public: } } // create a bound atom representing term >= k is lower_bound is true, and term <= k if it is false - app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound) { + expr_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound) { rational offset; expr_ref t(m); return mk_bound(term, k, lower_bound, offset, t); } - app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound, rational& offset, expr_ref& t) { + expr_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound, rational& offset, expr_ref& t) { offset = k; u_map coeffs; term2coeffs(term, coeffs); @@ -1782,7 +1782,7 @@ public: // lp().print_term(term, tout << "term: ") << "\n"; // tout << "offset: " << offset << " gcd: " << g << "\n";); - app_ref atom(m); + expr_ref atom(m); t = coeffs2app(coeffs, rational::zero(), is_int); if (lower_bound) { atom = a.mk_ge(t, a.mk_numeral(offset, is_int)); @@ -1791,6 +1791,7 @@ public: atom = a.mk_le(t, a.mk_numeral(offset, is_int)); } + // ctx().get_rewriter()(atom); TRACE("arith", tout << t << ": " << atom << "\n"; lp().print_term(term, tout << "bound atom: ") << (lower_bound?" >= ":" <= ") << k << "\n";); ctx().internalize(atom, true); @@ -1920,12 +1921,11 @@ public: case lp::lia_move::branch: { TRACE("arith", tout << "branch\n";); - app_ref b(m); bool u = m_lia->is_upper(); auto const & k = m_lia->get_offset(); rational offset; expr_ref t(m); - b = mk_bound(m_lia->get_term(), k, !u, offset, t); + expr_ref b = mk_bound(m_lia->get_term(), k, !u, offset, t); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(b, m.mk_not(b)); @@ -1953,7 +1953,7 @@ public: } // The call mk_bound() can set the m_infeasible_column in lar_solver // so the explanation is safer to take before this call. - expr_ref b(mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()), m); + expr_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); if (m.has_trace_stream()) { th.log_axiom_instantiation(b); m.trace_stream() << "[end-of-instance]\n"; @@ -2000,7 +2000,7 @@ public: default: UNREACHABLE(); } TRACE("arith", tout << "is_lower: " << is_lower << " pos " << pos << "\n";); - app_ref atom(m); + expr_ref atom(m); // TBD utility: lp::lar_term term = mk_term(ineq.m_poly); // then term is used instead of ineq.m_term if (is_eq) From aa9c7912dc481de503c4dceff4b0b4acbba77444 Mon Sep 17 00:00:00 2001 From: EyalBrilling <43183029+EyalBrilling@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:36:24 +0200 Subject: [PATCH 318/428] fixed possible undefined variable assigment (#6985) --- src/math/polynomial/polynomial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index b85ac1cf5..6c94d9b72 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -5648,7 +5648,7 @@ namespace polynomial { unsigned d0 = 0; unsigned d1 = n1 - n2; unsigned i = 1; - unsigned n3; + unsigned n3 = 0; S.reset(); while (true) { // Compute Gh_{i+2} From 3de5af3cb0ad8b2481598a61065dbdd51525daba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 10 Nov 2023 16:38:55 +0100 Subject: [PATCH 319/428] fix bug in bound simplification in Gomory for mixed integer linear cuts, enable fixed variable redution after bugfix, add notes that rewriting bounds does not work Signed-off-by: Nikolaj Bjorner --- src/math/lp/gomory.cpp | 209 +++++++++++++++---------------------- src/math/lp/hnf_cutter.cpp | 40 +++---- src/math/lp/int_solver.cpp | 116 ++------------------ src/math/lp/int_solver.h | 6 +- src/smt/theory_lra.cpp | 11 +- 5 files changed, 121 insertions(+), 261 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index cc752c931..2c096320a 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -32,7 +32,6 @@ class create_cut { unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value const row_strip& m_row; int_solver& lia; - mpq m_lcm_den = { mpq(1) }; mpq m_f; mpq m_one_minus_f; mpq m_fj; @@ -131,120 +130,7 @@ class create_cut { // conflict 0 >= k where k is positive return lia_move::conflict; } - - void divd(mpq& r, mpq const& d) { - r /= d; - if (!r.is_int()) - r = ceil(r); - } - - bool can_divide_by(vector> const& p, mpq const& d) { - mpq lhs(0), rhs(m_k); - mpq max_c(abs(m_k)); - for (auto const& [c, v] : p) { - auto c1 = c; - max_c = std::max(max_c, abs(c1)); - divd(c1, d); - if (c1 == 0) - return false; - VERIFY(lia.get_value(v).y == 0); - lhs += c1 * lia.get_value(v).x; - } - if (max_c == 1) - return false; - divd(rhs, d); - return lhs < rhs; - } - void adjust_term_and_k_for_some_ints_case_gomory() { - lp_assert(!m_t.is_empty()); - // k = 1 + sum of m_t at bounds - lar_term t = lia.lra.unfold_nested_subterms(m_t); - auto pol = t.coeffs_as_vector(); - m_t.clear(); - if (pol.size() == 1) { - TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); - auto const& [a, v] = pol[0]; - lp_assert(is_int(v)); - if (a.is_pos()) { // we have av >= k - divd(m_k, a); - m_t.add_monomial(mpq(1), v); - } - else { - // av >= k - // a/-a*v >= k / - a - // -v >= k / - a - // -v >= ceil(k / -a) - divd(m_k, -a); - m_t.add_monomial(-mpq(1), v); - } - } - else { - m_lcm_den = denominator(m_k); - for (auto const& [c, v] : pol) - m_lcm_den = lcm(m_lcm_den, denominator(c)); - lp_assert(m_lcm_den.is_pos()); - TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); - if (!m_lcm_den.is_one()) { - // normalize coefficients of integer parameters to be integers. - for (auto & [c,v]: pol) { - c *= m_lcm_den; - SASSERT(!is_int(v) || c.is_int()); - } - m_k *= m_lcm_den; - } -#if 0 - unsigned j = 0, i = 0; - for (auto & [c, v] : pol) { - if (lia.is_fixed(v)) { - push_explanation(column_lower_bound_constraint(v)); - push_explanation(column_upper_bound_constraint(v)); - m_k -= c; - IF_VERBOSE(0, verbose_stream() << "got fixed " << v << "\n"); - } - else - pol[j++] = pol[i]; - ++i; - } - pol.shrink(j); -#endif - - // gcd reduction is loss-less: - mpq g(1); - for (const auto & [c, v] : pol) - g = gcd(g, c); - - if (g != 1) { - for (auto & [c, v] : pol) - c /= g; - divd(m_k, g); - } - -#if 0 - // TODO: create self-contained rounding mode to weaken cuts - // whose cofficients are considered too large - // (larger than bounds from the input) - mpq min_c = abs(m_k); - for (const auto & [c, v] : pol) - min_c = std::min(min_c, abs(c)); - - if (min_c > 1 && can_divide_by(pol, min_c)) { - for (auto& [c, v] : pol) - divd(c, min_c); - divd(m_k, min_c); - } -#endif - - for (const auto & [c, v]: pol) - m_t.add_monomial(c, v); - VERIFY(m_t.size() > 0); - } - - TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); - lp_assert(m_k.is_int()); - - - } std::string var_name(unsigned j) const { return std::string("x") + std::to_string(j); @@ -359,12 +245,12 @@ public: // gomory will be t >= k and the current solution has a property t < k m_k = 1; m_t.clear(); - bool some_int_columns = false; mpq m_f = fractional_part(get_value(m_inf_col)); TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", "; tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f << "\n";); lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int()); + bool some_int_columns = false; #if SMALL_CUTS m_abs_max = 0; for (const auto & p : m_row) { @@ -408,13 +294,7 @@ public: if (m_t.is_empty()) return report_conflict_from_gomory_cut(); if (some_int_columns) - adjust_term_and_k_for_some_ints_case_gomory(); - if (!lia.current_solution_is_inf_on_cut()) { - m_ex->clear(); - m_t.clear(); - m_k = 1; - return lia_move::undef; - } + simplify_inequality(); lp_assert(lia.current_solution_is_inf_on_cut()); // checks that indices are columns TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t.coeffs_as_vector(), tout << "gomory cut: "); tout << " >= " << m_k << std::endl;); TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout); @@ -423,6 +303,91 @@ public: return lia_move::cut; } + // TODO: use this also for HNF cuts? + mpq m_lcm_den = { mpq(1) }; + + void simplify_inequality() { + + auto divd = [](mpq& r, mpq const& d) { + r /= d; + if (!r.is_int()) + r = ceil(r); + }; + SASSERT(!lia.m_upper); + lp_assert(!m_t.is_empty()); + // k = 1 + sum of m_t at bounds + lar_term t = lia.lra.unfold_nested_subterms(m_t); + auto pol = t.coeffs_as_vector(); + m_t.clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + auto const& [a, v] = pol[0]; + lp_assert(is_int(v)); + if (a.is_pos()) { // we have av >= k + divd(m_k, a); + m_t.add_monomial(mpq(1), v); + } + else { + // av >= k + // a/-a*v >= k / - a + // -v >= k / - a + // -v >= ceil(k / -a) + divd(m_k, -a); + m_t.add_monomial(-mpq(1), v); + } + } + else { + m_lcm_den = denominator(m_k); + for (auto const& [c, v] : pol) + m_lcm_den = lcm(m_lcm_den, denominator(c)); + lp_assert(m_lcm_den.is_pos()); + bool int_row = true; + TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); + if (!m_lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & [c,v]: pol) { + c *= m_lcm_den; + SASSERT(!is_int(v) || c.is_int()); + int_row &= is_int(v); + } + m_k *= m_lcm_den; + } + unsigned j = 0, i = 0; + for (auto & [c, v] : pol) { + if (lia.is_fixed(v)) { + push_explanation(column_lower_bound_constraint(v)); + push_explanation(column_upper_bound_constraint(v)); + m_k -= c * lower_bound(v).x; + } + else + pol[j++] = pol[i]; + ++i; + } + pol.shrink(j); + + // gcd reduction is loss-less: + mpq g(1); + for (const auto & [c, v] : pol) + g = gcd(g, c); + if (!int_row) + g = gcd(g, m_k); + + if (g != 1) { + for (auto & [c, v] : pol) + c /= g; + divd(m_k, g); + } + + for (const auto & [c, v]: pol) + m_t.add_monomial(c, v); + VERIFY(m_t.size() > 0); + } + + TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); + lp_assert(m_k.is_int()); + } + + create_cut(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip& row, int_solver& lia) : m_t(t), m_k(k), diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index d688eeb63..75095ca51 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -83,14 +83,13 @@ namespace lp { // consider return from here if b[i] is not an integer and return i } } - + vector hnf_cutter::create_b(const svector & basis_rows) { if (basis_rows.size() == m_right_sides.size()) return m_right_sides; vector b; - for (unsigned i : basis_rows) { - b.push_back(m_right_sides[i]); - } + for (unsigned i : basis_rows) + b.push_back(m_right_sides[i]); return b; } @@ -98,16 +97,15 @@ namespace lp { int ret = -1; int n = 0; for (int i = 0; i < static_cast(b.size()); i++) { - if (is_integer(b[i])) continue; - if (n == 0 ) { + if (is_integer(b[i])) + continue; + if (n == 0) { lp_assert(ret == -1); n = 1; ret = i; - } else { - if (m_settings.random_next() % (++n) == 0) { - ret = i; - } } + else if (m_settings.random_next() % (++n) == 0) + ret = i; } return ret; } @@ -240,10 +238,7 @@ branch y_i >= ceil(y0_i) is impossible. } bool hnf_cutter::hnf_has_var_with_non_integral_value() const { - for (unsigned j : vars()) - if (!lia.get_value(j).is_int()) - return true; - return false; + return any_of(vars(), [&](unsigned j) { return !lia.get_value(j).is_int(); }); } bool hnf_cutter::init_terms_for_hnf_cut() { @@ -252,17 +247,15 @@ branch y_i >= ceil(y0_i) is impossible. try_add_term_to_A_for_hnf(tv::term(i)); return hnf_has_var_with_non_integral_value(); } - + lia_move hnf_cutter::make_hnf_cut() { - if (!init_terms_for_hnf_cut()) { + if (!init_terms_for_hnf_cut()) return lia_move::undef; - } lia.settings().stats().m_hnf_cutter_calls++; TRACE("hnf_cut", tout << "settings().stats().m_hnf_cutter_calls = " << lia.settings().stats().m_hnf_cutter_calls << "\n"; - for (u_dependency* d : constraints_for_explanation()) { - for (auto ci : lra.flatten(d)) - lra.constraints().display(tout, ci); - } + for (u_dependency* d : constraints_for_explanation()) + for (auto ci : lra.flatten(d)) + lra.constraints().display(tout, ci); tout << lra.constraints(); ); #ifdef Z3DEBUG @@ -273,14 +266,14 @@ branch y_i >= ceil(y0_i) is impossible. , x0 #endif ); - + if (r == lia_move::cut) { TRACE("hnf_cut", lra.print_term(lia.m_t, tout << "cut:"); tout << " <= " << lia.m_k << std::endl; for (auto* dep : constraints_for_explanation()) for (auto ci : lra.flatten(dep)) - lra.constraints().display(tout, ci); + lra.constraints().display(tout, ci); ); lp_assert(lia.current_solution_is_inf_on_cut()); lia.settings().stats().m_hnf_cuts++; @@ -291,5 +284,4 @@ branch y_i >= ceil(y0_i) is impossible. } return r; } - } diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 602bb0a39..c06c891b0 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -197,11 +197,14 @@ namespace lp { if (r == lia_move::undef && should_find_cube()) r = int_cube(*this)(); if (r == lia_move::undef) lra.move_non_basic_columns_to_bounds(); if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); + + std::function gomory_fn = [&]() { return gomory(*this)(); }; m_cut_vars.reset(); #if 0 if (r == lia_move::undef && should_gomory_cut()) r = gomory(*this)(); #else - if (r == lia_move::undef && should_gomory_cut()) r = local_gomory(2); + if (r == lia_move::undef && should_gomory_cut()) r = local_cut(2, gomory_fn); + #endif m_cut_vars.reset(); if (r == lia_move::undef) r = int_branch(*this)(); @@ -711,6 +714,8 @@ namespace lp { return; +#if 0 + // in-processing simplification can go here, such as bounds improvements. if (!lra.is_feasible()) { @@ -728,113 +733,9 @@ namespace lp { if (has_inf_int()) local_gomory(5); -#if 0 stopwatch sw; explanation exp1, exp2; - // - // tighten integer bounds - // It is a weak method as it ownly strengthens bounds if - // variables are already at one of the to-be discovered bounds. - // - sw.start(); - unsigned changes = 0; - auto const& constraints = lra.constraints(); - auto print_var = [this](lpvar j) { - if (lra.column_corresponds_to_term(j)) { - std::stringstream strm; - lra.print_column_info(j, strm); - return strm.str(); - } - else - return std::string("j") + std::to_string(j); - }; - unsigned start = random(); - unsigned num_checks = 0; - for (lpvar j0 = 0; j0 < lra.column_count(); ++j0) { - lpvar j = (j0 + start) % lra.column_count(); - - if (num_checks > 1000) - break; - if (is_fixed(j)) - continue; - if (!lra.column_is_int(j)) - continue; - rational value = get_value(j).x; - bool tight_lower = false, tight_upper = false; - u_dependency* dep; - - if (!value.is_int()) - continue; - - bool at_up = at_upper(j); - - if (!at_lower(j)) { - ++num_checks; - lra.push(); - auto k = lp::lconstraint_kind::LE; - lra.update_column_type_and_bound(j, k, (value - 1).to_mpq(), nullptr); - lra.find_feasible_solution(); - if (!lra.is_feasible()) { - tight_upper = true; - ++changes; - lra.get_infeasibility_explanation(exp1); -#if 0 - display_column(std::cout, j); - std::cout << print_var(j) << " >= " << value << "\n"; - unsigned i = 0; - for (auto p : exp1) { - std::cout << "(" << p.ci() << ")"; - constraints.display(std::cout, print_var, p.ci()); - if (++i < exp1.size()) - std::cout << " "; - } -#endif - } - lra.pop(1); - if (tight_upper) { - dep = nullptr; - for (auto& cc : exp1) - dep = lra.join_deps(dep, constraints[cc.ci()].dep()); - lra.update_column_type_and_bound(j, lp::lconstraint_kind::GE, value.to_mpq(), dep); - } - } - - if (!at_up) { - ++num_checks; - lra.push(); - auto k = lp::lconstraint_kind::GE; - lra.update_column_type_and_bound(j, k, (value + 1).to_mpq(), nullptr); - lra.find_feasible_solution(); - if (!lra.is_feasible()) { - tight_lower = true; - ++changes; - lra.get_infeasibility_explanation(exp1); -#if 0 - display_column(std::cout, j); - std::cout << print_var(j) << " <= " << value << "\n"; - unsigned i = 0; - for (auto p : exp1) { - std::cout << "(" << p.ci() << ")"; - constraints.display(std::cout, print_var, p.ci()); - if (++i < exp1.size()) - std::cout << " "; - } -#endif - } - lra.pop(1); - if (tight_lower) { - dep = nullptr; - for (auto& cc : exp1) - dep = lra.join_deps(dep, constraints[cc.ci()].dep()); - lra.update_column_type_and_bound(j, lp::lconstraint_kind::LE, value.to_mpq(), dep); - } - } - } - sw.stop(); - std::cout << "changes " << changes << " columns " << lra.column_count() << " time: " << sw.get_seconds() << "\n"; - std::cout.flush(); - // // identify equalities // @@ -948,7 +849,8 @@ namespace lp { #endif } - lia_move int_solver::local_gomory(unsigned num_cuts) { + + lia_move int_solver::local_cut(unsigned num_cuts, std::function& cut_fn) { struct ex { explanation m_ex; lar_term m_term; mpq m_k; bool m_is_upper; }; vector cuts; @@ -956,7 +858,7 @@ namespace lp { m_ex->clear(); m_t.clear(); m_k.reset(); - auto r = gomory(*this)(); + auto r = cut_fn(); if (r != lia_move::cut) break; cuts.push_back({ *m_ex, m_t, m_k, is_upper() }); diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index 5a7d253d8..0832bd45e 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -68,7 +68,7 @@ class int_solver { hnf_cutter m_hnf_cutter; unsigned m_hnf_cut_period; unsigned_vector m_cut_vars; // variables that should not be selected for cuts - + vector m_equalities; public: int_solver(lar_solver& lp); @@ -111,8 +111,8 @@ private: bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; bool cut_indices_are_columns() const; - lia_move local_gomory(unsigned num_cuts); - + lia_move local_cut(unsigned num_cuts, std::function& cut_fn); + public: std::ostream& display_column(std::ostream & out, unsigned j) const; u_dependency* column_upper_bound_constraint(unsigned j) const; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 10ec08247..377db4fc2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1784,14 +1784,15 @@ public: expr_ref atom(m); t = coeffs2app(coeffs, rational::zero(), is_int); - if (lower_bound) { + if (lower_bound) atom = a.mk_ge(t, a.mk_numeral(offset, is_int)); - } - else { - atom = a.mk_le(t, a.mk_numeral(offset, is_int)); - } + else + atom = a.mk_le(t, a.mk_numeral(offset, is_int)); // ctx().get_rewriter()(atom); + // Note: it is not safe to rewrite atom because the rewriter can + // destroy structure, such as (div x 24) >= 0 becomes x >= 0 and the internal variable + // corresponding to (div x 24) is not constrained. TRACE("arith", tout << t << ": " << atom << "\n"; lp().print_term(term, tout << "bound atom: ") << (lower_bound?" >= ":" <= ") << k << "\n";); ctx().internalize(atom, true); From 8a4e8572944ad64cc4b7134173ca4183ae1d894a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Nov 2023 14:28:03 -0800 Subject: [PATCH 320/428] #6523 regressions from changes inside math/lp/int_solver --- src/math/lp/gomory.cpp | 19 ++++++++++++------- src/sat/smt/arith_solver.cpp | 4 ++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2c096320a..1538ee066 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -139,7 +139,7 @@ class create_cut { std::ostream& dump_coeff_val(std::ostream & out, const mpq & a) const { if (a.is_int()) out << a; - else if ( a >= zero_of_type()) + else if (a >= zero_of_type()) out << "(/ " << numerator(a) << " " << denominator(a) << ")"; else out << "(- (/ " << numerator(-a) << " " << denominator(-a) << "))"; @@ -148,9 +148,7 @@ class create_cut { template void dump_coeff(std::ostream & out, const T& c) const { - out << "(* "; - dump_coeff_val(out, c.coeff()); - out << " " << var_name(c.column().index()) << ")"; + dump_coeff_val(out << "(* ", c.coeff()) << " " << var_name(c.column().index()) << ")"; } std::ostream& dump_row_coefficients(std::ostream & out) const { @@ -178,9 +176,8 @@ class create_cut { dump_declaration(out, p.var()); for (lar_term::ival p : m_t) { auto t = lia.lra.column2tv(p.column()); - if (t.is_term()) { - dump_declaration(out, t.id()); - } + if (t.is_term()) + dump_declaration(out, t.id()); } } @@ -352,6 +349,14 @@ public: } m_k *= m_lcm_den; } + // ax + by >= k + // b > 0, c1 <= y <= c2 + // ax + b*c2 >= ax + by >= k + // => + // ax >= k - by >= k - b*c1 + // b < 0 + // ax + b*c1 >= ax + by >= k + // unsigned j = 0, i = 0; for (auto & [c, v] : pol) { if (lia.is_fixed(v)) { diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index cefbbbf54..2a9566873 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -323,6 +323,10 @@ namespace arith { return false; theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form + if (uv == euf::null_theory_var) + return false; + if (vv == euf::null_theory_var) + return false; if (is_equal(uv, vv)) return false; enode* n1 = var2enode(uv); From c2610cb37c214a1a58c4cd992bf0a2349d4fc6a2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Nov 2023 14:32:53 -0800 Subject: [PATCH 321/428] #6523 malformed models on giveup status --- src/sat/smt/arith_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 2a9566873..fe7b0aa46 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -632,7 +632,7 @@ namespace arith { else if (v != euf::null_theory_var) { rational r = get_value(v); TRACE("arith", tout << mk_pp(o, m) << " v" << v << " := " << r << "\n";); - SASSERT("integer variables should have integer values: " && (ctx.get_config().m_arith_ignore_int || !a.is_int(o) || r.is_int() || m.limit().is_canceled())); + SASSERT("integer variables should have integer values: " && (ctx.get_config().m_arith_ignore_int || !a.is_int(o) || r.is_int() || m_not_handled != nullptr || m.limit().is_canceled())); if (a.is_int(o) && !r.is_int()) r = floor(r); value = a.mk_numeral(r, o->get_sort()); From 3c2e97ddb6a37f36d873d62d257cd395fde3e700 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 07:30:32 -0800 Subject: [PATCH 322/428] fix #6988 --- src/qe/nlqsat.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 3a1fa19c6..d81048c3d 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -558,9 +558,13 @@ namespace qe { div_rewriter_cfg(nlqsat& s): m(s.m), a(s.m), m_zero(a.mk_real(0), m) {} ~div_rewriter_cfg() {} br_status reduce_app(func_decl* f, unsigned sz, expr* const* args, expr_ref& result, proof_ref& pr) { - rational r(1); + rational r1, r(1); + if (a.is_div(f) && sz == 2 && a.is_numeral(args[0], r1) && a.is_numeral(args[1], r) && !r.is_zero()) { + result = a.mk_real(r1 / r); + return BR_DONE; + } if (is_decl_of(f, a.get_family_id(), OP_DIV) && - sz == 2 && (!a.is_numeral(args[1], r) || r.is_zero()) && + sz == 2 && is_ground(args[0]) && is_ground(args[1])) { result = m.mk_fresh_const("div", a.mk_real()); m_divs.push_back(div(m, args[0], args[1], to_app(result))); @@ -609,9 +613,6 @@ namespace qe { } expr* n1, *n2; rational r; - if (a.is_div(n, n1, n2) && a.is_numeral(n2, r) && !r.is_zero()) { - return; - } if (a.is_power(n, n1, n2) && a.is_numeral(n2, r) && r.is_unsigned() && r.is_pos()) { return; } From 440601188135f02abd6353423297cd6c8a529288 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 07:40:32 -0800 Subject: [PATCH 323/428] fix #6984 --- src/qe/qsat.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 6472a83c2..daeb09b8a 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -551,6 +551,7 @@ namespace qe { void init() { m_solver = mk_smt2_solver(m, m_params, symbol::null); + m_last_assert = nullptr; } void collect_statistics(statistics & st) const { if (m_solver) @@ -633,7 +634,7 @@ namespace qe { \brief check alternating satisfiability. Even levels are existential, odd levels are universal. */ - lbool check_sat() { + lbool check_sat() { while (true) { ++m_stats.m_num_rounds; IF_VERBOSE(1, verbose_stream() << "(check-qsat level: " << m_level << " round: " << m_stats.m_num_rounds << ")\n";); From ad2107f0792b36b801594c24b191673219d0a9d4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 08:45:22 -0800 Subject: [PATCH 324/428] fix #6978 --- src/ast/euf/euf_enode.h | 1 + src/smt/smt_enode.cpp | 2 +- src/smt/theory_lra.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index cd47da2b1..064688d47 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -97,6 +97,7 @@ namespace euf { for (unsigned i = 0; i < num_args; ++i) { SASSERT(to_app(f)->get_arg(i) == args[i]->get_expr()); n->m_args[i] = args[i]; + n->m_args[i]->get_root()->set_is_shared(l_undef); } return n; } diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index df0606270..86b83af4c 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -54,7 +54,7 @@ namespace smt { for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; n->m_args[i] = arg; - arg->m_is_shared = 2; + arg->get_root()->m_is_shared = 2; SASSERT(n->get_arg(i) == arg); if (update_children_parent) arg->get_root()->m_parents.push_back(n); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 377db4fc2..b0dc3a9e4 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2077,7 +2077,7 @@ public: enode * n = get_enode(v); enode * r = n->get_root(); unsigned usz = m_underspecified.size(); - TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); + TRACE("shared", tout << ctx().get_scope_level() << " " << enode_pp(n, ctx()) << " " << v << " underspecified " << usz << " parents " << r->get_num_parents() << "\n";); if (r->get_num_parents() > 2*usz) { for (unsigned i = 0; i < usz; ++i) { app* u = m_underspecified[i]; From 7ed27a1f416232ddf0b967d540cb5ea9bc4f62ce Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 08:48:19 -0800 Subject: [PATCH 325/428] prepare release script Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 561b3084d..064ee7a65 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -582,7 +582,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(1,0) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 37b283fab96831cd54c05c4e7108438077a0a6aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 08:54:10 -0800 Subject: [PATCH 326/428] use python3 in nightly Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 4 ++-- src/api/c++/z3++.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index f0a775d2d..fb18cfc9c 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -103,8 +103,8 @@ stages: set -e eval `opam config env` cd doc - python mk_api_doc.py --mld --z3py-package-path=../build/python/z3 - python mk_params_doc.py + python3 mk_api_doc.py --mld --z3py-package-path=../build/python/z3 + python3 mk_params_doc.py mkdir api/html/ml ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build/api/ml ../build/api/ml/z3enums.mli ../build/api/ml/z3.mli cd .. diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 2f59a6ee7..cb1446a08 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -368,7 +368,7 @@ namespace z3 { func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); /** - * \brief add function definition body to declaration decl. decl needs to be declared using context::. + * \brief add function definition body to declaration decl. decl needs to be declared using context::recfun. * @param decl * @param args * @param body From 1ce95d3859b4298d490487729ae416d32eab1fd5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 10:01:13 -0800 Subject: [PATCH 327/428] pip install importlib resources Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 2 ++ scripts/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index fb18cfc9c..12cdfa040 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -85,6 +85,7 @@ stages: pool: vmImage: "ubuntu-latest" steps: + - script: pip3 install importlib-resources - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y - script: eval `opam config env`; opam install zarith ocamlfind -y @@ -103,6 +104,7 @@ stages: set -e eval `opam config env` cd doc + pip3 install importlib-resources python3 mk_api_doc.py --mld --z3py-package-path=../build/python/z3 python3 mk_params_doc.py mkdir api/html/ml diff --git a/scripts/release.yml b/scripts/release.yml index 064ee7a65..fc7231985 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -151,8 +151,8 @@ stages: set -e eval `opam config env` cd doc - python mk_api_doc.py --mld --z3py-package-path=../build/python/z3 - python mk_params_doc.py + python3 mk_api_doc.py --mld --z3py-package-path=../build/python/z3 + python3 mk_params_doc.py mkdir api/html/ml ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build/api/ml ../build/api/ml/z3enums.mli ../build/api/ml/z3.mli cd .. From c0ee4e96133ced638cc70240952f69ddb33fdd6d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Nov 2023 10:02:24 -0800 Subject: [PATCH 328/428] pip install importlib resources Signed-off-by: Nikolaj Bjorner --- scripts/nightly.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 12cdfa040..eebff5803 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -104,7 +104,6 @@ stages: set -e eval `opam config env` cd doc - pip3 install importlib-resources python3 mk_api_doc.py --mld --z3py-package-path=../build/python/z3 python3 mk_params_doc.py mkdir api/html/ml From 3baaba5edd3a3a41c9c969e5ec9cf95006ac7429 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Nov 2023 22:28:30 +0000 Subject: [PATCH 329/428] Revert unsound NaN constraints in theory_fpa (#6993) --- src/smt/theory_fpa.cpp | 28 ---------------------------- src/smt/theory_fpa.h | 2 -- 2 files changed, 30 deletions(-) diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index f99b86684..fb31a61ed 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -253,18 +253,6 @@ namespace smt { return true; } - void theory_fpa::mk_bv_nan(sort * s, expr_ref & result) { - SASSERT(m_fpa_util.is_float(s)); - unsigned sbits = m_fpa_util.get_sbits(s); - unsigned ebits = m_fpa_util.get_ebits(s); - expr_ref exp(m), sgn(m), sig(m); - exp = m_bv_util.mk_numeral(m_fpa_util.fm().m_powers2.m1(ebits), ebits); - sgn = m_bv_util.mk_numeral(0, 1); - sig = m_bv_util.mk_numeral(1, sbits - 1); - expr * args[3] = {sgn, exp, sig}; - result = m_bv_util.mk_concat(3, args); - } - bool theory_fpa::internalize_term(app * term) { TRACE("t_fpa_internalize", tout << "internalizing term: " << mk_ismt2_pp(term, m) << "\n";); SASSERT(term->get_family_id() == get_family_id()); @@ -298,22 +286,6 @@ namespace smt { default: /* ignore */; } - expr * owner = e->get_expr(); - sort * s = owner->get_sort(); - if (m_fpa_util.is_float(s)) - { - TRACE("t_fpa", tout << "extra nan constraint for: " << mk_ismt2_pp(owner, m) << "\n";); - expr_ref wrapped(m), is_nan(m), bv_nan(m); - app_ref impl(m); - wrapped = m_converter.wrap(owner); - is_nan = m_fpa_util.mk_is_nan(owner); - mk_bv_nan(s, bv_nan); - impl = m.mk_or(m.mk_and(is_nan, m.mk_eq(wrapped, bv_nan)), - m.mk_and(m.mk_not(is_nan), m.mk_not(m.mk_eq(wrapped, bv_nan)))); - assert_cnstr(impl); - assert_cnstr(mk_side_conditions()); - } - if (!ctx.relevancy()) relevant_eh(term); } diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 88927998e..262a239dd 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -124,8 +124,6 @@ namespace smt { enode* ensure_enode(expr* e); enode* get_root(expr* a) { return ensure_enode(a)->get_root(); } app* get_ite_value(expr* e); - - void mk_bv_nan(sort * s, expr_ref & result); }; }; From f1a39b888471ec6282717f26b17497b075ed068b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Nov 2023 11:54:59 -0800 Subject: [PATCH 330/428] add comment regarding usage model for flush_objects() to relate with pr #6992 Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 53f53347e..66cd715c9 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -78,6 +78,11 @@ namespace api { m().dec_ref(a); } + // flush_objects can only be called in the main thread. + // This ensures that the calls to m().dec_ref() and dealloc(o) + // only happens in the main thread. + // Calls to dec_ref are allowed in other threads when m_concurrent_dec_ref is + // set to true. void context::flush_objects() { #ifndef SINGLE_THREAD if (!m_concurrent_dec_ref) From 5b9fdcf46234ea241156c8450ba2c49674918444 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Nov 2023 18:08:48 -0800 Subject: [PATCH 331/428] fix #6997 Signed-off-by: Nikolaj Bjorner --- src/math/lp/gomory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 1538ee066..c3e8651d3 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -316,7 +316,7 @@ public: lar_term t = lia.lra.unfold_nested_subterms(m_t); auto pol = t.coeffs_as_vector(); m_t.clear(); - if (pol.size() == 1) { + if (pol.size() == 1 && is_int(pol[0].second)) { TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); auto const& [a, v] = pol[0]; lp_assert(is_int(v)); From 36382ccb5700428fb8ebdd3700d212cf8abe0be3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 17 Nov 2023 02:28:12 +0000 Subject: [PATCH 332/428] Fix memory and concurrency issues in OCaml API (#6992) * Fix memory and concurrency issues in OCaml API * Undo locking changes --- src/api/api_datatype.cpp | 14 ++++++++++++++ src/api/ml/z3.ml | 28 ++++++++++------------------ src/api/ml/z3native_stubs.c.pre | 8 +++++++- src/api/z3_api.h | 10 ++++++++++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index 71d1de212..1ef4ea626 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -241,6 +241,20 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + unsigned Z3_API Z3_constructor_num_fields(Z3_context c, Z3_constructor constr) { + Z3_TRY; + LOG_Z3_constructor_num_fields(c, constr); + RESET_ERROR_CODE(); + mk_c(c)->reset_last_result(); + if (!constr) { + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); + return 0; + } + constructor* c = reinterpret_cast(constr); + return c->m_field_names.size(); + Z3_CATCH_RETURN(0); + } + void Z3_API Z3_query_constructor(Z3_context c, Z3_constructor constr, diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index a5b61c8dc..c2de2b589 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -858,15 +858,6 @@ struct module Constructor = struct type constructor = Z3native.constructor - - module FieldNumTable = Hashtbl.Make(struct - type t = AST.ast - let equal x y = AST.compare x y = 0 - let hash = AST.hash - end) - - let _field_nums = FieldNumTable.create 0 - let create (ctx:context) (name:Symbol.symbol) (recognizer:Symbol.symbol) (field_names:Symbol.symbol list) (sorts:Sort.sort option list) (sort_refs:int list) = let n = List.length field_names in if n <> List.length sorts then @@ -882,10 +873,9 @@ struct (let f x = match x with None -> Z3native.mk_null_ast ctx | Some s -> s in List.map f sorts) sort_refs in - FieldNumTable.add _field_nums no n; no - let get_num_fields (x:constructor) = FieldNumTable.find _field_nums x + let get_num_fields (x:constructor) = Z3native.constructor_num_fields (gc x) x let get_constructor_decl (x:constructor) = let (a, _, _) = (Z3native.query_constructor (gc x) x (get_num_fields x)) in @@ -1512,13 +1502,15 @@ struct in Z3native.apply_result_inc_ref (gc x) arn; let sg = Z3native.apply_result_get_num_subgoals (gc x) arn in - let res = if sg = 0 then - raise (Error "No subgoals") - else - Z3native.apply_result_get_subgoal (gc x) arn 0 in - Z3native.apply_result_dec_ref (gc x) arn; - Z3native.tactic_dec_ref (gc x) tn; - res + if sg = 0 then ( + Z3native.apply_result_dec_ref (gc x) arn; + Z3native.tactic_dec_ref (gc x) tn; + raise (Error "No subgoals")) + else + let res:goal = Z3native.apply_result_get_subgoal (gc x) arn 0 in + Z3native.apply_result_dec_ref (gc x) arn; + Z3native.tactic_dec_ref (gc x) tn; + res let mk_goal = Z3native.mk_goal diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index 038b80725..c8afe90b9 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -2,6 +2,12 @@ #include #include +#ifndef __STDC_NO_ATOMICS__ +#include +#else +#define _Atomic(T) T +#endif + #ifdef __cplusplus extern "C" { #endif @@ -118,7 +124,7 @@ int compare_pointers(void* pt1, void* pt2) { blocks that get copied. */ typedef struct { Z3_context ctx; - unsigned long obj_count; + _Atomic(unsigned long) obj_count; } Z3_context_plus_data; /* A context is wrapped to an OCaml value by storing a pointer diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 894bd60dc..fafc3eebd 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -2082,6 +2082,16 @@ extern "C" { unsigned sort_refs[] ); + /** + \brief Retrieve the number of fields of a constructor + + \param c logical context. + \param constr constructor. + + def_API('Z3_constructor_num_fields', UINT, (_in(CONTEXT), _in(CONSTRUCTOR))) + */ + unsigned Z3_API Z3_constructor_num_fields(Z3_context c, Z3_constructor constr); + /** \brief Reclaim memory allocated to constructor. From 1b6c7d65412fafa36a8c3a5fe0c8e195c1708c15 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Nov 2023 18:58:12 -0800 Subject: [PATCH 333/428] fix #6996 --- src/qe/mbp/mbp_arith.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qe/mbp/mbp_arith.cpp b/src/qe/mbp/mbp_arith.cpp index 5d9d3c19c..91813c697 100644 --- a/src/qe/mbp/mbp_arith.cpp +++ b/src/qe/mbp/mbp_arith.cpp @@ -277,8 +277,14 @@ namespace mbp { extract_coefficients(mbo, eval, ts0, tids, coeffs); mbo.add_divides(coeffs, c0, mul1); } - else + else if (a.is_to_real(t)) + throw default_exception("mbp to-real"); + else if (a.is_to_int(t)) + throw default_exception("mbp to-int"); + else { + TRACE("qe", tout << "insert mul " << mk_pp(t, m) << "\n"); insert_mul(t, mul, ts); + } } bool is_numeral(expr* t, rational& r) { @@ -387,8 +393,7 @@ namespace mbp { return false; }; - for (auto& kv : tids) { - expr* e = kv.m_key; + for (auto& [e, v] : tids) { if (is_arith(e) && !is_pure(e) && !var_mark.is_marked(e)) mark_rec(fmls_mark, e); } From f94a475da386f2dd84a4711b03526648870644b6 Mon Sep 17 00:00:00 2001 From: Hari Govind V K Date: Fri, 17 Nov 2023 22:48:04 +0530 Subject: [PATCH 334/428] Qel fixes (#6999) * dont use qel for sequences. fix #6950 * handle negation of read-write. fix #6991 * handle neg-peq terms produced by distinct. fix #6889 * dbg print --- src/qe/mbp/mbp_arrays_tg.cpp | 46 +++++++++++++++++++++-------------- src/qe/mbp/mbp_basic_tg.cpp | 1 + src/qe/mbp/mbp_term_graph.cpp | 14 +++++++++-- src/qe/qe_mbp.cpp | 13 +++++++++- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index d3ed1a9eb..98224e8df 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -77,8 +77,7 @@ struct mbp_array_tg::impl { } // Returns true if e has a subterm store(v) where v is a variable to be - // eliminated. Assumes that has_store has already been called for - // subexpressions of e + // eliminated. Recurses on subexpressions of ee bool has_stores(expr *e) { if (m_has_stores.is_marked(e)) return true; if (!is_app(e)) return false; @@ -86,8 +85,13 @@ struct mbp_array_tg::impl { m_has_stores.mark(e, true); return true; } - for (auto c : *(to_app(e))) { - if (m_has_stores.is_marked(c)) { + if (any_of(*(to_app(e)), [&](expr* c) { return m_has_stores.is_marked(c); })) { + m_has_stores.mark(e, true); + return true; + } + //recurse + for(auto c : *(to_app(e))) { + if (has_stores(c)) { m_has_stores.mark(e, true); return true; } @@ -110,7 +114,10 @@ struct mbp_array_tg::impl { return m_array_util.is_array(lhs) && m_array_util.is_array(rhs) && (has_var(lhs) || has_var(rhs)); } - + bool is_neg_peq(expr *e) { + expr* ne; + return m.is_not(e, ne) && is_implicit_peq(ne); + } void mark_seen(expr *t) { m_seen.mark(t); } bool is_seen(expr *t) { return m_seen.is_marked(t); } void mark_seen(expr *t1, expr *t2) { m_seenp.insert(expr_pair(t1, t2)); } @@ -151,7 +158,7 @@ struct mbp_array_tg::impl { void elimwreq(peq p, bool is_neg) { SASSERT(is_arr_write(p.lhs())); TRACE("mbp_tg", - tout << "applying elimwreq on " << expr_ref(p.mk_peq(), m);); + tout << "applying elimwreq on " << expr_ref(p.mk_peq(), m) << " is neg: " << is_neg;); vector indices; expr *j = to_app(p.lhs())->get_arg(1); expr *elem = to_app(p.lhs())->get_arg(2); @@ -178,7 +185,7 @@ struct mbp_array_tg::impl { expr_ref p_new_expr(m); p_new_expr = is_neg ? m.mk_not(p_new.mk_peq()) : p_new.mk_peq(); m_tg.add_lit(p_new_expr); - m_tg.add_eq(p_new_expr, p.mk_peq()); + m_tg.add_eq(p_new.mk_peq(), p.mk_peq()); return; } for (expr *d : deq) { m_tg.add_deq(j, d); } @@ -193,14 +200,13 @@ struct mbp_array_tg::impl { m_tg.add_eq(rd, elem); m_tg.add_eq(p.mk_peq(), p_new.mk_peq()); } else { - SASSERT(m_mdl.is_false(p_new.mk_peq()) || - !m_mdl.are_equal(rd, elem)); - if (m_mdl.is_false(p_new.mk_peq())) { + expr_ref rd_eq(m.mk_eq(rd, elem), m); + if (m_mdl.is_false(rd_eq)) { m_tg.add_deq(rd, elem); } + else { expr_ref npeq(mk_not(p_new.mk_peq()), m); m_tg.add_lit(npeq); m_tg.add_eq(p.mk_peq(), p_new.mk_peq()); } - if (!m_mdl.are_equal(rd, elem)) { m_tg.add_deq(rd, elem); } } } @@ -281,13 +287,16 @@ struct mbp_array_tg::impl { if (m_seen.is_marked(term)) continue; if (m_tg.is_cgr(term)) continue; TRACE("mbp_tg", tout << "processing " << expr_ref(term, m);); - if (is_implicit_peq(term)) { + if (is_implicit_peq(term) || is_neg_peq(term)) { // rewrite array eq as peq mark_seen(term); progress = true; - e = mk_wr_peq(to_app(term)->get_arg(0), - to_app(term)->get_arg(1)) + nt = term; + bool is_not = m.is_not(term, nt); + e = mk_wr_peq(to_app(nt)->get_arg(0), + to_app(nt)->get_arg(1)) .mk_peq(); + e = is_not ? m.mk_not(e) : e; m_tg.add_lit(e); m_tg.add_eq(term, e); continue; @@ -303,9 +312,10 @@ struct mbp_array_tg::impl { elimwreq(p, is_neg); continue; } - if (!m_array_util.is_store(p.lhs()) && has_var(p.lhs())) { + if (!m_array_util.is_store(p.lhs()) && has_var(p.lhs()) && !is_neg) { // TODO: don't apply this rule if vars in p.lhs() also // appear in p.rhs() + mark_seen(p.lhs()); mark_seen(nt); mark_seen(term); @@ -314,7 +324,7 @@ struct mbp_array_tg::impl { continue; } // eliminate eq when the variable is on the rhs - if (!m_array_util.is_store(p.rhs()) && has_var(p.rhs())) { + if (!m_array_util.is_store(p.rhs()) && has_var(p.rhs()) && !is_neg) { mark_seen(p.rhs()); p.get_diff_indices(indices); peq p_new = mk_wr_peq(p.rhs(), p.lhs(), indices); @@ -325,10 +335,10 @@ struct mbp_array_tg::impl { continue; } } - if (m_use_mdl && is_rd_wr(term)) { + if (m_use_mdl && is_rd_wr(nt)) { mark_seen(term); progress = true; - elimrdwr(term); + elimrdwr(nt); continue; } } diff --git a/src/qe/mbp/mbp_basic_tg.cpp b/src/qe/mbp/mbp_basic_tg.cpp index fa657d990..ce5e99eb1 100644 --- a/src/qe/mbp/mbp_basic_tg.cpp +++ b/src/qe/mbp/mbp_basic_tg.cpp @@ -69,6 +69,7 @@ struct mbp_basic_tg::impl { m_tg.get_terms(terms, false); for (expr *term : terms) { if (is_seen(term)) continue; + TRACE("mbp_tg", tout << "Processing " << expr_ref(term, m) << "\n";); if (m.is_ite(term, c, th, el) && should_split(c)) { mark_seen(term); progress = true; diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index 190acecdf..e8ba29dfd 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -121,6 +121,8 @@ class term { unsigned m_is_peq : 1; // caches whether m_expr is the child of not unsigned m_is_neq_child : 1; + // caches whether m_expr is peq and the child of not + unsigned m_is_npeq_child : 1; // -- the term is a compound term can be rewritten to be ground or it is a // ground constant @@ -170,6 +172,7 @@ class term { : m_expr(v), m_root(this), m_repr(nullptr), m_next(this), m_mark(false), m_mark2(false), m_interpreted(false), m_is_eq(m_expr.get_manager().is_eq(m_expr)), m_is_peq(false), + m_is_npeq_child(false), m_is_neq_child(false), m_cgr(0), m_gr(0) { m_is_neq = m_expr.get_manager().is_not(m_expr) && m_expr.get_manager().is_eq(to_app(m_expr)->get_arg(0)); @@ -244,7 +247,9 @@ class term { bool is_eq_or_peq() const { return m_is_eq || m_is_peq; } bool is_neq() const { return m_is_neq; } void set_neq_child() { m_is_neq_child = true; } + void set_npeq_child() { m_is_npeq_child = true; } bool is_neq_child() const { return m_is_neq_child; } + bool is_npeq_child() const { return m_is_npeq_child; } unsigned get_decl_id() const { return is_app(m_expr) ? to_app(m_expr)->get_decl()->get_id() : m_expr->get_id(); @@ -491,11 +496,11 @@ void term_graph::get_terms(expr_ref_vector &res, bool exclude_cground) { std::function fil = nullptr; if (exclude_cground) { fil = [](term *t) { - return !t->is_neq_child() && (t->is_eq_or_peq() || !t->is_cgr()); + return !t->is_neq_child() && !t->is_npeq_child() && (t->is_eq_or_peq() || !t->is_cgr()); }; } else { - fil = [](term *t) { return !t->is_neq_child(); }; + fil = [](term *t) { return !t->is_neq_child() && !t->is_npeq_child(); }; } auto terms = m_terms.filter_pure(fil); res.resize(terms.size()); @@ -574,6 +579,11 @@ term *term_graph::internalize_term(expr *t) { } merge_flush(); SASSERT(res); + if (m.is_not(t) && is_app(to_app(t)->get_arg(0)) && is_partial_eq(to_app(to_app(t)->get_arg(0)))) { + term* p = get_term(to_app(t)->get_arg(0)); + SASSERT(p); + p->set_npeq_child(); + } return res; } diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index d53684100..861c841a9 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -29,6 +29,7 @@ Revision History: #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/scoped_proof.h" +#include "ast/seq_decl_plugin.h" #include "util/gparams.h" #include "model/model_evaluator.h" #include "model/model_pp.h" @@ -405,8 +406,18 @@ public: return true; } + bool has_unsupported_th(const expr_ref_vector fmls) { + seq_util seq(m); + expr_ref e(m); + e = mk_and(fmls); + return any_of(subterms::all(e), [&](expr* c) { return seq.is_char(c) || seq.is_seq(c); }); + } void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { - if (m_use_qel) { + //don't use mbp_qel on some theories where model evaluation is + //incomplete This is not a limitation of qel. Fix this either by + //making mbp choices less dependent on the model evaluation methods + //or fix theory rewriters to make terms evalution complete + if (m_use_qel && !has_unsupported_th(fmls)) { bool dsub = m_dont_sub; m_dont_sub = !force_elim; expr_ref fml(m); From 6d6d6b8ed08be7363b774f8248572ef31e670963 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 Nov 2023 09:20:17 -0800 Subject: [PATCH 335/428] build issue Signed-off-by: Nikolaj Bjorner --- src/qe/mbp/mbp_arrays_tg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index 98224e8df..ad9194047 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -296,7 +296,7 @@ struct mbp_array_tg::impl { e = mk_wr_peq(to_app(nt)->get_arg(0), to_app(nt)->get_arg(1)) .mk_peq(); - e = is_not ? m.mk_not(e) : e; + e = is_not ? m.mk_not(e) : e.get(); m_tg.add_lit(e); m_tg.add_eq(term, e); continue; From b9455c3692a8269d00a57b6c7a497b1f6e3b2068 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 Nov 2023 10:06:20 -0800 Subject: [PATCH 336/428] #6999 deal with implicit assumptions, more robust pattern matching The code is making some assumptions that arrays are 1-dimensional. This is not generally true. Introducing pattern matching to ensure the assumption is met. Avoid get_arg(..) especially when there is an approach based on pattern matching recognizers. --- src/ast/array_decl_plugin.h | 20 ++++++++ src/qe/mbp/mbp_arrays_tg.cpp | 90 ++++++++++++++++++++++-------------- 2 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 2804b4169..9a57476d4 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -186,6 +186,21 @@ public: bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value); + + bool is_select1(expr* n) const { return is_select(n) && to_app(n)->get_num_args() == 2; } + + bool is_select1(expr* n, expr*& a, expr*& i) const { + return is_select1(n) && (a = to_app(n)->get_arg(0), i = to_app(n)->get_arg(1), true); + } + + bool is_store1(expr* n) const { return is_store(n) && to_app(n)->get_num_args() == 3; } + + bool is_store1(expr* n, expr*& a, expr*& i, expr*& v) const { + app* _n; + return is_store1(n) && (_n = to_app(n), a = _n->get_arg(0), i = _n->get_arg(1), v = _n->get_arg(2), true); + } + + MATCH_BINARY(is_subset); }; @@ -213,6 +228,11 @@ public: return mk_store(args.size(), args.data()); } + app * mk_select(expr* a, expr* i) const { + expr* args[2] = { a, i }; + return mk_select(2, args); + } + app * mk_select(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args); } diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index ad9194047..4df5d2385 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -72,8 +72,11 @@ struct mbp_array_tg::impl { } bool is_arr_write(expr *t) { - if (!m_array_util.is_store(t)) return false; - return has_var(to_app(t)); + return m_array_util.is_store1(t) && has_var(to_app(t)); + } + + bool is_arr_write(expr *t, expr*& a, expr*& i, expr*& v) { + return m_array_util.is_store1(t, a, i, v) && has_var(to_app(t)); } // Returns true if e has a subterm store(v) where v is a variable to be @@ -99,25 +102,50 @@ struct mbp_array_tg::impl { return false; } + // + // the code that uses this assumes that select takes only two arguments. + // Note that select may take more than two arguments in general. + // bool is_rd_wr(expr *t) { - if (!m_array_util.is_select(t)) return false; - return m_array_util.is_store(to_app(t)->get_arg(0)) && - has_stores(to_app(t)->get_arg(0)); + expr* a, *idx; + return m_array_util.is_select1(t, a, idx) && + m_array_util.is_store(a) & + has_stores(a); + } + + bool is_rd_wr(expr* t, expr*& wr_ind, expr*& rd_ind, expr*& b, expr*& v) { + if (!is_rd_wr(t)) + return false; + expr* a; + VERIFY(m_array_util.is_select1(t, a, rd_ind)); + VERIFY(m_array_util.is_store1(a, b, wr_ind, v)); + return true; } bool is_implicit_peq(expr *e) { - return m.is_eq(e) && - is_implicit_peq(to_app(e)->get_arg(0), to_app(e)->get_arg(1)); + expr* a, *b; + return is_implicit_peq(e, a, b); + } + + bool is_implicit_peq(expr *e, expr*& a, expr*& b) { + return m.is_eq(e, a, b) && is_implicit_peq(a, b); } bool is_implicit_peq(expr *lhs, expr *rhs) { return m_array_util.is_array(lhs) && m_array_util.is_array(rhs) && (has_var(lhs) || has_var(rhs)); } + + bool is_neg_peq(expr *e, expr*& a, expr*& b) { + expr* ne; + return m.is_not(e, ne) && is_implicit_peq(ne, a, b); + } + bool is_neg_peq(expr *e) { expr* ne; return m.is_not(e, ne) && is_implicit_peq(ne); } + void mark_seen(expr *t) { m_seen.mark(t); } bool is_seen(expr *t) { return m_seen.is_marked(t); } void mark_seen(expr *t1, expr *t2) { m_seenp.insert(expr_pair(t1, t2)); } @@ -140,7 +168,8 @@ struct mbp_array_tg::impl { // create a peq where write terms are preferred on the left hand side peq mk_wr_peq(expr *e1, expr *e2, vector &indices) { expr *n_lhs = e1, *n_rhs = e2; - if (is_wr_on_rhs(e1, e2)) std::swap(n_lhs, n_rhs); + if (is_wr_on_rhs(e1, e2)) + std::swap(n_lhs, n_rhs); return peq(n_lhs, n_rhs, indices, m); } @@ -156,12 +185,11 @@ struct mbp_array_tg::impl { // or &&_{i \in indices} j \neq i && // !(select(y, j) = elem) void elimwreq(peq p, bool is_neg) { - SASSERT(is_arr_write(p.lhs())); + expr* a, *j, *elem; + VERIFY(is_arr_write(p.lhs(), a, j, elem)); TRACE("mbp_tg", tout << "applying elimwreq on " << expr_ref(p.mk_peq(), m) << " is neg: " << is_neg;); vector indices; - expr *j = to_app(p.lhs())->get_arg(1); - expr *elem = to_app(p.lhs())->get_arg(2); bool in = false; p.get_diff_indices(indices); expr_ref eq_index(m); @@ -180,7 +208,7 @@ struct mbp_array_tg::impl { if (in) { SASSERT(m_mdl.are_equal(j, eq_index)); peq p_new = - mk_wr_peq(to_app(p.lhs())->get_arg(0), p.rhs(), indices); + mk_wr_peq(to_app(a, p.rhs(), indices); m_tg.add_eq(j, eq_index); expr_ref p_new_expr(m); p_new_expr = is_neg ? m.mk_not(p_new.mk_peq()) : p_new.mk_peq(); @@ -192,9 +220,8 @@ struct mbp_array_tg::impl { expr_ref_vector setOne(m); setOne.push_back(j); indices.push_back(setOne); - peq p_new = mk_wr_peq(to_app(p.lhs())->get_arg(0), p.rhs(), indices); - expr *args[2] = {p.rhs(), j}; - expr_ref rd(m_array_util.mk_select(2, args), m); + peq p_new = mk_wr_peq(a, p.rhs(), indices); + expr_ref rd(m_array_util.mk_select(p.rhs(), j), m); if (!is_neg) { m_tg.add_lit(p_new.mk_peq()); m_tg.add_eq(rd, elem); @@ -240,8 +267,7 @@ struct mbp_array_tg::impl { m_tg.add_var(a); auto const &indx = std::next(itr, i); SASSERT(indx->size() == 1); - expr *args[2] = {to_app(p.lhs()), to_app(indx->get(0))}; - sel = m_array_util.mk_select(2, args); + sel = m_array_util.mk_select(p.lhs(), indx->get(0)); m_mdl.register_decl(a->get_decl(), m_mdl(sel)); i++; } @@ -252,21 +278,16 @@ struct mbp_array_tg::impl { // rewrite select(store(a, i, k), j) into either select(a, j) or k void elimrdwr(expr *term) { - SASSERT(is_rd_wr(term)); TRACE("mbp_tg", tout << "applying elimrdwr on " << expr_ref(term, m);); - expr *wr_ind = to_app(to_app(term)->get_arg(0))->get_arg(1); - expr *rd_ind = to_app(term)->get_arg(1); - expr *e; - if (m_mdl.are_equal(wr_ind, rd_ind)) { + expr* wr_ind, *rd_ind, *b, *v; + VERIFY(is_rd_wr(term, wr_ind, rd_ind, b, v)); + if (m_mdl.are_equal(wr_ind, rd_ind)) m_tg.add_eq(wr_ind, rd_ind); - e = to_app(to_app(term)->get_arg(0))->get_arg(2); - } else { + else { m_tg.add_deq(wr_ind, rd_ind); - expr *args[2] = {to_app(to_app(term)->get_arg(0))->get_arg(0), - to_app(term)->get_arg(1)}; - e = m_array_util.mk_select(2, args); + v = m_array_util.mk_select(b, rd_ind); } - m_tg.add_eq(term, e); + m_tg.add_eq(term, v); } // iterate through all terms in m_tg and apply all array MBP rules once @@ -284,18 +305,19 @@ struct mbp_array_tg::impl { m_tg.get_terms(terms, false); for (unsigned i = 0; i < terms.size(); i++) { term = terms.get(i); - if (m_seen.is_marked(term)) continue; - if (m_tg.is_cgr(term)) continue; + if (m_seen.is_marked(term)) + continue; + if (m_tg.is_cgr(term)) + continue; TRACE("mbp_tg", tout << "processing " << expr_ref(term, m);); - if (is_implicit_peq(term) || is_neg_peq(term)) { + expr* a, *b; + if (is_implicit_peq(term, a, b) || is_neg_peq(term, a, b)) { // rewrite array eq as peq mark_seen(term); progress = true; nt = term; bool is_not = m.is_not(term, nt); - e = mk_wr_peq(to_app(nt)->get_arg(0), - to_app(nt)->get_arg(1)) - .mk_peq(); + e = mk_wr_peq(a, b).mk_peq(); e = is_not ? m.mk_not(e) : e.get(); m_tg.add_lit(e); m_tg.add_eq(term, e); From a9f9d3ddf4180ab8a9c6c353b64656d6eacbfd71 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 Nov 2023 10:15:01 -0800 Subject: [PATCH 337/428] build fixes Signed-off-by: Nikolaj Bjorner --- src/qe/mbp/mbp_arrays_tg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qe/mbp/mbp_arrays_tg.cpp b/src/qe/mbp/mbp_arrays_tg.cpp index 4df5d2385..c3d91ae64 100644 --- a/src/qe/mbp/mbp_arrays_tg.cpp +++ b/src/qe/mbp/mbp_arrays_tg.cpp @@ -109,7 +109,7 @@ struct mbp_array_tg::impl { bool is_rd_wr(expr *t) { expr* a, *idx; return m_array_util.is_select1(t, a, idx) && - m_array_util.is_store(a) & + m_array_util.is_store(a) && has_stores(a); } @@ -208,7 +208,7 @@ struct mbp_array_tg::impl { if (in) { SASSERT(m_mdl.are_equal(j, eq_index)); peq p_new = - mk_wr_peq(to_app(a, p.rhs(), indices); + mk_wr_peq(a, p.rhs(), indices); m_tg.add_eq(j, eq_index); expr_ref p_new_expr(m); p_new_expr = is_neg ? m.mk_not(p_new.mk_peq()) : p_new.mk_peq(); From 1710fe4927e0da90fc0ec921b41ad56e6e12d7e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Nov 2023 09:52:58 -0800 Subject: [PATCH 338/428] use iterator shortcut --- src/smt/smt_context.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 6d90653ba..9a9b92393 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1672,12 +1672,7 @@ namespace smt { } bool context::can_theories_propagate() const { - for (theory* t : m_theory_set) { - if (t->can_propagate()) { - return true; - } - } - return false; + return any_of(m_theory_set, [&](theory* t) { return t->can_propagate(); }); } bool context::can_propagate() const { From 5ab1afe5c2e451f5d35f52867b1d58b69a4c85be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Nov 2023 09:53:20 -0800 Subject: [PATCH 339/428] expose enode pp convenciences --- src/smt/smt_context.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index f9560cb01..4feeae1b5 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -61,9 +61,16 @@ Revision History: namespace smt { class model_generator; + class context; struct cancel_exception {}; + struct enode_pp { + context const& ctx; + enode* n; + enode_pp(enode* n, context const& ctx): ctx(ctx), n(n) {} + }; + class context { friend class model_generator; friend class lookahead; @@ -1368,6 +1375,8 @@ namespace smt { void display_asserted_formulas(std::ostream & out) const; + enode_pp pp(enode* n) { return enode_pp(n, *this); } + std::ostream& display_literal(std::ostream & out, literal l) const; std::ostream& display_detailed_literal(std::ostream & out, literal l) const { return smt::display(out, l, m, m_bool_var2expr.data()); } @@ -1844,11 +1853,6 @@ namespace smt { std::ostream& operator<<(std::ostream& out, enode_eq_pp const& p); - struct enode_pp { - context const& ctx; - enode* n; - enode_pp(enode* n, context const& ctx): ctx(ctx), n(n) {} - }; std::ostream& operator<<(std::ostream& out, enode_pp const& p); From e40b8a2d13751fadd20de16e20803a1995ab6d82 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Nov 2023 09:56:06 -0800 Subject: [PATCH 340/428] household chores in legacy arithmetic solver --- src/smt/theory_arith.h | 10 ++++----- src/smt/theory_arith_aux.h | 41 ++++++++++++++------------------- src/smt/theory_arith_core.h | 45 +++++++++++++++---------------------- src/smt/theory_arith_eq.h | 21 +++++++---------- 4 files changed, 48 insertions(+), 69 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 8cbf844b9..86c05aec6 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -301,7 +301,7 @@ namespace smt { inf_numeral const & get_value() const { return m_value; } virtual bool has_justification() const { return false; } virtual void push_justification(antecedents& antecedents, numeral const& coeff, bool proofs_enabled) {} - virtual void display(theory_arith const& th, std::ostream& out) const; + virtual std::ostream& display(theory_arith const& th, std::ostream& out) const; }; @@ -327,7 +327,7 @@ namespace smt { void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override { a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); } - void display(theory_arith const& th, std::ostream& out) const override; + std::ostream& display(theory_arith const& th, std::ostream& out) const override; }; class eq_bound : public bound { @@ -345,7 +345,7 @@ namespace smt { SASSERT(m_lhs->get_root() == m_rhs->get_root()); a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); } - void display(theory_arith const& th, std::ostream& out) const override; + std::ostream& display(theory_arith const& th, std::ostream& out) const override; }; class derived_bound : public bound { @@ -361,7 +361,7 @@ namespace smt { void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override; virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); } virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); } - void display(theory_arith const& th, std::ostream& out) const override; + std::ostream& display(theory_arith const& th, std::ostream& out) const override; }; @@ -824,7 +824,7 @@ namespace smt { unsigned m_assume_eq_head = 0; bool random_update(theory_var v); void mutate_assignment(); - bool assume_eqs_core(); + bool assume_eqs(); bool delayed_assume_eqs(); // ----------------------------------- diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 470ea5f7b..8141377c4 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -370,8 +370,8 @@ namespace smt { template - void theory_arith::bound::display(theory_arith const& th, std::ostream& out) const { - out << "v" << get_var() << " " << get_bound_kind() << " " << get_value(); + std::ostream& theory_arith::bound::display(theory_arith const& th, std::ostream& out) const { + return out << "v" << get_var() << " " << get_bound_kind() << " " << get_value(); } @@ -414,11 +414,10 @@ namespace smt { } template - void theory_arith::atom::display(theory_arith const& th, std::ostream& out) const { + std::ostream& theory_arith::atom::display(theory_arith const& th, std::ostream& out) const { literal l(get_bool_var(), !m_is_true); - // out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << get_k() << " "; - // out << l << ":"; th.ctx.display_detailed_literal(out, l); + return out; } // ----------------------------------- @@ -428,10 +427,10 @@ namespace smt { // ----------------------------------- template - void theory_arith::eq_bound::display(theory_arith const& th, std::ostream& out) const { + std::ostream& theory_arith::eq_bound::display(theory_arith const& th, std::ostream& out) const { ast_manager& m = th.get_manager(); - out << "#" << m_lhs->get_owner_id() << " " << mk_pp(m_lhs->get_expr(), m) << " = " - << "#" << m_rhs->get_owner_id() << " " << mk_pp(m_rhs->get_expr(), m); + return out << "#" << m_lhs->get_owner_id() << " " << mk_pp(m_lhs->get_expr(), m) << " = " + << "#" << m_rhs->get_owner_id() << " " << mk_pp(m_rhs->get_expr(), m); } // ----------------------------------- @@ -752,7 +751,7 @@ namespace smt { } template - void theory_arith::derived_bound::display(theory_arith const& th, std::ostream& out) const { + std::ostream& theory_arith::derived_bound::display(theory_arith const& th, std::ostream& out) const { ast_manager& m = th.get_manager(); out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << bound::get_value() << "\n"; out << "expr: " << mk_pp(th.var2expr(bound::get_var()), m) << "\n"; @@ -765,8 +764,9 @@ namespace smt { << "#" << b->get_owner_id() << " " << mk_pp(b->get_expr(), m) << "\n"; } for (literal l : m_lits) { - out << l << ":"; th.ctx.display_detailed_literal(out, l) << "\n"; + out << l << ":"; th.ctx.display_detailed_literal(out, l) << "\n"; } + return out; } @@ -2195,33 +2195,27 @@ namespace smt { } template - bool theory_arith::assume_eqs_core() { + bool theory_arith::assume_eqs() { // See comment in m_liberal_final_check declaration if (m_liberal_final_check) mutate_assignment(); TRACE("assume_eq_int", display(tout);); unsigned old_sz = m_assume_eq_candidates.size(); - TRACE("func_interp_bug", display(tout);); m_var_value_table.reset(); bool result = false; int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { enode * n = get_enode(v); - TRACE("func_interp_bug", tout << mk_pp(n->get_expr(), get_manager()) << " -> " << m_value[v] << " root #" << n->get_root()->get_owner_id() << " " << is_relevant_and_shared(n) << "\n";); - if (!is_relevant_and_shared(n)) { + if (!is_relevant_and_shared(n)) continue; - } theory_var other = null_theory_var; other = m_var_value_table.insert_if_not_there(v); - if (other == v) { + if (other == v) continue; - } enode * n2 = get_enode(other); - if (n->get_root() == n2->get_root()) { + if (n->get_root() == n2->get_root()) continue; - } - TRACE("func_interp_bug", tout << "adding to assume_eq queue #" << n->get_owner_id() << " #" << n2->get_owner_id() << "\n";); m_assume_eq_candidates.push_back({ other , v }); result = true; } @@ -2242,10 +2236,9 @@ namespace smt { enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); m_assume_eq_head++; - CTRACE("func_interp_bug", - get_value(v1) == get_value(v2) && - n1->get_root() != n2->get_root(), - tout << "assuming eq: #" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n";); + CTRACE("arith", + get_value(v1) == get_value(v2) && n1->get_root() != n2->get_root(), + tout << "assuming eq: " << ctx.pp(n1) << " = #" << ctx.pp(n2) << "\n";); if (get_value(v1) == get_value(v2) && n1->get_root() != n2->get_root() && assume_eq(n1, n2)) { diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 9fb99c0b4..4e4464a52 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1167,10 +1167,7 @@ namespace smt { --i; } } - CTRACE("arith", atoms.size() > 1, - for (unsigned i = 0; i < atoms.size(); ++i) { - atoms[i]->display(*this, tout); tout << "\n"; - }); + CTRACE("arith", atoms.size() > 1, for (auto* a : atoms) a->display(*this, tout) << "\n";); ptr_vector occs(m_var_occs[v]); std::sort(atoms.begin(), atoms.end(), compare_atoms()); @@ -1277,7 +1274,7 @@ namespace smt { template bool theory_arith::internalize_atom(app * n, bool gate_ctx) { - TRACE("arith_internalize", tout << "internalizing atom:\n" << mk_pp(n, m) << "\n";); + TRACE("arith_internalize", tout << "internalizing atom:\n" << mk_bounded_pp(n, m) << "\n";); SASSERT(m_util.is_le(n) || m_util.is_ge(n) || m_util.is_is_int(n)); SASSERT(!ctx.b_internalized(n)); atom_kind kind; @@ -1302,7 +1299,8 @@ namespace smt { app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); expr * rhs2; - if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) { rhs = to_app(rhs2); } + if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) + rhs = to_app(rhs2); if (!m_util.is_numeral(rhs)) { throw default_exception("malformed atomic constraint"); } @@ -1335,16 +1333,14 @@ namespace smt { occs.push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); - TRACE("arith_internalize", tout << "succeeded... v" << v << " " << kind << " " << k << "\n"; - for (unsigned i = 0; i + 1 < occs.size(); ++i) tout << occs[i] << "\n";); + TRACE("arith_internalize", tout << "succeeded... v" << v << " " << kind << " " << k << "\n"); return true; } template bool theory_arith::internalize_term(app * term) { - TRACE("arith_internalize", tout << "internalising term:\n" << mk_pp(term, m) << "\n";); theory_var v = internalize_term_core(term); - TRACE("arith_internalize", tout << "theory_var: " << v << "\n";); + TRACE("arith_internalize", tout << "internalising term: v" << v << " " << mk_bounded_pp(term, m) << "\n";); return v != null_theory_var; } @@ -1375,9 +1371,9 @@ namespace smt { template void theory_arith::assign_eh(bool_var v, bool is_true) { - TRACE("arith_verbose", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";); atom * a = get_bv2a(v); if (!a) return; + TRACE("arith", tout << "assign p" << literal(v,!is_true) << " : " << mk_bounded_pp(ctx.bool_var2expr(v), m) << "\n";); SASSERT(ctx.get_assignment(a->get_bool_var()) != l_undef); SASSERT((ctx.get_assignment(a->get_bool_var()) == l_true) == is_true); a->assign_eh(is_true, get_epsilon(a->get_var())); @@ -1401,9 +1397,7 @@ namespace smt { template void theory_arith::new_eq_eh(theory_var v1, theory_var v2) { - TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); - TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_expr(), m) << "\n" << - mk_pp(get_enode(v2)->get_expr(), m) << "\n";); + TRACE("arith_new_eq_eh", tout << ctx.pp(get_enode(v1)) << "\n" << ctx.pp(get_enode(v2)) << "\n";); enode * n1 = get_enode(v1); @@ -1503,7 +1497,7 @@ namespace smt { TRACE("arith", tout << "check_int_feasibility(), ok: " << ok << "\n";); break; case 1: - if (assume_eqs_core()) + if (assume_eqs()) ok = FC_CONTINUE; else ok = FC_DONE; @@ -1571,7 +1565,6 @@ namespace smt { template void theory_arith::propagate() { - TRACE("arith_propagate", tout << "propagate\n"; display(tout);); if (!process_atoms()) return; propagate_core(); @@ -1597,9 +1590,9 @@ namespace smt { failed(); return false; } - if (ctx.get_cancel_flag()) { + if (ctx.get_cancel_flag()) return true; - } + CASSERT("arith", satisfy_bounds()); discard_update_trail(); @@ -2812,7 +2805,8 @@ namespace smt { template void theory_arith::explain_bound(row const & r, int idx, bool is_lower, inf_numeral & delta, antecedents& ante) { SASSERT(delta >= inf_numeral::zero()); - TRACE("arith_conflict", tout << "relax: " << relax_bounds() << " lits: " << ante.lits().size() << " eqs: " << ante.eqs().size() << " idx: " << idx << "\n";); + TRACE("arith_conflict", tout << "delta: " << delta << " relax: " << relax_bounds() << " lits: " << ante.lits().size() << " eqs: " << ante.eqs().size() << " idx: " << idx << "\n";); + if (!relax_bounds() && (!ante.lits().empty() || !ante.eqs().empty())) { return; } @@ -3002,12 +2996,10 @@ namespace smt { TRACE("propagate_bounds", ante.display(tout) << " --> "; - ctx.display_detailed_literal(tout, l); - tout << "\n";); + ctx.display_detailed_literal(tout, l) << "\n"); - - - TRACE("arith", tout << ctx.get_scope_level() << "\n"; + TRACE("arith", tout << "@" << ctx.get_scope_level() << ": "; + ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l) << "\n"); if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { @@ -3078,7 +3070,6 @@ namespace smt { } } - TRACE("arith_eq", tout << "done\n";); m_to_check.reset(); m_in_to_check.reset(); } @@ -3108,7 +3099,7 @@ namespace smt { TRACE("arith_conflict", if (proof_rule) tout << proof_rule << "\n"; - tout << "scope: " << ctx.get_scope_level() << "\n"; + tout << "@" << ctx.get_scope_level() << "\n"; for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); tout << " "; @@ -3392,7 +3383,7 @@ namespace smt { } template - void theory_arith::pop_scope_eh(unsigned num_scopes) { + void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); diff --git a/src/smt/theory_arith_eq.h b/src/smt/theory_arith_eq.h index cffac2459..ebdd6e73e 100644 --- a/src/smt/theory_arith_eq.h +++ b/src/smt/theory_arith_eq.h @@ -43,7 +43,6 @@ namespace smt { return; numeral const & val = lower_bound(v).get_rational(); value_sort_pair key(val, is_int_src(v)); - TRACE("arith_eq", tout << mk_pp(get_enode(v)->get_expr(), get_manager()) << " = " << val << "\n";); theory_var v2; if (m_fixed_var_table.find(key, v2)) { if (v2 < static_cast(get_num_vars()) && is_fixed(v2) && lower_bound(v2).get_rational() == val) { @@ -310,26 +309,22 @@ namespace smt { } // add new entry m_var_offset2row_id.insert(key, rid); - } - + } } template void theory_arith::propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents) { // Ignore equality if variables are already known to be equal. - ast_manager& m = get_manager(); - (void)m; if (is_equal(x, y)) return; - // I doesn't make sense to propagate an equality (to the core) of variables of different sort. - if (var2expr(x)->get_sort() != var2expr(y)->get_sort()) { - TRACE("arith", tout << mk_pp(var2expr(x), m) << " = " << mk_pp(var2expr(y), m) << "\n";); - return; - } - context & ctx = get_context(); enode * _x = get_enode(x); enode * _y = get_enode(y); + // I doesn't make sense to propagate an equality (to the core) of variables of different sort. + CTRACE("arith", _x->get_sort() != _y->get_sort(), tout << enode_pp(_x, ctx) << " = " << enode_pp(_y, ctx) << "\n"); + if (_x->get_sort() != _y->get_sort()) + return; + eq_vector const& eqs = antecedents.eqs(); literal_vector const& lits = antecedents.lits(); justification * js = @@ -346,9 +341,9 @@ namespace smt { for (literal lit : lits) ctx.display_detailed_literal(tout, lit) << "\n"; for (auto const& p : eqs) - tout << pp(p.first, m) << " = " << pp(p.second, m) << "\n"; + tout << enode_pp(p.first, ctx) << " = " << enode_pp(p.second, ctx) << "\n"; tout << " ==> "; - tout << pp(_x, m) << " = " << pp(_y, m) << "\n";); + tout << enode_pp(_x, ctx) << " = " << enode_pp(_y, ctx) << "\n";); ctx.assign_eq(_x, _y, eq_justification(js)); } }; From 5bec982cc59fbbfeb87e5297e0bea2fdfadb4772 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Nov 2023 10:05:26 -0800 Subject: [PATCH 341/428] chores in theory_lra --- src/smt/theory_lra.cpp | 214 ++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 142 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index b0dc3a9e4..a23472e5b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -77,7 +77,6 @@ class theory_lra::imp { bool get_cancel_flag() override { return !m_imp.m.inc(); } }; - theory_lra& th; ast_manager& m; arith_util a; @@ -198,12 +197,10 @@ class theory_lra::imp { imp & m_th; var_value_hash(imp & th):m_th(th) {} unsigned operator()(theory_var v) const { - if (m_th.use_nra_model()) { + if (m_th.use_nra_model()) return m_th.is_int(v); - } - else { + else return (unsigned)std::hash()(m_th.get_ivalue(v)); - } } }; int_hashtable m_model_eqs; @@ -229,12 +226,14 @@ class theory_lra::imp { bool is_real(enode* n) const { return a.is_real(n->get_expr()); } enode* get_enode(theory_var v) const { return th.get_enode(v); } enode* get_enode(expr* e) const { return ctx().get_enode(e); } - expr* get_owner(theory_var v) const { return get_enode(v)->get_expr(); } + expr* get_owner(theory_var v) const { return get_enode(v)->get_expr(); } + enode_pp pp(enode* n) const { return enode_pp(n, ctx()); } + enode_pp pp(theory_var v) const { return pp(get_enode(v)); } + mk_bounded_pp bpp(expr* e) { return mk_bounded_pp(e, m); } lpvar add_const(int c, lpvar& var, bool is_int) { - if (var != UINT_MAX) { + if (var != UINT_MAX) return var; - } app_ref cnst(a.mk_numeral(rational(c), is_int), m); mk_enode(cnst); theory_var v = mk_var(cnst); @@ -551,7 +550,7 @@ class theory_lra::imp { theory_var v = mk_var(n); vars.push_back(register_theory_var_in_lar_solver(v)); } - TRACE("arith", tout << "v" << v << " := " << mk_pp(t, m) << "\n" << vars << "\n";); + TRACE("arith", tout << "v" << v << " := " << bpp(t) << "\n" << vars << "\n";); m_solver->register_existing_terms(); ensure_nla(); m_nla->add_monic(register_theory_var_in_lar_solver(v), vars.size(), vars.data()); @@ -560,7 +559,7 @@ class theory_lra::imp { } enode * mk_enode(app * n) { - TRACE("arith", tout << mk_bounded_pp(n, m) << " internalized: " << ctx().e_internalized(n) << "\n";); + TRACE("arith_verbose", tout << bpp(n) << " internalized: " << ctx().e_internalized(n) << "\n";); if (reflect(n)) for (expr* arg : *n) if (!ctx().e_internalized(arg)) @@ -606,20 +605,18 @@ class theory_lra::imp { } theory_var mk_var(expr* n) { - if (!ctx().e_internalized(n)) { + if (!ctx().e_internalized(n)) ctx().internalize(n, false); - } enode* e = get_enode(n); theory_var v; - if (!th.is_attached_to_var(e)) { + if (th.is_attached_to_var(e)) + v = e->get_th_var(get_id()); + else { v = th.mk_var(e); SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); reserve_bounds(v); ctx().attach_th_var(e, &th, v); } - else { - v = e->get_th_var(get_id()); - } SASSERT(null_theory_var != v); return v; } @@ -640,12 +637,10 @@ class theory_lra::imp { for (unsigned i = 0; i < vars.size(); ++i) { theory_var var = vars[i]; rational const& coeff = coeffs[i]; - if (m_columns.size() <= static_cast(var)) { + if (m_columns.size() <= static_cast(var)) m_columns.setx(var, coeff, rational::zero()); - } - else { + else m_columns[var] += coeff; - } } m_left_side.clear(); // reset the coefficients after they have been used. @@ -653,7 +648,7 @@ class theory_lra::imp { theory_var var = vars[i]; rational const& r = m_columns[var]; if (!r.is_zero()) { - m_left_side.push_back(std::make_pair(r, register_theory_var_in_lar_solver(var))); + m_left_side.push_back({r, register_theory_var_in_lar_solver(var)}); m_columns[var].reset(); } } @@ -661,12 +656,7 @@ class theory_lra::imp { } bool all_zeros(vector const& v) const { - for (rational const& r : v) { - if (!r.is_zero()) { - return false; - } - } - return true; + return all_of(v, [](rational const& r) { return r.is_zero(); }); } void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { @@ -689,7 +679,6 @@ class theory_lra::imp { m_definitions.setx(index, v, null_theory_var); } - bool is_infeasible() const { return lp().get_status() == lp::lp_status::INFEASIBLE; } @@ -718,9 +707,8 @@ class theory_lra::imp { lpvar vi_equal; lp::constraint_index ci = lp().add_var_bound_check_on_equal(vi, kind, bound, vi_equal); add_def_constraint(ci); - if (vi_equal != lp::null_lpvar) { + if (vi_equal != lp::null_lpvar) report_equality_of_fixed_vars(vi, vi_equal); - } m_new_def = true; } @@ -734,22 +722,9 @@ class theory_lra::imp { theory_var z = internalize_linearized_def(term, st); lpvar vi = register_theory_var_in_lar_solver(z); add_def_constraint_and_equality(vi, lp::LE, rational::zero()); - if (is_infeasible()) { - IF_VERBOSE(0, verbose_stream() << "infeasible\n";); - // process_conflict(); // exit here? - } add_def_constraint_and_equality(vi, lp::GE, rational::zero()); - if (is_infeasible()) { - IF_VERBOSE(0, verbose_stream() << "infeasible\n";); - // process_conflict(); // exit here? - } TRACE("arith", - { - expr* o1 = get_enode(v1)->get_expr(); - expr* o2 = get_enode(v2)->get_expr(); - tout << "v" << v1 << " = " << "v" << v2 << ": " - << mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n"; - }); + tout << "v" << v1 << " = " << "v" << v2 << ": " << pp(v1) << " = " << pp(v2) << "\n"); } void del_bounds(unsigned old_size) { @@ -764,7 +739,7 @@ class theory_lra::imp { } void updt_unassigned_bounds(theory_var v, int inc) { - TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); + TRACE("arith_verbose", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); m_unassigned_bounds[v] += inc; } @@ -821,7 +796,7 @@ class theory_lra::imp { theory_var internalize_linearized_def(app* term, scoped_internalize_state& st) { theory_var v = mk_var(term); - TRACE("arith", tout << mk_bounded_pp(term, m) << " v" << v << "\n";); + TRACE("arith_internalize", tout << "v" << v << " " << bpp(term) << "\n";); if (is_unit_var(st) && v == st.vars()[0]) return st.vars()[0]; @@ -918,7 +893,7 @@ public: } bool internalize_atom(app * atom, bool gate_ctx) { - TRACE("arith", tout << mk_pp(atom, m) << "\n";); + TRACE("arith_internalize", tout << bpp(atom) << "\n";); SASSERT(!ctx().b_internalized(atom)); expr* n1, *n2; rational r; @@ -953,17 +928,16 @@ public: return true; } - if (is_int(v) && !r.is_int()) { + if (is_int(v) && !r.is_int()) r = (k == lp_api::upper_t) ? floor(r) : ceil(r); - } + api_bound* b = mk_var_bound(bv, v, k, r); m_bounds[v].push_back(b); updt_unassigned_bounds(v, +1); m_bounds_trail.push_back(v); m_bool_var2bound.insert(bv, b); - TRACE("arith_verbose", tout << "Internalized " << bv << ": " << mk_pp(atom, m) << "\n";); mk_bound_axioms(*b); - //add_use_lists(b); + TRACE("arith_internalize", tout << "Internalized " << bv << ": " << bpp(atom) << "\n";); return true; } @@ -989,14 +963,12 @@ public: enode * n1 = get_enode(lhs); enode * n2 = get_enode(rhs); - 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) + if (is_arith(n1) && is_arith(n2) && n1 != n2) m_arith_eq_adapter.mk_axioms(n1, n2); } void assign_eh(bool_var v, bool is_true) { - TRACE("arith", tout << mk_bounded_pp(ctx().bool_var2expr(v), m) << " " << (literal(v, !is_true)) << "\n";); + TRACE("arith", tout << "assign p" << literal(v, !is_true) << ": " << bpp(ctx().bool_var2expr(v)) << "\n";); m_asserted_atoms.push_back(delayed_atom(v, is_true)); } @@ -1041,16 +1013,14 @@ public: } void apply_sort_cnstr(enode* n, sort*) { - TRACE("arith", tout << "sort constraint: " << enode_pp(n, ctx()) << "\n";); + TRACE("arith", tout << "sort constraint: " << pp(n) << "\n";); #if 0 - if (!th.is_attached_to_var(n)) { + if (!th.is_attached_to_var(n)) mk_var(n->get_owner()); - } #endif } void push_scope_eh() { - TRACE("arith", tout << "push\n";); m_scopes.push_back(scope()); scope& sc = m_scopes.back(); sc.m_bounds_lim = m_bounds_trail.size(); @@ -1059,14 +1029,11 @@ public: lp().push(); if (m_nla) m_nla->push(); - } void pop_scope_eh(unsigned num_scopes) { - TRACE("arith", tout << "pop " << num_scopes << "\n";); - if (num_scopes == 0) { + if (num_scopes == 0) return; - } unsigned old_size = m_scopes.size() - num_scopes; del_bounds(m_scopes[old_size].m_bounds_lim); m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); @@ -1531,13 +1498,14 @@ public: } bool assume_eqs() { + if (delayed_assume_eqs()) return true; - - TRACE("arith", display(tout);); + + TRACE("arith_verbose", display(tout);); random_update(); m_model_eqs.reset(); - + theory_var sz = static_cast(th.get_num_vars()); unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; @@ -1545,30 +1513,23 @@ public: for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); - if (!th.is_relevant_and_shared(n1)) { + if (!th.is_relevant_and_shared(n1)) continue; - } ensure_column(v); if (!is_registered_var(v)) - continue; + continue; theory_var other = m_model_eqs.insert_if_not_there(v); - TRACE("arith", tout << "insert: v" << v << " := " << get_value(v) << " found: v" << other << "\n";); - if (other == v) { + if (other == v) continue; - } enode* n2 = get_enode(other); - if (n1->get_root() != n2->get_root()) { - TRACE("arith", tout << pp(n1, m) << " = " << pp(n2, m) << "\n"; - tout << pp(n1, m) << " = " << pp(n2, m) << "\n"; - tout << "v" << v << " = " << "v" << other << "\n";); - m_assume_eq_candidates.push_back(std::make_pair(v, other)); - num_candidates++; - } + if (n1->get_root() == n2->get_root()) + continue; + m_assume_eq_candidates.push_back({v, other}); + num_candidates++; } - if (num_candidates > 0) { + if (num_candidates > 0) ctx().push_trail(restore_vector(m_assume_eq_candidates, old_sz)); - } return delayed_assume_eqs(); } @@ -1642,9 +1603,8 @@ public: IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n"); lbool is_sat = l_true; SASSERT(lp().ax_is_correct()); - if (!lp().is_feasible() || lp().has_changed_columns()) { + if (!lp().is_feasible() || lp().has_changed_columns()) is_sat = make_feasible(); - } final_check_status st = FC_DONE; switch (is_sat) { case l_true: @@ -2128,31 +2088,28 @@ public: propagate_nla(); if (ctx().inconsistent()) return true; - if (!can_propagate_core()) return false; m_new_def = false; while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent() && m.inc()) { auto [bv, is_true] = m_asserted_atoms[m_asserted_qhead]; - - // m_bv_to_propagate.push_back(bv); - + api_bound* b = nullptr; TRACE("arith", tout << "propagate: " << literal(bv, !is_true) << "\n"; if (!m_bool_var2bound.contains(bv)) tout << "not found\n"); - if (m_bool_var2bound.find(bv, b)) - assert_bound(bv, is_true, *b); + if (m_bool_var2bound.find(bv, b) && !assert_bound(bv, is_true, *b)) { + get_infeasibility_explanation_and_set_conflict(); + return true; + } ++m_asserted_qhead; } - if (ctx().inconsistent()) { - m_bv_to_propagate.reset(); + if (ctx().inconsistent()) return true; - } lbool lbl = make_feasible(); if (!m.inc()) - return false; + return true; switch(lbl) { case l_false: @@ -2160,7 +2117,6 @@ public: get_infeasibility_explanation_and_set_conflict(); break; case l_true: - propagate_basic_bounds(); propagate_bounds_with_lp_solver(); break; case l_undef: @@ -2236,10 +2192,8 @@ public: if (!m.inc()) return; - if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); - // verbose_stream() << "unsat\n"; } else { for (auto& ib : m_bp.ibounds()) { @@ -2417,7 +2371,7 @@ public: enode* n1 = get_enode(uv); enode* n2 = get_enode(vv); - TRACE("arith", tout << "add-eq " << mk_pp(n1->get_expr(), m) << " == " << mk_pp(n2->get_expr(), m) << " " << n1->get_expr_id() << " == " << n2->get_expr_id() << "\n";); + TRACE("arith", tout << "add-eq " << pp(n1) << " == " << pp(n2) << "\n";); if (n1->get_root() == n2->get_root()) return false; expr* e1 = n1->get_expr(); @@ -2721,18 +2675,6 @@ public: } return end; } - - void propagate_basic_bounds() { - for (auto const& bv : m_bv_to_propagate) { - api_bound* b = nullptr; - if (m_bool_var2bound.find(bv, b)) { - propagate_bound(bv, ctx().get_assignment(bv) == l_true, *b); - if (ctx().inconsistent()) - break; - } - } - m_bv_to_propagate.reset(); - } // for glb lo': lo' < lo: // lo <= x -> lo' <= x @@ -2865,7 +2807,7 @@ public: // void propagate_bound_compound(bool_var bv, bool is_true, api_bound& b) { theory_var v = b.get_var(); - TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); + TRACE("arith", tout << pp(v) << "\n";); if (static_cast(v) >= m_use_list.size()) { return; } @@ -2974,13 +2916,12 @@ public: return lp::EQ; } - void assert_bound(bool_var bv, bool is_true, api_bound& b) { + bool assert_bound(bool_var bv, bool is_true, api_bound& b) { TRACE("arith", tout << b << "\n";); lp::constraint_index ci = b.get_constraint(is_true); lp().activate(ci); - if (is_infeasible()) { - return; - } + if (is_infeasible()) + return false; lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true); if (k == lp::LT || k == lp::LE) { ++m_stats.m_assert_lower; @@ -2989,9 +2930,9 @@ public: ++m_stats.m_assert_upper; } inf_rational value = b.get_value(is_true); - if (propagate_eqs() && value.is_rational()) { + if (propagate_eqs() && value.is_rational()) propagate_eqs(b.tv(), ci, k, b, value.get_rational()); - } + return true; #if 0 if (should_propagate()) lp().add_column_rows_to_touched_rows(b.tv().id()); @@ -3079,7 +3020,6 @@ public: return true; } else { - TRACE("arith", tout << "not a term " << tv.to_string() << "\n";); // m_solver already tracks bounds on proper variables, but not on terms. bool is_strict = false; rational b; @@ -3155,7 +3095,7 @@ public: u_dependency* ci1 = nullptr, *ci2 = nullptr, *ci3 = nullptr, *ci4 = nullptr; theory_var v1 = lp().local_to_external(vi1); theory_var v2 = lp().local_to_external(vi2); - TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << "\n";); + TRACE("arith", tout << "fixed: " << pp(v1) << " " << pp(v2) << "\n";); // we expect lp() to ensure that none of these returns happen. if (is_equal(v1, v2)) return; @@ -3191,8 +3131,8 @@ public: for (auto c : m_core) ctx().display_detailed_literal(tout << ctx().get_assign_level(c.var()) << " " << c << " ", c) << "\n"; for (auto e : m_eqs) - tout << pp(e.first, m) << " = " << pp(e.second, m) << "\n"; - tout << " ==> " << pp(x, m) << " = " << pp(y, m) << "\n"; + tout << pp(e.first) << " = " << pp(e.second) << "\n"; + tout << " ==> " << pp(x) << " = " << pp(y) << "\n"; ); std::function fn = [&]() { return m.mk_eq(x->get_expr(), y->get_expr()); }; @@ -3214,6 +3154,7 @@ public: else return; enode* y = get_enode(w); + TRACE("arith", tout << pp(x) << " == " << pp(y) << "\n"); if (x->get_sort() != y->get_sort()) return; if (x->get_root() == y->get_root()) @@ -3303,8 +3244,8 @@ public: // lp().shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed ++m_num_conflicts; ++m_stats.m_conflicts; - TRACE("arith", - tout << "lemma scope: " << ctx().get_scope_level(); + TRACE("arith_conflict", + tout << "@" << ctx().get_scope_level() << (is_conflict ? " conflict":" lemma"); for (auto const& p : m_params) tout << " " << p; tout << "\n"; display_evidence(tout, m_explanation);); @@ -3330,16 +3271,6 @@ public: ctx().mark_as_relevant(c); } TRACE("arith", ctx().display_literals_verbose(tout, m_core) << "\n";); - // DEBUG_CODE( - // for (literal const& c : m_core) { - // if (ctx().get_assignment(c) == l_true) { - // TRACE("arith", ctx().display_literal_verbose(tout, c) << " is true\n";); - // SASSERT(false); - // } - // }); // TODO: this check seems to be too strict. - // The lemmas can come in batches - // and the same literal can appear in several lemmas in a batch: it becomes l_true - // in earlier processing, but it was not so when the lemma was produced ctx().mk_th_axiom(get_id(), m_core.size(), m_core.data()); } } @@ -3359,7 +3290,6 @@ public: m_assume_eq_head = 0; m_scopes.reset(); m_stats.reset(); - m_bv_to_propagate.reset(); m_model_is_initialized = false; } @@ -3385,7 +3315,7 @@ public: m_nla->am().set(r, m_nla->am_value(t.id())); else { - m_todo_terms.push_back(std::make_pair(t, rational::one())); + m_todo_terms.push_back({t, rational::one()}); TRACE("nl_value", tout << "v" << v << " " << t.to_string() << "\n";); TRACE("nl_value", tout << "v" << v << " := w" << t.to_string() << "\n"; lp().print_term(lp().get_term(t), tout) << "\n";); @@ -3405,7 +3335,7 @@ public: auto wi = lp().column2tv(arg.column()); c1 = arg.coeff() * wcoeff; if (wi.is_term()) { - m_todo_terms.push_back(std::make_pair(wi, c1)); + m_todo_terms.push_back({wi, c1}); } else { m_nla->am().set(r1, c1.to_mpq()); @@ -3785,6 +3715,7 @@ public: m_bounds[v].push_back(a); m_bounds_trail.push_back(v); m_bool_var2bound.insert(bv, a); + TRACE("arith", tout << "internalized " << bv << ": " << mk_pp(b, m) << "\n";); } if (is_strict) { @@ -3814,7 +3745,7 @@ public: else if (can_get_value(v)) out << " = " << get_value(v); if (is_int(v)) out << ", int"; if (ctx().is_shared(get_enode(v))) out << ", shared"; - out << " := " << enode_pp(get_enode(v), ctx()) << "\n"; + out << " := " << pp(v) << "\n"; } } @@ -3830,17 +3761,17 @@ public: case inequality_source: { literal lit = m_inequalities[idx]; ctx().literal2expr(lit, e); - out << e << " " << ctx().get_assignment(lit) << "\n"; + out << bpp(e) << " " << ctx().get_assignment(lit) << "\n"; break; } case equality_source: - out << pp(m_equalities[idx].first, m) << " = " - << pp(m_equalities[idx].second, m) << "\n"; + out << pp(m_equalities[idx].first) << " = " + << pp(m_equalities[idx].second) << "\n"; break; case definition_source: { theory_var v = m_definitions[idx]; if (v != null_theory_var) - out << "def: v" << v << " := " << pp(th.get_enode(v), m) << "\n"; + out << "def: v" << v << " := " << pp(th.get_enode(v)) << "\n"; break; } case null_source: @@ -3851,9 +3782,8 @@ public: break; } } - for (lp::explanation::cimpq ev : evidence) { + for (lp::explanation::cimpq ev : evidence) lp().constraints().display(out << ev.coeff() << ": ", ev.ci()); - } } void collect_statistics(::statistics & st) const { From 924c2967049711ec73d318b5d7edc8873fbd1093 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 Nov 2023 12:30:40 -0800 Subject: [PATCH 342/428] add logging --- src/nlsat/nlsat_solver.cpp | 10 ++++++++++ src/tactic/smtlogics/qfnia_tactic.cpp | 8 +++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 568a25a29..8993182a2 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1491,6 +1491,7 @@ namespace nlsat { m_bk = 0; m_xk = null_var; m_conflicts = 0; + m_next_conflict = 100; while (true) { CASSERT("nlsat", check_satisfied()); @@ -1527,6 +1528,7 @@ namespace nlsat { return l_false; if (m_conflicts >= m_max_conflicts) return l_undef; + log(); } if (m_xk == null_var) { @@ -1541,6 +1543,14 @@ namespace nlsat { } } + unsigned m_next_conflict = 100; + void log() { + if (m_conflicts < m_next_conflict) + return; + m_next_conflict += 100; + IF_VERBOSE(2, verbose_stream() << "(nlsat :conflicts " << m_conflicts << " :decisions " << m_decisions << " :propagations " << m_propagations << " :clauses " << m_clauses.size() << " :learned " << m_learned.size() << ")\n"); + } + lbool search_check() { lbool r = l_undef; diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index 3dd66606d..0dad56a19 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -117,10 +117,8 @@ tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { mk_report_verbose_tactic("(qfnia-tactic)", 10), mk_qfnia_preamble(m, p), or_else(mk_qfnia_sat_solver(m, p), - try_for(mk_qfnia_smt_solver(m, p), 2000), - mk_qfnia_nlsat_solver(m, p), - mk_qfnia_smt_solver(m, p)) - ) - ; + try_for(mk_qfnia_smt_solver(m, p), 2000), + mk_qfnia_nlsat_solver(m, p), + mk_qfnia_smt_solver(m, p))); } From 35bc522dae99fbafd1862d4c60336e8a0cc29517 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 09:59:44 -0800 Subject: [PATCH 343/428] #7003 minor tweaks to gomory and reset n3 within loop (but the entire function is dead code). --- src/api/dotnet/Constructor.cs | 8 +++++++- src/math/lp/gomory.cpp | 4 ++-- src/math/polynomial/polynomial.cpp | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/api/dotnet/Constructor.cs b/src/api/dotnet/Constructor.cs index f635d78e4..323301bf9 100644 --- a/src/api/dotnet/Constructor.cs +++ b/src/api/dotnet/Constructor.cs @@ -91,7 +91,13 @@ namespace Microsoft.Z3 /// ~Constructor() { - Native.Z3_del_constructor(Context.nCtx, NativeObject); + if (Context.nCtx != IntPtr.Zero) { + lock (Context) + { + if (Context.nCtx != IntPtr.Zero) + Native.Z3_del_constructor(Context.nCtx, NativeObject); + } + } } #region Internal diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index c3e8651d3..406b2a290 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -338,14 +338,14 @@ public: for (auto const& [c, v] : pol) m_lcm_den = lcm(m_lcm_den, denominator(c)); lp_assert(m_lcm_den.is_pos()); - bool int_row = true; + bool int_row = all_of(pol, [&](auto const& kv) { return is_int(kv.second); }); TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); + if (!m_lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. for (auto & [c,v]: pol) { c *= m_lcm_den; SASSERT(!is_int(v) || c.is_int()); - int_row &= is_int(v); } m_k *= m_lcm_den; } diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 6c94d9b72..c303a1541 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -5658,7 +5658,9 @@ namespace polynomial { if (!is_zero(Gh3) && d1%2 == 0) Gh3 = neg(Gh3); } - + else + n3 = 0; + // Compute hi if (i > 1) { g1 = lc(G1, x); From 32f8705ac32b9369955f02f465ccd3cd8e2abd7a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 10:43:14 -0800 Subject: [PATCH 344/428] #7001 - align is_numeral without to behavior if is_numeral with return numeral. --- src/api/api_numeral.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index 7d8c00fbe..ebac4de31 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -29,13 +29,12 @@ bool is_numeral_sort(Z3_context c, Z3_sort ty) { if (!ty) return false; sort * _ty = to_sort(ty); family_id fid = _ty->get_family_id(); - if (fid != mk_c(c)->get_arith_fid() && - fid != mk_c(c)->get_bv_fid() && - fid != mk_c(c)->get_datalog_fid() && - fid != mk_c(c)->get_fpa_fid()) { - return false; - } - return true; + return + fid == mk_c(c)->get_arith_fid() || + fid == mk_c(c)->get_bv_fid() || + fid == mk_c(c)->get_datalog_fid() || + fid == mk_c(c)->get_fpa_fid(); + } static bool check_numeral_sort(Z3_context c, Z3_sort ty) { @@ -152,7 +151,7 @@ extern "C" { mk_c(c)->bvutil().is_numeral(e) || mk_c(c)->fpautil().is_numeral(e) || mk_c(c)->fpautil().is_rm_numeral(e) || - mk_c(c)->datalog_util().is_numeral_ext(e); + mk_c(c)->datalog_util().is_numeral(e); Z3_CATCH_RETURN(false); } From 4350bd77ac1ac759eaf51377d28d018df20b73ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 11:43:52 -0800 Subject: [PATCH 345/428] check cancel flag to avoid unsound conflicts --- src/math/lp/int_solver.cpp | 10 ++++++++-- src/math/lp/nla_core.cpp | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index c06c891b0..b619b1c8a 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -208,6 +208,7 @@ namespace lp { #endif m_cut_vars.reset(); if (r == lia_move::undef) r = int_branch(*this)(); + if (settings().get_cancel_flag()) r = lia_move::undef; return r; } @@ -854,7 +855,7 @@ namespace lp { struct ex { explanation m_ex; lar_term m_term; mpq m_k; bool m_is_upper; }; vector cuts; - for (unsigned i = 0; i < num_cuts && has_inf_int() && !settings().get_cancel_flag(); ++i) { + for (unsigned i = 0; i < num_cuts && has_inf_int(); ++i) { m_ex->clear(); m_t.clear(); m_k.reset(); @@ -862,6 +863,8 @@ namespace lp { if (r != lia_move::cut) break; cuts.push_back({ *m_ex, m_t, m_k, is_upper() }); + if (settings().get_cancel_flag()) + return lia_move::undef; } m_cut_vars.reset(); @@ -882,7 +885,7 @@ namespace lp { auto _check_feasible = [&](void) { auto st = lra.find_feasible_solution(); - if (!lra.is_feasible()) { + if (!lra.is_feasible() && !settings().get_cancel_flag()) { lra.get_infeasibility_explanation(*m_ex); return false; } @@ -910,6 +913,9 @@ namespace lp { bool feas = _check_feasible(); lra.pop(1); + if (settings().get_cancel_flag()) + return lia_move::undef; + if (!feas) return lia_move::conflict; diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 635103121..570f51cc5 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1567,6 +1567,8 @@ lbool core::check() { {1, check3} }; check_weighted(3, checks); + if (lp_settings().get_cancel_flag()) + return l_undef; if (!m_lemmas.empty() || !m_literals.empty() || m_check_feasible) return l_false; } From ac105b7d8c50c24d9cb27230591934212abd1064 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 11:47:00 -0800 Subject: [PATCH 346/428] remove unused code Signed-off-by: Nikolaj Bjorner --- src/math/polynomial/polynomial.cpp | 169 ----------------------------- 1 file changed, 169 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index c303a1541..a352d1d37 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -5614,92 +5614,6 @@ namespace polynomial { } } - void psc_chain1(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { - subresultant_chain(p, q, x, S); - unsigned sz = S.size(); - TRACE("psc", tout << "subresultant_chain\n"; - for (unsigned i = 0; i < sz; i++) { tout << "i: " << i << " "; S.get(i)->display(tout, m_manager); tout << "\n"; }); - for (unsigned i = 0; i < sz - 1; i++) { - S.set(i, coeff(S.get(i), x, i)); - } - S.set(sz-1, mk_one()); - } - - // Store in S a list of the non-zero principal subresultant coefficients of A and B - // If i < j then psc_{i}(A,B) precedes psc_{j}(A,B) in S. - // The leading coefficients of A and B are not included in S. - void psc_chain2(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { - polynomial_ref G1(pm()); - polynomial_ref G2(pm()); - polynomial_ref G3(pm()); - polynomial_ref Gh3(pm()); - polynomial_ref g1(pm()), h0(pm()), hs0(pm()), h1(pm()), hs1(pm()); - unsigned n1 = degree(A, x); - unsigned n2 = degree(B, x); - if (n1 > n2) { - G1 = const_cast(A); - G2 = const_cast(B); - } - else { - G1 = const_cast(B); - G2 = const_cast(A); - std::swap(n1, n2); - } - unsigned d0 = 0; - unsigned d1 = n1 - n2; - unsigned i = 1; - unsigned n3 = 0; - S.reset(); - while (true) { - // Compute Gh_{i+2} - if (!is_zero(G2)) { - exact_pseudo_remainder(G1, G2, x, Gh3); - n3 = degree(Gh3, x); - if (!is_zero(Gh3) && d1%2 == 0) - Gh3 = neg(Gh3); - } - else - n3 = 0; - - // Compute hi - if (i > 1) { - g1 = lc(G1, x); - pw(g1, d0, h1); - if (i > 2) { - pw(h0, d0 - 1, hs0); - h1 = exact_div(h1, hs0); - S.push_back(h1); - if (is_zero(G2)) { - std::reverse(S.data(), S.data() + S.size()); - return; - } - } - } - - // Compute G_{i+2} - if (i == 1 || is_zero(Gh3)) { - G3 = Gh3; - } - else { - pw(h1, d1, hs1); - hs1 = mul(g1, hs1); - G3 = exact_div(Gh3, hs1); - hs1 = nullptr; - } - - // prepare for next iteration - n1 = n2; - n2 = n3; - d0 = d1; - d1 = n1 - n2; - G1 = G2; - G2 = G3; - if (i > 1) - h0 = h1; - i = i + 1; - } - } - // Optimized calculation of S_e using "Dichotomous Lazard" void Se_Lazard(unsigned d, polynomial const * lc_S_d, polynomial const * S_d_1, var x, polynomial_ref & S_e) { unsigned n = d - degree(S_d_1, x) - 1; @@ -5860,90 +5774,7 @@ namespace polynomial { std::reverse(S.data(), S.data() + S.size()); } - void psc_chain_classic_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { - TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";); - unsigned degP = degree(P, x); - unsigned degQ = degree(Q, x); - SASSERT(degP >= degQ); - polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), lc_B(pm()), lc_A(pm()); - polynomial_ref tmp1(pm()), tmp2(pm()), s_delta(pm()), minus_B(pm()), ps(pm()); - - lc_Q = lc(Q, x); - polynomial_ref s(pm()); - // s <- lc(Q)^(deg(P)-deg(Q)) - pw(lc_Q, degP - degQ, s); - minus_Q = neg(Q); - // A <- Q - A = const_cast(Q); - // B <- prem(P, -Q) - exact_pseudo_remainder(P, minus_Q, x, B); - while (true) { - unsigned d = degree(A, x); - unsigned e = degree(B, x); - if (is_zero(B)) - return; - TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";); - // B is S_{d-1} - ps = coeff(B, x, d-1); - if (!is_zero(ps)) - S.push_back(ps); - unsigned delta = d - e; - if (delta > 1) { - // C <- S_e - // Standard S_e calculation - // C <- (lc(B)^(delta-1) B) / s^(delta-1) - lc_B = lc(B, x); - pw(lc_B, delta-1, lc_B); - lc_B = mul(lc_B, B); - pw(s, delta - 1, s_delta); // s_delta <- s^(delta-1) - C = exact_div(lc_B, s_delta); - - // s_delta <- s^delta - s_delta = mul(s_delta, s); - // C is S_e - ps = coeff(C, x, e); - if (!is_zero(ps)) - S.push_back(ps); - - } - else { - SASSERT(delta == 0 || delta == 1); - C = B; - // s_delta <- s^delta - pw(s, delta, s_delta); - } - if (e == 0) - return; - // B <- prem(A, -B)/(s^delta * lc(A) - lc_A = lc(A, x); - minus_B = neg(B); - exact_pseudo_remainder(A, minus_B, x, tmp1); - tmp2 = mul(lc_A, s_delta); - B = exact_div(tmp1, tmp2); - // A <- C - A = C; - // s <- lc(A) - s = lc(A, x); - } - } - - void psc_chain_classic(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { - SASSERT(degree(P, x) > 0); - SASSERT(degree(Q, x) > 0); - S.reset(); - if (degree(P, x) >= degree(Q, x)) - psc_chain_classic_core(P, Q, x, S); - else - psc_chain_classic_core(Q, P, x, S); - if (S.empty()) - S.push_back(mk_zero()); - std::reverse(S.data(), S.data() + S.size()); - } - void psc_chain(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { - // psc_chain1(A, B, x, S); - //psc_chain2(A, B, x, S); - //psc_chain_classic(A, B, x, S); psc_chain_optimized(A, B, x, S); } From d272acc3ac4169aaf0e1db89d674f1ae6e64faf9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 12:48:33 -0800 Subject: [PATCH 347/428] fix crash when api_solver sets reset_tracked_assertions --- src/cmd_context/cmd_context.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 6f04799f6..019b7fd4f 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1678,6 +1678,8 @@ void cmd_context::restore_assertions(unsigned old_sz) { SASSERT(m_assertions.empty()); return; } + if (m_assertions.empty()) + return; if (old_sz == m_assertions.size()) return; SASSERT(old_sz < m_assertions.size()); @@ -2296,6 +2298,8 @@ vector> cmd_context::tracked_assertions() { } void cmd_context::reset_tracked_assertions() { + for (expr* a : m_assertion_names) + m().dec_ref(a); m_assertion_names.reset(); for (expr* a : m_assertions) m().dec_ref(a); From 9382b96a3250c1bfd1516211c5b3296e7a8b918f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 19 Nov 2023 16:30:09 -0800 Subject: [PATCH 348/428] add API to access symbols associated with quantifiers --- src/api/api_quant.cpp | 30 ++++++++++++++++++++++++++++++ src/api/python/z3/z3.py | 10 ++++++++++ src/api/z3_api.h | 18 ++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index 0da5c5a92..bb9efa9c3 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -383,6 +383,36 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_symbol Z3_API Z3_get_quantifier_skolem_id(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_get_quantifier_skolem_id(c, a); + RESET_ERROR_CODE(); + ast * _a = to_ast(a); + if (_a->get_kind() == AST_QUANTIFIER) { + return of_symbol(to_quantifier(_a)->get_skid()); + } + else { + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); + return of_symbol(symbol::null); + } + Z3_CATCH_RETURN(of_symbol(symbol::null)); + } + + Z3_symbol Z3_API Z3_get_quantifier_id(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_get_quantifier_skolem_id(c, a); + RESET_ERROR_CODE(); + ast * _a = to_ast(a); + if (_a->get_kind() == AST_QUANTIFIER) { + return of_symbol(to_quantifier(_a)->get_qid()); + } + else { + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); + return of_symbol(symbol::null); + } + Z3_CATCH_RETURN(of_symbol(symbol::null)); + } + unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_patterns(c, a); diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index bac80b33e..053fd7dd1 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -2081,6 +2081,16 @@ class QuantifierRef(BoolRef): """ return int(Z3_get_quantifier_weight(self.ctx_ref(), self.ast)) + def skolem_id(self): + """Return the skolem id of `self`. + """ + return _symbol2py(self.ctx, Z3_get_quantifier_skolem_id(self.ctx_ref(), self.ast)) + + def qid(self): + """Return the quantifier id of `self`. + """ + return _symbol2py(self.ctx, Z3_get_quantifier_id(self.ctx_ref(), self.ast)) + def num_patterns(self): """Return the number of patterns (i.e., quantifier instantiation hints) in `self`. diff --git a/src/api/z3_api.h b/src/api/z3_api.h index fafc3eebd..0468ab91d 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5206,6 +5206,24 @@ extern "C" { */ unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a); + /** + \brief Obtain skolem id of quantifier. + + \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST + + def_API('Z3_get_quantifier_skolem_id', SYMBOL, (_in(CONTEXT), _in(AST))) + */ + Z3_symbol Z3_API Z3_get_quantifier_skolem_id(Z3_context c, Z3_ast a); + + /** + \brief Obtain id of quantifier. + + \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST + + def_API('Z3_get_quantifier_id', SYMBOL, (_in(CONTEXT), _in(AST))) + */ + Z3_symbol Z3_API Z3_get_quantifier_id(Z3_context c, Z3_ast a); + /** \brief Return number of patterns used in quantifier. From 16753e43f1ba11f9b32eeea3a76acc3015736696 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 23 Nov 2023 16:54:23 +0000 Subject: [PATCH 349/428] Add accessors for RCF numeral internals (#7013) --- examples/ml/ml_example.ml | 133 ++++++++++----- src/api/api_rcf.cpp | 147 +++++++++++++++- src/api/ml/z3.ml | 82 ++++++++- src/api/ml/z3.mli | 166 ++++++++++++------ src/api/z3_rcf.h | 120 +++++++++++++- src/math/realclosure/realclosure.cpp | 240 +++++++++++++++++++++++++++ src/math/realclosure/realclosure.h | 74 +++++++-- 7 files changed, 845 insertions(+), 117 deletions(-) diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index b6a71b69d..e2c7734ff 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -1,4 +1,4 @@ -(* +(* Copyright (C) 2012 Microsoft Corporation Author: CM Wintersteiger (cwinter) 2012-12-17 *) @@ -19,7 +19,6 @@ open Z3.Arithmetic.Integer open Z3.Arithmetic.Real open Z3.BitVector - exception TestFailedException of string (** @@ -31,14 +30,14 @@ let model_converter_test ( ctx : context ) = let yr = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Real.mk_sort ctx)) in let g4 = (mk_goal ctx true false false ) in (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10 1)) ]) ; - (Goal.add g4 [ (mk_eq ctx + (Goal.add g4 [ (mk_eq ctx yr (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1 1) ])) ]) ; (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1 1)) ]) ; ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g4 None) in - if ((get_num_subgoals ar) == 1 && - ((is_decided_sat (get_subgoal ar 0)) || + if ((get_num_subgoals ar) == 1 && + ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else @@ -46,8 +45,8 @@ let model_converter_test ( ctx : context ) = ); ( let ar = (Tactic.apply (and_then ctx (mk_tactic ctx ("simplify")) (mk_tactic ctx "solve-eqs") []) g4 None) in - if ((get_num_subgoals ar) == 1 && - ((is_decided_sat (get_subgoal ar 0)) || + if ((get_num_subgoals ar) == 1 && + ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else @@ -57,15 +56,15 @@ let model_converter_test ( ctx : context ) = let f e = (Solver.add solver [ e ]) in ignore (List.map f (get_formulas (get_subgoal ar 0))) ; let q = (check solver []) in - if q != SATISFIABLE then + if q != SATISFIABLE then raise (TestFailedException "") else - let m = (get_model solver) in - match m with + let m = (get_model solver) in + match m with | None -> raise (TestFailedException "") - | Some (m) -> + | Some (m) -> Printf.printf "Solver says: %s\n" (string_of_status q) ; - Printf.printf "Model: \n%s\n" (Model.to_string m) + Printf.printf "Model: \n%s\n" (Model.to_string m) ) (** @@ -79,7 +78,7 @@ let basic_tests ( ctx : context ) = let bs = (Boolean.mk_sort ctx) in let domain = [ bs; bs ] in let f = (FuncDecl.mk_func_decl ctx fname domain bs) in - let fapp = (mk_app ctx f + let fapp = (mk_app ctx f [ (Expr.mk_const ctx x bs); (Expr.mk_const ctx y bs) ]) in let fargs2 = [ (mk_fresh_const ctx "cp" bs) ] in let domain2 = [ bs ] in @@ -100,8 +99,8 @@ let basic_tests ( ctx : context ) = ); ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g None) in - if ((get_num_subgoals ar) == 1 && - ((is_decided_sat (get_subgoal ar 0)) || + if ((get_num_subgoals ar) == 1 && + ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else @@ -109,28 +108,28 @@ let basic_tests ( ctx : context ) = ); ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in - if ((get_num_subgoals ar) == 1 && + if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); - (Goal.add g [ (mk_eq ctx + (Goal.add g [ (mk_eq ctx (mk_numeral_int ctx 1 (BitVector.mk_sort ctx 32)) (mk_numeral_int ctx 2 (BitVector.mk_sort ctx 32))) ] ) ; ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in - if ((get_num_subgoals ar) == 1 && + if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") - else + else Printf.printf "Test passed.\n" ); ( let g2 = (mk_goal ctx true true false) in let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in - if ((get_num_subgoals ar) == 1 && + if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else @@ -140,10 +139,10 @@ let basic_tests ( ctx : context ) = let g2 = (mk_goal ctx true true false) in (Goal.add g2 [ (Boolean.mk_false ctx) ]) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in - if ((get_num_subgoals ar) == 1 && + if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") - else + else Printf.printf "Test passed.\n" ); ( @@ -155,10 +154,10 @@ let basic_tests ( ctx : context ) = let constr = (mk_eq ctx xc yc) in (Goal.add g3 [ constr ] ) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g3 None) in - if ((get_num_subgoals ar) == 1 && + if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") - else + else Printf.printf "Test passed.\n" ) ; model_converter_test ctx ; @@ -169,12 +168,12 @@ let basic_tests ( ctx : context ) = Printf.printf "Numerator: %s Denominator: %s\n" (Real.numeral_to_string inum) (Real.numeral_to_string iden) ; if ((Real.numeral_to_string inum) <> "42" || (Real.numeral_to_string iden) <> "43") then raise (TestFailedException "") - else + else Printf.printf "Test passed.\n" ; if ((to_decimal_string rn 3) <> "0.976?") then raise (TestFailedException "") - else + else Printf.printf "Test passed.\n" ; if (to_decimal_string (Real.mk_numeral_s ctx "-1231231232/234234333") 5 <> "-5.25640?") then @@ -193,7 +192,7 @@ let basic_tests ( ctx : context ) = raise (TestFailedException "check") ) with Z3.Error(_) -> ( - Printf.printf "Exception caught, OK.\n" + Printf.printf "Exception caught, OK.\n" ) (** @@ -212,22 +211,22 @@ let quantifier_example1 ( ctx : context ) = let xs = [ (Integer.mk_const ctx (List.nth names 0)); (Integer.mk_const ctx (List.nth names 1)); (Integer.mk_const ctx (List.nth names 2)) ] in - - let body_vars = (Boolean.mk_and ctx - [ (mk_eq ctx - (Arithmetic.mk_add ctx [ (List.nth vars 0) ; (Integer.mk_numeral_i ctx 1)]) + + let body_vars = (Boolean.mk_and ctx + [ (mk_eq ctx + (Arithmetic.mk_add ctx [ (List.nth vars 0) ; (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; - (mk_eq ctx + (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth vars 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth vars 2); (Integer.mk_numeral_i ctx 3)])) ]) in let body_const = (Boolean.mk_and ctx - [ (mk_eq ctx - (Arithmetic.mk_add ctx [ (List.nth xs 0); (Integer.mk_numeral_i ctx 1)]) + [ (mk_eq ctx + (Arithmetic.mk_add ctx [ (List.nth xs 0); (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; - (mk_eq ctx + (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth xs 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth xs 2); (Integer.mk_numeral_i ctx 3)])) ]) in - + let x = (Quantifier.mk_forall ctx types names body_vars (Some 1) [] [] (Some (Symbol.mk_string ctx "Q1")) (Some (Symbol.mk_string ctx "skid1"))) in Printf.printf "Quantifier X: %s\n" (Quantifier.to_string x) ; let y = (Quantifier.mk_forall_const ctx xs body_const (Some 1) [] [] (Some (Symbol.mk_string ctx "Q2")) (Some (Symbol.mk_string ctx "skid2"))) in @@ -242,8 +241,8 @@ let quantifier_example1 ( ctx : context ) = open Z3.FloatingPoint -(** - A basic example of floating point arithmetic +(** + A basic example of floating point arithmetic **) let fpa_example ( ctx : context ) = Printf.printf "FPAExample\n" ; @@ -271,7 +270,7 @@ let fpa_example ( ctx : context ) = (Boolean.mk_not ctx (mk_is_nan ctx y)) ; (Boolean.mk_not ctx (mk_is_infinite ctx y)) ] in let args3 = [ c3 ; (Boolean.mk_and ctx and_args) ] in - let c4 = (Boolean.mk_and ctx args3) in + let c4 = (Boolean.mk_and ctx args3) in (Printf.printf "c4: %s\n" (Expr.to_string c4)) ; ( let solver = (mk_solver ctx None) in @@ -293,7 +292,7 @@ let fpa_example ( ctx : context ) = let c2 = (mk_to_fp_bv ctx (mk_numeral_string ctx "4619567317775286272" (BitVector.mk_sort ctx 64)) (mk_sort ctx 11 53)) in - let c3 = (mk_to_fp_int_real ctx + let c3 = (mk_to_fp_int_real ctx (RoundingMode.mk_rtz ctx) (mk_numeral_string ctx "2" (Integer.mk_sort ctx)) (mk_numeral_string ctx "1.75" (Real.mk_sort ctx)) @@ -304,7 +303,7 @@ let fpa_example ( ctx : context ) = let args3 = [ (mk_eq ctx c1 c2) ; (mk_eq ctx c1 c3) ; (mk_eq ctx c1 c4) ] in - let c5 = (Boolean.mk_and ctx args3) in + let c5 = (Boolean.mk_and ctx args3) in (Printf.printf "c5: %s\n" (Expr.to_string c5)) ; ( let solver = (mk_solver ctx None) in @@ -313,7 +312,7 @@ let fpa_example ( ctx : context ) = raise (TestFailedException "") else Printf.printf "Test passed.\n" - ) + ) (** A basic example of RCF usage @@ -322,11 +321,58 @@ let rcf_example ( ctx : context ) = Printf.printf "RCFExample\n" ; let pi = RCF.mk_pi ctx in let e = RCF.mk_e ctx in + let inf0 = RCF.mk_infinitesimal ctx in + let inf1 = RCF.mk_infinitesimal ctx in + let r = RCF.mk_rational ctx "42.001" in + let pi_div_e = RCF.div ctx pi e in + let pi_div_r = RCF.div ctx pi r in (Printf.printf "e: %s, pi: %s, e==pi: %b, e < pi: %b\n" (RCF.num_to_string ctx e true false) (RCF.num_to_string ctx pi true false) (RCF.eq ctx e pi) (RCF.lt ctx e pi)) ; + Printf.printf "pi_div_e: %s.\n" (RCF.num_to_string ctx pi_div_e true false); + Printf.printf "pi_div_r: %s.\n" (RCF.num_to_string ctx pi_div_r true false); + Printf.printf "inf0: %s.\n" (RCF.num_to_string ctx inf0 true false); + Printf.printf "(RCF.is_rational ctx pi): %b.\n" (RCF.is_rational ctx pi); + Printf.printf "(RCF.is_algebraic ctx pi): %b.\n" (RCF.is_algebraic ctx pi); + Printf.printf "(RCF.is_transcendental ctx pi): %b.\n" (RCF.is_transcendental ctx pi); + Printf.printf "(RCF.is_rational ctx r): %b.\n" (RCF.is_rational ctx r); + Printf.printf "(RCF.is_algebraic ctx r): %b.\n" (RCF.is_algebraic ctx r); + Printf.printf "(RCF.is_transcendental ctx r): %b.\n" (RCF.is_transcendental ctx r); + Printf.printf "(RCF.is_infinitesimal ctx inf0): %b.\n" (RCF.is_infinitesimal ctx inf0); + Printf.printf "(RCF.extension_index ctx inf0): %d.\n" (RCF.extension_index ctx inf0); + Printf.printf "(RCF.extension_index ctx inf1): %d.\n" (RCF.extension_index ctx inf1); + let poly:RCF.rcf_num list = [ e; pi; inf0 ] in + let rs:RCF.root list = RCF.roots ctx poly in + let print_root (x:RCF.root) = + begin + Printf.printf "root: %s\n%!" (RCF.num_to_string ctx x.obj true false); + if RCF.is_algebraic ctx x.obj then ( + (match x.interval with + | Some ivl -> Printf.printf " interval: (%b, %b, %s, %b, %b, %s)\n" + ivl.lower_is_inf + ivl.lower_is_open + (RCF.num_to_string ctx ivl.lower true false) + ivl.upper_is_inf + ivl.upper_is_open + (RCF.num_to_string ctx ivl.upper true false); + | None -> ()); + Printf.printf " polynomial coefficients:"; + List.iter (fun c -> Printf.printf " %s" (RCF.num_to_string ctx c false false)) x.polynomial; + Printf.printf "\n"; + Printf.printf " sign conditions:"; + List.iter + (fun (poly, sign) -> + List.iter (fun p -> Printf.printf " %s" (RCF.num_to_string ctx p true false)) poly; + Printf.printf " %s" (if sign > 0 then "> 0" else if sign < 0 then "< 0" else "= 0")) + x.sign_conditions; + Printf.printf "\n") + end + in + List.iter print_root rs; + RCF.del_roots ctx rs; + RCF.del_list ctx [pi; e; inf0; inf1; r; pi_div_e; pi_div_r]; Printf.printf "Test passed.\n" let _ = @@ -363,5 +409,6 @@ let _ = ) with Error(msg) -> ( Printf.printf "Z3 EXCEPTION: %s\n" msg ; exit 1 - ) + ) ;; + diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 8a000be1a..ccc79bc46 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -17,7 +17,7 @@ Author: Leonardo de Moura (leonardo) 2012-01-05 Notes: - + --*/ #include "api/z3.h" #include "api/api_log_macros.h" @@ -32,12 +32,12 @@ static void reset_rcf_cancel(Z3_context c) { // no-op } -static Z3_rcf_num from_rcnumeral(rcnumeral a) { - return reinterpret_cast(a.data()); +static Z3_rcf_num from_rcnumeral(rcnumeral a) { + return reinterpret_cast(a.data()); } -static rcnumeral to_rcnumeral(Z3_rcf_num a) { - return rcnumeral::mk(a); +static rcnumeral to_rcnumeral(Z3_rcf_num a) { + return rcnumeral::mk(a); } extern "C" { @@ -179,7 +179,7 @@ extern "C" { RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } - + Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a) { Z3_TRY; LOG_Z3_rcf_neg(c, a); @@ -302,4 +302,139 @@ extern "C" { Z3_CATCH; } + bool Z3_API Z3_rcf_is_rational(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_is_rational(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).is_rational(to_rcnumeral(a)); + Z3_CATCH_RETURN(false); + } + + bool Z3_API Z3_rcf_is_algebraic(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_is_algebraic(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).is_algebraic(to_rcnumeral(a)); + Z3_CATCH_RETURN(false); + } + + bool Z3_API Z3_rcf_is_infinitesimal(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_is_infinitesimal(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).is_infinitesimal(to_rcnumeral(a)); + Z3_CATCH_RETURN(false); + } + + bool Z3_API Z3_rcf_is_transcendental(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_is_transcendental(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).is_transcendental(to_rcnumeral(a)); + Z3_CATCH_RETURN(false); + } + + unsigned Z3_API Z3_rcf_extension_index(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_extension_index(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).extension_index(to_rcnumeral(a)); + Z3_CATCH_RETURN(false); + } + + Z3_symbol Z3_API Z3_rcf_transcendental_name(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_transcendental_name(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return of_symbol(rcfm(c).transcendental_name(to_rcnumeral(a))); + Z3_CATCH_RETURN(of_symbol(symbol::null)); + } + + Z3_symbol Z3_API Z3_rcf_infinitesimal_name(Z3_context c, Z3_rcf_num a) { + Z3_TRY; + LOG_Z3_rcf_infinitesimal_name(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return of_symbol(rcfm(c).infinitesimal_name(to_rcnumeral(a))); + Z3_CATCH_RETURN(of_symbol(symbol::null)); + } + + unsigned Z3_API Z3_rcf_num_coefficients(Z3_context c, Z3_rcf_num a) + { + Z3_TRY; + LOG_Z3_rcf_num_coefficients(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).num_coefficients(to_rcnumeral(a)); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_coefficient(Z3_context c, Z3_rcf_num a, unsigned i) + { + Z3_TRY; + LOG_Z3_rcf_coefficient(c, a, i); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return from_rcnumeral(rcfm(c).get_coefficient(to_rcnumeral(a), i)); + Z3_CATCH_RETURN(nullptr); + } + + int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper) { + Z3_TRY; + LOG_Z3_rcf_interval(c, a, lower_is_inf, lower_is_open, lower, upper_is_inf, upper_is_open, upper); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + rcnumeral r_lower, r_upper; + bool r = rcfm(c).get_interval(to_rcnumeral(a), *lower_is_inf, *lower_is_open, r_lower, *upper_is_inf, *upper_is_open, r_upper); + *lower = from_rcnumeral(r_lower); + *upper = from_rcnumeral(r_upper); + return r; + Z3_CATCH_RETURN(0); + } + + unsigned Z3_API Z3_rcf_num_sign_conditions(Z3_context c, Z3_rcf_num a) + { + Z3_TRY; + LOG_Z3_rcf_num_sign_conditions(c, a); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).num_sign_conditions(to_rcnumeral(a)); + Z3_CATCH_RETURN(0); + } + + int Z3_API Z3_rcf_sign_condition_sign(Z3_context c, Z3_rcf_num a, unsigned i) + { + Z3_TRY; + LOG_Z3_rcf_sign_condition_sign(c, a, i); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).get_sign_condition_sign(to_rcnumeral(a), i); + Z3_CATCH_RETURN(0); + } + + unsigned Z3_API Z3_rcf_num_sign_condition_coefficients(Z3_context c, Z3_rcf_num a, unsigned i) + { + Z3_TRY; + LOG_Z3_rcf_num_sign_condition_coefficients(c, a, i); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return rcfm(c).num_sign_condition_coefficients(to_rcnumeral(a), i); + Z3_CATCH_RETURN(0); + } + + Z3_rcf_num Z3_API Z3_rcf_sign_condition_coefficient(Z3_context c, Z3_rcf_num a, unsigned i, unsigned j) + { + Z3_TRY; + LOG_Z3_rcf_sign_condition_coefficient(c, a, i, j); + RESET_ERROR_CODE(); + reset_rcf_cancel(c); + return from_rcnumeral(rcfm(c).get_sign_condition_coefficient(to_rcnumeral(a), i, j)); + Z3_CATCH_RETURN(nullptr); + } }; diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index c2de2b589..5bd554313 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -2080,6 +2080,7 @@ struct type rcf_num = Z3native.rcf_num let del (ctx:context) (a:rcf_num) = Z3native.rcf_del ctx a + let del_list (ctx:context) (ns:rcf_num list) = List.iter (fun a -> Z3native.rcf_del ctx a) ns let mk_rational (ctx:context) (v:string) = Z3native.rcf_mk_rational ctx v let mk_small_int (ctx:context) (v:int) = Z3native.rcf_mk_small_int ctx v @@ -2087,7 +2088,9 @@ struct let mk_e (ctx:context) = Z3native.rcf_mk_e ctx let mk_infinitesimal (ctx:context) = Z3native.rcf_mk_infinitesimal ctx - let mk_roots (ctx:context) (n:int) (a:rcf_num list) = let n, r = Z3native.rcf_mk_roots ctx n a in r + let mk_roots (ctx:context) (a:rcf_num list) = + let n, r = Z3native.rcf_mk_roots ctx (List.length a) a in + List.init n (fun x -> List.nth r x) let add (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_add ctx a b let sub (ctx:context) (a:rcf_num) (b:rcf_num) = Z3native.rcf_sub ctx a b @@ -2109,6 +2112,83 @@ struct let num_to_string (ctx:context) (a:rcf_num) (compact:bool) (html:bool) = Z3native.rcf_num_to_string ctx a compact html let num_to_decimal_string (ctx:context) (a:rcf_num) (prec:int) = Z3native.rcf_num_to_decimal_string ctx a prec let get_numerator_denominator (ctx:context) (a:rcf_num) = Z3native.rcf_get_numerator_denominator ctx a + + let is_rational (ctx:context) (a:rcf_num) = Z3native.rcf_is_rational ctx a + let is_algebraic (ctx:context) (a:rcf_num) = Z3native.rcf_is_algebraic ctx a + let is_infinitesimal (ctx:context) (a:rcf_num) = Z3native.rcf_is_infinitesimal ctx a + let is_transcendental (ctx:context) (a:rcf_num) = Z3native.rcf_is_transcendental ctx a + + let extension_index (ctx:context) (a:rcf_num) = Z3native.rcf_extension_index ctx a + let transcendental_name (ctx:context) (a:rcf_num) = Z3native.rcf_transcendental_name ctx a + let infinitesimal_name (ctx:context) (a:rcf_num) = Z3native.rcf_infinitesimal_name ctx a + + let num_coefficients (ctx:context) (a:rcf_num) = Z3native.rcf_num_coefficients ctx a + let get_coefficient (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_coefficient ctx a i + + let coefficients (ctx:context) (a:rcf_num) = + List.init (num_coefficients ctx a) (fun i -> Z3native.rcf_coefficient ctx a i) + + type interval = { + lower_is_inf : bool; + lower_is_open : bool; + lower : rcf_num; + upper_is_inf : bool; + upper_is_open : bool; + upper : rcf_num; + } + + let root_interval (ctx:context) (a:rcf_num) = + let ok, linf, lopen, l, uinf, uopen, u = Z3native.rcf_interval ctx a in + let i:interval = { + lower_is_inf = linf != 0; + lower_is_open = lopen != 0; + lower = l; + upper_is_inf = uinf != 0; + upper_is_open = uopen != 0; + upper = u } in + if ok != 0 then Some i else None + + let sign_condition_sign (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_sign_condition_sign ctx a i + + let sign_condition_coefficient (ctx:context) (a:rcf_num) (i:int) (j:int) = Z3native.rcf_sign_condition_coefficient ctx a i j + + let num_sign_condition_coefficients (ctx:context) (a:rcf_num) (i:int) = Z3native.rcf_num_sign_condition_coefficients ctx a i + + let sign_condition_coefficients (ctx:context) (a:rcf_num) (i:int) = + let n = Z3native.rcf_num_sign_condition_coefficients ctx a i in + List.init n (fun j -> Z3native.rcf_sign_condition_coefficient ctx a i j) + + let sign_conditions (ctx:context) (a:rcf_num) = + let n = Z3native.rcf_num_sign_conditions ctx a in + List.init n (fun i -> + (let nc = Z3native.rcf_num_sign_condition_coefficients ctx a i in + List.init nc (fun j -> Z3native.rcf_sign_condition_coefficient ctx a i j)), + Z3native.rcf_sign_condition_sign ctx a i) + + type root = { + obj : rcf_num; + polynomial : rcf_num list; + interval : interval option; + sign_conditions : (rcf_num list * int) list; + } + + let roots (ctx:context) (a:rcf_num list) = + let rs = mk_roots ctx a in + List.map + (fun r -> { + obj = r; + polynomial = coefficients ctx r; + interval = root_interval ctx r; + sign_conditions = sign_conditions ctx r}) + rs + + let del_root (ctx:context) (r:root) = + del ctx r.obj; + List.iter (fun n -> del ctx n) r.polynomial; + List.iter (fun (ns, _) -> del_list ctx ns) r.sign_conditions + + let del_roots (ctx:context) (rs:root list) = + List.iter (fun r -> del_root ctx r) rs end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index e16abe717..1380bd519 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3553,77 +3553,147 @@ end (** Real closed field *) module RCF : sig - type rcf_num + type rcf_num - (** Delete a RCF numeral created using the RCF API. *) - val del : context -> rcf_num -> unit + (** Delete a RCF numeral created using the RCF API. *) + val del : context -> rcf_num -> unit - (** Return a RCF rational using the given string. *) - val mk_rational : context -> string -> rcf_num + (** Delete RCF numerals created using the RCF API. *) + val del_list : context -> rcf_num list -> unit - (** Return a RCF small integer. *) - val mk_small_int : context -> int -> rcf_num + (** Return a RCF rational using the given string. *) + val mk_rational : context -> string -> rcf_num - (** Return Pi *) - val mk_pi : context -> rcf_num + (** Return a RCF small integer. *) + val mk_small_int : context -> int -> rcf_num - (** Return e (Euler's constant) *) - val mk_e : context -> rcf_num + (** Return Pi *) + val mk_pi : context -> rcf_num - (** Return a new infinitesimal that is smaller than all elements in the Z3 field. *) - val mk_infinitesimal : context -> rcf_num + (** Return e (Euler's constant) *) + val mk_e : context -> rcf_num - (** Extract the roots of a polynomial. Precondition: The input polynomial is not the zero polynomial. *) - val mk_roots : context -> int -> rcf_num list -> rcf_num list + (** Return a new infinitesimal that is smaller than all elements in the Z3 field. *) + val mk_infinitesimal : context -> rcf_num - (** Addition *) - val add : context -> rcf_num -> rcf_num -> rcf_num + (** Extract the roots of a polynomial. Precondition: The input polynomial is not the zero polynomial. *) + val mk_roots : context -> rcf_num list -> rcf_num list - (** Subtraction *) - val sub : context -> rcf_num -> rcf_num -> rcf_num + (** Addition *) + val add : context -> rcf_num -> rcf_num -> rcf_num - (** Multiplication *) - val mul : context -> rcf_num -> rcf_num -> rcf_num + (** Subtraction *) + val sub : context -> rcf_num -> rcf_num -> rcf_num - (** Division *) - val div : context -> rcf_num -> rcf_num -> rcf_num + (** Multiplication *) + val mul : context -> rcf_num -> rcf_num -> rcf_num - (** Negation *) - val neg : context -> rcf_num -> rcf_num + (** Division *) + val div : context -> rcf_num -> rcf_num -> rcf_num - (** Multiplicative Inverse *) - val inv : context -> rcf_num -> rcf_num + (** Negation *) + val neg : context -> rcf_num -> rcf_num - (** Power *) - val power : context -> rcf_num -> int -> rcf_num + (** Multiplicative Inverse *) + val inv : context -> rcf_num -> rcf_num - (** less-than *) - val lt : context -> rcf_num -> rcf_num -> bool + (** Power *) + val power : context -> rcf_num -> int -> rcf_num - (** greater-than *) - val gt : context -> rcf_num -> rcf_num -> bool + (** less-than *) + val lt : context -> rcf_num -> rcf_num -> bool - (** less-than or equal *) - val le : context -> rcf_num -> rcf_num -> bool + (** greater-than *) + val gt : context -> rcf_num -> rcf_num -> bool - (** greater-than or equal *) - val ge : context -> rcf_num -> rcf_num -> bool + (** less-than or equal *) + val le : context -> rcf_num -> rcf_num -> bool - (** equality *) - val eq : context -> rcf_num -> rcf_num -> bool + (** greater-than or equal *) + val ge : context -> rcf_num -> rcf_num -> bool - (** not equal *) - val neq : context -> rcf_num -> rcf_num -> bool + (** equality *) + val eq : context -> rcf_num -> rcf_num -> bool - (** Convert the RCF numeral into a string. *) - val num_to_string : context -> rcf_num -> bool -> bool -> string + (** not equal *) + val neq : context -> rcf_num -> rcf_num -> bool - (** Convert the RCF numeral into a string in decimal notation. *) - val num_to_decimal_string : context -> rcf_num -> int -> string + (** Convert the RCF numeral into a string. *) + val num_to_string : context -> rcf_num -> bool -> bool -> string - (** Extract the "numerator" and "denominator" of the given RCF numeral. - We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. *) - val get_numerator_denominator : context -> rcf_num -> (rcf_num * rcf_num) + (** Convert the RCF numeral into a string in decimal notation. *) + val num_to_decimal_string : context -> rcf_num -> int -> string + + (** Extract the "numerator" and "denominator" of the given RCF numeral. + We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. *) + val get_numerator_denominator : context -> rcf_num -> (rcf_num * rcf_num) + + (** Return \c true if \c a represents a rational number. *) + val is_rational : context -> rcf_num -> bool + + (** Return \c true if \c a represents an algebraic number. *) + val is_algebraic : context -> rcf_num -> bool + + (** Return \c true if \c a represents an infinitesimal. *) + val is_infinitesimal : context -> rcf_num -> bool + + (** Return \c true if \c a represents a transcendental number. *) + val is_transcendental : context -> rcf_num -> bool + + (** Return the index of a field extension. *) + val extension_index : context -> rcf_num -> int + + (** Return the name of a transcendental. *) + val transcendental_name : context -> rcf_num -> Symbol.symbol + + (** Return the name of an infinitesimal. *) + val infinitesimal_name : context -> rcf_num -> Symbol.symbol + + (** Return the number of coefficients in an algebraic number. *) + val num_coefficients : context -> rcf_num -> int + + (** Extract a coefficient from an algebraic number. *) + val get_coefficient : context -> rcf_num -> int -> rcf_num + + (** Extract the coefficients from an algebraic number. *) + val coefficients : context -> rcf_num -> rcf_num list + + (** Extract the sign of a sign condition from an algebraic number. *) + val sign_condition_sign : context -> rcf_num -> int -> int + + (** Return the size of a sign condition polynomial. *) + val num_sign_condition_coefficients : context -> rcf_num -> int -> int + + (** Extract a sign condition polynomial coefficient from an algebraic number. *) + val sign_condition_coefficient : context -> rcf_num -> int -> int -> rcf_num + + (** Extract sign conditions from an algebraic number. *) + val sign_conditions : context -> rcf_num -> (rcf_num list * int) list + + (** Extract the interval from an algebraic number. *) + type interval = { + lower_is_inf : bool; + lower_is_open : bool; + lower : rcf_num; + upper_is_inf : bool; + upper_is_open : bool; + upper : rcf_num; + } + + val root_interval : context -> rcf_num -> interval option + + type root = { + obj : rcf_num; + polynomial : rcf_num list; + interval : interval option; + sign_conditions : (rcf_num list * int) list; + } + + val roots : context -> rcf_num list -> root list + + val del_root : context -> root -> unit + + val del_roots : context -> root list -> unit end (** Set a global (or module) parameter, which is shared by all Z3 contexts. diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index 88c27db61..b3842f1b6 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -193,8 +193,124 @@ extern "C" { We have that \ccode{a = n/d}, moreover \c n and \c d are not represented using rational functions. def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM))) - */ - void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); + */ + void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); + + /** + \brief Return \c true if \c a represents a rational number. + + def_API('Z3_rcf_is_rational', BOOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + bool Z3_API Z3_rcf_is_rational(Z3_context c, Z3_rcf_num a); + + /** + \brief Return \c true if \c a represents an algebraic number. + + def_API('Z3_rcf_is_algebraic', BOOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + bool Z3_API Z3_rcf_is_algebraic(Z3_context c, Z3_rcf_num a); + + /** + \brief Return \c true if \c a represents an infinitesimal. + + def_API('Z3_rcf_is_infinitesimal', BOOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + bool Z3_API Z3_rcf_is_infinitesimal(Z3_context c, Z3_rcf_num a); + + /** + \brief Return \c true if \c a represents a transcendental number. + + def_API('Z3_rcf_is_transcendental', BOOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + bool Z3_API Z3_rcf_is_transcendental(Z3_context c, Z3_rcf_num a); + + /** + \brief Return the index of a field extension. + + def_API('Z3_rcf_extension_index', UINT, (_in(CONTEXT), _in(RCF_NUM))) + */ + unsigned Z3_API Z3_rcf_extension_index(Z3_context c, Z3_rcf_num a); + + /** + \brief Return the name of a transcendental. + + \pre Z3_rcf_is_transcendtal(ctx, a); + + def_API('Z3_rcf_transcendental_name', SYMBOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + Z3_symbol Z3_API Z3_rcf_transcendental_name(Z3_context c, Z3_rcf_num a); + + /** + \brief Return the name of an infinitesimal. + + \pre Z3_rcf_is_infinitesimal(ctx, a); + + def_API('Z3_rcf_infinitesimal_name', SYMBOL, (_in(CONTEXT), _in(RCF_NUM))) + */ + Z3_symbol Z3_API Z3_rcf_infinitesimal_name(Z3_context c, Z3_rcf_num a); + + /** + \brief Return the number of coefficients in an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_num_coefficients', UINT, (_in(CONTEXT), _in(RCF_NUM))) + */ + unsigned Z3_API Z3_rcf_num_coefficients(Z3_context c, Z3_rcf_num a); + + /** + \brief Extract a coefficient from an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_coefficient', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) + */ + Z3_rcf_num Z3_API Z3_rcf_coefficient(Z3_context c, Z3_rcf_num a, unsigned i); + + /** + \brief Extract an interval from an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_interval', INT, (_in(CONTEXT), _in(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM), _out(INT), _out(INT), _out(RCF_NUM))) + */ + int Z3_API Z3_rcf_interval(Z3_context c, Z3_rcf_num a, int * lower_is_inf, int * lower_is_open, Z3_rcf_num * lower, int * upper_is_inf, int * upper_is_open, Z3_rcf_num * upper); + + /** + \brief Return the number of sign conditions of an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_num_sign_conditions', UINT, (_in(CONTEXT), _in(RCF_NUM))) + */ + unsigned Z3_API Z3_rcf_num_sign_conditions(Z3_context c, Z3_rcf_num a); + + /** + \brief Extract the sign of a sign condition from an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_sign_condition_sign', INT, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) + */ + int Z3_API Z3_rcf_sign_condition_sign(Z3_context c, Z3_rcf_num a, unsigned i); + + /** + \brief Return the number of sign condition polynomial coefficients of an algebraic number. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_num_sign_condition_coefficients', UINT, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) + */ + unsigned Z3_API Z3_rcf_num_sign_condition_coefficients(Z3_context c, Z3_rcf_num a, unsigned i); + + /** + \brief Extract the j-th polynomial coefficient of the i-th sign condition. + + \pre Z3_rcf_is_algebraic(ctx, a); + + def_API('Z3_rcf_sign_condition_coefficient', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT), _in(UINT))) + */ + Z3_rcf_num Z3_API Z3_rcf_sign_condition_coefficient(Z3_context c, Z3_rcf_num a, unsigned i, unsigned j); /**@}*/ /**@}*/ diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 68ed35b5d..d179ec575 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -2498,6 +2498,35 @@ namespace realclosure { } } + /** + \brief Return true if a is an algebraic number. + */ + bool is_algebraic(numeral const & a) { + return is_rational_function(a) && to_rational_function(a)->ext()->is_algebraic(); + } + + /** + \brief Return true if a represents an infinitesimal. + */ + bool is_infinitesimal(numeral const & a) { + return is_rational_function(a) && to_rational_function(a)->ext()->is_infinitesimal(); + } + + /** + \brief Return true if a is a transcendental. + */ + bool is_transcendental(numeral const & a) { + return is_rational_function(a) && to_rational_function(a)->ext()->is_transcendental(); + } + + /** + \brief Return true if a is a rational. + */ + bool is_rational(numeral const & a) { + return a.m_value->is_rational(); + } + + /** \brief Return true if a depends on infinitesimal extensions. */ @@ -3330,6 +3359,151 @@ namespace realclosure { set(q, _q); } + unsigned extension_index(numeral const & a) { + if (!is_rational_function(a)) + return -1; + return to_rational_function(a)->ext()->idx(); + } + + symbol transcendental_name(numeral const & a) { + if (!is_transcendental(a)) + return symbol(); + return to_transcendental(to_rational_function(a)->ext())->m_name; + } + + symbol infinitesimal_name(numeral const & a) { + if (!is_infinitesimal(a)) + return symbol(); + return to_infinitesimal(to_rational_function(a)->ext())->m_name; + } + + unsigned num_coefficients(numeral const & a) { + if (!is_algebraic(a)) + return 0; + return to_algebraic(to_rational_function(a)->ext())->p().size(); + } + + numeral get_coefficient(numeral const & a, unsigned i) + { + if (!is_algebraic(a)) + return numeral(); + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + if (i >= ext->p().size()) + return numeral(); + value_ref v(*this); + v = ext->p()[i]; + numeral r; + set(r, v); + return r; + } + + unsigned num_sign_conditions(numeral const & a) { + unsigned r = 0; + if (is_algebraic(a)) { + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + const sign_det * sdt = ext->sdt(); + if (sdt) { + sign_condition * sc = sdt->sc(ext->sc_idx()); + while (sc) { + r++; + sc = sc->prev(); + } + } + } + return r; + } + + int get_sign_condition_sign(numeral const & a, unsigned i) + { + if (!is_algebraic(a)) + return 0; + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + const sign_det * sdt = ext->sdt(); + if (!sdt) + return 0; + else { + sign_condition * sc = sdt->sc(ext->sc_idx()); + while (i) { + if (sc) sc = sc->prev(); + i--; + } + return sc ? sc->sign() : 0; + } + } + + bool get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper) + { + if (!is_algebraic(a)) + return false; + lower = numeral(); + upper = numeral(); + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + mpbqi &ivl = ext->iso_interval(); + lower_is_inf = ivl.lower_is_inf(); + lower_is_open = ivl.lower_is_open(); + if (!m_bqm.is_zero(ivl.lower())) + set(lower, mk_rational(ivl.lower())); + upper_is_inf = ivl.upper_is_inf(); + upper_is_open = ivl.upper_is_open(); + if (!m_bqm.is_zero(ivl.upper())) + set(upper, mk_rational(ivl.upper())); + return true; + } + + unsigned get_sign_condition_size(numeral const &a, unsigned i) { + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + const sign_det * sdt = ext->sdt(); + if (!sdt) + return 0; + sign_condition * sc = sdt->sc(ext->sc_idx()); + while (i) { + if (sc) sc = sc->prev(); + i--; + } + return ext->sdt()->qs()[sc->qidx()].size(); + } + + int num_sign_condition_coefficients(numeral const &a, unsigned i) + { + if (!is_algebraic(a)) + return 0; + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + const sign_det * sdt = ext->sdt(); + if (!sdt) + return 0; + sign_condition * sc = sdt->sc(ext->sc_idx()); + while (i) { + if (sc) sc = sc->prev(); + i--; + } + const polynomial & q = ext->sdt()->qs()[sc->qidx()]; + return q.size(); + } + + numeral get_sign_condition_coefficient(numeral const &a, unsigned i, unsigned j) + { + if (!is_algebraic(a)) + return numeral(); + algebraic * ext = to_algebraic(to_rational_function(a)->ext()); + const sign_det * sdt = ext->sdt(); + if (!sdt) + return numeral(); + sign_condition * sc = sdt->sc(ext->sc_idx()); + while (i) { + if (sc) sc = sc->prev(); + i--; + } + const polynomial & q = ext->sdt()->qs()[sc->qidx()]; + if (j >= q.size()) + return numeral(); + value_ref v(*this); + v = q[j]; + numeral r; + set(r, v); + return r; + } + + // --------------------------------- // // GCD of integer coefficients @@ -6103,6 +6277,22 @@ namespace realclosure { return m_imp->is_int(a); } + bool manager::is_rational(numeral const & a) { + return m_imp->is_rational(a); + } + + bool manager::is_algebraic(numeral const & a) { + return m_imp->is_algebraic(a); + } + + bool manager::is_infinitesimal(numeral const & a) { + return m_imp->is_infinitesimal(a); + } + + bool manager::is_transcendental(numeral const & a) { + return m_imp->is_transcendental(a); + } + bool manager::depends_on_infinitesimals(numeral const & a) { return m_imp->depends_on_infinitesimals(a); } @@ -6251,6 +6441,56 @@ namespace realclosure { save_interval_ctx ctx(this); m_imp->clean_denominators(a, p, q); } + + unsigned manager::extension_index(numeral const & a) + { + return m_imp->extension_index(a); + } + + symbol manager::transcendental_name(numeral const &a) + { + return m_imp->transcendental_name(a); + } + + symbol manager::infinitesimal_name(numeral const &a) + { + return m_imp->infinitesimal_name(a); + } + + unsigned manager::num_coefficients(numeral const &a) + { + return m_imp->num_coefficients(a); + } + + manager::numeral manager::get_coefficient(numeral const &a, unsigned i) + { + return m_imp->get_coefficient(a, i); + } + + unsigned manager::num_sign_conditions(numeral const &a) + { + return m_imp->num_sign_conditions(a); + } + + int manager::get_sign_condition_sign(numeral const &a, unsigned i) + { + return m_imp->get_sign_condition_sign(a, i); + } + + bool manager::get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper) + { + return m_imp->get_interval(a, lower_is_inf, lower_is_open, lower, upper_is_inf, upper_is_open, upper); + } + + unsigned manager::num_sign_condition_coefficients(numeral const &a, unsigned i) + { + return m_imp->num_sign_condition_coefficients(a, i); + } + + manager::numeral manager::get_sign_condition_coefficient(numeral const &a, unsigned i, unsigned j) + { + return m_imp->get_sign_condition_coefficient(a, i, j); + } }; void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) { diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 788db4bbf..f63b43a5d 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -70,14 +70,14 @@ namespace realclosure { */ void mk_infinitesimal(char const * name, char const * pp_name, numeral & r); void mk_infinitesimal(numeral & r); - + /** - \brief Add a new transcendental real value to the field. + \brief Add a new transcendental real value to the field. The functor \c mk_interval is used to compute approximations of the transcendental value. This procedure should be used with care, if the value is not really transcendental with respect to the current field, computations with the new numeral may not terminate. Example: we extended the field with Pi. Pi is transcendental with respect to a field that contains only algebraic real numbers. - So, this step is fine. Let us call the resultant field F. + So, this step is fine. Let us call the resultant field F. Then, we extend the field F with 1 - Pi. 1 - Pi is transcendental with respect to algebraic real numbers, but it is NOT transcendental with respect to F, since F contains Pi. */ @@ -109,12 +109,12 @@ namespace realclosure { \brief Return the sign of a. */ int sign(numeral const & a); - + /** \brief Return true if a is zero. */ bool is_zero(numeral const & a); - + /** \brief Return true if a is positive. */ @@ -129,13 +129,33 @@ namespace realclosure { \brief Return true if a is an integer. */ bool is_int(numeral const & a); - + + /** + \brief Return true if a is a rational. + */ + bool is_rational(numeral const & a); + + /** + \brief Return true if a is an algebraic number. + */ + bool is_algebraic(numeral const & a); + + /** + \brief Return true if a represents an infinitesimal. + */ + bool is_infinitesimal(numeral const & a); + + /** + \brief Return true if a is a transcendental. + */ + bool is_transcendental(numeral const & a); + /** \brief Return true if the representation of \c a depends on infinitesimal extensions. */ bool depends_on_infinitesimals(numeral const & a); - + /** \brief a <- n */ @@ -148,14 +168,14 @@ namespace realclosure { /** \brief Return a^{1/k} - + Throws an exception if (a is negative and k is even) or (k is zero). - */ + */ void root(numeral const & a, unsigned k, numeral & b); - + /** \brief Return a^k - + Throws an exception if 0^0. */ void power(numeral const & a, unsigned k, numeral & b); @@ -180,7 +200,7 @@ namespace realclosure { \brief a <- -a */ void neg(numeral & a); - + /** \brief b <- -a */ @@ -190,7 +210,7 @@ namespace realclosure { \brief a <- 1/a if a != 0 */ void inv(numeral & a); - + /** \brief b <- 1/a if a != 0 */ @@ -207,7 +227,7 @@ namespace realclosure { Return 1 if a > b */ int compare(numeral const & a, numeral const & b); - + /** \brief a == b */ @@ -249,7 +269,7 @@ namespace realclosure { bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } - + void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const; /** @@ -259,10 +279,30 @@ namespace realclosure { */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; - + void display_interval(std::ostream & out, numeral const & a) const; - + void clean_denominators(numeral const & a, numeral & p, numeral & q); + + unsigned extension_index(numeral const & a); + + symbol transcendental_name(numeral const &a); + + symbol infinitesimal_name(numeral const &a); + + unsigned num_coefficients(numeral const &a); + + numeral get_coefficient(numeral const &a, unsigned i); + + unsigned num_sign_conditions(numeral const &a); + + int get_sign_condition_sign(numeral const &a, unsigned i); + + bool get_interval(numeral const & a, int & lower_is_inf, int & lower_is_open, numeral & lower, int & upper_is_inf, int & upper_is_open, numeral & upper); + + unsigned num_sign_condition_coefficients(numeral const &a, unsigned i); + + numeral get_sign_condition_coefficient(numeral const &a, unsigned i, unsigned j); }; struct value; From a10c93e203f4892ce8176db7b2392a1121c1239b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:54:45 +0100 Subject: [PATCH 350/428] Bump docker/build-push-action from 5.0.0 to 5.1.0 (#7008) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.0.0...v5.1.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 82e0c4c0b..5bb7d2cad 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -41,7 +41,7 @@ jobs: type=edge type=sha,prefix=ubuntu-20.04-bare-z3-sha- - name: Build and push Bare Z3 Docker Image - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v5.1.0 with: context: . push: true From 2354998cd2c033b7c75d661ac60c1d9015180ab8 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 24 Nov 2023 22:46:32 +0700 Subject: [PATCH 351/428] z3.h: Don't include `stdio.h` (#7014) This doesn't seem to actually be used or needed here. --- src/api/z3.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/z3.h b/src/api/z3.h index 2ad00d835..178f8f618 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -20,7 +20,6 @@ Notes: #pragma once -#include #include #include #include "z3_macros.h" From 9d3fef3e2bdf6d1806aa9c2b171b1bb6f9d41d2f Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sun, 26 Nov 2023 22:30:22 +0700 Subject: [PATCH 352/428] cmake: Require cmake 3.16 or later. (#7015) Support for requiring cmake < 3.4 may go away soon (according to a deprecation notice when building). Ubuntu 20.04 provides cmake 3.16 and current is 3.27, so that seems like a reasonable version to require. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7173e946..a3c17d12d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Enforce some CMake policies -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.16) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") project(Z3 VERSION 4.12.3.0 LANGUAGES CXX) From b5e8f59eae631c35b55c1cee5c5d19cd8d6cde7d Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sun, 26 Nov 2023 22:34:08 +0700 Subject: [PATCH 353/428] mbp: term: Fix reorder ctor warning. (#7016) Initialize members in same order they are defined. --- src/qe/mbp/mbp_term_graph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qe/mbp/mbp_term_graph.cpp b/src/qe/mbp/mbp_term_graph.cpp index e8ba29dfd..e5e5309f3 100644 --- a/src/qe/mbp/mbp_term_graph.cpp +++ b/src/qe/mbp/mbp_term_graph.cpp @@ -172,8 +172,8 @@ class term { : m_expr(v), m_root(this), m_repr(nullptr), m_next(this), m_mark(false), m_mark2(false), m_interpreted(false), m_is_eq(m_expr.get_manager().is_eq(m_expr)), m_is_peq(false), - m_is_npeq_child(false), - m_is_neq_child(false), m_cgr(0), m_gr(0) { + m_is_neq_child(false), m_is_npeq_child(false), + m_cgr(0), m_gr(0) { m_is_neq = m_expr.get_manager().is_not(m_expr) && m_expr.get_manager().is_eq(to_app(m_expr)->get_arg(0)); m_is_distinct = m_expr.get_manager().is_distinct(m_expr); From 9d1ceab1f28c032c71484b2a3772be514b645ad9 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 27 Nov 2023 17:20:21 +0700 Subject: [PATCH 354/428] cmake: Use `FindPython3`. (#7019) `FindPythonInterp` has been deprecated for a long time and is more verbal about that deprecation now. The build system no longer uses `PYTHON_EXECUTABLE` but instead uses `Python3_EXECUTABLE`. --- CMakeLists.txt | 4 ++-- README-CMake.md | 2 +- cmake/z3_add_component.cmake | 8 ++++---- doc/CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/api/CMakeLists.txt | 2 +- src/api/dotnet/CMakeLists.txt | 4 ++-- src/api/java/CMakeLists.txt | 4 ++-- src/api/python/CMakeLists.txt | 6 +++--- src/ast/pattern/CMakeLists.txt | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c17d12d..8144e1328 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,8 +153,8 @@ list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_EXTERNAL_RELEAS ################################################################################ # Find Python ################################################################################ -find_package(PythonInterp 3 REQUIRED) -message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") +find_package(Python3 REQUIRED COMPONENTS Interpreter) +message(STATUS "Python3_EXECUTABLE: ${Python3_EXECUTABLE}") ################################################################################ # Target architecture detection diff --git a/README-CMake.md b/README-CMake.md index e28ffd4fd..9bfc2d9ef 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -277,7 +277,7 @@ The following useful options can be passed to CMake whilst configuring. * ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute. * ``CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR`` - STRING. The path to install CMake package files (e.g. ``/usr/lib/cmake/z3``). * ``CMAKE_INSTALL_API_BINDINGS_DOC`` - STRING. The path to install documentation for API bindings. -* ``PYTHON_EXECUTABLE`` - STRING. The python executable to use during the build. +* ``Python3_EXECUTABLE`` - STRING. The python executable to use during the build. * ``Z3_ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``Z3_BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. * ``Z3_ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. diff --git a/cmake/z3_add_component.cmake b/cmake/z3_add_component.cmake index aa5f8517f..962b02f68 100644 --- a/cmake/z3_add_component.cmake +++ b/cmake/z3_add_component.cmake @@ -116,7 +116,7 @@ macro(z3_add_component component_name) set(_full_output_file_path "${CMAKE_CURRENT_BINARY_DIR}/${_output_file}") message(STATUS "Adding rule to generate \"${_output_file}\"") add_custom_command(OUTPUT "${_output_file}" - COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/pyg2hpp.py" "${_full_pyg_file_path}" "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/pyg2hpp.py" "${_full_pyg_file_path}" "${CMAKE_CURRENT_BINARY_DIR}" MAIN_DEPENDENCY "${_full_pyg_file_path}" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/pyg2hpp.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} @@ -275,7 +275,7 @@ macro(z3_add_install_tactic_rule) string(REPLACE ";" "\n" _tactic_header_files "${_tactic_header_files}") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.deps" ${_tactic_header_files}) add_custom_command(OUTPUT "install_tactic.cpp" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_install_tactic_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.deps" @@ -313,7 +313,7 @@ macro(z3_add_memory_initializer_rule) endforeach() add_custom_command(OUTPUT "mem_initializer.cpp" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_mem_initializer_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" ${_mem_init_finalize_headers} @@ -349,7 +349,7 @@ macro(z3_add_gparams_register_modules_rule) unset(_component_register_module_header_files) add_custom_command(OUTPUT "gparams_register_modules.cpp" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_gparams_register_modules_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" ${_register_module_header_files} diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 54ba73fb0..6f81edf0d 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -58,7 +58,7 @@ endif() add_custom_target(api_docs ${ALWAYS_BUILD_DOCS_ARG} COMMAND - "${PYTHON_EXECUTABLE}" "${MK_API_DOC_SCRIPT}" + "${Python3_EXECUTABLE}" "${MK_API_DOC_SCRIPT}" --build "${PROJECT_BINARY_DIR}" --doxygen-executable "${DOXYGEN_EXECUTABLE}" --output-dir "${DOC_DEST_DIR}" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e091db15f..a12571f35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -217,7 +217,7 @@ if (MSVC) set(dll_module_exports_file "${CMAKE_CURRENT_BINARY_DIR}/api_dll.def") add_custom_command(OUTPUT "${dll_module_exports_file}" COMMAND - "${PYTHON_EXECUTABLE}" + "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_def_file.py" "${dll_module_exports_file}" "libz3" diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 5cc604f15..38a09b631 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -18,7 +18,7 @@ foreach (gen_file ${generated_files}) endforeach() add_custom_command(OUTPUT ${generated_files} - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--api_output_dir" diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index fcd7b0d85..d3cb87bc7 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -9,7 +9,7 @@ set(VER_TWEAK "${Z3_VERSION_TWEAK}") # Generate Native.cs set(Z3_DOTNET_NATIVE_FILE "${CMAKE_CURRENT_BINARY_DIR}/Native.cs") add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--dotnet-output-dir" @@ -25,7 +25,7 @@ add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}" # Generate Enumerations.cs set(Z3_DOTNET_CONST_FILE "${CMAKE_CURRENT_BINARY_DIR}/Enumerations.cs") add_custom_command(OUTPUT "${Z3_DOTNET_CONST_FILE}" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--dotnet-output-dir" diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index bd4338f7b..9501de3eb 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -16,7 +16,7 @@ set(Z3_JAVA_PACKAGE_NAME "com.microsoft.z3") set(Z3_JAVA_NATIVE_JAVA "${CMAKE_CURRENT_BINARY_DIR}/Native.java") set(Z3_JAVA_NATIVE_CPP "${CMAKE_CURRENT_BINARY_DIR}/Native.cpp") add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--java-input-dir" @@ -74,7 +74,7 @@ foreach (enum_file ${Z3_JAVA_ENUMERATION_PACKAGE_FILES}) ) endforeach() add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--java-output-dir" diff --git a/src/api/python/CMakeLists.txt b/src/api/python/CMakeLists.txt index f5e449ea8..5da66dfe4 100644 --- a/src/api/python/CMakeLists.txt +++ b/src/api/python/CMakeLists.txt @@ -33,7 +33,7 @@ endforeach() # Generate z3core.py add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3core.py" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--z3py-output-dir" @@ -49,7 +49,7 @@ list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest} # Generate z3consts.py add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3consts.py" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--z3py-output-dir" @@ -96,7 +96,7 @@ if (Z3_INSTALL_PYTHON_BINDINGS) if (NOT DEFINED CMAKE_INSTALL_PYTHON_PKG_DIR) message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess") execute_process( - COMMAND "${PYTHON_EXECUTABLE}" "-c" + COMMAND "${Python3_EXECUTABLE}" "-c" "import sysconfig; print(sysconfig.get_path('purelib'))" RESULT_VARIABLE exit_code OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR diff --git a/src/ast/pattern/CMakeLists.txt b/src/ast/pattern/CMakeLists.txt index 7393b7110..88dfd198b 100644 --- a/src/ast/pattern/CMakeLists.txt +++ b/src/ast/pattern/CMakeLists.txt @@ -7,7 +7,7 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/database.h") endif() add_custom_command(OUTPUT "database.h" - COMMAND "${PYTHON_EXECUTABLE}" + COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_pat_db.py" "${CMAKE_CURRENT_SOURCE_DIR}/database.smt2" "${CMAKE_CURRENT_BINARY_DIR}/database.h" From 8192b327e12277a6922260a6d5b5e483a6dcebb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 02:36:16 +0000 Subject: [PATCH 355/428] Bump mymindstorm/setup-emsdk from 12 to 13 (#7021) Bumps [mymindstorm/setup-emsdk](https://github.com/mymindstorm/setup-emsdk) from 12 to 13. - [Release notes](https://github.com/mymindstorm/setup-emsdk/releases) - [Commits](https://github.com/mymindstorm/setup-emsdk/compare/v12...v13) --- updated-dependencies: - dependency-name: mymindstorm/setup-emsdk dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/wasm-release.yml | 2 +- .github/workflows/wasm.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml index 805cd3517..f437fcc5f 100644 --- a/.github/workflows/wasm-release.yml +++ b/.github/workflows/wasm-release.yml @@ -36,7 +36,7 @@ jobs: cp ../../../LICENSE.txt . - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v12 + uses: mymindstorm/setup-emsdk@v13 with: no-install: true version: ${{env.EM_VERSION}} diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 4e4f7aa4d..320f36817 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -29,7 +29,7 @@ jobs: node-version: "lts/*" - name: Setup emscripten - uses: mymindstorm/setup-emsdk@v12 + uses: mymindstorm/setup-emsdk@v13 with: no-install: true version: ${{env.EM_VERSION}} From 3422f44cea4e73572d1e22d1c483a960ec788771 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 28 Nov 2023 22:55:25 +0700 Subject: [PATCH 356/428] Fix syntax warning when using Python 3.12. (#7022) This happens when generating the Python API and you are using Python 3.12 in the build environment: ``` .../z3/scripts/update_api.py:1828: SyntaxWarning: invalid escape sequence '\#' ``` This was a `DeprecationWarning` previously, but Python 3.12 changed it to a `SyntaxWarning` to make it more visible. The release notes indicate that this will be a syntax error in the future. --- scripts/update_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 5cd53e16f..ddfb31f26 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1895,10 +1895,10 @@ if _lib is None: print(" - to the custom Z3_LIB_DIRS Python-builtin before importing the z3 module, e.g. via") if sys.version < '3': print(" import __builtin__") - print(" __builtin__.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] \# directory containing libz3.%s" % _ext) + print(" __builtin__.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] # directory containing libz3.%s" % _ext) else: print(" import builtins") - print(" builtins.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] \# directory containing libz3.%s" % _ext) + print(" builtins.Z3_LIB_DIRS = [ '/path/to/z3/lib/dir' ] # directory containing libz3.%s" % _ext) print(_failures) raise Z3Exception("libz3.%s not found." % _ext) From 69f9640fdff33d196be69dc9e03e996b25e21389 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 13:14:44 -0800 Subject: [PATCH 357/428] fix #7018 --- src/opt/opt_context.cpp | 18 ++++++++++++------ src/opt/opt_context.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 11eddc2eb..1b57a7200 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -213,7 +213,7 @@ namespace opt { void context::add_hard_constraint(expr* f, expr* t) { if (m_calling_on_model) - throw default_exception("adding soft constraints is not supported during callbacks"); + throw default_exception("adding hard constraints is not supported during callbacks"); m_scoped_state.m_asms.push_back(t); m_scoped_state.add(m.mk_implies(t, f)); clear_state(); @@ -905,12 +905,14 @@ namespace opt { ptr_vector deps; expr_dependency_ref core(r->dep(i), m); m.linearize(core, deps); - if (!deps.empty()) { - fmls.push_back(m.mk_implies(m.mk_and(deps.size(), deps.data()), r->form(i))); - } - else { + if (deps.empty()) + fmls.push_back(r->form(i)); + else if (deps.size() == 1 && deps[0] == r->form(i)) + continue; + else if (is_objective(r->form(i))) fmls.push_back(r->form(i)); - } + else + fmls.push_back(m.mk_implies(mk_and(m, deps.size(), deps.data()), r->form(i))); } if (r->inconsistent()) { ptr_vector core_elems; @@ -920,6 +922,10 @@ namespace opt { } } + bool context::is_objective(expr* fml) { + return is_app(fml) && m_objective_fns.contains(to_app(fml)->get_decl()); + } + bool context::is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index) { if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && m_objectives[index].m_type == O_MAXIMIZE) { diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 9e61ae92c..4e791531e 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -303,6 +303,7 @@ namespace opt { void import_scoped_state(); void normalize(expr_ref_vector const& asms); void internalize(); + bool is_objective(expr* fml); bool is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); bool is_minimize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); bool is_maxsat(expr* fml, expr_ref_vector& terms, From f90b10a0c8e7f292bbe6288452f6176d1d73e608 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 13:26:10 -0800 Subject: [PATCH 358/428] fix #7012 omitting constructor, simplifying operator definitions, omitting incorrect type annotations --- 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 053fd7dd1..d029769e0 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1571,6 +1571,9 @@ class BoolRef(ExprRef): def sort(self): return BoolSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) + def __add__(self, other): + return If(self, 1, 0) + If(other, 1, 0) + def __rmul__(self, other): return self * other @@ -1584,6 +1587,17 @@ class BoolRef(ExprRef): if isinstance(other, BoolRef): other = If(other, 1, 0) return If(self, other, 0) + + def __and__(self, other): + return And(self, other) + + def __or__(self, other): + return Or(self, other) + + def __invert__(self): + return Not(self) + + def is_bool(a): From f36f21fa8c64c30c8a775ae6ca4950674bda33ae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 13:36:41 -0800 Subject: [PATCH 359/428] add comments for API versions of bit-vector overflow/underflow checks for #7011 --- src/api/api_bv.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index bb4263730..3ea5ba918 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -227,6 +227,9 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); + // l1 := t1 t1 + t2 y Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); From 8179f8b5d7424c697d18afa5515f33e120f6bc32 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 14:32:56 -0800 Subject: [PATCH 360/428] fix #7017 --- src/sat/smt/arith_solver.cpp | 7 ++++++- src/sat/smt/arith_solver.h | 1 + src/smt/theory_lra.cpp | 7 +++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index fe7b0aa46..537291280 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -899,6 +899,11 @@ namespace arith { lp().random_update(vars.size(), vars.data()); } + bool solver::include_func_interp(enode* n) const { + func_decl* d = n->get_decl(); + return d && include_func_interp(d); + } + bool solver::assume_eqs() { if (delayed_assume_eqs()) return true; @@ -913,7 +918,7 @@ namespace arith { theory_var v = (i + start) % sz; if (is_bool(v)) continue; - if (!ctx.is_shared(var2enode(v))) + if (!ctx.is_shared(var2enode(v)) && !include_func_interp(var2enode(v))) continue; ensure_column(v); if (!is_registered_var(v)) diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index ddaaa6164..84be3caa7 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -401,6 +401,7 @@ namespace arith { bool delayed_assume_eqs(); bool is_eq(theory_var v1, theory_var v2); bool use_nra_model(); + bool include_func_interp(enode* n) const; lbool make_feasible(); bool check_delayed_eqs(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a23472e5b..bbe29da74 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1513,7 +1513,7 @@ public: for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); - if (!th.is_relevant_and_shared(n1)) + if (!th.is_relevant_and_shared(n1) && !include_func_interp(n1)) continue; ensure_column(v); if (!is_registered_var(v)) @@ -3346,7 +3346,6 @@ public: } } return r; - } model_value_proc * mk_value(enode * n, model_generator & mg) { @@ -3402,6 +3401,10 @@ public: a.is_mod0(f); } + bool include_func_interp(enode* n) { + return include_func_interp(n->get_decl()); + } + bool get_lower(enode* n, rational& val, bool& is_strict) { theory_var v = n->get_th_var(get_id()); if (!is_registered_var(v)) From 79bbbf76d0c123481c8ca05cd3a98939270074d3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 15:06:27 -0800 Subject: [PATCH 361/428] fix #7006 --- src/ast/rewriter/rewriter_types.h | 12 ++++++++++ src/model/model_evaluator.cpp | 38 +++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/ast/rewriter/rewriter_types.h b/src/ast/rewriter/rewriter_types.h index ff1755b91..937bcdc6b 100644 --- a/src/ast/rewriter/rewriter_types.h +++ b/src/ast/rewriter/rewriter_types.h @@ -33,6 +33,18 @@ enum br_status { BR_FAILED // no builtin rewrite is available }; +inline std::ostream& operator<<(std::ostream& out, br_status st) { + switch (st) { + case BR_REWRITE1: return out << "rewrite1"; + case BR_REWRITE2: return out << "rewrite2"; + case BR_REWRITE3: return out << "rewrite3"; + case BR_REWRITE_FULL: return out << "rewrite_full"; + case BR_DONE: return out << "done"; + case BR_FAILED: return out << "failed"; + default: return out << "unknown"; + } +} + #define RW_UNBOUNDED_DEPTH 3 inline br_status unsigned2br_status(unsigned u) { br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast(u); diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 6ecb3c337..5a5bd79b6 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -161,7 +161,7 @@ struct evaluator_cfg : public default_rewriter_cfg { return st; } - bool contains_as_array(expr* e) { + bool contains_redex(expr* e) { if (m_ar.is_as_array(e)) return true; if (is_var(e)) @@ -169,22 +169,24 @@ struct evaluator_cfg : public default_rewriter_cfg { if (is_app(e) && to_app(e)->get_num_args() == 0) return false; - struct has_as_array {}; - struct has_as_array_finder { + struct has_redex {}; + struct has_redex_finder { array_util& au; - has_as_array_finder(array_util& au): au(au) {} + has_redex_finder(array_util& au): au(au) {} void operator()(var* v) {} void operator()(quantifier* q) {} void operator()(app* a) { if (au.is_as_array(a->get_decl())) - throw has_as_array(); + throw has_redex(); + if (au.get_manager().is_eq(a)) + throw has_redex(); } }; - has_as_array_finder ha(m_ar); + has_redex_finder ha(m_ar); try { for_each_expr(ha, e); } - catch (has_as_array) { + catch (has_redex) { return true; } return false; @@ -216,8 +218,8 @@ struct evaluator_cfg : public default_rewriter_cfg { expr* val = m_model.get_const_interp(f); if (val != nullptr) { result = val; - st = contains_as_array(val) ? BR_REWRITE_FULL : BR_DONE; - TRACE("model_evaluator", tout << result << "\n";); + st = contains_redex(val) ? BR_REWRITE_FULL : BR_DONE; + TRACE("model_evaluator", tout << st << " " << result << "\n";); return st; } if (!m_model_completion) @@ -443,10 +445,22 @@ struct evaluator_cfg : public default_rewriter_cfg { result = m.get_some_value(f->get_range()); return BR_DONE; } - else if (m_dt.is_accessor(f) && !is_ground(args[0])) { - result = m.mk_app(f, num, args); - return BR_DONE; + else if (m_dt.is_accessor(f)) { + expr* arg = args[0]; + if (is_ground(arg) && !fi) { + fi = alloc(func_interp, m, f->get_arity()); + expr* val = m_model.get_some_value(f->get_range()); + fi->set_else(val); + m_model.register_decl(f, fi); + result = val; + return BR_DONE; + } + if (!is_ground(arg)) { + result = m.mk_app(f, num, args); + return BR_DONE; + } } + if (fi) { if (fi->is_partial()) fi->set_else(m.get_some_value(f->get_range())); From 1d4644f718fcc34db43bfab8040cd4f4246e0aa2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 28 Nov 2023 16:50:28 -0800 Subject: [PATCH 362/428] fix typos in script --- scripts/test-java-cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-java-cmake.yml b/scripts/test-java-cmake.yml index 0f7045344..fa9f767b5 100644 --- a/scripts/test-java-cmake.yml +++ b/scripts/test-java-cmake.yml @@ -3,7 +3,7 @@ steps: cd build mkdir -p examples/java cp ../examples/java/JavaExample.java examples/java/ - javac examples/java/Javaexamplejava -classpath com.microsoft.z3.jar + javac examples/java/JavaExample.java -classpath com.microsoft.z3.jar export LD_LIBRARY_PATH=$(pwd):${LD_LIBRARY_PATH} java -cp .:examples/java:com.microsoft.z3.jar JavaExample cd .. From e972eb33b236272b93b8296a8f1df29043dd8c72 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 10:44:36 -0800 Subject: [PATCH 363/428] #6523 - contains_ptr bug regarding etable reinserts --- src/ast/euf/euf_egraph.cpp | 33 +++++++++++++++++++++------------ src/ast/euf/euf_egraph.h | 7 +++++++ src/ast/euf/euf_etable.cpp | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 34781aad5..06f712f00 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -69,7 +69,7 @@ namespace euf { } enode_bool_pair egraph::insert_table(enode* p) { - TRACE("euf", tout << bpp(p) << "\n"); + TRACE("euf", tout << "insert_table " << bpp(p) << "\n"); //SASSERT(!m_table.contains_ptr(p)); auto rc = m_table.insert(p); p->m_cg = rc.first; @@ -83,7 +83,12 @@ namespace euf { void egraph::reinsert_equality(enode* p) { SASSERT(p->is_equality()); if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) - add_literal(p, nullptr); + queue_literal(p, nullptr); + } + + void egraph::queue_literal(enode* p, enode* ante) { + if (m_on_propagate_literal) + m_to_add_literal.push_back({ p, ante }); } void egraph::force_push() { @@ -164,19 +169,14 @@ namespace euf { if (!ante) m_on_propagate_literal(n, ante); else if (m.is_true(ante->get_expr()) || m.is_false(ante->get_expr())) { - for (enode* k : enode_class(n)) { - if (k != ante) { - //verbose_stream() << "eq: " << k->value() << " " <value() << "\n"; - m_on_propagate_literal(k, ante); - } - } + for (enode* k : enode_class(n)) + if (k != ante) + m_on_propagate_literal(k, ante); } else { for (enode* k : enode_class(n)) { - if (k->value() != ante->value()) { - //verbose_stream() << "eq: " << k->value() << " " <value() << "\n"; - m_on_propagate_literal(k, ante); - } + if (k->value() != ante->value()) + m_on_propagate_literal(k, ante); } } } @@ -352,6 +352,7 @@ namespace euf { if (num_scopes <= m_num_scopes) { m_num_scopes -= num_scopes; m_to_merge.reset(); + m_to_add_literal.reset(); return; } num_scopes -= m_num_scopes; @@ -434,6 +435,7 @@ namespace euf { m_scopes.shrink(old_lim); m_region.pop_scope(num_scopes); m_to_merge.reset(); + m_to_add_literal.reset(); SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size()); @@ -494,6 +496,7 @@ namespace euf { void egraph::remove_parents(enode* r) { TRACE("euf", tout << bpp(r) << "\n"); + DEBUG_CODE(for (enode* p : enode_parents(r)) SASSERT(!p->is_marked1()); ); for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; @@ -582,11 +585,17 @@ namespace euf { bool egraph::propagate() { SASSERT(m_num_scopes == 0 || m_to_merge.empty()); force_push(); + unsigned j = 0; for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { auto const& w = m_to_merge[i]; merge(w.a, w.b, justification::congruence(w.commutativity, m_congruence_timestamp++)); + for (; j < m_to_add_literal.size() && m.limit().inc() && !inconsistent(); ++j) { + auto const& [p, ante] = m_to_add_literal[j]; + add_literal(p, ante); + } } m_to_merge.reset(); + m_to_add_literal.reset(); return (m_new_th_eqs_qhead < m_new_th_eqs.size()) || inconsistent(); diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index 1193d4df0..0dbabecea 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -90,6 +90,11 @@ namespace euf { to_merge(enode* a, enode* b, bool c) : a(a), b(b), commutativity(c) {} }; + struct to_add_literal { + enode* p, *ante; + to_add_literal(enode* p, enode* ante) : p(p), ante(ante) {} + }; + struct stats { unsigned m_num_merge; unsigned m_num_th_eqs; @@ -162,6 +167,7 @@ namespace euf { }; ast_manager& m; svector m_to_merge; + svector m_to_add_literal; etable m_table; region m_region; svector m_updates; @@ -207,6 +213,7 @@ namespace euf { void add_th_diseqs(theory_id id, theory_var v1, enode* r); bool th_propagates_diseqs(theory_id id) const; void add_literal(enode* n, enode* ante); + void queue_literal(enode* n, enode* ante); void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); void undo_add_th_var(enode* n, theory_id id); enode* mk_enode(expr* f, unsigned generation, unsigned num_args, enode * const* args); diff --git a/src/ast/euf/euf_etable.cpp b/src/ast/euf/euf_etable.cpp index d79944c9b..1fc8aa0e0 100644 --- a/src/ast/euf/euf_etable.cpp +++ b/src/ast/euf/euf_etable.cpp @@ -203,7 +203,6 @@ namespace euf { SASSERT(n->num_args() > 0); enode * n_prime; void * t = get_table(n); - //verbose_stream() << "insert " << n << "\n"; switch (static_cast(GET_TAG(t))) { case UNARY: n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); @@ -238,6 +237,7 @@ namespace euf { UNTAG(table*, t)->erase(n); break; } + CTRACE("euf", contains_ptr(n), display(tout)); SASSERT(!contains_ptr(n)); } From d469c1054e4c06578a4bc12e869780dffc670a56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 12:45:43 -0800 Subject: [PATCH 364/428] remove separate to_add_literal queue --- src/ast/euf/euf_egraph.cpp | 18 +++++++++--------- src/ast/euf/euf_egraph.h | 13 +++++-------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 06f712f00..a4502030f 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -88,7 +88,7 @@ namespace euf { void egraph::queue_literal(enode* p, enode* ante) { if (m_on_propagate_literal) - m_to_add_literal.push_back({ p, ante }); + m_to_merge.push_back({ p, ante }); } void egraph::force_push() { @@ -352,7 +352,6 @@ namespace euf { if (num_scopes <= m_num_scopes) { m_num_scopes -= num_scopes; m_to_merge.reset(); - m_to_add_literal.reset(); return; } num_scopes -= m_num_scopes; @@ -435,7 +434,6 @@ namespace euf { m_scopes.shrink(old_lim); m_region.pop_scope(num_scopes); m_to_merge.reset(); - m_to_add_literal.reset(); SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size()); @@ -588,14 +586,16 @@ namespace euf { unsigned j = 0; for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { auto const& w = m_to_merge[i]; - merge(w.a, w.b, justification::congruence(w.commutativity, m_congruence_timestamp++)); - for (; j < m_to_add_literal.size() && m.limit().inc() && !inconsistent(); ++j) { - auto const& [p, ante] = m_to_add_literal[j]; - add_literal(p, ante); - } + switch (w.t) { + case to_merge_plain: + merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++)); + break; + case to_add_literal: + add_literal(w.a, w.b); + break; + } } m_to_merge.reset(); - m_to_add_literal.reset(); return (m_new_th_eqs_qhead < m_new_th_eqs.size()) || inconsistent(); diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index 0dbabecea..caa09f82b 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -84,15 +84,13 @@ namespace euf { typedef ptr_vector trail_stack; + enum to_merge_t { to_merge_plain, to_merge_comm, to_add_literal }; struct to_merge { enode* a, * b; - bool commutativity; - to_merge(enode* a, enode* b, bool c) : a(a), b(b), commutativity(c) {} - }; - - struct to_add_literal { - enode* p, *ante; - to_add_literal(enode* p, enode* ante) : p(p), ante(ante) {} + to_merge_t t; + bool commutativity() const { return t == to_merge_comm; } + to_merge(enode* a, enode* b, bool c) : a(a), b(b), t(c ? to_merge_comm : to_merge_comm) {} + to_merge(enode* p, enode* ante): a(p), b(ante), t(to_add_literal) {} }; struct stats { @@ -167,7 +165,6 @@ namespace euf { }; ast_manager& m; svector m_to_merge; - svector m_to_add_literal; etable m_table; region m_region; svector m_updates; From 41a3196c890c9cff15b664679cd1cc0b951ad57f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 13:35:30 -0800 Subject: [PATCH 365/428] fix #7024 --- src/math/lp/nla_solver.cpp | 2 +- src/sat/smt/arith_solver.cpp | 2 +- src/smt/theory_lra.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/math/lp/nla_solver.cpp b/src/math/lp/nla_solver.cpp index 5ed9b4538..23d4016bd 100644 --- a/src/math/lp/nla_solver.cpp +++ b/src/math/lp/nla_solver.cpp @@ -95,7 +95,7 @@ namespace nla { scoped_anum& solver::tmp2() { SASSERT(use_nra_model()); - return m_core->m_nra.tmp1(); + return m_core->m_nra.tmp2(); } diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 537291280..a32a4e964 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -918,7 +918,7 @@ namespace arith { theory_var v = (i + start) % sz; if (is_bool(v)) continue; - if (!ctx.is_shared(var2enode(v)) && !include_func_interp(var2enode(v))) + if (!ctx.is_shared(var2enode(v))) continue; ensure_column(v); if (!is_registered_var(v)) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index bbe29da74..8669a514c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1513,7 +1513,7 @@ public: for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); - if (!th.is_relevant_and_shared(n1) && !include_func_interp(n1)) + if (!th.is_relevant_and_shared(n1)) continue; ensure_column(v); if (!is_registered_var(v)) From 4289cfac8d273c6bfd1b490e82fd68f5e9c1bce6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 13:47:59 -0800 Subject: [PATCH 366/428] revert some fixes to euf Signed-off-by: Nikolaj Bjorner --- src/ast/euf/euf_egraph.cpp | 4 ++-- src/ast/euf/euf_egraph.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index a4502030f..0d820ad8b 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -83,7 +83,7 @@ namespace euf { void egraph::reinsert_equality(enode* p) { SASSERT(p->is_equality()); if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) - queue_literal(p, nullptr); + add_literal(p, nullptr); } void egraph::queue_literal(enode* p, enode* ante) { @@ -581,13 +581,13 @@ namespace euf { bool egraph::propagate() { - SASSERT(m_num_scopes == 0 || m_to_merge.empty()); force_push(); unsigned j = 0; for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { auto const& w = m_to_merge[i]; switch (w.t) { case to_merge_plain: + case to_merge_comm: merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++)); break; case to_add_literal: diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index caa09f82b..83a3574ce 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -89,7 +89,7 @@ namespace euf { enode* a, * b; to_merge_t t; bool commutativity() const { return t == to_merge_comm; } - to_merge(enode* a, enode* b, bool c) : a(a), b(b), t(c ? to_merge_comm : to_merge_comm) {} + to_merge(enode* a, enode* b, bool c) : a(a), b(b), t(c ? to_merge_comm : to_merge_plain) {} to_merge(enode* p, enode* ante): a(p), b(ante), t(to_add_literal) {} }; From 2f01b5b567cff438b686c4b565130fc1e7f1abfe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 14:00:17 -0800 Subject: [PATCH 367/428] re-enable delayed literal propagation --- src/ast/euf/euf_egraph.cpp | 2 +- src/sat/smt/euf_solver.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 0d820ad8b..d370ceed3 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -83,7 +83,7 @@ namespace euf { void egraph::reinsert_equality(enode* p) { SASSERT(p->is_equality()); if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) - add_literal(p, nullptr); + queue_literal(p, nullptr); } void egraph::queue_literal(enode* p, enode* ante) { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 6a6b43650..d25df5b06 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -1059,6 +1059,7 @@ namespace euf { }; r->m_egraph.copy_from(m_egraph, copy_justification); r->set_solver(s); + r->m_egraph.copy_from(m_egraph, copy_justification); for (euf::enode* n : r->m_egraph.nodes()) { auto b = n->bool_var(); if (b != sat::null_bool_var) { From faa2d8ac6c44e6306ee479ae08ddc848bf4c512f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 14:00:37 -0800 Subject: [PATCH 368/428] re-enable delayed literal propagation --- src/sat/smt/euf_solver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index d25df5b06..d21e7a12a 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -1057,7 +1057,6 @@ namespace euf { SASSERT(true_lit != sat::null_literal); return (void*)(r->to_ptr(true_lit)); }; - r->m_egraph.copy_from(m_egraph, copy_justification); r->set_solver(s); r->m_egraph.copy_from(m_egraph, copy_justification); for (euf::enode* n : r->m_egraph.nodes()) { From 9efe6f6afa5bef2975040b6e014ae9da149549e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 14:54:54 -0800 Subject: [PATCH 369/428] fix regression in fix for #7006 Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 5a5bd79b6..9cef0472f 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -447,7 +447,7 @@ struct evaluator_cfg : public default_rewriter_cfg { } else if (m_dt.is_accessor(f)) { expr* arg = args[0]; - if (is_ground(arg) && !fi) { + if (m.is_value(arg) && !fi) { fi = alloc(func_interp, m, f->get_arity()); expr* val = m_model.get_some_value(f->get_range()); fi->set_else(val); From e9abdbb7a4a55c38193a6b39af00c10742dfa457 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 15:08:08 -0800 Subject: [PATCH 370/428] fix #7011 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bv_rewriter.cpp | 16 +++++++++++++--- src/ast/rewriter/bv_rewriter.h | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 3b52ca372..fc672584e 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -216,7 +216,7 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons case OP_BUSUB_OVFL: return mk_bvusub_underflow(num_args, args, result); case OP_BSSUB_OVFL: - return mk_bvssub_overflow(num_args, args, result); + return mk_bvssub_under_overflow(num_args, args, result); default: return BR_FAILED; } @@ -3081,19 +3081,29 @@ br_status bv_rewriter::mk_bvusub_underflow(unsigned num, expr * const * args, ex return status; } -br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) { +// +// no_overflow := if t2 = min_int then t1 no_underflow(t1 + -t2) +// over_underflow := 0 =s 0 || t2 != min_int & under_overflow+(t1 + -t2) +// := if t2 == min_int then t1 >=s 0 else under_overflow+(t1 + -t2) +// because when 0 { br_status mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result); - br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result); + // br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvssub_under_overflow(unsigned num, expr * const * args, expr_ref & result); bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); From 26440ed3d807cc5134ceefef28a3bd7d9ac367c4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 Nov 2023 15:45:19 -0800 Subject: [PATCH 371/428] deal with ubuntu/clang warnings Signed-off-by: Nikolaj Bjorner --- src/ast/euf/euf_egraph.cpp | 1 - src/math/lp/bound_analyzer_on_row.h | 1 + src/math/lp/int_solver.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index d370ceed3..6c8cbda06 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -582,7 +582,6 @@ namespace euf { bool egraph::propagate() { force_push(); - unsigned j = 0; for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { auto const& w = m_to_merge[i]; switch (w.t) { diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 576f14599..b4d587bec 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -286,6 +286,7 @@ private: unsigned row_index = this->m_row_index; auto* lar = &m_bp.lp(); auto explain = [bound_j, coeff_before_j_is_pos, is_lower_bound, strict, row_index, lar]() { + (void) strict; TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << ", row_index = " << row_index << "\n";); int bound_sign = (is_lower_bound ? 1 : -1); int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index b619b1c8a..c324af5b6 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -884,7 +884,7 @@ namespace lp { }; auto _check_feasible = [&](void) { - auto st = lra.find_feasible_solution(); + lra.find_feasible_solution(); if (!lra.is_feasible() && !settings().get_cancel_flag()) { lra.get_infeasibility_explanation(*m_ex); return false; From d540d881ef3d0d701e91ae8d7170ed2d74b529fa Mon Sep 17 00:00:00 2001 From: Alper Altuntas Date: Thu, 30 Nov 2023 09:35:08 -0700 Subject: [PATCH 372/428] Add __enter__ and __exit__ methods to Solver class (#7025) To enable the usage of the with statement for the Solver class instances, this commit adds __enter__ and __exit__ methods. The with statement should offer a more concise and safer alternative to explicit usage of push() and pop() methods. --- src/api/python/z3/z3.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index d029769e0..7551d8a20 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6955,6 +6955,13 @@ class Solver(Z3PPObject): if self.solver is not None and self.ctx.ref() is not None and Z3_solver_dec_ref is not None: Z3_solver_dec_ref(self.ctx.ref(), self.solver) + def __enter__(self): + self.push() + return self + + def __exit__(self, *exc_info): + self.pop() + def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. From 5784c2da795c90a1231e905b60eaaebccdbd95a4 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 30 Nov 2023 08:59:05 -1000 Subject: [PATCH 373/428] remove an unnecessary if --- src/math/lp/lp_primal_core_solver_tableau_def.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 437d27ae8..cec0e316b 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -72,14 +72,13 @@ template void lp_primal_core_solver::advance_on_e if (t < j_nz) { j_nz = t; entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; + number_of_benefitial_columns_to_go_over--; n = 1; } 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); + } if (entering_iter == m_non_basis_list.end()) return -1; unsigned entering = *entering_iter; From b52fd8d9542dc51cd5c75496a9ee22b377fa04df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 30 Nov 2023 13:58:24 -0800 Subject: [PATCH 374/428] add EUF plugin framework. plugin setting allows adding equality saturation within the E-graph propagation without involving externalizing theory solver dispatch. It makes equality saturation independent of SAT integration. Add a special relation operator to support ad-hoc AC symbols. --- src/ast/euf/CMakeLists.txt | 8 +- src/ast/euf/euf_ac_plugin.cpp | 1058 +++++++++++++++++++++ src/ast/euf/euf_ac_plugin.h | 309 ++++++ src/ast/euf/euf_arith_plugin.cpp | 71 ++ src/ast/euf/euf_arith_plugin.h | 53 ++ src/ast/euf/euf_bv_plugin.cpp | 361 +++++++ src/ast/euf/euf_bv_plugin.h | 100 ++ src/ast/euf/euf_egraph.cpp | 53 +- src/ast/euf/euf_egraph.h | 30 +- src/ast/euf/euf_enode.cpp | 11 + src/ast/euf/euf_enode.h | 6 + src/ast/euf/euf_justification.cpp | 54 ++ src/ast/euf/euf_justification.h | 61 +- src/ast/euf/euf_plugin.cpp | 47 + src/ast/euf/euf_plugin.h | 58 ++ src/ast/euf/euf_specrel_plugin.cpp | 71 ++ src/ast/euf/euf_specrel_plugin.h | 56 ++ src/ast/special_relations_decl_plugin.cpp | 48 +- src/ast/special_relations_decl_plugin.h | 11 +- src/sat/smt/CMakeLists.txt | 1 + src/sat/smt/euf_solver.cpp | 4 + src/sat/smt/specrel_solver.cpp | 120 +++ src/sat/smt/specrel_solver.h | 75 ++ src/test/CMakeLists.txt | 2 + src/test/euf_arith_plugin.cpp | 106 +++ src/test/euf_bv_plugin.cpp | 183 ++++ src/test/main.cpp | 2 + src/util/dependency.h | 172 +++- 28 files changed, 3063 insertions(+), 68 deletions(-) create mode 100644 src/ast/euf/euf_ac_plugin.cpp create mode 100644 src/ast/euf/euf_ac_plugin.h create mode 100644 src/ast/euf/euf_arith_plugin.cpp create mode 100644 src/ast/euf/euf_arith_plugin.h create mode 100644 src/ast/euf/euf_bv_plugin.cpp create mode 100644 src/ast/euf/euf_bv_plugin.h create mode 100644 src/ast/euf/euf_justification.cpp create mode 100644 src/ast/euf/euf_plugin.cpp create mode 100644 src/ast/euf/euf_plugin.h create mode 100644 src/ast/euf/euf_specrel_plugin.cpp create mode 100644 src/ast/euf/euf_specrel_plugin.h create mode 100644 src/sat/smt/specrel_solver.cpp create mode 100644 src/sat/smt/specrel_solver.h create mode 100644 src/test/euf_arith_plugin.cpp create mode 100644 src/test/euf_bv_plugin.cpp diff --git a/src/ast/euf/CMakeLists.txt b/src/ast/euf/CMakeLists.txt index 8d3fa2e74..aa71e7fba 100644 --- a/src/ast/euf/CMakeLists.txt +++ b/src/ast/euf/CMakeLists.txt @@ -1,8 +1,14 @@ z3_add_component(euf SOURCES + euf_ac_plugin.cpp + euf_arith_plugin.cpp + euf_bv_plugin.cpp + euf_egraph.cpp euf_enode.cpp euf_etable.cpp - euf_egraph.cpp + euf_justification.cpp + euf_plugin.cpp + euf_specrel_plugin.cpp COMPONENT_DEPENDENCIES ast util diff --git a/src/ast/euf/euf_ac_plugin.cpp b/src/ast/euf/euf_ac_plugin.cpp new file mode 100644 index 000000000..5b4b1df66 --- /dev/null +++ b/src/ast/euf/euf_ac_plugin.cpp @@ -0,0 +1,1058 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_ac_plugin.cpp + +Abstract: + + plugin structure for ac functions + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +Completion modulo AC + + E set of eqs + pick critical pair xy = z by j1 xu = v by j2 in E + Add new equation zu = xyu = vy by j1, j2 + + + Notes: + - Some equalities come from shared terms, some do not. + + - V2 can use multiplicities of elements to handle larger domains. + - e.g. 3x + 100000y + +More notes: + + Justifications for new equations are joined (requires extension to egraph/justification) + + Process new merges so use list is updated + Justifications for processed merges are recorded + + Updated equations are recorded for restoration on backtracking + + Keep track of foreign / shared occurrences of AC functions. + - use register_shared to accumulate shared occurrences. + + Shared occurrences are rewritten modulo completion. + When equal to a different shared occurrence, propagate equality. + + - Elimination of redundant rules. + -> forward and backward subsumption + - apply forward subsumption when simplifying equality using processed + - apply backward subsumption when simplifying processed and to_simplify + + Rewrite rules are reoriented after a merge of enodes. + It simulates creating a critical pair: + n -> n' + n + k = j + k + after merge + n' + k = j + k, could be that n' + k < j + k < n + k in term ordering because n' < j, m < n + +TODOs: + +- Efficiency of handling shared terms. + - The shared terms hash table is not incremental. + It could be made incremental by updating it on every merge similar to how the egraph handles it. +- V2 using multiplicities instead of repeated values in monomials. +- Squash trail updates when equations or monomials are modified within the same epoque. + - by an epoque counter that can be updated by the egraph class whenever there is a push/pop. + - store the epoque as a tick on equations and possibly when updating monomials on equations. + +--*/ + +#include "ast/euf/euf_ac_plugin.h" +#include "ast/euf/euf_egraph.h" +#include "ast/ast_pp.h" + +namespace euf { + + ac_plugin::ac_plugin(egraph& g, unsigned fid, unsigned op) : + plugin(g), m_fid(fid), m_op(op), + m_dep_manager(get_region()), + m_hash(*this), m_eq(*this), m_monomial_table(m_hash, m_eq) + { + g.set_th_propagates_diseqs(m_fid); + } + + ac_plugin::ac_plugin(egraph& g, func_decl* f) : + plugin(g), m_decl(f), m_fid(f->get_family_id()), + m_dep_manager(get_region()), + m_hash(*this), m_eq(*this), m_monomial_table(m_hash, m_eq) + { + if (m_fid != null_family_id) + g.set_th_propagates_diseqs(m_fid); + } + + void ac_plugin::register_node(enode* n) { + if (is_op(n)) + return; + for (auto arg : enode_args(n)) + if (is_op(arg)) + register_shared(arg); // TODO optimization to avoid registering shared terms twice + } + + void ac_plugin::register_shared(enode* n) { + if (m_shared_nodes.get(n->get_id(), false)) + return; + auto m = to_monomial(n); + auto const& ns = monomial(m); + for (auto arg : ns) { + arg->shared.push_back(m); + m_node_trail.push_back(arg); + push_undo(is_add_shared_index); + } + m_shared_nodes.setx(n->get_id(), true, false); + sort(monomial(m)); + m_shared_todo.insert(m_shared.size()); + m_shared.push_back({ n, m, justification::axiom() }); + push_undo(is_register_shared); + } + + void ac_plugin::undo() { + auto k = m_undo.back(); + m_undo.pop_back(); + switch (k) { + case is_add_eq: { + m_eqs.pop_back(); + break; + } + case is_add_node: { + auto* n = m_node_trail.back(); + m_node_trail.pop_back(); + m_nodes[n->n->get_id()] = nullptr; + n->~node(); + break; + } + case is_add_monomial: { + m_monomials.pop_back(); + break; + } + case is_merge_node: { + auto [other, old_shared, old_eqs] = m_merge_trail.back(); + auto* root = other->root; + std::swap(other->next, root->next); + root->shared.shrink(old_shared); + root->eqs.shrink(old_eqs); + m_merge_trail.pop_back(); + ++m_tick; + break; + } + case is_update_eq: { + auto const& [idx, eq] = m_update_eq_trail.back(); + m_eqs[idx] = eq; + m_update_eq_trail.pop_back(); + break; + } + case is_add_shared_index: { + auto n = m_node_trail.back(); + m_node_trail.pop_back(); + n->shared.pop_back(); + break; + } + case is_add_eq_index: { + auto n = m_node_trail.back(); + m_node_trail.pop_back(); + n->eqs.pop_back(); + break; + } + case is_register_shared: { + auto s = m_shared.back(); + m_shared_nodes[s.n->get_id()] = false; + m_shared.pop_back(); + break; + } + case is_update_shared: { + auto [id, s] = m_update_shared_trail.back(); + m_shared[id] = s; + m_update_shared_trail.pop_back(); + break; + } + default: + UNREACHABLE(); + } + } + + std::ostream& ac_plugin::display_monomial(std::ostream& out, ptr_vector const& m) const { + for (auto n : m) { + if (n->n->num_args() == 0) + out << mk_pp(n->n->get_expr(), g.get_manager()) << " "; + else + out << g.bpp(n->n) << " "; + } + return out; + } + + std::ostream& ac_plugin::display_equation(std::ostream& out, eq const& e) const { + display_status(out, e.status) << " "; + display_monomial(out, monomial(e.l)); + out << "== "; + display_monomial(out, monomial(e.r)); + return out; + } + + std::ostream& ac_plugin::display_status(std::ostream& out, eq_status s) const { + switch (s) { + case eq_status::is_dead: out << "d"; break; + case eq_status::processed: out << "p"; break; + case eq_status::to_simplify: out << "s"; break; + } + return out; + } + + std::ostream& ac_plugin::display(std::ostream& out) const { + unsigned i = 0; + for (auto const& eq : m_eqs) { + out << i << ": " << eq.l << " == " << eq.r << ": "; + display_equation(out, eq); + out << "\n"; + ++i; + } + i = 0; + for (auto m : m_monomials) { + out << i << ": "; + display_monomial(out, m); + out << "\n"; + ++i; + } + for (auto n : m_nodes) { + if (!n) + continue; + if (n->eqs.empty() && n->shared.empty()) + continue; + out << g.bpp(n->n) << " r: " << n->root_id() << " "; + if (!n->eqs.empty()) { + out << "eqs "; + for (auto l : n->eqs) + out << l << " "; + } + if (!n->shared.empty()) { + out << "shared "; + for (auto s : n->shared) + out << s << " "; + } + out << "\n"; + } + return out; + } + + void ac_plugin::merge_eh(enode* l, enode* r) { + if (l == r) + return; + auto j = justification::equality(l, r); + if (!is_op(l) && !is_op(r)) + merge(mk_node(l), mk_node(r), j); + else + init_equation(eq(to_monomial(l), to_monomial(r), j)); + } + + void ac_plugin::diseq_eh(enode* eq) { + SASSERT(g.get_manager().is_eq(eq->get_expr())); + enode* a = eq->get_arg(0), * b = eq->get_arg(1); + a = a->get_closest_th_node(m_fid); + b = b->get_closest_th_node(m_fid); + SASSERT(a && b); + register_shared(a); + register_shared(b); + } + + void ac_plugin::init_equation(eq const& e) { + m_eqs.push_back(e); + auto& eq = m_eqs.back(); + if (orient_equation(eq)) { + + unsigned eq_id = m_eqs.size() - 1; + + for (auto n : monomial(eq.l)) { + if (!n->root->n->is_marked1()) { + n->root->eqs.push_back(eq_id); + n->root->n->mark1(); + push_undo(is_add_eq_index); + m_node_trail.push_back(n->root); + } + } + + for (auto n : monomial(eq.r)) { + if (!n->root->n->is_marked1()) { + n->root->eqs.push_back(eq_id); + n->root->n->mark1(); + push_undo(is_add_eq_index); + m_node_trail.push_back(n->root); + } + } + + for (auto n : monomial(eq.l)) + n->root->n->unmark1(); + + for (auto n : monomial(eq.r)) + n->root->n->unmark1(); + + m_to_simplify_todo.insert(eq_id); + } + else + m_eqs.pop_back(); + } + + bool ac_plugin::orient_equation(eq& e) { + auto& ml = monomial(e.l); + auto& mr = monomial(e.r); + if (ml.size() > mr.size()) + return true; + if (ml.size() < mr.size()) { + std::swap(e.l, e.r); + return true; + } + else { + sort(ml); + sort(mr); + for (unsigned i = ml.size(); i-- > 0;) { + if (ml[i]->root_id() == mr[i]->root_id()) + continue; + if (ml[i]->root_id() < mr[i]->root_id()) + std::swap(e.l, e.r); + return true; + } + return false; + } + } + + void ac_plugin::sort(monomial_t& m) { + std::sort(m.begin(), m.end(), [&](node* a, node* b) { return a->root_id() < b->root_id(); }); + } + + bool ac_plugin::is_sorted(monomial_t const& m) const { + if (m.m_bloom.m_tick == m_tick) + return true; + for (unsigned i = m.size(); i-- > 1; ) + if (m[i - 1]->root_id() > m[i]->root_id()) + return false; + return true; + } + + uint64_t ac_plugin::filter(monomial_t& m) { + auto& bloom = m.m_bloom; + if (bloom.m_tick == m_tick) + return bloom.m_filter; + bloom.m_filter = 0; + for (auto n : m) + bloom.m_filter |= (1ull << (n->root_id() % 64ull)); + if (!is_sorted(m)) + sort(m); + bloom.m_tick = m_tick; + return bloom.m_filter; + } + + bool ac_plugin::can_be_subset(monomial_t& subset, monomial_t& superset) { + if (subset.size() > superset.size()) + return false; + auto f1 = filter(subset); + auto f2 = filter(superset); + return (f1 | f2) == f2; + } + + bool ac_plugin::can_be_subset(monomial_t& subset, ptr_vector const& m, bloom& bloom) { + if (subset.size() > m.size()) + return false; + if (bloom.m_tick != m_tick) { + bloom.m_filter = 0; + for (auto n : m) + bloom.m_filter |= (1ull << (n->root_id() % 64ull)); + bloom.m_tick = m_tick; + } + auto f2 = bloom.m_filter; + return (filter(subset) | f2) == f2; + } + + void ac_plugin::merge(node* root, node* other, justification j) { + for (auto n : equiv(other)) + n->root = root; + m_merge_trail.push_back({ other, root->shared.size(), root->eqs.size() }); + for (auto eq_id : other->eqs) + set_status(eq_id, eq_status::to_simplify); + for (auto m : other->shared) + m_shared_todo.insert(m); + root->shared.append(other->shared); + root->eqs.append(other->eqs); + std::swap(root->next, other->next); + push_undo(is_merge_node); + ++m_tick; + } + + void ac_plugin::push_undo(undo_kind k) { + m_undo.push_back(k); + push_plugin_undo(get_id()); + m_undo_notify(); // tell main plugin to dispatch undo to this module. + } + + unsigned ac_plugin::to_monomial(enode* n) { + enode_vector& ns = m_todo; + ns.reset(); + ptr_vector m; + ns.push_back(n); + for (unsigned i = 0; i < ns.size(); ++i) { + n = ns[i]; + if (is_op(n)) + ns.append(n->num_args(), n->args()); + else + m.push_back(mk_node(n)); + } + return to_monomial(n, m); + } + + unsigned ac_plugin::to_monomial(enode* e, ptr_vector const& ms) { + unsigned id = m_monomials.size(); + m_monomials.push_back({ ms, bloom() }); + push_undo(is_add_monomial); + return id; + } + + ac_plugin::node* ac_plugin::node::mk(region& r, enode* n) { + auto* mem = r.allocate(sizeof(node)); + node* res = new (mem) node(); + res->n = n; + res->root = res; + res->next = res; + return res; + } + + ac_plugin::node* ac_plugin::mk_node(enode* n) { + unsigned id = n->get_id(); + if (m_nodes.size() > id && m_nodes[id]) + return m_nodes[id]; + auto* r = node::mk(get_region(), n); + push_undo(is_add_node); + m_nodes.setx(id, r, nullptr); + m_node_trail.push_back(r); + return r; + } + + void ac_plugin::propagate() { + while (true) { + loop_start: + unsigned eq_id = pick_next_eq(); + if (eq_id == UINT_MAX) + break; + + TRACE("plugin", tout << "propagate " << eq_id << ": " << eq_pp(*this, m_eqs[eq_id]) << "\n"); + + // simplify eq using processed + for (auto other_eq : backward_iterator(eq_id)) + TRACE("plugin", tout << "backward iterator " << eq_id << " vs " << other_eq << " " << is_processed(other_eq) << "\n"); + for (auto other_eq : backward_iterator(eq_id)) + if (is_processed(other_eq) && backward_simplify(eq_id, other_eq)) + goto loop_start; + + set_status(eq_id, eq_status::processed); + + // simplify processed using eq + for (auto other_eq : forward_iterator(eq_id)) + if (is_processed(other_eq)) + forward_simplify(eq_id, other_eq); + + // superpose, create new equations + for (auto other_eq : superpose_iterator(eq_id)) + if (is_processed(other_eq)) + superpose(eq_id, other_eq); + + // simplify to_simplify using eq + for (auto other_eq : forward_iterator(eq_id)) + if (is_to_simplify(other_eq)) + forward_simplify(eq_id, other_eq); + } + propagate_shared(); + + CTRACE("plugin", !m_shared.empty() || !m_eqs.empty(), display(tout)); + } + + unsigned ac_plugin::pick_next_eq() { + while (!m_to_simplify_todo.empty()) { + unsigned id = *m_to_simplify_todo.begin(); + if (id < m_eqs.size() && is_to_simplify(id)) + return id; + m_to_simplify_todo.remove(id); + } + return UINT_MAX; + } + + // reorient equations when the status of equations are set to to_simplify. + void ac_plugin::set_status(unsigned id, eq_status s) { + auto& eq = m_eqs[id]; + if (eq.status == eq_status::is_dead) + return; + if (s == eq_status::to_simplify && are_equal(monomial(eq.l), monomial(eq.r))) + s = eq_status::is_dead; + + if (eq.status != s) { + m_update_eq_trail.push_back({ id, eq }); + eq.status = s; + push_undo(is_update_eq); + } + switch (s) { + case eq_status::processed: + case eq_status::is_dead: + m_to_simplify_todo.remove(id); + break; + case eq_status::to_simplify: + m_to_simplify_todo.insert(id); + orient_equation(eq); + break; + } + } + + // + // superpose iterator enumerates all equations where lhs of eq have element in common. + // + unsigned_vector const& ac_plugin::superpose_iterator(unsigned eq_id) { + auto const& eq = m_eqs[eq_id]; + m_src_r.reset(); + m_src_r.append(monomial(eq.r).m_nodes); + init_ref_counts(monomial(eq.l), m_src_l_counts); + init_overlap_iterator(eq_id, monomial(eq.l)); + return m_eq_occurs; + } + + // + // backward iterator allows simplification of eq + // The rhs of eq is a super-set of lhs of other eq. + // + unsigned_vector const& ac_plugin::backward_iterator(unsigned eq_id) { + auto const& eq = m_eqs[eq_id]; + init_ref_counts(monomial(eq.r), m_dst_r_counts); + init_ref_counts(monomial(eq.l), m_dst_l_counts); + m_dst_r.reset(); + m_dst_r.append(monomial(eq.r).m_nodes); + init_subset_iterator(eq_id, monomial(eq.r)); + return m_eq_occurs; + } + + void ac_plugin::init_overlap_iterator(unsigned eq_id, monomial_t const& m) { + m_eq_occurs.reset(); + for (auto n : m) + m_eq_occurs.append(n->root->eqs); + compress_eq_occurs(eq_id); + } + + // + // add all but one of the use lists. Identify the largest use list and skip it. + // The rationale is that [a, b] is a subset of [a, b, c, d, e] if + // it has at least two elements (otherwise it would not apply as a rewrite over AC). + // then one of the two elements has to be in the set of [a, b, c, d, e] \ { x } + // where x is an arbitrary value from a, b, c, d, e. Not a two-element watch list, but still. + // + void ac_plugin::init_subset_iterator(unsigned eq_id, monomial_t const& m) { + unsigned max_use = 0; + node* max_n = nullptr; + bool has_two = false; + for (auto n : m) + if (n->root->eqs.size() >= max_use) + has_two |= max_n && (max_n != n->root), max_n = n->root, max_use = n->root->eqs.size(); + m_eq_occurs.reset(); + if (has_two) { + for (auto n : m) + if (n->root != max_n) + m_eq_occurs.append(n->root->eqs); + } + else { + for (auto n : m) { + m_eq_occurs.append(n->root->eqs); + break; + } + } + compress_eq_occurs(eq_id); + } + + // prune m_eq_occurs to single occurrences + void ac_plugin::compress_eq_occurs(unsigned eq_id) { + unsigned j = 0; + m_eq_seen.reserve(m_eqs.size() + 1, false); + for (unsigned i = 0; i < m_eq_occurs.size(); ++i) { + unsigned id = m_eq_occurs[i]; + if (m_eq_seen[id]) + continue; + if (id == eq_id) + continue; + m_eq_occurs[j++] = id; + m_eq_seen[id] = true; + } + m_eq_occurs.shrink(j); + for (auto id : m_eq_occurs) + m_eq_seen[id] = false; + } + + // + // forward iterator simplifies other eqs where their rhs is a superset of lhs of eq + // + unsigned_vector const& ac_plugin::forward_iterator(unsigned eq_id) { + auto& eq = m_eqs[eq_id]; + m_src_r.reset(); + m_src_r.append(monomial(eq.r).m_nodes); + init_ref_counts(monomial(eq.l), m_src_l_counts); + init_ref_counts(monomial(eq.r), m_src_r_counts); + unsigned min_r = UINT_MAX; + node* min_n = nullptr; + for (auto n : monomial(eq.l)) + if (n->root->eqs.size() < min_r) + min_n = n, min_r = n->root->eqs.size(); + // found node that occurs in fewest eqs + VERIFY(min_n); + return min_n->eqs; + } + + void ac_plugin::init_ref_counts(monomial_t const& monomial, ref_counts& counts) const { + init_ref_counts(monomial.m_nodes, counts); + } + + void ac_plugin::init_ref_counts(ptr_vector const& monomial, ref_counts& counts) const { + counts.reset(); + for (auto n : monomial) + counts.inc(n->root_id(), 1); + } + + bool ac_plugin::is_correct_ref_count(monomial_t const& m, ref_counts const& counts) const { + return is_correct_ref_count(m.m_nodes, counts); + } + + bool ac_plugin::is_correct_ref_count(ptr_vector const& m, ref_counts const& counts) const { + ref_counts check; + init_ref_counts(m, check); + return + all_of(counts, [&](unsigned i) { return check[i] == counts[i]; }) && + all_of(check, [&](unsigned i) { return check[i] == counts[i]; }); + } + + void ac_plugin::forward_simplify(unsigned src_eq, unsigned dst_eq) { + + if (src_eq == dst_eq) + return; + + // check that left src.l is a subset of dst.r + // dst = A -> BC + // src = B -> D + // post(dst) := A -> CD + auto& src = m_eqs[src_eq]; // src_r_counts, src_l_counts are initialized + auto& dst = m_eqs[dst_eq]; + + TRACE("plugin", tout << "forward simplify " << eq_pp(*this, src) << " " << eq_pp(*this, dst) << "\n"); + + + if (forward_subsumes(src_eq, dst_eq)) { + TRACE("plugin", tout << "forward subsumed\n"); + set_status(dst_eq, eq_status::is_dead); + return; + } + + if (!can_be_subset(monomial(src.l), monomial(dst.r))) + return; + + + m_dst_r_counts.reset(); + + unsigned src_l_size = monomial(src.l).size(); + unsigned src_r_size = m_src_r.size(); + + SASSERT(is_correct_ref_count(monomial(src.l), m_src_l_counts)); + // subtract src.l from dst.r if src.l is a subset of dst.r + // dst_rhs := dst_rhs - src_lhs + src_rhs + // := src_rhs + (dst_rhs - src_lhs) + // := src_rhs + elements from dst_rhs that are in excess of src_lhs + unsigned num_overlap = 0; + for (auto n : monomial(dst.r)) { + unsigned id = n->root_id(); + unsigned dst_count = m_dst_r_counts[id]; + unsigned src_count = m_src_l_counts[id]; + if (dst_count > src_count) { + m_src_r.push_back(n); + m_dst_r_counts.dec(id, 1); + } + else if (dst_count < src_count) { + m_src_r.shrink(src_r_size); + return; + } + else + ++num_overlap; + } + // The dst.r has to be a superset of src.l, otherwise simplification does not apply + if (num_overlap != src_l_size) { + m_src_r.shrink(src_r_size); + return; + } + auto j = justify_rewrite(src_eq, dst_eq); + reduce(m_src_r, j); + auto new_r = to_monomial(m_src_r); + index_new_r(dst_eq, monomial(m_eqs[dst_eq].r), monomial(new_r)); + m_update_eq_trail.push_back({ dst_eq, m_eqs[dst_eq] }); + m_eqs[dst_eq].r = new_r; + m_eqs[dst_eq].j = j; + push_undo(is_update_eq); + m_src_r.reset(); + m_src_r.append(monomial(src.r).m_nodes); + TRACE("plugin", tout << "rewritten to " << m_pp(*this, monomial(new_r)) << "\n"); + } + + bool ac_plugin::backward_simplify(unsigned dst_eq, unsigned src_eq) { + if (src_eq == dst_eq) + return false; + + auto& src = m_eqs[src_eq]; + auto& dst = m_eqs[dst_eq]; // pre-computed dst_r_counts, dst_l_counts + // + // dst_ids, dst_count contain rhs of dst_eq + // + TRACE("plugin", tout << "backward simplify " << eq_pp(*this, src) << " " << eq_pp(*this, dst) << " can-be-subset: " << can_be_subset(monomial(src.l), monomial(dst.r)) << "\n"); + + if (backward_subsumes(src_eq, dst_eq)) { + TRACE("plugin", tout << "backward subsumed\n"); + set_status(dst_eq, eq_status::is_dead); + return true; + } + // check that src.l is a subset of dst.r + if (!can_be_subset(monomial(src.l), monomial(dst.r))) + return false; + if (!is_subset(m_dst_r_counts, m_src_l_counts, monomial(src.l))) { + TRACE("plugin", tout << "not subset\n"); + return false; + } + + SASSERT(is_correct_ref_count(monomial(dst.r), m_dst_r_counts)); + + ptr_vector m(m_dst_r); + init_ref_counts(monomial(src.l), m_src_l_counts); + + rewrite1(m_src_l_counts, monomial(src.r), m_dst_r_counts, m); + auto j = justify_rewrite(src_eq, dst_eq); + reduce(m, j); + auto new_r = to_monomial(m); + index_new_r(dst_eq, monomial(m_eqs[dst_eq].r), monomial(new_r)); + m_update_eq_trail.push_back({ dst_eq, m_eqs[dst_eq] }); + m_eqs[dst_eq].r = new_r; + m_eqs[dst_eq].j = j; + TRACE("plugin", tout << "rewritten to " << m_pp(*this, monomial(new_r)) << "\n"); + push_undo(is_update_eq); + return true; + } + + // dst_eq is fixed, dst_l_count is pre-computed for monomial(dst.l) + // dst_r_counts is pre-computed for monomial(dst.r). + // is dst_eq subsumed by src_eq? + bool ac_plugin::backward_subsumes(unsigned src_eq, unsigned dst_eq) { + auto& src = m_eqs[src_eq]; + auto& dst = m_eqs[dst_eq]; + SASSERT(is_correct_ref_count(monomial(dst.l), m_dst_l_counts)); + SASSERT(is_correct_ref_count(monomial(dst.r), m_dst_r_counts)); + if (!can_be_subset(monomial(src.l), monomial(dst.l))) + return false; + if (!can_be_subset(monomial(src.r), monomial(dst.r))) + return false; + unsigned size_diff = monomial(dst.l).size() - monomial(src.l).size(); + if (size_diff != monomial(dst.r).size() - monomial(src.r).size()) + return false; + if (!is_subset(m_dst_l_counts, m_src_l_counts, monomial(src.l))) + return false; + if (!is_subset(m_dst_r_counts, m_src_r_counts, monomial(src.r))) + return false; + SASSERT(is_correct_ref_count(monomial(src.l), m_src_l_counts)); + SASSERT(is_correct_ref_count(monomial(src.r), m_src_r_counts)); + // add difference betwen dst.l and src.l to both src.l, src.r + for (auto n : monomial(dst.l)) { + unsigned id = n->root_id(); + SASSERT(m_dst_l_counts[id] >= m_src_l_counts[id]); + unsigned diff = m_dst_l_counts[id] - m_src_l_counts[id]; + if (diff > 0) { + m_src_l_counts.inc(id, diff); + m_src_r_counts.inc(id, diff); + } + } + // now dst.r and src.r should align and have the same elements. + // since src.r is a subset of dst.r we iterate over dst.r + return all_of(monomial(dst.r), [&](node* n) { unsigned id = n->root_id(); return m_src_r_counts[id] == m_dst_r_counts[id]; }); + } + + // src_l_counts, src_r_counts are initialized for src.l, src.r + bool ac_plugin::forward_subsumes(unsigned src_eq, unsigned dst_eq) { + auto& src = m_eqs[src_eq]; + auto& dst = m_eqs[dst_eq]; + SASSERT(is_correct_ref_count(monomial(src.l), m_src_l_counts)); + SASSERT(is_correct_ref_count(monomial(src.r), m_src_r_counts)); + if (!can_be_subset(monomial(src.l), monomial(dst.l))) + return false; + if (!can_be_subset(monomial(src.r), monomial(dst.r))) + return false; + unsigned size_diff = monomial(dst.l).size() - monomial(src.l).size(); + if (size_diff != monomial(dst.r).size() - monomial(src.r).size()) + return false; + if (!is_superset(m_src_l_counts, m_dst_l_counts, monomial(dst.l))) + return false; + if (!is_superset(m_src_r_counts, m_dst_r_counts, monomial(dst.r))) + return false; + SASSERT(is_correct_ref_count(monomial(dst.l), m_dst_l_counts)); + SASSERT(is_correct_ref_count(monomial(dst.r), m_dst_r_counts)); + for (auto n : monomial(src.l)) { + unsigned id = n->root_id(); + SASSERT(m_src_l_counts[id] <= m_dst_l_counts[id]); + unsigned diff = m_dst_l_counts[id] - m_src_l_counts[id]; + if (diff == 0) + continue; + m_dst_l_counts.dec(id, diff); + if (m_dst_r_counts[id] < diff) + return false; + m_dst_r_counts.dec(id, diff); + } + + return all_of(monomial(dst.r), [&](node* n) { unsigned id = n->root_id(); return m_src_r_counts[id] == m_dst_r_counts[id]; }); + } + + void ac_plugin::rewrite1(ref_counts const& src_l, monomial_t const& src_r, ref_counts& dst_counts, ptr_vector& dst) { + // pre-condition: is-subset is invoked so that src_l is initialized. + // pre-condition: dst_count is also initialized. + // remove from dst elements that are in src_l + // add elements from src_r + SASSERT(is_correct_ref_count(dst, dst_counts)); + SASSERT(&src_r.m_nodes != &dst); + unsigned sz = dst.size(), j = 0; + for (unsigned i = 0; i < sz; ++i) { + auto* n = dst[i]; + unsigned id = n->root_id(); + unsigned dst_count = dst_counts[id]; + unsigned src_count = src_l[id]; + SASSERT(dst_count > 0); + if (src_count == 0) + dst[j++] = n; + else if (src_count < dst_count) { + dst[j++] = n; + dst_counts.dec(id, 1); + } + } + dst.shrink(j); + dst.append(src_r.m_nodes); + } + + // rewrite monomial to normal form. + bool ac_plugin::reduce(ptr_vector& m, justification& j) { + bool change = false; + do { + init_loop: + if (m.size() == 1) + return change; + bloom b; + init_ref_counts(m, m_m_counts); + for (auto n : m) { + for (auto eq : n->root->eqs) { + if (!is_processed(eq)) + continue; + auto& src = m_eqs[eq]; + + if (!can_be_subset(monomial(src.l), m, b)) + continue; + if (!is_subset(m_m_counts, m_eq_counts, monomial(src.l))) + continue; + TRACE("plugin", display_equation(tout << "reduce ", src) << "\n"); + SASSERT(is_correct_ref_count(monomial(src.l), m_eq_counts)); + rewrite1(m_eq_counts, monomial(src.r), m_m_counts, m); + j = join(j, eq); + change = true; + goto init_loop; + } + } + } + while (false); + return change; + } + + // check that src is a subset of dst, where dst_counts are precomputed + bool ac_plugin::is_subset(ref_counts const& dst_counts, ref_counts& src_counts, monomial_t const& src) { + SASSERT(&dst_counts != &src_counts); + init_ref_counts(src, src_counts); + return all_of(src_counts, [&](unsigned idx) { return src_counts[idx] <= dst_counts[idx]; }); + } + + // check that dst is a superset of src, where src_counts are precomputed + bool ac_plugin::is_superset(ref_counts const& src_counts, ref_counts& dst_counts, monomial_t const& dst) { + SASSERT(&dst_counts != &src_counts); + init_ref_counts(dst, dst_counts); + return all_of(src_counts, [&](unsigned idx) { return src_counts[idx] <= dst_counts[idx]; }); + } + + void ac_plugin::index_new_r(unsigned eq, monomial_t const& old_r, monomial_t const& new_r) { + for (auto n : old_r) + n->root->n->mark1(); + for (auto n : new_r) + if (!n->root->n->is_marked1()) { + n->root->eqs.push_back(eq); + m_node_trail.push_back(n->root); + n->root->n->mark1(); + push_undo(is_add_eq_index); + } + for (auto n : old_r) + n->root->n->unmark1(); + for (auto n : new_r) + n->root->n->unmark1(); + } + + + void ac_plugin::superpose(unsigned src_eq, unsigned dst_eq) { + if (src_eq == dst_eq) + return; + auto& src = m_eqs[src_eq]; + auto& dst = m_eqs[dst_eq]; + + TRACE("plugin", tout << "superpose: "; display_equation(tout, src); tout << " "; display_equation(tout, dst); tout << "\n";); + // AB -> C, AD -> E => BE ~ CD + // m_src_ids, m_src_counts contains information about src (call it AD -> E) + m_dst_l_counts.reset(); + + m_dst_r.reset(); + m_dst_r.append(monomial(dst.r).m_nodes); + unsigned src_r_size = m_src_r.size(); + unsigned dst_r_size = m_dst_r.size(); + SASSERT(src_r_size == monomial(src.r).size()); + // dst_r contains C + // src_r contains E + + // compute BE, initialize dst_ids, dst_counts + bool overlap = false; + for (auto n : monomial(dst.l)) { + unsigned id = n->root_id(); + m_dst_l_counts.inc(id, 1); + if (m_src_l_counts[id] < m_dst_l_counts[id]) + m_src_r.push_back(n); + overlap |= m_src_l_counts[id] > 0; + } + + if (!overlap) { + m_src_r.shrink(src_r_size); + return; + } + + // compute CD + for (auto n : monomial(src.l)) { + unsigned id = n->root_id(); + if (m_dst_l_counts[id] > 0) + m_dst_l_counts.dec(id, 1); + else + m_dst_r.push_back(n); + } + + if (are_equal(m_src_r, m_dst_r)) { + m_src_r.shrink(src_r_size); + return; + } + + TRACE("plugin", tout << m_pp(*this, m_src_r) << "== " << m_pp(*this, m_dst_r) << "\n";); + + justification j = justify_rewrite(src_eq, dst_eq); + reduce(m_dst_r, j); + reduce(m_src_r, j); + if (m_src_r.size() == 1 && m_dst_r.size() == 1) + push_merge(m_src_r[0]->n, m_dst_r[0]->n, j); + else + init_equation(eq(to_monomial(m_src_r), to_monomial(m_dst_r), j)); + + m_src_r.reset(); + m_src_r.append(monomial(src.r).m_nodes); + } + + bool ac_plugin::are_equal(monomial_t& a, monomial_t& b) { + return filter(a) == filter(b) && are_equal(a.m_nodes, b.m_nodes); + } + + bool ac_plugin::are_equal(ptr_vector const& a, ptr_vector const& b) { + if (a.size() != b.size()) + return false; + m_eq_counts.reset(); + for (auto n : a) + m_eq_counts.inc(n->root_id(), 1); + + for (auto n : b) { + unsigned id = n->root_id(); + if (m_eq_counts[id] == 0) + return false; + m_eq_counts.dec(id, 1); + } + return true; + } + + // + // simple version based on propagating all shared + // todo: version touching only newly processed shared, and maintaining incremental data-structures. + // - hash-tables for shared monomials similar to the ones used for euf_table. + // the tables have to be updated (and re-sorted) whenever a child changes root. + // + + void ac_plugin::propagate_shared() { + if (m_shared_todo.empty()) + return; + while (!m_shared_todo.empty()) { + auto idx = *m_shared_todo.begin(); + m_shared_todo.remove(idx); + if (idx < m_shared.size()) + simplify_shared(idx, m_shared[idx]); + } + m_monomial_table.reset(); + for (auto const& s1 : m_shared) { + shared s2; + TRACE("plugin", tout << "shared " << m_pp(*this, monomial(s1.m)) << "\n"); + if (!m_monomial_table.find(s1.m, s2)) + m_monomial_table.insert(s1.m, s1); + else if (s2.n->get_root() != s1.n->get_root()) { + TRACE("plugin", tout << m_pp(*this, monomial(s1.m)) << " == " << m_pp(*this, monomial(s2.m)) << "\n"); + push_merge(s1.n, s2.n, justification::dependent(m_dep_manager.mk_join(m_dep_manager.mk_leaf(s1.j), m_dep_manager.mk_leaf(s2.j)))); + } + } + } + + void ac_plugin::simplify_shared(unsigned idx, shared s) { + auto j = s.j; + auto old_m = s.m; + ptr_vector m1(monomial(old_m).m_nodes); + TRACE("plugin", tout << "simplify " << m_pp(*this, monomial(old_m)) << "\n"); + if (!reduce(m1, j)) + return; + + auto new_m = to_monomial(m1); + // update shared occurrences for members of the new monomial that are not already in the old monomial. + for (auto n : monomial(old_m)) + n->root->n->mark1(); + for (auto n : m1) + if (!n->root->n->is_marked1()) { + n->root->shared.push_back(idx); + m_shared_todo.insert(idx); + m_node_trail.push_back(n->root); + push_undo(is_add_shared_index); + } + for (auto n : monomial(old_m)) + n->root->n->unmark1(); + m_update_shared_trail.push_back({ idx, s }); + push_undo(is_update_shared); + m_shared[idx].m = new_m; + m_shared[idx].j = j; + } + + justification ac_plugin::justify_rewrite(unsigned eq1, unsigned eq2) { + auto* j = m_dep_manager.mk_join(justify_equation(eq1), justify_equation(eq2)); + return justification::dependent(j); + } + + justification::dependency* ac_plugin::justify_equation(unsigned eq) { + auto const& e = m_eqs[eq]; + auto* j = m_dep_manager.mk_leaf(e.j); + j = justify_monomial(j, monomial(e.l)); + j = justify_monomial(j, monomial(e.r)); + return j; + } + + justification::dependency* ac_plugin::justify_monomial(justification::dependency* j, monomial_t const& m) { + for (auto n : m) + if (n->root->n != n->n) + j = m_dep_manager.mk_join(j, m_dep_manager.mk_leaf(justification::equality(n->root->n, n->n))); + return j; + } + + justification ac_plugin::join(justification j, unsigned eq) { + return justification::dependent(m_dep_manager.mk_join(m_dep_manager.mk_leaf(j), justify_equation(eq))); + } + +} diff --git a/src/ast/euf/euf_ac_plugin.h b/src/ast/euf/euf_ac_plugin.h new file mode 100644 index 000000000..ea444b60c --- /dev/null +++ b/src/ast/euf/euf_ac_plugin.h @@ -0,0 +1,309 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_ac_plugin.h + +Abstract: + + plugin structure for ac functions + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +ex: +xyz -> xy, then xyzz -> xy by repeated rewriting + +monomials = [0 |-> xyz, 1 |-> xy, 2 |-> xyzz] +parents(x) = [0, 1, 2] +parents(z) = [0, 1] +for p in parents(xyzz): + p != xyzz + p' := simplify_using(xyzz, p) + if p != p': + repeat reduction using p := p' + +--*/ + +#pragma once + +#include +#include "ast/euf/euf_plugin.h" + +namespace euf { + + class ac_plugin : public plugin { + + // enode structure for AC equivalences + struct node { + enode* n; // associated enode + node* root; // path compressed root + node* next; // next in equaivalence class + justification j; // justification for equality + node* target = nullptr; // justified next + unsigned_vector shared; // shared occurrences + unsigned_vector eqs; // equality occurrences + + unsigned root_id() const { return root->n->get_id(); } + ~node() {} + static node* mk(region& r, enode* n); + }; + + class equiv { + node& n; + public: + class iterator { + node* m_first; + node* m_last; + public: + iterator(node* n, node* m) : m_first(n), m_last(m) {} + node* operator*() { return m_first; } + iterator& operator++() { if (!m_last) m_last = m_first; m_first = m_first->next; return *this; } + iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } + bool operator==(iterator const& other) const { return m_last == other.m_last && m_first == other.m_first; } + bool operator!=(iterator const& other) const { return !(*this == other); } + }; + equiv(node& _n) :n(_n) {} + equiv(node* _n) :n(*_n) {} + iterator begin() const { return iterator(&n, nullptr); } + iterator end() const { return iterator(&n, &n); } + }; + + struct bloom { + uint64_t m_tick = 0; + uint64_t m_filter = 0; + }; + + enum eq_status { + processed, to_simplify, is_dead + }; + + // represent equalities added by merge_eh and by superposition + struct eq { + eq(unsigned l, unsigned r, justification j): + l(l), r(r), j(j) {} + unsigned l, r; // refer to monomials + eq_status status = to_simplify; + justification j; // justification for equality + }; + + // represent shared enodes that use the AC symbol. + struct shared { + enode* n; // original shared enode + unsigned m; // monomial index + justification j; // justification for current simplification of monomial + }; + + struct monomial_t { + ptr_vector m_nodes; + bloom m_bloom; + node* operator[](unsigned i) const { return m_nodes[i]; } + unsigned size() const { return m_nodes.size(); } + void set(ptr_vector const& ns) { m_nodes.reset(); m_nodes.append(ns); m_bloom.m_tick = 0; } + node* const* begin() const { return m_nodes.begin(); } + node* const* end() const { return m_nodes.end(); } + node* * begin() { return m_nodes.begin(); } + node* * end() { return m_nodes.end(); } + }; + + + struct monomial_hash { + ac_plugin& p; + monomial_hash(ac_plugin& p) :p(p) {} + unsigned operator()(unsigned i) const { + unsigned h = 0; + auto& m = p.monomial(i); + if (!p.is_sorted(m)) + p.sort(m); + for (auto* n : m) + h = combine_hash(h, n->root_id()); + return h; + } + }; + + struct monomial_eq { + ac_plugin& p; + monomial_eq(ac_plugin& p) :p(p) {} + bool operator()(unsigned i, unsigned j) const { + auto const& m1 = p.monomial(i); + auto const& m2 = p.monomial(j); + if (m1.size() != m2.size()) return false; + for (unsigned k = 0; k < m1.size(); ++k) + if (m1[k]->root_id() != m2[k]->root_id()) + return false; + return true; + } + }; + + unsigned m_fid = 0; + unsigned m_op = null_decl_kind; + func_decl* m_decl = nullptr; + vector m_eqs; + ptr_vector m_nodes; + bool_vector m_shared_nodes; + vector m_monomials; + svector m_shared; + justification::dependency_manager m_dep_manager; + tracked_uint_set m_to_simplify_todo; + tracked_uint_set m_shared_todo; + uint64_t m_tick = 1; + + + + monomial_hash m_hash; + monomial_eq m_eq; + map m_monomial_table; + + + // backtrackable state + enum undo_kind { + is_add_eq, + is_add_monomial, + is_add_node, + is_merge_node, + is_update_eq, + is_add_shared_index, + is_add_eq_index, + is_register_shared, + is_update_shared + }; + svector m_undo; + ptr_vector m_node_trail; + + svector> m_update_shared_trail; + svector> m_merge_trail; + svector> m_update_eq_trail; + + + + node* mk_node(enode* n); + void merge(node* r1, node* r2, justification j); + + bool is_op(enode* n) const { auto d = n->get_decl(); return d && (d == m_decl || (m_fid == d->get_family_id() && m_op == d->get_decl_kind())); } + + std::function m_undo_notify; + void push_undo(undo_kind k); + enode_vector m_todo; + unsigned to_monomial(enode* n); + unsigned to_monomial(enode* n, ptr_vector const& ms); + unsigned to_monomial(ptr_vector const& ms) { return to_monomial(nullptr, ms); } + monomial_t const& monomial(unsigned i) const { return m_monomials[i]; } + monomial_t& monomial(unsigned i) { return m_monomials[i]; } + void sort(monomial_t& monomial); + bool is_sorted(monomial_t const& monomial) const; + uint64_t filter(monomial_t& m); + bool can_be_subset(monomial_t& subset, monomial_t& superset); + bool can_be_subset(monomial_t& subset, ptr_vector const& m, bloom& b); + bool are_equal(ptr_vector const& a, ptr_vector const& b); + bool are_equal(monomial_t& a, monomial_t& b); + bool backward_subsumes(unsigned src_eq, unsigned dst_eq); + bool forward_subsumes(unsigned src_eq, unsigned dst_eq); + + void init_equation(eq const& e); + bool orient_equation(eq& e); + void set_status(unsigned eq_id, eq_status s); + unsigned pick_next_eq(); + + void forward_simplify(unsigned eq_id, unsigned using_eq); + bool backward_simplify(unsigned eq_id, unsigned using_eq); + void superpose(unsigned src_eq, unsigned dst_eq); + + ptr_vector m_src_r, m_src_l, m_dst_r, m_dst_l; + + struct ref_counts { + unsigned_vector ids; + unsigned_vector counts; + void reset() { for (auto idx : ids) counts[idx] = 0; ids.reset(); } + unsigned operator[](unsigned idx) const { return counts.get(idx, 0); } + void inc(unsigned idx, unsigned amount) { counts.reserve(idx + 1, 0); ids.push_back(idx); counts[idx] += amount; } + void dec(unsigned idx, unsigned amount) { counts.reserve(idx + 1, 0); ids.push_back(idx); counts[idx] -= amount; } + unsigned const* begin() const { return ids.begin(); } + unsigned const* end() const { return ids.end(); } + }; + ref_counts m_src_l_counts, m_dst_l_counts, m_src_r_counts, m_dst_r_counts, m_eq_counts, m_m_counts; + unsigned_vector m_eq_occurs; + bool_vector m_eq_seen; + + unsigned_vector const& forward_iterator(unsigned eq); + unsigned_vector const& superpose_iterator(unsigned eq); + unsigned_vector const& backward_iterator(unsigned eq); + void init_ref_counts(monomial_t const& monomial, ref_counts& counts) const; + void init_ref_counts(ptr_vector const& monomial, ref_counts& counts) const; + void init_overlap_iterator(unsigned eq, monomial_t const& m); + void init_subset_iterator(unsigned eq, monomial_t const& m); + void compress_eq_occurs(unsigned eq_id); + // check that src is a subset of dst, where dst_counts are precomputed + bool is_subset(ref_counts const& dst_counts, ref_counts& src_counts, monomial_t const& src); + + // check that dst is a superset of dst, where src_counts are precomputed + bool is_superset(ref_counts const& src_counts, ref_counts& dst_counts, monomial_t const& dst); + void rewrite1(ref_counts const& src_l, monomial_t const& src_r, ref_counts& dst_r_counts, ptr_vector& dst_r); + bool reduce(ptr_vector& m, justification& j); + void index_new_r(unsigned eq, monomial_t const& old_r, monomial_t const& new_r); + + bool is_to_simplify(unsigned eq) const { return m_eqs[eq].status == eq_status::to_simplify; } + bool is_processed(unsigned eq) const { return m_eqs[eq].status == eq_status::processed; } + bool is_alive(unsigned eq) const { return m_eqs[eq].status != eq_status::is_dead; } + + justification justify_rewrite(unsigned eq1, unsigned eq2); + justification::dependency* justify_equation(unsigned eq); + justification::dependency* justify_monomial(justification::dependency* d, monomial_t const& m); + justification join(justification j1, unsigned eq); + + bool is_correct_ref_count(monomial_t const& m, ref_counts const& counts) const; + bool is_correct_ref_count(ptr_vector const& m, ref_counts const& counts) const; + + void register_shared(enode* n); + void propagate_shared(); + void simplify_shared(unsigned idx, shared s); + + std::ostream& display_monomial(std::ostream& out, monomial_t const& m) const { return display_monomial(out, m.m_nodes); } + std::ostream& display_monomial(std::ostream& out, ptr_vector const& m) const; + std::ostream& display_equation(std::ostream& out, eq const& e) const; + std::ostream& display_status(std::ostream& out, eq_status s) const; + + + public: + + ac_plugin(egraph& g, unsigned fid, unsigned op); + + ac_plugin(egraph& g, func_decl* f); + + ~ac_plugin() override {} + + unsigned get_id() const override { return m_fid; } + + void register_node(enode* n) override; + + void merge_eh(enode* n1, enode* n2) override; + + void diseq_eh(enode* eq) override; + + void undo() override; + + void propagate() override; + + std::ostream& display(std::ostream& out) const override; + + void set_undo(std::function u) { m_undo_notify = u; } + + struct eq_pp { + ac_plugin& p; eq const& e; + eq_pp(ac_plugin& p, eq const& e) : p(p), e(e) {}; + eq_pp(ac_plugin& p, unsigned eq_id): p(p), e(p.m_eqs[eq_id]) {} + std::ostream& display(std::ostream& out) const { return p.display_equation(out, e); } + }; + + struct m_pp { + ac_plugin& p; ptr_vector const& m; + m_pp(ac_plugin& p, monomial_t const& m) : p(p), m(m.m_nodes) {} + m_pp(ac_plugin& p, ptr_vector const& m) : p(p), m(m) {} + std::ostream& display(std::ostream& out) const { return p.display_monomial(out, m); } + }; + }; + + inline std::ostream& operator<<(std::ostream& out, ac_plugin::eq_pp const& d) { return d.display(out); } + inline std::ostream& operator<<(std::ostream& out, ac_plugin::m_pp const& d) { return d.display(out); } +} diff --git a/src/ast/euf/euf_arith_plugin.cpp b/src/ast/euf/euf_arith_plugin.cpp new file mode 100644 index 000000000..26f8e0bd9 --- /dev/null +++ b/src/ast/euf/euf_arith_plugin.cpp @@ -0,0 +1,71 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_arith_plugin.cpp + +Abstract: + + plugin structure for arithetic + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +--*/ + +#include "ast/euf/euf_arith_plugin.h" +#include "ast/euf/euf_egraph.h" +#include + +namespace euf { + + arith_plugin::arith_plugin(egraph& g) : + plugin(g), + a(g.get_manager()), + m_add(g, get_id(), OP_ADD), + m_mul(g, get_id(), OP_MUL) { + std::function uadd = [&]() { m_undo.push_back(undo_t::undo_add); }; + m_add.set_undo(uadd); + std::function umul = [&]() { m_undo.push_back(undo_t::undo_mul); }; + m_mul.set_undo(umul); + } + + void arith_plugin::register_node(enode* n) { + // no-op + } + + void arith_plugin::merge_eh(enode* n1, enode* n2) { + m_add.merge_eh(n1, n2); + m_mul.merge_eh(n1, n2); + } + + void arith_plugin::propagate() { + m_add.propagate(); + m_mul.propagate(); + } + + void arith_plugin::undo() { + auto k = m_undo.back(); + m_undo.pop_back(); + switch (k) { + case undo_t::undo_add: + m_add.undo(); + break; + case undo_t::undo_mul: + m_mul.undo(); + break; + default: + UNREACHABLE(); + } + } + + std::ostream& arith_plugin::display(std::ostream& out) const { + out << "add\n"; + m_add.display(out); + out << "mul\n"; + m_mul.display(out); + return out; + } +} diff --git a/src/ast/euf/euf_arith_plugin.h b/src/ast/euf/euf_arith_plugin.h new file mode 100644 index 000000000..7cca01f1c --- /dev/null +++ b/src/ast/euf/euf_arith_plugin.h @@ -0,0 +1,53 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_arith_plugin.h + +Abstract: + + plugin structure for arithetic +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +--*/ + +#pragma once + +#include "ast/arith_decl_plugin.h" +#include "ast/euf/euf_plugin.h" +#include "ast/euf/euf_ac_plugin.h" + +namespace euf { + + class egraph; + + class arith_plugin : public plugin { + enum undo_t { undo_add, undo_mul }; + arith_util a; + svector m_undo; + ac_plugin m_add, m_mul; + + public: + arith_plugin(egraph& g); + + ~arith_plugin() override {} + + unsigned get_id() const override { return a.get_family_id(); } + + void register_node(enode* n) override; + + void merge_eh(enode* n1, enode* n2) override; + + void diseq_eh(enode* eq) override {} + + void undo() override; + + void propagate() override; + + std::ostream& display(std::ostream& out) const override; + + }; +} diff --git a/src/ast/euf/euf_bv_plugin.cpp b/src/ast/euf/euf_bv_plugin.cpp new file mode 100644 index 000000000..99bf8941b --- /dev/null +++ b/src/ast/euf/euf_bv_plugin.cpp @@ -0,0 +1,361 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_bv_plugin.cpp + +Abstract: + + plugin structure for bit-vectors + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-08 + Jakob Rath 2023-11-08 + +Objective: + +satisfies extract/concat axioms. + - concat(n{I],n[J]) = n[IJ] for I, J consecutive. + - concat(v1, v2) = 2^width(v1)*v2 + v1 + - concat(n[width(n)-1:0]) = n + - concat(a, b)[I] = concat(a[I1], b[I2]) + - concat(a, concat(b, c)) = concat(concat(a, b), c) + +E-graph: + +The E-graph contains node definitions of the form + + n := f(n1,n2,..) + +and congruences: + + n ~ n' means root(n) = root(n') + +Saturated state: + + 1. n := n1[I], n' := n2[J], n1 ~ n2 => root(n1) contains tree refining both I, J from smaller intervals + + 2. n := concat(n1[I], n2[J]), n1 ~ n2 ~ n3 I and J are consecutive => n ~ n3[IJ] + + 3. n := concat(n1[I], n2[J]), I and J are consecutive & n1 ~ n2, n1[I] ~ v1, n1[J] ~ v2 => n ~ 2^width(v1)*v2 + v1 + + 4. n := concat(n1[I], n2[J], I, J are consecutive, n1 ~ n2, n ~ v => n1[I] ~ v mod 2^width(n1[I]), n2[J] ~ v div 2^width(n1[I]) + + 5. n' := n[I] => n ~ n[width(n)-1:0] + + 6. n := concat(a, concat(b, c)) => n ~ concat(concat(a, b), c) + - handled by rewriter pre-processing for inputs + - terms created internally are not equated modulo associativity + + 7, n := concat(n1, n2)[I] => n ~ concat(n1[I1],n2[I2]) or n[I1] or n[I2] + - handled by rewriter pre-processing + +Example: + x == (x1 x2) x3 + y == y1 (y2 y3) + x1 == y1, x2 == y2, x3 == y3 + => + x = y + + by x2 == y2, x3 == y3 => (x2 x3) = (y2 y3) + by (2) => x[I23] = (x2 x3) + by (2) => x[I123] = (x1 (x2 x3)) + by (5) => x = x[I123] + +The formal properties of saturation have to be established. + +- Saturation does not complete with respect to associativity. +Instead the claim is along the lines that the resulting E-graph can be used as a canonizer. +If given a set of equations E that are saturated, and terms t1, t2 that are +both simplified with respect to left-associativity of concatentation, and t1, t2 belong to the E-graph, +then t1 = t2 iff t1 ~ t2 in the E-graph. + +TODO: Is saturation for (7) overkill for the purpose of canonization? + +TODO: revisit re-entrancy during register_node. It can be called when creating internal extract terms. +Instead of allowing re-entrancy we can accumulate nodes that are registered during recursive calls +and have the main call perform recursive slicing. + +--*/ + + +#include "ast/euf/euf_bv_plugin.h" +#include "ast/euf/euf_egraph.h" + +namespace euf { + + bv_plugin::bv_plugin(egraph& g): + plugin(g), + bv(g.get_manager()) + {} + + enode* bv_plugin::mk_value_concat(enode* a, enode* b) { + auto v1 = get_value(a); + auto v2 = get_value(b); + auto v3 = v1 + v2 * power(rational(2), width(a)); + return mk_value(v3, width(a) + width(b)); + } + + enode* bv_plugin::mk_value(rational const& v, unsigned sz) { + auto e = bv.mk_numeral(v, sz); + return mk(e, 0, nullptr); + } + + void bv_plugin::merge_eh(enode* x, enode* y) { + SASSERT(x == x->get_root()); + SASSERT(x == y->get_root()); + + TRACE("bv", tout << "merge_eh " << g.bpp(x) << " == " << g.bpp(y) << "\n"); + SASSERT(!m_internal); + flet _internal(m_internal, true); + + propagate_values(x); + + // ensure slices align + if (has_sub(x) || has_sub(y)) { + enode_vector& xs = m_xs, & ys = m_ys; + xs.reset(); + ys.reset(); + xs.push_back(x); + ys.push_back(y); + merge(xs, ys, justification::equality(x, y)); + } + + // ensure p := concat(n1[I], n2[J]), n1 ~ n2 ~ n3 I and J are consecutive => p ~ n3[IJ] + for (auto* n : enode_class(x)) + propagate_extract(n); + } + + // enforce concat(v1, v2) = v2*2^|v1| + v1 + void bv_plugin::propagate_values(enode* x) { + if (!is_value(x)) + return; + + enode* a, * b; + for (enode* p : enode_parents(x)) + if (is_concat(p, a, b) && is_value(a) && is_value(b) && !is_value(p)) + push_merge(mk_concat(a->get_interpreted(), b->get_interpreted()), mk_value_concat(a, b)); + + for (enode* sib : enode_class(x)) { + if (is_concat(sib, a, b)) { + if (!is_value(a) || !is_value(b)) { + auto val = get_value(x); + auto v1 = mod2k(val, width(a)); + auto v2 = machine_div2k(val, width(a)); + push_merge(mk_concat(mk_value(v1, width(a)), mk_value(v2, width(b))), x->get_interpreted()); + } + } + } + } + + // + // p := concat(n1[I], n2[J]), n1 ~ n2 ~ n3 I and J are consecutive => p ~ n3[IJ] + // + // n is of form arg[I] + // p is of form concat(n, b) or concat(a, n) + // b is congruent to arg[J], I is consecutive with J => ensure that arg[IJ] = p + // a is congruent to arg[J], J is consecutive with I => ensure that arg[JI] = p + // + + void bv_plugin::propagate_extract(enode* n) { + unsigned lo1, hi1, lo2, hi2; + enode* a, * b; + if (!is_extract(n, lo1, hi1)) + return; + + enode* arg = n->get_arg(0); + enode* arg_r = arg->get_root(); + enode* n_r = n->get_root(); + + auto ensure_concat = [&](unsigned lo, unsigned mid, unsigned hi) { + TRACE("bv", tout << "ensure-concat " << lo << " " << mid << " " << hi << "\n"); + unsigned lo_, hi_; + for (enode* p1 : enode_parents(n)) + if (is_extract(p1, lo_, hi_) && lo_ == lo && hi_ == hi && p1->get_arg(0)->get_root() == arg_r) + return; + // add the axiom instead of merge(p, mk_extract(arg, lo, hi)), which would require tracking justifications + push_merge(mk_concat(mk_extract(arg, lo, mid), mk_extract(arg, mid + 1, hi)), mk_extract(arg, lo, hi)); + }; + + auto propagate_left = [&](enode* b) { + TRACE("bv", tout << "propagate-left " << g.bpp(b) << "\n"); + for (enode* sib : enode_class(b)) + if (is_extract(sib, lo2, hi2) && sib->get_arg(0)->get_root() == arg_r && hi1 + 1 == lo2) + ensure_concat(lo1, hi1, hi2); + }; + + auto propagate_right = [&](enode* a) { + TRACE("bv", tout << "propagate-right " << g.bpp(a) << "\n"); + for (enode* sib : enode_class(a)) + if (is_extract(sib, lo2, hi2) && sib->get_arg(0)->get_root() == arg_r && hi2 + 1 == lo1) + ensure_concat(lo2, hi2, hi1); + }; + + for (enode* p : enode_parents(n)) { + if (is_concat(p, a, b)) { + if (a->get_root() == n_r) + propagate_left(b); + if (b->get_root() == n_r) + propagate_right(a); + } + } + } + + void bv_plugin::push_undo_split(enode* n) { + m_undo_split.push_back(n); + push_plugin_undo(bv.get_family_id()); + } + + void bv_plugin::undo() { + enode* n = m_undo_split.back(); + m_undo_split.pop_back(); + auto& i = info(n); + i.lo = nullptr; + i.hi = nullptr; + i.cut = null_cut; + } + + void bv_plugin::register_node(enode* n) { + TRACE("bv", tout << "register " << g.bpp(n) << "\n"); + auto& i = info(n); + i.value = n; + enode* a, * b; + if (is_concat(n, a, b)) { + i.lo = a; + i.hi = b; + i.cut = width(a); + push_undo_split(n); + } + unsigned lo, hi; + if (is_extract(n, lo, hi) && (lo != 0 || hi + 1 != width(n->get_arg(0)))) { + enode* arg = n->get_arg(0); + unsigned w = width(arg); + if (all_of(enode_parents(arg), [&](enode* p) { unsigned _lo, _hi; return !is_extract(p, _lo, _hi) || _lo != 0 || _hi + 1 != w; })) + push_merge(mk_extract(arg, 0, w - 1), arg); + ensure_slice(arg, lo, hi); + } + } + + // + // Ensure that there are slices at boundaries of n[hi:lo] + // + void bv_plugin::ensure_slice(enode* n, unsigned lo, unsigned hi) { + enode* r = n; + unsigned lb = 0, ub = width(n) - 1; + while (true) { + TRACE("bv", tout << "ensure slice " << g.bpp(n) << " " << lb << " [" << lo << ", " << hi << "] " << ub << "\n"); + SASSERT(lb <= lo && hi <= ub); + SASSERT(ub - lb + 1 == width(r)); + if (lb == lo && ub == hi) + return; + slice_info& i = info(r); + if (!i.lo) { + if (lo > lb) { + split(r, lo - lb); + if (hi < ub) // or split(info(r).hi, ...) + ensure_slice(n, lo, hi); + } + else if (hi < ub) + split(r, ub - hi); + break; + } + auto cut = i.cut; + if (cut + lb <= lo) { + lb += cut; + r = i.hi; + continue; + } + if (cut + lb > hi) { + ub = cut + lb - 1; + r = i.lo; + continue; + } + SASSERT(lo < cut + lb && cut + lb <= hi); + ensure_slice(n, lo, cut + lb - 1); + ensure_slice(n, cut + lb, hi); + break; + } + } + + enode* bv_plugin::mk_extract(enode* n, unsigned lo, unsigned hi) { + SASSERT(lo <= hi && width(n) > hi - lo); + unsigned lo1, hi1; + while (is_extract(n, lo1, hi1)) { + lo += lo1; + hi += lo1; + n = n->get_arg(0); + } + return mk(bv.mk_extract(hi, lo, n->get_expr()), 1, &n); + } + + enode* bv_plugin::mk_concat(enode* lo, enode* hi) { + enode* args[2] = { lo, hi }; + return mk(bv.mk_concat(lo->get_expr(), hi->get_expr()), 2, args); + } + + void bv_plugin::merge(enode_vector& xs, enode_vector& ys, justification dep) { + while (!xs.empty()) { + SASSERT(!ys.empty()); + auto x = xs.back(); + auto y = ys.back(); + if (unfold_sub(x, xs)) + continue; + else if (unfold_sub(y, ys)) + continue; + else if (unfold_width(x, xs, y, ys)) + continue; + else if (unfold_width(y, ys, x, xs)) + continue; + else if (x->get_root() != y->get_root()) + push_merge(x, y, dep); + xs.pop_back(); + ys.pop_back(); + } + SASSERT(ys.empty()); + } + + bool bv_plugin::unfold_sub(enode* x, enode_vector& xs) { + if (!has_sub(x)) + return false; + xs.pop_back(); + xs.push_back(sub_hi(x)); + xs.push_back(sub_lo(x)); + return true; + } + + bool bv_plugin::unfold_width(enode* x, enode_vector& xs, enode* y, enode_vector& ys) { + if (width(x) <= width(y)) + return false; + split(x, width(y)); + xs.pop_back(); + xs.push_back(sub_hi(x)); + xs.push_back(sub_lo(x)); + return true; + } + + void bv_plugin::split(enode* n, unsigned cut) { + TRACE("bv", tout << "split: " << g.bpp(n) << " " << cut << "\n"); + unsigned w = width(n); + SASSERT(!info(n).hi); + SASSERT(0 < cut && cut < w); + enode* hi = mk_extract(n, cut, w - 1); + enode* lo = mk_extract(n, 0, cut - 1); + auto& i = info(n); + SASSERT(i.value); + i.hi = hi; + i.lo = lo; + i.cut = cut; + push_undo_split(n); + push_merge(mk_concat(lo, hi), n); + } + + std::ostream& bv_plugin::display(std::ostream& out) const { + out << "bv\n"; + for (auto const& i : m_info) + if (i.lo) + out << g.bpp(i.value) << " cut " << i.cut << " lo " << g.bpp(i.lo) << " hi " << g.bpp(i.hi) << "\n"; + return out; + } +} diff --git a/src/ast/euf/euf_bv_plugin.h b/src/ast/euf/euf_bv_plugin.h new file mode 100644 index 000000000..b8d62051e --- /dev/null +++ b/src/ast/euf/euf_bv_plugin.h @@ -0,0 +1,100 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_bv_plugin.h + +Abstract: + + plugin structure for bit-vectors + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-08 + Jakob Rath 2023-11-08 + + +--*/ + +#pragma once + +#include "ast/bv_decl_plugin.h" +#include "ast/euf/euf_plugin.h" + +namespace euf { + + class egraph; + + class bv_plugin : public plugin { + static constexpr unsigned null_cut = std::numeric_limits::max(); + + struct slice_info { + unsigned cut = null_cut; // = bv.get_bv_size(lo) + enode* hi = nullptr; // + enode* lo = nullptr; // + enode* value = nullptr; + void reset() { *this = slice_info(); } + }; + using slice_info_vector = svector; + + bv_util bv; + slice_info_vector m_info; // indexed by enode::get_id() + + enode_vector m_xs, m_ys; + + bool is_concat(enode* n) const { return bv.is_concat(n->get_expr()); } + bool is_concat(enode* n, enode*& a, enode*& b) { return is_concat(n) && (a = n->get_arg(0), b = n->get_arg(1), true); } + bool is_extract(enode* n, unsigned& lo, unsigned& hi) { expr* body; return bv.is_extract(n->get_expr(), lo, hi, body); } + bool is_extract(enode* n) const { return bv.is_extract(n->get_expr()); } + unsigned width(enode* n) const { return bv.get_bv_size(n->get_expr()); } + + enode* mk_extract(enode* n, unsigned lo, unsigned hi); + enode* mk_concat(enode* lo, enode* hi); + enode* mk_value_concat(enode* lo, enode* hi); + enode* mk_value(rational const& v, unsigned sz); + unsigned width(enode* n) { return bv.get_bv_size(n->get_expr()); } + bool is_value(enode* n) { return n->get_root()->interpreted(); } + rational get_value(enode* n) { rational val; VERIFY(bv.is_numeral(n->get_interpreted()->get_expr(), val)); return val; } + slice_info& info(enode* n) { unsigned id = n->get_id(); m_info.reserve(id + 1); return m_info[id]; } + slice_info& root_info(enode* n) { unsigned id = n->get_root_id(); m_info.reserve(id + 1); return m_info[id]; } + bool has_sub(enode* n) { return !!info(n).lo; } + enode* sub_lo(enode* n) { return info(n).lo; } + enode* sub_hi(enode* n) { return info(n).hi; } + + bool m_internal = false; + void ensure_slice(enode* n, unsigned lo, unsigned hi); + + + void split(enode* n, unsigned cut); + + bool unfold_width(enode* x, enode_vector& xs, enode* y, enode_vector& ys); + bool unfold_sub(enode* x, enode_vector& xs); + void merge(enode_vector& xs, enode_vector& ys, justification j); + void propagate_extract(enode* n); + void propagate_values(enode* n); + + enode_vector m_undo_split; + void push_undo_split(enode* n); + + public: + bv_plugin(egraph& g); + + ~bv_plugin() override {} + + unsigned get_id() const override { return bv.get_family_id(); } + + void register_node(enode* n) override; + + void merge_eh(enode* n1, enode* n2) override; + + void diseq_eh(enode* eq) override {} + + void propagate() override {} + + void undo() override; + + std::ostream& display(std::ostream& out) const override; + + }; +} diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 6c8cbda06..154106e23 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -130,8 +130,8 @@ namespace euf { if (n2 == n) update_children(n); else - merge(n, n2, justification::congruence(comm, m_congruence_timestamp++)); - + push_merge(n, n2, comm); + return n; } @@ -146,19 +146,36 @@ namespace euf { memory::deallocate(m_tmp_node); } + void egraph::add_plugin(plugin* p) { + m_plugins.reserve(p->get_id() + 1); + m_plugins.set(p->get_id(), p); + } + + void egraph::propagate_plugins() { + for (auto* p : m_plugins) + if (p) + p->propagate(); + } + void egraph::add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) { TRACE("euf_verbose", tout << "eq: " << v1 << " == " << v2 << "\n";); m_new_th_eqs.push_back(th_eq(id, v1, v2, c, r)); m_updates.push_back(update_record(update_record::new_th_eq())); ++m_stats.m_num_th_eqs; + auto* p = get_plugin(id); + if (p) + p->merge_eh(c, r); } - void egraph::add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq) { + void egraph::add_th_diseq(theory_id id, theory_var v1, theory_var v2, enode* eq) { if (!th_propagates_diseqs(id)) return; TRACE("euf_verbose", tout << "eq: " << v1 << " != " << v2 << "\n";); - m_new_th_eqs.push_back(th_eq(id, v1, v2, eq)); + m_new_th_eqs.push_back(th_eq(id, v1, v2, eq->get_expr())); m_updates.push_back(update_record(update_record::new_th_eq())); + auto* p = get_plugin(id); + if (p) + p->diseq_eh(eq); ++m_stats.m_num_th_diseqs; } @@ -202,7 +219,7 @@ namespace euf { return; theory_var v1 = arg1->get_closest_th_var(id); theory_var v2 = arg2->get_closest_th_var(id); - add_th_diseq(id, v1, v2, n->get_expr()); + add_th_diseq(id, v1, v2, n); return; } for (auto const& p : euf::enode_th_vars(r1)) { @@ -210,8 +227,8 @@ namespace euf { continue; for (auto const& q : euf::enode_th_vars(r2)) if (p.get_id() == q.get_id()) - add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n->get_expr()); - } + add_th_diseq(p.get_id(), p.get_var(), q.get_var(), n); + } } @@ -230,7 +247,7 @@ namespace euf { n = n->get_root(); theory_var v2 = n->get_closest_th_var(id); if (v2 != null_theory_var) - add_th_diseq(id, v1, v2, p->get_expr()); + add_th_diseq(id, v1, v2, p); } } } @@ -249,6 +266,10 @@ namespace euf { theory_var w = n->get_th_var(id); enode* r = n->get_root(); + auto* p = get_plugin(id); + if (p) + p->register_node(n); + if (w == null_theory_var) { n->add_th_var(v, id, m_region); m_updates.push_back(update_record(n, id, update_record::add_th_var())); @@ -424,6 +445,9 @@ namespace euf { p.r1->m_args[i]->get_root()->m_parents.pop_back(); } break; + case update_record::tag_t::is_plugin_undo: + m_plugins[p.m_th_id]->undo(); + break; default: UNREACHABLE(); break; @@ -589,6 +613,9 @@ namespace euf { case to_merge_comm: merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++)); break; + case to_justified: + merge(w.a, w.b, w.j); + break; case to_add_literal: add_literal(w.a, w.b); break; @@ -760,6 +787,13 @@ namespace euf { justifications.push_back(j.ext()); else if (j.is_congruence()) push_congruence(a, b, j.is_commutative()); + else if (j.is_dependent()) { + vector js; + for (auto const& j2 : justification::dependency_manager::s_linearize(j.get_dependency(), js)) + explain_eq(justifications, cc, a, b, j2); + } + else if (j.is_equality()) + explain_eq(justifications, cc, j.lhs(), j.rhs()); if (cc && j.is_congruence()) cc->push_back(std::tuple(a->get_app(), b->get_app(), j.timestamp(), j.is_commutative())); } @@ -879,6 +913,9 @@ namespace euf { max_args = std::max(max_args, n->num_args()); for (enode* n : m_nodes) display(out, max_args, n); + for (auto* p : m_plugins) + if (p) + p->display(out); return out; } diff --git a/src/ast/euf/euf_egraph.h b/src/ast/euf/euf_egraph.h index 83a3574ce..f2eedc21d 100644 --- a/src/ast/euf/euf_egraph.h +++ b/src/ast/euf/euf_egraph.h @@ -29,8 +29,10 @@ Notes: #include "util/statistics.h" #include "util/trail.h" #include "util/lbool.h" +#include "util/scoped_ptr_vector.h" #include "ast/euf/euf_enode.h" #include "ast/euf/euf_etable.h" +#include "ast/euf/euf_plugin.h" #include "ast/ast_ll_pp.h" #include @@ -82,14 +84,18 @@ namespace euf { class egraph { + friend class plugin; + typedef ptr_vector trail_stack; - enum to_merge_t { to_merge_plain, to_merge_comm, to_add_literal }; + enum to_merge_t { to_merge_plain, to_merge_comm, to_justified, to_add_literal }; struct to_merge { enode* a, * b; to_merge_t t; + justification j; bool commutativity() const { return t == to_merge_comm; } to_merge(enode* a, enode* b, bool c) : a(a), b(b), t(c ? to_merge_comm : to_merge_plain) {} + to_merge(enode* a, enode* b, justification j): a(a), b(b), t(to_justified), j(j) {} to_merge(enode* p, enode* ante): a(p), b(ante), t(to_add_literal) {} }; @@ -116,10 +122,12 @@ namespace euf { struct lbl_set {}; struct update_children {}; struct set_relevant {}; + struct plugin_undo {}; enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, is_update_children, is_add_th_var, is_replace_th_var, is_new_th_eq, is_lbl_hash, is_new_th_eq_qhead, - is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant }; + is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant, + is_plugin_undo }; tag_t tag; enode* r1; enode* n1; @@ -162,11 +170,14 @@ namespace euf { tag(tag_t::is_update_children), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} update_record(enode* n, set_relevant) : tag(tag_t::is_set_relevant), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} + update_record(unsigned th_id, plugin_undo) : + tag(tag_t::is_plugin_undo), r1(nullptr), n1(nullptr), m_th_id(th_id) {} }; ast_manager& m; svector m_to_merge; etable m_table; region m_region; + scoped_ptr_vector m_plugins; svector m_updates; unsigned_vector m_scopes; enode_vector m_expr2enode; @@ -205,6 +216,12 @@ namespace euf { } void push_node(enode* n) { m_updates.push_back(update_record(n)); } + // plugin related methods + void push_plugin_undo(unsigned th_id) { m_updates.push_back(update_record(th_id, update_record::plugin_undo())); } + void push_merge(enode* a, enode* b, justification j) { m_to_merge.push_back({ a, b, j }); } + void push_merge(enode* a, enode* b, bool comm) { m_to_merge.push_back({ a, b, comm }); } + void propagate_plugins(); + void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r); void add_th_diseqs(theory_id id, theory_var v1, enode* r); @@ -245,11 +262,15 @@ namespace euf { public: egraph(ast_manager& m); ~egraph(); + + void add_plugin(plugin* p); + plugin* get_plugin(family_id fid) const { return m_plugins.get(fid, nullptr); } + enode* find(expr* f) const { return m_expr2enode.get(f->get_id(), nullptr); } enode* find(expr* f, unsigned n, enode* const* args); enode* mk(expr* f, unsigned generation, unsigned n, enode *const* args); enode_vector const& enodes_of(func_decl* f); - void push() { if (!m_to_merge.empty()) propagate(); ++m_num_scopes; } + void push() { if (can_propagate()) propagate(); ++m_num_scopes; } void pop(unsigned num_scopes); /** @@ -269,6 +290,7 @@ namespace euf { of new equalities. */ bool propagate(); + bool can_propagate() const { return !m_to_merge.empty(); } bool inconsistent() const { return m_inconsistent; } /** @@ -286,7 +308,7 @@ namespace euf { where \c n is an enode and \c is_eq indicates whether the enode is an equality consequence. */ - void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq); + void add_th_diseq(theory_id id, theory_var v1, theory_var v2, enode* eq); bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); } th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; } void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; } diff --git a/src/ast/euf/euf_enode.cpp b/src/ast/euf/euf_enode.cpp index 08df9f493..335c8f3e9 100644 --- a/src/ast/euf/euf_enode.cpp +++ b/src/ast/euf/euf_enode.cpp @@ -93,6 +93,17 @@ namespace euf { return null_theory_var; } + enode* enode::get_closest_th_node(theory_id id) { + enode* n = this; + while (n) { + theory_var v = n->get_th_var(id); + if (v != null_theory_var) + return n; + n = n->m_target; + } + return nullptr; + } + bool enode::acyclic() const { enode const* n = this; enode const* p = this; diff --git a/src/ast/euf/euf_enode.h b/src/ast/euf/euf_enode.h index 064688d47..0f22ff20e 100644 --- a/src/ast/euf/euf_enode.h +++ b/src/ast/euf/euf_enode.h @@ -207,6 +207,7 @@ namespace euf { enode* get_root() const { return m_root; } expr* get_expr() const { return m_expr; } sort* get_sort() const { return m_expr->get_sort(); } + enode* get_interpreted() const { return get_root(); } app* get_app() const { return to_app(m_expr); } func_decl* get_decl() const { return is_app(m_expr) ? to_app(m_expr)->get_decl() : nullptr; } unsigned get_expr_id() const { return m_expr->get_id(); } @@ -216,6 +217,10 @@ namespace euf { bool children_are_roots() const; enode* get_next() const { return m_next; } + enode* get_target() const { return m_target; } + justification get_justification() const { return m_justification; } + justification get_lit_justification() const { return m_lit_justification; } + bool has_lbl_hash() const { return m_lbl_hash >= 0; } unsigned char get_lbl_hash() const { SASSERT(m_lbl_hash >= 0 && static_cast(m_lbl_hash) < approx_set_traits::capacity); @@ -229,6 +234,7 @@ namespace euf { theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); } theory_var get_closest_th_var(theory_id id) const; + enode* get_closest_th_node(theory_id id); bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; } bool has_th_vars() const { return !m_th_vars.empty(); } bool has_one_th_var() const { return !m_th_vars.empty() && !m_th_vars.get_next();} diff --git a/src/ast/euf/euf_justification.cpp b/src/ast/euf/euf_justification.cpp new file mode 100644 index 000000000..22b52ea84 --- /dev/null +++ b/src/ast/euf/euf_justification.cpp @@ -0,0 +1,54 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + euf_justification.cpp + +Abstract: + + justification structure for euf + +Author: + + Nikolaj Bjorner (nbjorner) 2020-08-23 + +--*/ + + +#include "ast/euf/euf_justification.h" +#include "ast/euf/euf_enode.h" + +namespace euf { + + + std::ostream& justification::display(std::ostream& out, std::function const& ext) const { + switch (m_kind) { + case kind_t::external_t: + if (ext) + ext(out, m_external); + else + out << "external"; + return out; + case kind_t::axiom_t: + return out << "axiom"; + case kind_t::congruence_t: + return out << "congruence"; + case kind_t::dependent_t: { + vector js; + out << "dependent"; + for (auto const& j : dependency_manager::s_linearize(m_dependency, js)) + j.display(out << " ", ext); + return out; + } + case kind_t::equality_t: + return out << "equality #" << m_n1->get_id() << " == #" << m_n2->get_id(); + + default: + UNREACHABLE(); + return out; + } + return out; + } + +} diff --git a/src/ast/euf/euf_justification.h b/src/ast/euf/euf_justification.h index c98002396..f9d3b3637 100644 --- a/src/ast/euf/euf_justification.h +++ b/src/ast/euf/euf_justification.h @@ -22,19 +22,34 @@ Notes: #pragma once +#include "util/dependency.h" + namespace euf { + class enode; + class justification { + public: + typedef stacked_dependency_manager dependency_manager; + typedef stacked_dependency_manager::dependency dependency; + private: enum class kind_t { axiom_t, congruence_t, - external_t + external_t, + dependent_t, + equality_t }; kind_t m_kind; - bool m_comm; + union { + bool m_comm; + enode* m_n1; + }; union { void* m_external; uint64_t m_timestamp; + dependency* m_dependency; + enode* m_n2; }; justification(bool comm, uint64_t ts): @@ -49,6 +64,18 @@ namespace euf { m_external(ext) {} + justification(dependency* dep, int): + m_kind(kind_t::dependent_t), + m_comm(false), + m_dependency(dep) + {} + + justification(enode* n1, enode* n2): + m_kind(kind_t::equality_t), + m_n1(n1), + m_n2(n2) + {} + public: justification(): m_kind(kind_t::axiom_t), @@ -59,10 +86,17 @@ namespace euf { static justification axiom() { return justification(); } static justification congruence(bool c, uint64_t ts) { return justification(c, ts); } static justification external(void* ext) { return justification(ext); } + static justification dependent(dependency* d) { return justification(d, 1); } + static justification equality(enode* a, enode* b) { return justification(a, b); } bool is_external() const { return m_kind == kind_t::external_t; } bool is_congruence() const { return m_kind == kind_t::congruence_t; } bool is_commutative() const { return m_comm; } + bool is_dependent() const { return m_kind == kind_t::dependent_t; } + bool is_equality() const { return m_kind == kind_t::equality_t; } + dependency* get_dependency() const { SASSERT(is_dependent()); return m_dependency; } + enode* lhs() const { SASSERT(is_equality()); return m_n1; } + enode* rhs() const { SASSERT(is_equality()); return m_n2; } uint64_t timestamp() const { SASSERT(is_congruence()); return m_timestamp; } template T* ext() const { SASSERT(is_external()); return static_cast(m_external); } @@ -75,30 +109,17 @@ namespace euf { return axiom(); case kind_t::congruence_t: return congruence(m_comm, m_timestamp); + case kind_t::dependent_t: + NOT_IMPLEMENTED_YET(); + return dependent(m_dependency); default: UNREACHABLE(); return axiom(); } } - std::ostream& display(std::ostream& out, std::function const& ext) const { - switch (m_kind) { - case kind_t::external_t: - if (ext) - ext(out, m_external); - else - out << "external"; - return out; - case kind_t::axiom_t: - return out << "axiom"; - case kind_t::congruence_t: - return out << "congruence"; - default: - UNREACHABLE(); - return out; - } - return out; - } + std::ostream& display(std::ostream& out, std::function const& ext) const; + }; inline std::ostream& operator<<(std::ostream& out, justification const& j) { diff --git a/src/ast/euf/euf_plugin.cpp b/src/ast/euf/euf_plugin.cpp new file mode 100644 index 000000000..57c1849fd --- /dev/null +++ b/src/ast/euf/euf_plugin.cpp @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_plugin.cpp + +Abstract: + + plugin structure for euf + + Plugins allow adding equality saturation for theories. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-08 + +--*/ + +#include "ast/euf/euf_egraph.h" + +namespace euf { + + void plugin::push_plugin_undo(unsigned th_id) { + g.push_plugin_undo(th_id); + } + + void plugin::push_merge(enode* a, enode* b, justification j) { + g.push_merge(a, b, j); + } + + void plugin::push_merge(enode* a, enode* b) { + TRACE("plugin", tout << g.bpp(a) << " == " << g.bpp(b) << "\n"); + g.push_merge(a, b, justification::axiom()); + } + + enode* plugin::mk(expr* e, unsigned n, enode* const* args) { + enode* r = g.find(e); + if (!r) + r = g.mk(e, 0, n, args); + return r; + } + + region& plugin::get_region() { + return g.m_region; + } +} diff --git a/src/ast/euf/euf_plugin.h b/src/ast/euf/euf_plugin.h new file mode 100644 index 000000000..ff49d6c40 --- /dev/null +++ b/src/ast/euf/euf_plugin.h @@ -0,0 +1,58 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_plugin.h + +Abstract: + + plugin structure for euf + + Plugins allow adding equality saturation for theories. + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-08 + +--*/ + +#pragma once + +#include "ast/euf/euf_enode.h" +#include "ast/euf/euf_justification.h" + +namespace euf { + + + class plugin { + protected: + egraph& g; + void push_plugin_undo(unsigned th_id); + void push_merge(enode* a, enode* b, justification j); + void push_merge(enode* a, enode* b); + enode* mk(expr* e, unsigned n, enode* const* args); + region& get_region(); + public: + plugin(egraph& g): + g(g) + {} + + virtual ~plugin() {} + + virtual unsigned get_id() const = 0; + + virtual void register_node(enode* n) = 0; + + virtual void merge_eh(enode* n1, enode* n2) = 0; + + virtual void diseq_eh(enode* eq) {}; + + virtual void propagate() = 0; + + virtual void undo() = 0; + + virtual std::ostream& display(std::ostream& out) const = 0; + + }; +} diff --git a/src/ast/euf/euf_specrel_plugin.cpp b/src/ast/euf/euf_specrel_plugin.cpp new file mode 100644 index 000000000..3220a24e6 --- /dev/null +++ b/src/ast/euf/euf_specrel_plugin.cpp @@ -0,0 +1,71 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_specrel_plugin.cpp + +Abstract: + + plugin structure for specrel + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +--*/ + +#include "ast/euf/euf_specrel_plugin.h" +#include "ast/euf/euf_egraph.h" +#include + +namespace euf { + + specrel_plugin::specrel_plugin(egraph& g) : + plugin(g), + sp(g.get_manager()) { + } + + void specrel_plugin::register_node(enode* n) { + func_decl* f = n->get_decl(); + if (!f) + return; + if (!sp.is_ac(f)) + return; + ac_plugin* p = nullptr; + if (!m_decl2plugin.find(f, p)) { + p = alloc(ac_plugin, g, f); + m_decl2plugin.insert(f, p); + m_plugins.push_back(p); + std::function undo_op = [&]() { m_undo.push_back(p); }; + p->set_undo(undo_op); + } + } + + void specrel_plugin::merge_eh(enode* n1, enode* n2) { + for (auto * p : m_plugins) + p->merge_eh(n1, n2); + } + + void specrel_plugin::diseq_eh(enode* eq) { + for (auto* p : m_plugins) + p->diseq_eh(eq); + } + + void specrel_plugin::propagate() { + for (auto * p : m_plugins) + p->propagate(); + } + + void specrel_plugin::undo() { + auto p = m_undo.back(); + m_undo.pop_back(); + p->undo(); + } + + std::ostream& specrel_plugin::display(std::ostream& out) const { + for (auto * p : m_plugins) + p->display(out); + return out; + } +} \ No newline at end of file diff --git a/src/ast/euf/euf_specrel_plugin.h b/src/ast/euf/euf_specrel_plugin.h new file mode 100644 index 000000000..228bb5e15 --- /dev/null +++ b/src/ast/euf/euf_specrel_plugin.h @@ -0,0 +1,56 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + euf_specrel_plugin.h + +Abstract: + + plugin structure for specrel functions + +Author: + + Nikolaj Bjorner (nbjorner) 2023-11-11 + +--*/ + +#pragma once + +#include +#include "util/scoped_ptr_vector.h" +#include "ast/special_relations_decl_plugin.h" +#include "ast/euf/euf_plugin.h" +#include "ast/euf/euf_ac_plugin.h" + +namespace euf { + + class specrel_plugin : public plugin { + scoped_ptr_vector m_plugins; + ptr_vector m_undo; + obj_map m_decl2plugin; + special_relations_util sp; + + public: + + specrel_plugin(egraph& g); + + ~specrel_plugin() override {} + + unsigned get_id() const override { return sp.get_family_id(); } + + void register_node(enode* n) override; + + void merge_eh(enode* n1, enode* n2) override; + + void diseq_eh(enode* eq) override; + + void undo() override; + + void propagate() override; + + std::ostream& display(std::ostream& out) const override; + + }; + +} diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index 24a756bf7..bbfe819d4 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -26,7 +26,8 @@ special_relations_decl_plugin::special_relations_decl_plugin(): m_po("partial-order"), m_plo("piecewise-linear-order"), m_to("tree-order"), - m_tc("transitive-closure") + m_tc("transitive-closure"), + m_ac("ac-op") {} func_decl * special_relations_decl_plugin::mk_func_decl( @@ -41,24 +42,53 @@ func_decl * special_relations_decl_plugin::mk_func_decl( m_manager->raise_exception("argument sort missmatch. The two arguments should have the same sort"); return nullptr; } + if (!range && k == OP_SPECIAL_RELATION_AC) + range = domain[0]; + if (!range) { range = m_manager->mk_bool_sort(); } - if (!m_manager->is_bool(range)) { - m_manager->raise_exception("range type is expected to be Boolean for special relations"); - } + auto check_bool_range = [&]() { + if (!m_manager->is_bool(range)) + m_manager->raise_exception("range type is expected to be Boolean for special relations"); + }; + + m_has_special_relation = true; func_decl_info info(m_family_id, k, num_parameters, parameters); symbol name; switch(k) { - case OP_SPECIAL_RELATION_PO: name = m_po; break; - case OP_SPECIAL_RELATION_LO: name = m_lo; break; - case OP_SPECIAL_RELATION_PLO: name = m_plo; break; - case OP_SPECIAL_RELATION_TO: name = m_to; break; + case OP_SPECIAL_RELATION_PO: check_bool_range(); name = m_po; break; + case OP_SPECIAL_RELATION_LO: check_bool_range(); name = m_lo; break; + case OP_SPECIAL_RELATION_PLO: check_bool_range(); name = m_plo; break; + case OP_SPECIAL_RELATION_TO: check_bool_range(); name = m_to; break; + case OP_SPECIAL_RELATION_AC: { + if (range != domain[0]) + m_manager->raise_exception("AC operation should have the same range as domain type"); + name = m_ac; + if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) + m_manager->raise_exception("parameter to transitive closure should be a function declaration"); + func_decl* f = to_func_decl(parameters[0].get_ast()); + if (f->get_arity() != 2) + m_manager->raise_exception("ac function should be binary"); + if (f->get_domain(0) != f->get_domain(1)) + m_manager->raise_exception("ac function should have same domain"); + if (f->get_domain(0) != f->get_range()) + m_manager->raise_exception("ac function should have same domain and range"); + break; + } case OP_SPECIAL_RELATION_TC: + check_bool_range(); name = m_tc; if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) m_manager->raise_exception("parameter to transitive closure should be a function declaration"); + func_decl* f = to_func_decl(parameters[0].get_ast()); + if (f->get_arity() != 2) + m_manager->raise_exception("tc relation should be binary"); + if (f->get_domain(0) != f->get_domain(1)) + m_manager->raise_exception("tc relation should have same domain"); + if (!m_manager->is_bool(f->get_range())) + m_manager->raise_exception("tc relation should be Boolean"); break; } return m_manager->mk_func_decl(name, arity, domain, range, info); @@ -71,6 +101,7 @@ void special_relations_decl_plugin::get_op_names(svector & op_name op_names.push_back(builtin_name(m_plo.str(), OP_SPECIAL_RELATION_PLO)); op_names.push_back(builtin_name(m_to.str(), OP_SPECIAL_RELATION_TO)); op_names.push_back(builtin_name(m_tc.str(), OP_SPECIAL_RELATION_TC)); + op_names.push_back(builtin_name(m_ac.str(), OP_SPECIAL_RELATION_AC)); } } @@ -81,6 +112,7 @@ sr_property special_relations_util::get_property(func_decl* f) const { case OP_SPECIAL_RELATION_PLO: return sr_plo; case OP_SPECIAL_RELATION_TO: return sr_to; case OP_SPECIAL_RELATION_TC: return sr_tc; + case OP_SPECIAL_RELATION_AC: return sr_none; default: UNREACHABLE(); return sr_po; diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index c422cbcdc..a65f98758 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -16,6 +16,8 @@ Author: Revision History: + 2023-11-27: Added ac-op for E-graph plugin + --*/ #pragma once @@ -28,6 +30,7 @@ enum special_relations_op_kind { OP_SPECIAL_RELATION_PLO, OP_SPECIAL_RELATION_TO, OP_SPECIAL_RELATION_TC, + OP_SPECIAL_RELATION_AC, LAST_SPECIAL_RELATIONS_OP }; @@ -37,6 +40,7 @@ class special_relations_decl_plugin : public decl_plugin { symbol m_plo; symbol m_to; symbol m_tc; + symbol m_ac; bool m_has_special_relation = false; public: special_relations_decl_plugin(); @@ -86,13 +90,16 @@ class special_relations_util { public: special_relations_util(ast_manager& m) : m(m), m_fid(null_family_id) { } + family_id get_family_id() const { return fid(); } + bool has_special_relation() const { return static_cast(m.get_plugin(m.mk_family_id("specrels")))->has_special_relation(); } bool is_special_relation(func_decl* f) const { return f->get_family_id() == fid(); } - bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } + bool is_special_relation(expr* e) const { return is_app(e) && is_special_relation(to_app(e)->get_decl()); } sr_property get_property(func_decl* f) const; sr_property get_property(app* e) const { return get_property(e->get_decl()); } func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); } + func_decl* get_relation(expr* e) const { SASSERT(is_special_relation(e)); return to_func_decl(to_app(e)->get_parameter(0).get_ast()); } func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); } func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); } @@ -105,12 +112,14 @@ public: bool is_plo(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_PLO); } bool is_to(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TO); } bool is_tc(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TC); } + bool is_ac(expr const* e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_AC); } bool is_lo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_LO); } bool is_po(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PO); } bool is_plo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PLO); } bool is_to(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TO); } bool is_tc(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TC); } + bool is_ac(func_decl const* e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_AC); } app * mk_lo (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_LO, arg1, arg2); } app * mk_po (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PO, arg1, arg2); } diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index 4a899ca9d..7caccded6 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -44,6 +44,7 @@ z3_add_component(sat_smt q_solver.cpp recfun_solver.cpp sat_th.cpp + specrel_solver.cpp tseitin_theory_checker.cpp user_solver.cpp COMPONENT_DEPENDENCIES diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index d21e7a12a..8e4bd765d 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -28,6 +28,7 @@ Author: #include "sat/smt/fpa_solver.h" #include "sat/smt/dt_solver.h" #include "sat/smt/recfun_solver.h" +#include "sat/smt/specrel_solver.h" namespace euf { @@ -130,6 +131,7 @@ namespace euf { arith_util arith(m); datatype_util dt(m); recfun::util rf(m); + special_relations_util sp(m); if (pb.get_family_id() == fid) ext = alloc(pb::solver, *this, fid); else if (bvu.get_family_id() == fid) @@ -144,6 +146,8 @@ namespace euf { ext = alloc(dt::solver, *this, fid); else if (rf.get_family_id() == fid) ext = alloc(recfun::solver, *this); + else if (sp.get_family_id() == fid) + ext = alloc(specrel::solver, *this, fid); if (ext) add_solver(ext); diff --git a/src/sat/smt/specrel_solver.cpp b/src/sat/smt/specrel_solver.cpp new file mode 100644 index 000000000..d59029e6b --- /dev/null +++ b/src/sat/smt/specrel_solver.cpp @@ -0,0 +1,120 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + specrel_solver.h + +Abstract: + + Theory plugin for special relations + +Author: + + Nikolaj Bjorner (nbjorner) 2020-09-08 + +--*/ + +#include "sat/smt/specrel_solver.h" +#include "sat/smt/euf_solver.h" +#include "ast/euf/euf_specrel_plugin.h" + +namespace euf { + class solver; +} + +namespace specrel { + + solver::solver(euf::solver& ctx, theory_id id) : + th_euf_solver(ctx, ctx.get_manager().get_family_name(id), id), + sp(m) + { + ctx.get_egraph().add_plugin(alloc(euf::specrel_plugin, ctx.get_egraph())); + } + + solver::~solver() { + } + + void solver::asserted(sat::literal l) { + + } + + sat::check_result solver::check() { + return sat::check_result::CR_DONE; + } + + std::ostream& solver::display(std::ostream& out) const { + return out; + } + + void solver::collect_statistics(statistics& st) const { + } + + euf::th_solver* solver::clone(euf::solver& ctx) { + return alloc(solver, ctx, get_id()); + } + + void solver::new_eq_eh(euf::th_eq const& eq) { + TRACE("specrel", tout << "new-eq\n"); + if (eq.is_eq()) { + auto* p = ctx.get_egraph().get_plugin(sp.get_family_id()); + p->merge_eh(var2enode(eq.v1()), var2enode(eq.v2())); + TRACE("specrel", tout << eq.v1() << " " << eq.v2() << "\n"); + } + } + + void solver::add_value(euf::enode* n, model& mdl, expr_ref_vector& values) { + } + + bool solver::add_dep(euf::enode* n, top_sort& dep) { + return false; + } + + bool solver::include_func_interp(func_decl* f) const { + return false; + } + + sat::literal solver::internalize(expr* e, bool sign, bool root) { + if (!visit_rec(m, e, sign, root)) + return sat::null_literal; + auto lit = ctx.expr2literal(e); + if (sign) + lit.neg(); + return lit; + } + + void solver::internalize(expr* e) { + visit_rec(m, e, false, false); + } + + bool solver::visit(expr* e) { + if (visited(e)) + return true; + m_stack.push_back(sat::eframe(e)); + return false; + } + + bool solver::visited(expr* e) { + euf::enode* n = expr2enode(e); + return n && n->is_attached_to(get_id()); + } + + bool solver::post_visit(expr* term, bool sign, bool root) { + euf::enode* n = expr2enode(term); + SASSERT(!n || !n->is_attached_to(get_id())); + if (!n) + n = mk_enode(term); + SASSERT(!n->is_attached_to(get_id())); + mk_var(n); + TRACE("specrel", tout << ctx.bpp(n) << "\n"); + return true; + } + + euf::theory_var solver::mk_var(euf::enode* n) { + if (is_attached_to_var(n)) + return n->get_th_var(get_id()); + euf::theory_var r = th_euf_solver::mk_var(n); + ctx.attach_th_var(n, this, r); + return r; + } +} diff --git a/src/sat/smt/specrel_solver.h b/src/sat/smt/specrel_solver.h new file mode 100644 index 000000000..9ebb76916 --- /dev/null +++ b/src/sat/smt/specrel_solver.h @@ -0,0 +1,75 @@ +/*++ +Copyright (c) 2020 Microsoft Corporation + +Module Name: + + specrel_solver.h + +Abstract: + + Theory plugin for special relations + +Author: + + Nikolaj Bjorner (nbjorner) 2020-09-08 + +--*/ +#pragma once + +#include "sat/smt/sat_th.h" +#include "ast/special_relations_decl_plugin.h" + +namespace euf { + class solver; +} + +namespace specrel { + + class solver : public euf::th_euf_solver { + typedef euf::theory_var theory_var; + typedef euf::theory_id theory_id; + typedef euf::enode enode; + typedef euf::enode_pair enode_pair; + typedef euf::enode_pair_vector enode_pair_vector; + typedef sat::bool_var bool_var; + typedef sat::literal literal; + typedef sat::literal_vector literal_vector; + + special_relations_util sp; + + public: + solver(euf::solver& ctx, theory_id id); + ~solver() override; + + bool is_external(bool_var v) override { return false; } + void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override {} + void asserted(literal l) override; + sat::check_result check() override; + + std::ostream& display(std::ostream& out) const override; + std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override { return euf::th_explain::from_index(idx).display(out); } + std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override { return display_justification(out, idx); } + void collect_statistics(statistics& st) const override; + euf::th_solver* clone(euf::solver& ctx) override; + void new_eq_eh(euf::th_eq const& eq) override; + bool unit_propagate() override { return false; } + void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override; + bool add_dep(euf::enode* n, top_sort& dep) override; + bool include_func_interp(func_decl* f) const override; + sat::literal internalize(expr* e, bool sign, bool root) override; + void internalize(expr* e) override; + bool visit(expr* e) override; + bool visited(expr* e) override; + bool post_visit(expr* e, bool sign, bool root) override; + + euf::theory_var mk_var(euf::enode* n) override; + void apply_sort_cnstr(euf::enode* n, sort* s) override {} + bool is_shared(theory_var v) const override { return false; } + lbool get_phase(bool_var v) override { return l_true; } + bool enable_self_propagate() const override { return true; } + + void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); + void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} + void unmerge_eh(theory_var v1, theory_var v2) {} + }; +} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index df3010295..14b51f822 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -39,6 +39,8 @@ add_executable(test-z3 doc.cpp egraph.cpp escaped.cpp + euf_bv_plugin.cpp + euf_arith_plugin.cpp ex.cpp expr_rand.cpp expr_substitution.cpp diff --git a/src/test/euf_arith_plugin.cpp b/src/test/euf_arith_plugin.cpp new file mode 100644 index 000000000..41d629ad5 --- /dev/null +++ b/src/test/euf_arith_plugin.cpp @@ -0,0 +1,106 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +--*/ + +#include "util/util.h" +#include "util/timer.h" +#include "ast/euf/euf_egraph.h" +#include "ast/euf/euf_arith_plugin.h" +#include "ast/reg_decl_plugins.h" +#include "ast/ast_pp.h" +#include + +unsigned s_var = 0; + +static euf::enode* get_node(euf::egraph& g, arith_util& a, expr* e) { + auto* n = g.find(e); + if (n) + return n; + euf::enode_vector args; + for (expr* arg : *to_app(e)) + args.push_back(get_node(g, a, arg)); + n = g.mk(e, 0, args.size(), args.data()); + g.add_th_var(n, s_var++, a.get_family_id()); + return n; +} + +// +static void test1() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::arith_plugin, g)); + arith_util a(m); + sort_ref I(a.mk_int(), m); + + expr_ref x(m.mk_const("x", I), m); + expr_ref y(m.mk_const("y", I), m); + auto* nx = get_node(g, a, a.mk_add(a.mk_add(y, y), a.mk_add(x, x))); + auto* ny = get_node(g, a, a.mk_add(a.mk_add(y, x), x)); + TRACE("plugin", tout << "before merge\n" << g << "\n"); + g.merge(nx, ny, nullptr); + + TRACE("plugin", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("plugin", tout << "after propagate\n" << g << "\n"); + g.merge(get_node(g, a, a.mk_add(x, a.mk_add(y, y))), get_node(g, a, a.mk_add(y, x)), nullptr); + g.propagate(); + std::cout << g << "\n"; +} + +static void test2() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::arith_plugin, g)); + arith_util a(m); + sort_ref I(a.mk_int(), m); + + expr_ref x(m.mk_const("x", I), m); + expr_ref y(m.mk_const("y", I), m); + auto* nxy = get_node(g, a, a.mk_add(x, y)); + auto* nyx = get_node(g, a, a.mk_add(y, x)); + auto* nx = get_node(g, a, x); + auto* ny = get_node(g, a, y); + + TRACE("plugin", tout << "before merge\n" << g << "\n"); + g.merge(nxy, nx, nullptr); + g.merge(nyx, ny, nullptr); + TRACE("plugin", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("plugin", tout << "after propagate\n" << g << "\n"); + SASSERT(nx->get_root() == ny->get_root()); + g.merge(get_node(g, a, a.mk_add(x, a.mk_add(y, y))), get_node(g, a, a.mk_add(y, x)), nullptr); + g.propagate(); + std::cout << g << "\n"; +} + +static void test3() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::arith_plugin, g)); + arith_util a(m); + sort_ref I(a.mk_int(), m); + + expr_ref x(m.mk_const("x", I), m); + expr_ref y(m.mk_const("y", I), m); + auto* nxyy = get_node(g, a, a.mk_add(a.mk_add(x, y), y)); + auto* nyxx = get_node(g, a, a.mk_add(a.mk_add(y, x), x)); + auto* nx = get_node(g, a, x); + auto* ny = get_node(g, a, y); + g.merge(nxyy, nx, nullptr); + g.merge(nyxx, ny, nullptr); + TRACE("plugin", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("plugin", tout << "after propagate\n" << g << "\n"); + std::cout << g << "\n"; +} + +void tst_euf_arith_plugin() { + enable_trace("plugin"); + test1(); + test2(); + test3(); +} diff --git a/src/test/euf_bv_plugin.cpp b/src/test/euf_bv_plugin.cpp new file mode 100644 index 000000000..501bd7b14 --- /dev/null +++ b/src/test/euf_bv_plugin.cpp @@ -0,0 +1,183 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +--*/ + +#include "util/util.h" +#include "util/timer.h" +#include "ast/euf/euf_egraph.h" +#include "ast/euf/euf_bv_plugin.h" +#include "ast/reg_decl_plugins.h" +#include "ast/ast_pp.h" +#include + +static unsigned s_var = 0; +static euf::enode* get_node(euf::egraph& g, bv_util& b, expr* e) { + auto* n = g.find(e); + if (n) + return n; + euf::enode_vector args; + for (expr* arg : *to_app(e)) + args.push_back(get_node(g, b, arg)); + n = g.mk(e, 0, args.size(), args.data()); + g.add_th_var(n, s_var++, b.get_family_id()); + return n; +} + +// align slices, and propagate extensionality +static void test1() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + + expr_ref x(m.mk_const("x", u32), m); + expr_ref y(m.mk_const("y", u32), m); + expr_ref x3(bv.mk_extract(31, 16, x), m); + expr_ref x2(bv.mk_extract(15, 8, x), m); + expr_ref x1(bv.mk_extract(7, 0, x), m); + expr_ref y3(bv.mk_extract(31, 24, y), m); + expr_ref y2(bv.mk_extract(23, 8, y), m); + expr_ref y1(bv.mk_extract(7, 0, y), m); + expr_ref xx(bv.mk_concat(x1, bv.mk_concat(x2, x3)), m); + expr_ref yy(bv.mk_concat(y1, bv.mk_concat(y2, y3)), m); + auto* nx = get_node(g, bv, xx); + auto* ny = get_node(g, bv, yy); + TRACE("bv", tout << "before merge\n" << g << "\n"); + g.merge(nx, ny, nullptr); + TRACE("bv", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("bv", tout << "after propagate\n" << g << "\n"); + std::cout << g << "\n"; + SASSERT(nx->get_root() == ny->get_root()); +} + +// propagate values down +static void test2() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + + expr_ref x(m.mk_const("x", u32), m); + expr_ref x3(bv.mk_extract(31, 16, x), m); + expr_ref x2(bv.mk_extract(15, 8, x), m); + expr_ref x1(bv.mk_extract(7, 0, x), m); + expr_ref xx(bv.mk_concat(x1, bv.mk_concat(x2, x3)), m); + g.merge(get_node(g, bv, xx), get_node(g, bv, bv.mk_numeral((1 << 27) + (1 << 17) + (1 << 3), 32)), nullptr); + g.propagate(); + SASSERT(get_node(g, bv, x1)->get_root()->interpreted()); + SASSERT(get_node(g, bv, x2)->get_root()->interpreted()); + SASSERT(get_node(g, bv, x3)->get_root()->interpreted()); + SASSERT(get_node(g, bv, x)->get_root()->interpreted()); +} + + +// propagate values up +static void test3() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + + expr_ref x(m.mk_const("x", u32), m); + expr_ref x3(bv.mk_extract(31, 16, x), m); + expr_ref x2(bv.mk_extract(15, 8, x), m); + expr_ref x1(bv.mk_extract(7, 0, x), m); + expr_ref xx(bv.mk_concat(bv.mk_concat(x1, x2), x3), m); + expr_ref y(m.mk_const("y", u32), m); + g.merge(get_node(g, bv, xx), get_node(g, bv, y), nullptr); + g.merge(get_node(g, bv, x1), get_node(g, bv, bv.mk_numeral(2, 8)), nullptr); + g.merge(get_node(g, bv, x2), get_node(g, bv, bv.mk_numeral(8, 8)), nullptr); + g.propagate(); + SASSERT(get_node(g, bv, bv.mk_concat(x1, x2))->get_root()->interpreted()); + SASSERT(get_node(g, bv, x1)->get_root()->interpreted()); + SASSERT(get_node(g, bv, x2)->get_root()->interpreted()); +} + +// propagate extract up +static void test4() { + // concat(a, x[J]), a = x[I] => x[IJ] = concat(x[I],x[J]) + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + sort_ref u8(bv.mk_sort(8), m); + sort_ref u16(bv.mk_sort(16), m); + expr_ref a(m.mk_const("a", u8), m); + expr_ref x(m.mk_const("x", u32), m); + expr_ref y(m.mk_const("y", u16), m); + expr_ref x1(bv.mk_extract(15, 8, x), m); + expr_ref x2(bv.mk_extract(23, 16, x), m); + g.merge(get_node(g, bv, bv.mk_concat(a, x2)), get_node(g, bv, y), nullptr); + g.merge(get_node(g, bv, x1), get_node(g, bv, a), nullptr); + g.propagate(); + TRACE("bv", tout << g << "\n"); + SASSERT(get_node(g, bv, bv.mk_extract(23, 8, x))->get_root() == get_node(g, bv, y)->get_root()); +} + +// iterative slicing +static void test5() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + + expr_ref x(m.mk_const("x", u32), m); + expr_ref x1(bv.mk_extract(31, 4, x), m); + expr_ref x2(bv.mk_extract(27, 0, x), m); + auto* nx = get_node(g, bv, x1); + auto* ny = get_node(g, bv, x2); + TRACE("bv", tout << "before merge\n" << g << "\n"); + g.merge(nx, ny, nullptr); + TRACE("bv", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("bv", tout << "after propagate\n" << g << "\n"); + std::cout << g << "\n"; +} + +// iterative slicing +static void test6() { + ast_manager m; + reg_decl_plugins(m); + euf::egraph g(m); + g.add_plugin(alloc(euf::bv_plugin, g)); + bv_util bv(m); + sort_ref u32(bv.mk_sort(32), m); + + expr_ref x(m.mk_const("x", u32), m); + expr_ref x1(bv.mk_extract(31, 3, x), m); + expr_ref x2(bv.mk_extract(28, 0, x), m); + auto* nx = get_node(g, bv, x1); + auto* ny = get_node(g, bv, x2); + TRACE("bv", tout << "before merge\n" << g << "\n"); + g.merge(nx, ny, nullptr); + TRACE("bv", tout << "before propagate\n" << g << "\n"); + g.propagate(); + TRACE("bv", tout << "after propagate\n" << g << "\n"); + std::cout << g << "\n"; +} + + +void tst_euf_bv_plugin() { + enable_trace("bv"); + enable_trace("plugin"); + test6(); + return; + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 7cd4b6cf9..3f073abf2 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -265,4 +265,6 @@ int main(int argc, char ** argv) { TST(finder); TST(totalizer); TST(distribution); + TST(euf_bv_plugin); + TST(euf_arith_plugin); } diff --git a/src/util/dependency.h b/src/util/dependency.h index 57057460c..a76d43f88 100644 --- a/src/util/dependency.h +++ b/src/util/dependency.h @@ -44,8 +44,39 @@ public: public: unsigned get_ref_count() const { return m_ref_count; } bool is_leaf() const { return m_leaf == 1; } + value const& leaf_value() const { SASSERT(is_leaf()); return static_cast(this)->m_value; } }; + static void linearize_todo(ptr_vector& todo, vector& vs) { + unsigned qhead = 0; + while (qhead < todo.size()) { + dependency* d = todo[qhead]; + qhead++; + if (d->is_leaf()) { + vs.push_back(to_leaf(d)->m_value); + } + else { + for (unsigned i = 0; i < 2; i++) { + dependency* child = to_join(d)->m_children[i]; + if (!child->is_marked()) { + todo.push_back(child); + child->mark(); + } + } + } + } + for (auto* d : todo) + d->unmark(); + } + + static void s_linearize(dependency* d, vector& vs) { + if (!d) + return; + ptr_vector todo; + todo.push_back(d); + linearize_todo(todo, vs); + } + private: struct join : public dependency { dependency * m_children[2]; @@ -69,7 +100,7 @@ private: value_manager & m_vmanager; allocator & m_allocator; - mutable ptr_vector m_todo; + ptr_vector m_todo; void inc_ref(value const & v) { if (C::ref_count) @@ -83,6 +114,7 @@ private: void del(dependency * d) { SASSERT(d); + SASSERT(m_todo.empty()); m_todo.push_back(d); while (!m_todo.empty()) { d = m_todo.back(); @@ -106,8 +138,8 @@ private: } } - void unmark_todo() const { - for (auto* d : m_todo) + void unmark_todo() { + for (auto* d : m_todo) d->unmark(); m_todo.reset(); } @@ -190,30 +222,30 @@ public: return false; } - void linearize(dependency * d, vector & vs) const { - if (d) { - m_todo.reset(); - d->mark(); - m_todo.push_back(d); - unsigned qhead = 0; - while (qhead < m_todo.size()) { - d = m_todo[qhead]; - qhead++; - if (d->is_leaf()) { - vs.push_back(to_leaf(d)->m_value); - } - else { - for (unsigned i = 0; i < 2; i++) { - dependency * child = to_join(d)->m_children[i]; - if (!child->is_marked()) { - m_todo.push_back(child); - child->mark(); - } - } - } + + + void linearize(dependency * d, vector & vs) { + if (!d) + return; + SASSERT(m_todo.empty()); + d->mark(); + m_todo.push_back(d); + linearize_todo(m_todo, vs); + m_todo.reset(); + } + + void linearize(ptr_vector& deps, vector & vs) { + if (deps.empty()) + return; + SASSERT(m_todo.empty()); + for (auto* d : deps) { + if (d && !d->is_marked()) { + d->mark(); + m_todo.push_back(d); } - unmark_todo(); } + linearize_todo(m_todo, vs); + m_todo.reset(); } }; @@ -297,7 +329,16 @@ public: return m_dep_manager.contains(d, v); } - void linearize(dependency * d, vector & vs) const { + void linearize(dependency * d, vector & vs) { + return m_dep_manager.linearize(d, vs); + } + + static vector const& s_linearize(dependency* d, vector& vs) { + dep_manager::s_linearize(d, vs); + return vs; + } + + void linearize(ptr_vector& d, vector & vs) { return m_dep_manager.linearize(d, vs); } @@ -320,4 +361,83 @@ typedef scoped_dependency_manager::dependency v_dependency; typedef scoped_dependency_manager u_dependency_manager; typedef scoped_dependency_manager::dependency u_dependency; +/** + \brief Version of the scoped-depenendcy-manager where region scopes are handled externally. +*/ +template +class stacked_dependency_manager { + class config { + public: + static const bool ref_count = true; + + typedef Value value; + + class value_manager { + public: + void inc_ref(value const& v) { + } + + void dec_ref(value const& v) { + } + }; + + class allocator { + region& m_region; + public: + allocator(region& r) : m_region(r) {} + + void* allocate(size_t sz) { + return m_region.allocate(sz); + } + + void deallocate(size_t sz, void* mem) { + } + }; + }; + + typedef dependency_manager dep_manager; +public: + typedef typename dep_manager::dependency dependency; + typedef Value value; + +private: + typename config::value_manager m_vmanager; + typename config::allocator m_allocator; + dep_manager m_dep_manager; + +public: + stacked_dependency_manager(region& r) : + m_allocator(r), + m_dep_manager(m_vmanager, m_allocator) { + } + + dependency* mk_empty() { + return m_dep_manager.mk_empty(); + } + + dependency* mk_leaf(value const& v) { + return m_dep_manager.mk_leaf(v); + } + + dependency* mk_join(dependency* d1, dependency* d2) { + return m_dep_manager.mk_join(d1, d2); + } + + bool contains(dependency* d, value const& v) { + return m_dep_manager.contains(d, v); + } + + void linearize(dependency* d, vector& vs) { + return m_dep_manager.linearize(d, vs); + } + + static vector const& s_linearize(dependency* d, vector& vs) { + dep_manager::s_linearize(d, vs); + return vs; + } + + void linearize(ptr_vector& d, vector& vs) { + return m_dep_manager.linearize(d, vs); + } +}; \ No newline at end of file From 8a0dec1a4b2d05e04e12accac0540f1ca765d521 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 30 Nov 2023 14:08:29 -0800 Subject: [PATCH 375/428] fix build Signed-off-by: Nikolaj Bjorner --- src/util/dependency.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/dependency.h b/src/util/dependency.h index a76d43f88..6094cc555 100644 --- a/src/util/dependency.h +++ b/src/util/dependency.h @@ -100,7 +100,7 @@ private: value_manager & m_vmanager; allocator & m_allocator; - ptr_vector m_todo; + mutable ptr_vector m_todo; void inc_ref(value const & v) { if (C::ref_count) @@ -138,7 +138,7 @@ private: } } - void unmark_todo() { + void unmark_todo() const { for (auto* d : m_todo) d->unmark(); m_todo.reset(); @@ -224,7 +224,7 @@ public: - void linearize(dependency * d, vector & vs) { + void linearize(dependency * d, vector & vs) const { if (!d) return; SASSERT(m_todo.empty()); @@ -234,7 +234,7 @@ public: m_todo.reset(); } - void linearize(ptr_vector& deps, vector & vs) { + void linearize(ptr_vector& deps, vector & vs) const { if (deps.empty()) return; SASSERT(m_todo.empty()); @@ -329,7 +329,7 @@ public: return m_dep_manager.contains(d, v); } - void linearize(dependency * d, vector & vs) { + void linearize(dependency * d, vector & vs) const { return m_dep_manager.linearize(d, vs); } @@ -338,7 +338,7 @@ public: return vs; } - void linearize(ptr_vector& d, vector & vs) { + void linearize(ptr_vector& d, vector & vs) const { return m_dep_manager.linearize(d, vs); } @@ -440,4 +440,4 @@ public: void linearize(ptr_vector& d, vector& vs) { return m_dep_manager.linearize(d, vs); } -}; \ No newline at end of file +}; From 99e2794a6db5aba5db6fd5eea55462e403eb886d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 30 Nov 2023 17:20:39 -0800 Subject: [PATCH 376/428] update output Signed-off-by: Nikolaj Bjorner --- src/sat/smt/q_mbi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index c66f1b3a2..539c4f943 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -498,8 +498,8 @@ namespace q { if (m_model->is_false(eq)) { IF_VERBOSE(0, verbose_stream() << mk_pp(s, m) << " := " << (*m_model)(s) << "\n"; - verbose_stream() << mk_pp(term, m) << " := " << (*m_model)(term) << "\n"; - verbose_stream() << value << " -> " << (*m_model)(ctx.values2root()[value]->get_expr()) << "\n"; + verbose_stream() << term << " := " << (*m_model)(term) << "\n"; + verbose_stream() << value << " -> " << (*m_model)(ctx.values2root()[(*m_model)(term)]->get_expr()) << "\n"; verbose_stream() << (*m_model)(s) << " -> " << (*m_model)(ctx.values2root()[(*m_model)(s)]->get_expr()) << "\n"; verbose_stream() << *m_model << "\n";); } From faf14012ba18d21c1fcddbdc321ac127f019fa03 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 1 Dec 2023 13:32:13 -0800 Subject: [PATCH 377/428] Regressions reported by Guido --- src/ast/rewriter/bool_rewriter.cpp | 2 +- src/ast/rewriter/hoist_rewriter.cpp | 16 +++++++++++----- src/smt/mam.cpp | 5 ++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 9354b53f6..a92799c4c 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -282,7 +282,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args get_num_internal_exprs(m_counts2, m_todo2, args[i]); unsigned count1 = count_internal_nodes(m_counts1, m_todo1); unsigned count2 = count_internal_nodes(m_counts2, m_todo2); - if (count1 > count2) + if (count1 > count2 + num_args) st = BR_FAILED; } if (st != BR_FAILED) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 72a764bfa..7b217fc24 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -137,11 +137,17 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & m_subst.insert(p, m.mk_true()); fmls.push_back(p); } - for (auto& p : m_eqs) { - if (m.is_value(p.first)) - std::swap(p.first, p.second); - m_subst.insert(p.first, p.second); - fmls.push_back(m.mk_eq(p.first, p.second)); + for (auto& [a, b] : m_eqs) { + if (m.is_value(a)) + std::swap(a, b); + if (m.are_equal(a, b)) + continue; + if (m.are_distinct(a, b)) { + result = m.mk_false(); + return BR_DONE; + } + m_subst.insert(a, b); + fmls.push_back(m.mk_eq(a, b)); } expr_ref ors(::mk_or(m, num_args, es), m); m_subst(ors); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 3804b7228..b9ac45039 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -2413,7 +2413,10 @@ namespace { m_n2 = static_cast(m_pc)->m_enode; SASSERT(m_n1 != 0); SASSERT(m_n2 != 0); - if (m_n1->get_root() != m_n2->get_root()) + + // hack to handle dynamically generated patterns: + // if the pattern is ground and an if-expression, ignore equality check. + if (m_n1->get_root() != m_n2->get_root() && !m.is_ite(m_n2->get_expr())) goto backtrack; // we used the equality m_n1 = m_n2 for the match and need to make sure it ends up in the log From a15a7cee7bd7528e1e5b8e29f77083a03daa4ad8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 1 Dec 2023 14:13:05 -0800 Subject: [PATCH 378/428] touch Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/hoist_rewriter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 7b217fc24..1b99469a1 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -137,6 +137,7 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & m_subst.insert(p, m.mk_true()); fmls.push_back(p); } + bool new_eq = false; for (auto& [a, b] : m_eqs) { if (m.is_value(a)) std::swap(a, b); @@ -146,6 +147,7 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result = m.mk_false(); return BR_DONE; } + new_eq = true; m_subst.insert(a, b); fmls.push_back(m.mk_eq(a, b)); } @@ -154,7 +156,7 @@ br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & fmls.push_back(ors); result = mk_and(fmls); TRACE("hoist", tout << ors << " => " << result << "\n";); - return BR_DONE; + return new_eq ? BR_REWRITE3 : BR_DONE; } unsigned hoist_rewriter::mk_var(expr* e) { From ed5ab54ab60657f7e646346860c005889ae18fa5 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sat, 2 Dec 2023 18:47:57 +0100 Subject: [PATCH 379/428] CMake: Improve handling of git hash/describe (#7028) Only check for and depend on the .git folder if requested. Only warn about disabling when enabled. Only add git hash to version if valid. --- CMakeLists.txt | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8144e1328..0ab59f18f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,19 +41,22 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/git_utils.cmake) macro(disable_git_describe) - message(WARNING "Disabling Z3_INCLUDE_GIT_DESCRIBE") - set(Z3_INCLUDE_GIT_DESCRIBE OFF CACHE BOOL "Include git describe output in version output" FORCE) + if(Z3_INCLUDE_GIT_DESCRIBE) + message(WARNING "Disabling Z3_INCLUDE_GIT_DESCRIBE") + set(Z3_INCLUDE_GIT_DESCRIBE OFF CACHE BOOL "Include git describe output in version output" FORCE) + endif() endmacro() macro(disable_git_hash) - message(WARNING "Disabling Z3_INCLUDE_GIT_HASH") - set(Z3_INCLUDE_GIT_HASH OFF CACHE BOOL "Include git hash in version output" FORCE) - unset(Z3GITHASH) # Used in configure_file() + if(Z3_INCLUDE_GIT_HASH) + message(WARNING "Disabling Z3_INCLUDE_GIT_HASH") + set(Z3_INCLUDE_GIT_HASH OFF CACHE BOOL "Include git hash in version output" FORCE) + endif() endmacro() option(Z3_INCLUDE_GIT_HASH "Include git hash in version output" ON) option(Z3_INCLUDE_GIT_DESCRIBE "Include git describe output in version output" ON) set(GIT_DIR "${PROJECT_SOURCE_DIR}/.git") -if (EXISTS "${GIT_DIR}") +if ((Z3_INCLUDE_GIT_HASH OR Z3_INCLUDE_GIT_HASH) AND EXISTS "${GIT_DIR}") # Try to make CMake configure depend on the current git HEAD so that # a re-configure is triggered when the HEAD changes. add_git_dir_dependency("${GIT_DIR}" ADD_GIT_DEP_SUCCESS) @@ -63,13 +66,13 @@ if (EXISTS "${GIT_DIR}") if (NOT Z3GITHASH) message(WARNING "Failed to get Git hash") disable_git_hash() + else() + message(STATUS "Using Git hash in version output: ${Z3GITHASH}") + # This mimics the behaviour of the old build system. + set(Z3_FULL_VERSION_STR "${Z3_FULL_VERSION_STR} ${Z3GITHASH}") endif() - message(STATUS "Using Git hash in version output: ${Z3GITHASH}") - # This mimics the behaviour of the old build system. - set(Z3_FULL_VERSION_STR "${Z3_FULL_VERSION_STR} ${Z3GITHASH}") else() message(STATUS "Not using Git hash in version output") - unset(Z3GITHASH) # Used in configure_file() endif() if (Z3_INCLUDE_GIT_DESCRIBE) get_git_head_describe("${GIT_DIR}" Z3_GIT_DESCRIPTION) @@ -93,6 +96,9 @@ else() disable_git_describe() disable_git_hash() endif() +if(NOT Z3_INCLUDE_GIT_HASH) + unset(Z3GITHASH) # Used in configure_file() +endif() ################################################################################ # Useful CMake functions/Macros From 5c1e7f711211e47e2710ac2c1caf35acf7dd1dec Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 10:46:29 -0800 Subject: [PATCH 380/428] fix #7029 --- src/ast/rewriter/arith_rewriter.cpp | 68 +++++++++++++++++++---------- src/ast/rewriter/arith_rewriter.h | 6 ++- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index dee5b7f44..44b91826f 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -551,25 +551,10 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin } if (m_anum_simp) { - if (is_numeral(arg1, a1) && m_util.is_irrational_algebraic_numeral(arg2)) { - anum_manager & am = m_util.am(); - scoped_anum v1(am); - am.set(v1, a1.to_mpq()); - anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); - ANUM_LE_GE_EQ(); - } - if (m_util.is_irrational_algebraic_numeral(arg1) && is_numeral(arg2, a2)) { - anum_manager & am = m_util.am(); - anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); - scoped_anum v2(am); - am.set(v2, a2.to_mpq()); - ANUM_LE_GE_EQ(); - } - if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) { - anum_manager & am = m_util.am(); - anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); - anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); - ANUM_LE_GE_EQ(); + auto& am = m_util.am(); + scoped_anum v1(am), v2(am); + if (is_algebraic_numeral(arg1, v1) && is_algebraic_numeral(arg2, v2)) { + ANUM_LE_GE_EQ(); } } br_status st1 = is_separated(arg1, arg2, kind, result); @@ -669,6 +654,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin return BR_FAILED; } + br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result) { return mk_le_ge_eq_core(arg1, arg2, LE, result); } @@ -744,18 +730,26 @@ bool arith_rewriter::mk_eq_mod(expr* arg1, expr* arg2, expr_ref& result) { if (g == 1) { expr_ref nb(m_util.mk_numeral(b, true), m); result = m.mk_eq(m_util.mk_mod(u, y), - m_util.mk_mod(m_util.mk_mul(nb, arg2), y)); + m_util.mk_mod(m_util.mk_mul(nb, arg2), y)); return true; } } return false; } -expr_ref arith_rewriter::neg_monomial(expr* e) const { +expr_ref arith_rewriter::neg_monomial(expr* e) { expr_ref_vector args(m); rational a1; if (m_util.is_numeral(e, a1)) args.push_back(m_util.mk_numeral(-a1, e->get_sort())); + else if (m_util.is_irrational_algebraic_numeral(e)) { + auto& n = m_util.to_irrational_algebraic_numeral(e); + auto& am = m_util.am(); + scoped_anum new_n(am); + am.set(new_n, n); + am.neg(new_n); + args.push_back(m_util.mk_numeral(am, new_n, m_util.is_int(e))); + } else if (is_app(e) && m_util.is_mul(e)) { if (is_numeral(to_app(e)->get_arg(0), a1)) { if (!a1.is_minus_one()) { @@ -780,7 +774,7 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const { } } -bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const { +bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) { rational r; if (m_util.is_mul(t) && is_numeral(to_app(t)->get_arg(0), r) && r.is_neg()) { neg = neg_monomial(t); @@ -824,6 +818,36 @@ bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args) return false; } +bool arith_rewriter::is_algebraic_numeral(expr* n, scoped_anum& a) { + auto& am = m_util.am(); + expr* x, *y; + rational r; + if (m_util.is_mul(n, x, y)) { + scoped_anum ax(am), ay(am); + if (is_algebraic_numeral(x, ax) && is_algebraic_numeral(y, ay)) { + am.mul(ax, ay, a); + return true; + } + } + else if (m_util.is_add(n, x, y)) { + scoped_anum ax(am), ay(am); + if (is_algebraic_numeral(x, ax) && is_algebraic_numeral(y, ay)) { + am.add(ax, ay, a); + return true; + } + } + else if (m_util.is_numeral(n, r)) { + am.set(a, r.to_mpq()); + return true; + } + else if (m_util.is_irrational_algebraic_numeral(n)) { + am.set(a, m_util.to_irrational_algebraic_numeral(n)); + return true; + } + return false; +} + + br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { expr_ref_buffer new_args(m); diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index 3cd9d6165..edc84b25a 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -21,6 +21,7 @@ Notes: #include "ast/rewriter/poly_rewriter.h" #include "ast/arith_decl_plugin.h" #include "ast/seq_decl_plugin.h" +#include "math/polynomial/algebraic_numbers.h" class arith_rewriter_core { protected: @@ -80,6 +81,7 @@ class arith_rewriter : public poly_rewriter { void updt_local_params(params_ref const & p); bool is_anum_simp_target(unsigned num_args, expr * const * args); + bool is_algebraic_numeral(expr* n, scoped_anum& a); br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result); br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result); @@ -97,8 +99,8 @@ class arith_rewriter : public poly_rewriter { bool is_2_pi_integer_offset(expr * t, expr * & m); bool is_pi_integer(expr * t); bool is_pi_integer_offset(expr * t, expr * & m); - bool is_neg_poly(expr* e, expr_ref& neg) const; - expr_ref neg_monomial(expr * e) const; + bool is_neg_poly(expr* e, expr_ref& neg); + expr_ref neg_monomial(expr * e); expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); bool divides(expr* d, expr* n, expr_ref& result); From 7eab26e3ef365cfab076dd13c23d9512dba16d08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 10:48:35 -0800 Subject: [PATCH 381/428] try with missed bounds --- src/math/lp/nla_grobner.cpp | 40 +++++++++++++++++++++++++++++++++++++ src/math/lp/nla_grobner.h | 2 ++ 2 files changed, 42 insertions(+) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 92c02ab80..a95aa766d 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -399,9 +399,49 @@ namespace nla { for (auto eq : m_solver.equations()) if (propagate_linear_equations(*eq)) ++changed; +#if 0 + for (auto eq : m_solver.equations()) + if (check_missed_bound(*eq)) + return true; +#endif return changed > 0; } + bool grobner::check_missed_bound(dd::solver::equation const& e) { + auto& di = c().m_intervals.get_dep_intervals(); + auto set_var_interval = [&](lpvar j, scoped_dep_interval& a) { + c().m_intervals.set_var_interval(j, a); + }; + scoped_dep_interval i(di), t(di), s(di), u(di); + di.set_value(i, rational::zero()); + + for (auto const& [coeff, vars] : e.poly()) { + if (vars.empty()) + di.add(coeff, i); + else { + di.set_value(t, rational::one()); + for (auto v : vars) { + set_var_interval(v, s); + di.mul(coeff, s, s); + di.add(t, s, t); + } + if (m_mon2var.find(vars) != m_mon2var.end()) { + auto v = m_mon2var.find(vars)->second; + set_var_interval(v, u); + di.intersect(t, u, t); + } + di.add(i, t, i); + } + } + if (!di.separated_from_zero(i)) + return false; + std::function f = [this](const lp::explanation& e) { + new_lemma lemma(m_core, "pdd"); + lemma &= e; + }; + return di.check_interval_for_conflict_on_zero(i, e.dep(), f); + } + bool grobner::propagate_linear_equations(dd::solver::equation const& e) { if (equation_is_true(e)) return false; diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index bf5ea8dcf..e70b5473d 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -47,6 +47,8 @@ namespace nla { bool propagate_linear_equations(); bool propagate_linear_equations(dd::solver::equation const& eq); + + bool check_missed_bound(dd::solver::equation const& eq); void add_dependencies(new_lemma& lemma, dd::solver::equation const& eq); void explain(dd::solver::equation const& eq, lp::explanation& exp); From 331507c4cd9fbc06cdfede50c586217155bb6320 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 12:05:06 -0800 Subject: [PATCH 382/428] #7027 Signed-off-by: Nikolaj Bjorner --- src/sat/smt/arith_solver.cpp | 15 ++++++++++----- src/sat/smt/euf_internalize.cpp | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index a32a4e964..9cf41412a 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -53,17 +53,22 @@ namespace arith { euf::th_solver* solver::clone(euf::solver& dst_ctx) { arith::solver* result = alloc(arith::solver, dst_ctx, get_id()); + unsigned_vector var2var; + for (unsigned i = 0; i < result->get_num_vars(); ++i) + var2var.push_back(i); + for (unsigned i = result->get_num_vars(); i < get_num_vars(); ++i) - result->mk_evar(ctx.copy(dst_ctx, var2enode(i))->get_expr()); + var2var.push_back(result->mk_evar(ctx.copy(dst_ctx, var2enode(i))->get_expr())); unsigned v = 0; result->m_bounds.resize(m_bounds.size()); for (auto const& bounds : m_bounds) { + auto w = var2var[v]; for (auto* b : bounds) { - auto* b2 = result->mk_var_bound(b->get_lit(), v, b->get_bound_kind(), b->get_value()); - result->m_bounds[v].push_back(b2); - result->m_bounds_trail.push_back(v); - result->updt_unassigned_bounds(v, +1); + auto* b2 = result->mk_var_bound(b->get_lit(), w, b->get_bound_kind(), b->get_value()); + result->m_bounds[w].push_back(b2); + result->m_bounds_trail.push_back(w); + result->updt_unassigned_bounds(w, +1); result->m_bool_var2bound.insert(b->get_lit().var(), b2); result->m_new_bounds.push_back(b2); } diff --git a/src/sat/smt/euf_internalize.cpp b/src/sat/smt/euf_internalize.cpp index 22e220eaa..943d0b324 100644 --- a/src/sat/smt/euf_internalize.cpp +++ b/src/sat/smt/euf_internalize.cpp @@ -425,7 +425,7 @@ namespace euf { // not marked as shared. for (auto const& p : euf::enode_th_vars(n)) - if (fid2solver(p.get_id())->is_shared(p.get_var())) { + if (fid2solver(p.get_id()) && fid2solver(p.get_id())->is_shared(p.get_var())) { n->set_is_shared(l_true); return true; } @@ -436,7 +436,7 @@ namespace euf { bool solver::is_beta_redex(enode* p, enode* n) const { for (auto const& th : enode_th_vars(p)) - if (fid2solver(th.get_id())->is_beta_redex(p, n)) + if (fid2solver(th.get_id()) && fid2solver(th.get_id())->is_beta_redex(p, n)) return true; return false; } From 585d02766883de3f7087fc65d742ade814d45f8e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 14:12:41 -0800 Subject: [PATCH 383/428] remove assert #7032 Signed-off-by: Nikolaj Bjorner --- src/math/lp/lar_solver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index aca888d18..15d03fb86 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1158,7 +1158,6 @@ namespace lp { if (!v.y.is_zero()) { v = impq(v.x + delta * v.y); TRACE("lar_solver_feas", tout << "x[" << j << "] = " << v << "\n";); - SASSERT(!column_is_int(j) || v.is_int()); } } } From ba8d8f0af746040f9c39f9da35c6acf30d3c0409 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 15:40:47 -0800 Subject: [PATCH 384/428] Disable hoist entirely, it is bad on QF_LIA and does not help on other observed cases --- src/ast/rewriter/bool_rewriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index a92799c4c..7728716fd 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -270,7 +270,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args return BR_DONE; } -#if 1 +#if 0 br_status st; expr_ref r(m()); st = m_hoist.mk_or(buffer.size(), buffer.data(), r); @@ -282,7 +282,7 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args get_num_internal_exprs(m_counts2, m_todo2, args[i]); unsigned count1 = count_internal_nodes(m_counts1, m_todo1); unsigned count2 = count_internal_nodes(m_counts2, m_todo2); - if (count1 > count2 + num_args) + if (count1 > count2) st = BR_FAILED; } if (st != BR_FAILED) From 362d299a5ccfd6f4daad533d8209a1a28c9b0463 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 19:34:36 -0800 Subject: [PATCH 385/428] #7027 --- src/sat/smt/arith_solver.cpp | 9 ++++----- src/sat/smt/euf_solver.h | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 9cf41412a..2be9b6b60 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -60,11 +60,11 @@ namespace arith { for (unsigned i = result->get_num_vars(); i < get_num_vars(); ++i) var2var.push_back(result->mk_evar(ctx.copy(dst_ctx, var2enode(i))->get_expr())); - unsigned v = 0; - result->m_bounds.resize(m_bounds.size()); - for (auto const& bounds : m_bounds) { + result->m_bounds.resize(get_num_vars()); + unsigned nv = std::min(m_bounds.size(), get_num_vars()); + for (unsigned v = 0; v < nv; ++v) { auto w = var2var[v]; - for (auto* b : bounds) { + for (auto* b : m_bounds[v]) { auto* b2 = result->mk_var_bound(b->get_lit(), w, b->get_bound_kind(), b->get_value()); result->m_bounds[w].push_back(b2); result->m_bounds_trail.push_back(w); @@ -72,7 +72,6 @@ namespace arith { result->m_bool_var2bound.insert(b->get_lit().var(), b2); result->m_new_bounds.push_back(b2); } - ++v; } // clone rows into m_solver, m_nla, m_lia diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index a9dc20e0e..4121b68c9 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -348,6 +348,7 @@ namespace euf { bool is_external(bool_var v) override; bool propagated(literal l, ext_constraint_idx idx) override; bool unit_propagate() override; + bool should_propagate() override; bool should_research(sat::literal_vector const& core) override; void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; From b22daa98163b877bebe24f2d93e28d1e2f454a9f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 19:39:43 -0800 Subject: [PATCH 386/428] missing header --- src/sat/sat_extension.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index ae99cae12..3bb190cf4 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -87,6 +87,7 @@ namespace sat { virtual void init_search() {} virtual bool propagated(sat::literal l, sat::ext_constraint_idx idx) { UNREACHABLE(); return false; } virtual bool unit_propagate() = 0; + virtual bool should_propagate() { return false; } virtual bool is_external(bool_var v) { return false; } virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const { return 0; } virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) = 0; From 1de25ed09c0f3cf80d921a208946e0a15fa63c3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 19:43:51 -0800 Subject: [PATCH 387/428] pending files --- src/ast/euf/euf_egraph.cpp | 47 ++++++++++++++++++++++---------------- src/sat/sat_solver.cpp | 31 ++++++++++++------------- src/sat/smt/euf_solver.cpp | 12 +++++++--- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 154106e23..c32a81739 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -81,14 +81,14 @@ namespace euf { } void egraph::reinsert_equality(enode* p) { - SASSERT(p->is_equality()); + SASSERT(p->is_equality()); if (p->value() != l_true && p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) queue_literal(p, nullptr); } - void egraph::queue_literal(enode* p, enode* ante) { + void egraph::queue_literal(enode* p, enode* ante) { if (m_on_propagate_literal) - m_to_merge.push_back({ p, ante }); + m_to_merge.push_back(to_merge(p, ante)); } void egraph::force_push() { @@ -180,6 +180,7 @@ namespace euf { } void egraph::add_literal(enode* n, enode* ante) { + TRACE("euf", tout << "propagate " << bpp(n) << " " << bpp(ante) << "\n"); if (!m_on_propagate_literal) return; if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; @@ -518,7 +519,7 @@ namespace euf { void egraph::remove_parents(enode* r) { TRACE("euf", tout << bpp(r) << "\n"); - DEBUG_CODE(for (enode* p : enode_parents(r)) SASSERT(!p->is_marked1()); ); + SASSERT(all_of(enode_parents(r), [&](enode* p) { return !p->is_marked1(); })); for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; @@ -545,7 +546,7 @@ namespace euf { if (p->cgc_enabled()) { auto [p_other, comm] = insert_table(p); SASSERT(m_table.contains_ptr(p) == (p_other == p)); - TRACE("euf", tout << "other " << bpp(p_other) << "\n";); + CTRACE("euf", p_other != p, tout << "reinsert " << bpp(p) << " == " << bpp(p_other) << " " << p->value() << " " << p_other->value() << "\n"); if (p_other != p) m_to_merge.push_back(to_merge(p_other, p, comm)); else @@ -606,20 +607,26 @@ namespace euf { bool egraph::propagate() { force_push(); - for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { - auto const& w = m_to_merge[i]; - switch (w.t) { - case to_merge_plain: - case to_merge_comm: - merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++)); - break; - case to_justified: - merge(w.a, w.b, w.j); - break; - case to_add_literal: - add_literal(w.a, w.b); - break; - } + unsigned i = 0; + bool change = true; + while (change) { + change = false; + propagate_plugins(); + for (; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) { + auto const& w = m_to_merge[i]; + switch (w.t) { + case to_merge_plain: + case to_merge_comm: + merge(w.a, w.b, justification::congruence(w.commutativity(), m_congruence_timestamp++)); + break; + case to_justified: + merge(w.a, w.b, w.j); + break; + case to_add_literal: + add_literal(w.a, w.b); + break; + } + } } m_to_merge.reset(); return @@ -635,7 +642,7 @@ namespace euf { m_updates.push_back(update_record(false, update_record::inconsistent())); m_n1 = n1; m_n2 = n2; - TRACE("euf", tout << "conflict " << bpp(n1) << " " << bpp(n2) << " " << j << "\n"); + TRACE("euf", tout << "conflict " << bpp(n1) << " " << bpp(n2) << " " << j << " " << n1->get_root()->value() << " " << n2->get_root()->value() << "\n"); m_justification = j; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index b899ee479..ba5170c7f 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -962,6 +962,8 @@ namespace sat { // ----------------------- bool solver::propagate_core(bool update) { + if (m_ext && (!is_probing() || at_base_lvl())) + m_ext->unit_propagate(); while (m_qhead < m_trail.size() && !m_inconsistent) { do { checkpoint(); @@ -1783,7 +1785,7 @@ namespace sat { } bool solver::should_propagate() const { - return !inconsistent() && m_qhead < m_trail.size(); + return !inconsistent() && (m_qhead < m_trail.size() || (m_ext && m_ext->should_propagate())); } lbool solver::final_check() { @@ -2533,17 +2535,8 @@ namespace sat { case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js, false); TRACE("sat", tout << "ext antecedents: " << m_ext_antecedents << "\n";); - for (literal l : m_ext_antecedents) - process_antecedent(l, num_marks); - -#if 0 - if (m_ext_antecedents.size() <= 1) { - for (literal& l : m_ext_antecedents) - l.neg(); - m_ext_antecedents.push_back(consequent); - mk_clause(m_ext_antecedents.size(), m_ext_antecedents.c_ptr(), sat::status::redundant()); - } -#endif + for (literal l : m_ext_antecedents) + process_antecedent(l, num_marks); break; } default: @@ -2822,25 +2815,27 @@ namespace sat { switch (js.get_kind()) { case justification::NONE: level = std::max(level, js.level()); - return level; + break; case justification::BINARY: level = update_max_level(js.get_literal(), level, unique_max); - return level; + break; case justification::CLAUSE: for (literal l : get_clause(js)) level = update_max_level(l, level, unique_max); - return level; + break; case justification::EXT_JUSTIFICATION: if (not_l != null_literal) not_l.neg(); fill_ext_antecedents(not_l, js, true); for (literal l : m_ext_antecedents) level = update_max_level(l, level, unique_max); - return level; + break; default: UNREACHABLE(); - return 0; + break; } + TRACE("sat", tout << "max-level " << level << " " << unique_max << "\n"); + return level; } /** @@ -3493,6 +3488,8 @@ namespace sat { SASSERT(!inconsistent()); TRACE("sat_verbose", tout << "q:" << m_qhead << " trail: " << m_trail.size() << "\n";); SASSERT(m_qhead == m_trail.size()); + if (m_ext) + m_ext->unit_propagate(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); m_scope_lvl++; diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8e4bd765d..f4a563876 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -435,6 +435,9 @@ namespace euf { } + bool solver::should_propagate() { + return m_egraph.can_propagate(); + } bool solver::unit_propagate() { bool propagated = false; @@ -477,6 +480,7 @@ namespace euf { SASSERT(m.is_bool(e)); size_t cnstr; literal lit; + if (!ante) { VERIFY(m.is_eq(e, a, b)); cnstr = eq_constraint().to_index(); @@ -494,7 +498,7 @@ namespace euf { if (val == l_undef) { SASSERT(m.is_value(ante->get_expr())); val = m.is_true(ante->get_expr()) ? l_true : l_false; - } + } auto& c = lit_constraint(ante); cnstr = c.to_index(); lit = literal(v, val == l_false); @@ -1012,8 +1016,10 @@ namespace euf { return out << "euf conflict"; case constraint::kind_t::eq: return out << "euf equality propagation"; - case constraint::kind_t::lit: - return out << "euf literal propagation " << m_egraph.bpp(c.node()) ; + case constraint::kind_t::lit: { + euf::enode* n = c.node(); + return out << "euf literal propagation " << (sat::literal(n->bool_var(), n->value() == l_false)) << " " << m_egraph.bpp(n); + } default: UNREACHABLE(); return out; From 965bee5801e95054d74d62a9749ba031e6179999 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Dec 2023 19:52:59 -0800 Subject: [PATCH 388/428] fix build --- src/sat/sat_extension.h | 2 +- src/sat/sat_solver.cpp | 2 +- src/sat/smt/euf_solver.cpp | 2 +- src/sat/smt/euf_solver.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 3bb190cf4..b42cc33e2 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -87,7 +87,7 @@ namespace sat { virtual void init_search() {} virtual bool propagated(sat::literal l, sat::ext_constraint_idx idx) { UNREACHABLE(); return false; } virtual bool unit_propagate() = 0; - virtual bool should_propagate() { return false; } + virtual bool can_propagate() { return false; } virtual bool is_external(bool_var v) { return false; } virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const { return 0; } virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) = 0; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index ba5170c7f..96b3c13c4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1785,7 +1785,7 @@ namespace sat { } bool solver::should_propagate() const { - return !inconsistent() && (m_qhead < m_trail.size() || (m_ext && m_ext->should_propagate())); + return !inconsistent() && (m_qhead < m_trail.size() || (m_ext && m_ext->can_propagate())); } lbool solver::final_check() { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index f4a563876..3ae4425fc 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -435,7 +435,7 @@ namespace euf { } - bool solver::should_propagate() { + bool solver::can_propagate() { return m_egraph.can_propagate(); } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 4121b68c9..db99ec512 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -348,7 +348,7 @@ namespace euf { bool is_external(bool_var v) override; bool propagated(literal l, ext_constraint_idx idx) override; bool unit_propagate() override; - bool should_propagate() override; + bool can_propagate() override; bool should_research(sat::literal_vector const& core) override; void add_assumptions(sat::literal_set& assumptions) override; bool tracking_assumptions() override; From a8f3396b24740f157bdf12c60ed51fce24216005 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 10:34:16 -0800 Subject: [PATCH 389/428] #7033 --- src/qe/qsat.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index daeb09b8a..a4d32b505 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -85,8 +85,10 @@ namespace qe { if (m_flevel.find(a->get_decl(), lvl)) { lvl0.merge(lvl); } - for (unsigned i = 0; i < a->get_num_args(); ++i) { - app* arg = to_app(a->get_arg(i)); + for (expr* f : *a) { + if (!is_app(f)) + throw tactic_exception("atom is non-ground"); + app* arg = to_app(f); if (m_elevel.find(arg, lvl)) { lvl0.merge(lvl); } @@ -265,12 +267,9 @@ namespace qe { continue; } - unsigned sz = a->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* f = a->get_arg(i); - if (!mark.is_marked(f)) { - todo.push_back(f); - } + for (expr* f : *a) { + if (!mark.is_marked(f)) + todo.push_back(f); } bool is_boolop = @@ -324,8 +323,8 @@ namespace qe { unsigned sz = a->get_num_args(); bool diff = false; args.reset(); - for (unsigned i = 0; i < sz; ++i) { - expr* f = a->get_arg(i), *f1; + for (expr* f : *a) { + expr *f1; if (cache.find(f, f1)) { args.push_back(f1); diff |= f != f1; @@ -413,8 +412,8 @@ namespace qe { unsigned sz = a->get_num_args(); args.reset(); bool diff = false; - for (unsigned i = 0; i < sz; ++i) { - expr* f = a->get_arg(i), *f1; + for (expr* f : *a) { + expr *f1; if (cache.find(f, f1)) { args.push_back(f1); diff |= f != f1; @@ -1018,12 +1017,12 @@ namespace qe { expr_ref_vector args(m); unsigned num_args = a->get_num_args(); bool all_visited = true; - for (unsigned i = 0; i < num_args; ++i) { - if (visited.find(a->get_arg(i), r)) { + for (expr* arg : *a) { + if (visited.find(arg, r)) { args.push_back(r); } else { - todo.push_back(a->get_arg(i)); + todo.push_back(arg); all_visited = false; } } From 9cc2ce42f78fbb65982e097cac7f02113f95e366 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 11:14:18 -0800 Subject: [PATCH 390/428] #7027 fix lossy function declaration inclusion functionality exposed when fixing a bug for incomplete model generation. --- src/sat/smt/dt_solver.cpp | 17 ++++++++--------- src/sat/smt/dt_solver.h | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sat/smt/dt_solver.cpp b/src/sat/smt/dt_solver.cpp index 22e446a13..daecb7325 100644 --- a/src/sat/smt/dt_solver.cpp +++ b/src/sat/smt/dt_solver.cpp @@ -554,12 +554,12 @@ namespace dt { } // Assuming `app` is equal to a constructor term, return the constructor enode - inline euf::enode* solver::oc_get_cstor(enode* app) { + inline euf::enode* solver::oc_get_cstor(enode* app) const { theory_var v = app->get_root()->get_th_var(get_id()); - SASSERT(v != euf::null_theory_var); + if (v == euf::null_theory_var) + return nullptr; v = m_find.find(v); var_data* d = m_var_data[v]; - SASSERT(d->m_constructor); return d->m_constructor; } @@ -783,7 +783,7 @@ namespace dt { if (v == euf::null_theory_var) return false; euf::enode* con = m_var_data[m_find.find(v)]->m_constructor; - CTRACE("dt", !con, display(tout) << ctx.bpp(n) << "\n";); + TRACE("dt", display(tout) << ctx.bpp(n) << " con: " << ctx.bpp(con) << "\n";); if (con->num_args() == 0) dep.insert(n, nullptr); for (enode* arg : euf::enode_args(con)) @@ -794,16 +794,15 @@ namespace dt { bool solver::include_func_interp(func_decl* f) const { if (!dt.is_accessor(f)) return false; - func_decl* con = dt.get_accessor_constructor(f); - for (enode* app : ctx.get_egraph().enodes_of(f)) { - enode* arg = app->get_arg(0)->get_root(); - if (is_constructor(arg) && arg->get_decl() != con) + func_decl* con_decl = dt.get_accessor_constructor(f); + for (enode* app : ctx.get_egraph().enodes_of(f)) { + enode* con = oc_get_cstor(app->get_arg(0)); + if (con && is_constructor(con) && con->get_decl() != con_decl) return true; } return false; } - sat::literal solver::internalize(expr* e, bool sign, bool root) { if (!visit_rec(m, e, sign, root)) return sat::null_literal; diff --git a/src/sat/smt/dt_solver.h b/src/sat/smt/dt_solver.h index 51a7679fd..b2cbba63b 100644 --- a/src/sat/smt/dt_solver.h +++ b/src/sat/smt/dt_solver.h @@ -116,7 +116,7 @@ namespace dt { void pop_core(unsigned n) override; - enode * oc_get_cstor(enode * n); + enode * oc_get_cstor(enode * n) const; bool occurs_check(enode * n); bool occurs_check_enter(enode * n); void occurs_check_explain(enode * top, enode * root); From 25dd29907b652bd22d2470bc0ca918c4d15a0b70 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 12:41:21 -0800 Subject: [PATCH 391/428] refine no-effect predicate to include value of ret --- src/math/lp/nla_core.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 570f51cc5..96f1b4a30 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1418,20 +1418,17 @@ void core::patch_monomial(lpvar j) { void core::patch_monomials_on_to_refine() { // the rest of the function might change m_to_refine, so have to copy unsigned_vector to_refine; - for (unsigned j :m_to_refine) { + for (unsigned j : m_to_refine) to_refine.push_back(j); - } unsigned sz = to_refine.size(); unsigned start = random(); - for (unsigned i = 0; i < sz; i++) { + for (unsigned i = 0; i < sz && !m_to_refine.empty(); i++) patch_monomial(to_refine[(start + i) % sz]); - if (m_to_refine.size() == 0) - break; - } + TRACE("nla_solver", tout << "sz = " << sz << ", m_to_refine = " << m_to_refine.size() << - (sz > m_to_refine.size()? " less" : "same" ) << "\n";); + (sz > m_to_refine.size()? " less" : " same" ) << "\n";); } void core::patch_monomials() { @@ -1551,7 +1548,7 @@ lbool core::check() { bool run_horner = need_run_horner(); bool run_bounds = params().arith_nl_branching(); - auto no_effect = [&]() { return !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; }; + auto no_effect = [&]() { return ret == l_undef && !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; }; if (no_effect()) m_monomial_bounds.propagate(); @@ -1585,7 +1582,7 @@ lbool core::check() { if (no_effect()) m_divisions.check(); - if (no_effect() && ret == l_undef) { + if (no_effect()) { std::function check1 = [&]() { m_order.order_lemma(); }; std::function check2 = [&]() { m_monotone.monotonicity_lemma(); }; std::function check3 = [&]() { m_tangents.tangent_lemma(); }; @@ -1601,7 +1598,7 @@ lbool core::check() { ret = bounded_nlsat(); } - if (no_effect() && params().arith_nl_nra() && ret == l_undef) { + if (no_effect() && params().arith_nl_nra()) { ret = m_nra.check(); lp_settings().stats().m_nra_calls++; } From f06e07ad0a111e3de7cb1a203746a510ea631213 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 12:42:42 -0800 Subject: [PATCH 392/428] fix cone of influence computation for terms with nested variables exposed by #7027, but generally missing. It is less likely to be exposed if input is normalized by distributing multiplication over addition. --- src/math/lp/nra_solver.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 5661f2e89..ccd95e6be 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -45,6 +45,7 @@ struct solver::imp { struct occurs { unsigned_vector constraints; unsigned_vector monics; + unsigned_vector terms; }; void init_cone_of_influence() { @@ -70,6 +71,15 @@ struct solver::imp { } } + for (unsigned i = lra.terms().size(); i-- > 0; ) { + auto const& t = lra.term(i); + for (auto const iv : t) { + auto v = iv.column().index(); + var2occurs.reserve(v + 1); + var2occurs[v].terms.push_back(i); + } + } + for (auto const& m : m_nla_core.m_to_refine) todo.push_back(m); @@ -88,12 +98,19 @@ struct solver::imp { for (auto w : var2occurs[v].monics) todo.push_back(w); + for (auto ti : var2occurs[v].terms) { + for (auto iv : lra.term(ti)) + todo.push_back(iv.column().index()); + auto vi = lp::tv::mask_term(ti); + todo.push_back(lra.map_term_index_to_column_index(vi)); + } + if (lra.column_corresponds_to_term(v)) { m_term_set.insert(v); lp::tv ti = lp::tv::raw(lra.column_to_reported_index(v)); for (auto kv : lra.get_term(ti)) todo.push_back(kv.column().index()); - } + } if (m_nla_core.is_monic_var(v)) { m_mon_set.insert(v); @@ -153,12 +170,10 @@ struct solver::imp { throw; } } -#if 0 TRACE("nra", m_nlsat->display(tout << r << "\n"); display(tout); for (auto [j, x] : m_lp2nl) tout << "j" << j << " := x" << x << "\n";); -#endif switch (r) { case l_true: m_nla_core.set_use_nra_model(true); From 36725383d342d30b012a252fd4f5a9285b8e4d1d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 12:43:14 -0800 Subject: [PATCH 393/428] minor simplification of terms during internalization. --- src/sat/smt/arith_internalize.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index 3174ad775..3c6286317 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -291,6 +291,13 @@ namespace arith { internalize_term(n->get_arg(1)->get_expr()); } + expr* solver::mk_sub(expr* x, expr* y) { + rational r; + if (a.is_numeral(y, r) && r == 0) + return x; + return a.mk_sub(x, y); + } + bool solver::internalize_atom(expr* atom) { TRACE("arith", tout << mk_pp(atom, m) << "\n";); expr* n1, *n2; @@ -319,26 +326,26 @@ namespace arith { k = lp_api::upper_t; } else if (a.is_le(atom, n1, n2)) { - expr_ref n3(a.mk_sub(n1, n2), m); + expr_ref n3(mk_sub(n1, n2), m); v = internalize_def(n3); k = lp_api::upper_t; r = 0; } else if (a.is_ge(atom, n1, n2)) { - expr_ref n3(a.mk_sub(n1, n2), m); + expr_ref n3(mk_sub(n1, n2), m); v = internalize_def(n3); k = lp_api::lower_t; r = 0; } else if (a.is_lt(atom, n1, n2)) { - expr_ref n3(a.mk_sub(n1, n2), m); + expr_ref n3(mk_sub(n1, n2), m); v = internalize_def(n3); k = lp_api::lower_t; r = 0; lit.neg(); } - else if (a.is_gt(atom, n1, n2)) { - expr_ref n3(a.mk_sub(n1, n2), m); + else if (a.is_gt(atom, n1, n2)) { + expr_ref n3(mk_sub(n1, n2), m); v = internalize_def(n3); k = lp_api::upper_t; r = 0; From 1b1ebaa3b03703cc76131e66c7cf9631685bf0ed Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 12:43:39 -0800 Subject: [PATCH 394/428] minor simplification during internalization --- src/sat/smt/arith_solver.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index 84be3caa7..20ae599c2 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -292,10 +292,11 @@ namespace arith { theory_var internalize_linearized_def(expr* term, scoped_internalize_state& st); void init_left_side(scoped_internalize_state& st); bool internalize_term(expr* term); - bool internalize_atom(expr* atom); + bool internalize_atom(expr* atom); bool is_unit_var(scoped_internalize_state& st); bool is_one(scoped_internalize_state& st); bool is_zero(scoped_internalize_state& st); + expr* mk_sub(expr* a, expr* b); enode* mk_enode(expr* e); lpvar register_theory_var_in_lar_solver(theory_var v); From bd8bed1759d823ed418e41050e6c95594e9d6ddf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 12:55:37 -0800 Subject: [PATCH 395/428] handle ac-op in legacy special relations procedure by adding warning Signed-off-by: Nikolaj Bjorner --- src/smt/theory_special_relations.cpp | 11 ++++++++--- src/smt/theory_special_relations.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index b6370f153..6203df103 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -130,7 +130,11 @@ namespace smt { } bool theory_special_relations::internalize_term(app * term) { - verbose_stream() << mk_pp(term, m) << "\n"; + m_terms.push_back(term); + ctx.push_trail(push_back_vector(m_terms)); + std::stringstream strm; + strm << "term not not handled by special relations procedure. Use sat.smt=true " << mk_pp(term, m); + warning_msg(strm.str().c_str()); return false; } @@ -207,9 +211,10 @@ namespace smt { if (new_equality) { return FC_CONTINUE; } - else { + else if (!m_terms.empty()) + return FC_GIVEUP; + else return FC_DONE; - } } lbool theory_special_relations::final_check_lo(relation& r) { diff --git a/src/smt/theory_special_relations.h b/src/smt/theory_special_relations.h index 73e889a5d..65ce17907 100644 --- a/src/smt/theory_special_relations.h +++ b/src/smt/theory_special_relations.h @@ -131,6 +131,7 @@ namespace smt { special_relations_util m_util; atoms m_atoms; unsigned_vector m_atoms_lim; + ptr_vector m_terms; obj_map m_relations; bool_var2atom m_bool_var2atom; bool m_can_propagate; From ea3628e50bb77adf0bc2a8e0e32132282bde6542 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 16:28:43 -0800 Subject: [PATCH 396/428] remove hoist functionality Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/CMakeLists.txt | 1 - src/ast/rewriter/bool_rewriter.cpp | 23 --- src/ast/rewriter/bool_rewriter.h | 4 +- src/ast/rewriter/hoist_rewriter.cpp | 256 ---------------------------- src/ast/rewriter/hoist_rewriter.h | 87 ---------- 5 files changed, 1 insertion(+), 370 deletions(-) delete mode 100644 src/ast/rewriter/hoist_rewriter.cpp delete mode 100644 src/ast/rewriter/hoist_rewriter.h diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index df803b0f1..7f351ecb6 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -23,7 +23,6 @@ z3_add_component(rewriter factor_rewriter.cpp fpa_rewriter.cpp func_decl_replace.cpp - hoist_rewriter.cpp inj_axiom.cpp label_rewriter.cpp macro_replacer.cpp diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 7728716fd..9afab7a29 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -34,7 +34,6 @@ void bool_rewriter::updt_params(params_ref const & _p) { m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); - m_hoist.set_elim_and(m_elim_and); } void bool_rewriter::get_param_descrs(param_descrs & r) { @@ -270,28 +269,6 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args return BR_DONE; } -#if 0 - br_status st; - expr_ref r(m()); - st = m_hoist.mk_or(buffer.size(), buffer.data(), r); - if (st != BR_FAILED) { - m_counts1.reserve(m().get_num_asts() + 1); - m_counts2.reserve(m().get_num_asts() + 1); - get_num_internal_exprs(m_counts1, m_todo1, r); - for (unsigned i = 0; i < num_args; ++i) - get_num_internal_exprs(m_counts2, m_todo2, args[i]); - unsigned count1 = count_internal_nodes(m_counts1, m_todo1); - unsigned count2 = count_internal_nodes(m_counts2, m_todo2); - if (count1 > count2) - st = BR_FAILED; - } - if (st != BR_FAILED) - result = r; - if (st == BR_DONE) - return BR_REWRITE1; - if (st != BR_FAILED) - return st; -#endif if (s) { if (m_sort_disjunctions) { ast_lt lt; diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 1c1e7c60e..7c840b647 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -20,7 +20,6 @@ Notes: #include "ast/ast.h" #include "ast/rewriter/rewriter.h" -#include "ast/rewriter/hoist_rewriter.h" #include "util/params.h" /** @@ -51,7 +50,6 @@ Notes: */ 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; @@ -84,7 +82,7 @@ class bool_rewriter { void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits); public: - bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { + bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } ast_manager & m() const { return m_manager; } diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp deleted file mode 100644 index 1b99469a1..000000000 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/*++ -Copyright (c) 2019 Microsoft Corporation - -Module Name: - - hoist_rewriter.cpp - -Abstract: - - Hoist predicates over disjunctions - -Author: - - Nikolaj Bjorner (nbjorner) 2019-2-4 - ---*/ - - -#include "ast/rewriter/hoist_rewriter.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/ast_util.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" - -hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): - m(m), m_args1(m), m_args2(m), m_refs(m), m_subst(m) { - updt_params(p); -} - -expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { - if (m_elim_and) { - expr_ref_vector negs(m); - for (expr* a : args) - if (m.is_false(a)) - return expr_ref(m.mk_false(), m); - else if (m.is_true(a)) - continue; - else - negs.push_back(::mk_not(m, a)); - return ::mk_not(mk_or(negs)); - } - else - return ::mk_and(args); -} - -expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { - return ::mk_or(args); -} - -br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { - if (num_args < 2) - return BR_FAILED; - - for (unsigned i = 0; i < num_args; ++i) - if (!is_and(es[i], nullptr)) - return BR_FAILED; - - bool turn = false; - m_preds1.reset(); - m_preds2.reset(); - m_uf1.reset(); - m_uf2.reset(); - m_expr2var.reset(); - m_var2expr.reset(); - basic_union_find* uf[2] = { &m_uf1, &m_uf2 }; - obj_hashtable* preds[2] = { &m_preds1, &m_preds2 }; - expr_ref_vector* args[2] = { &m_args1, &m_args2 }; - VERIFY(is_and(es[0], args[turn])); - expr* e1, *e2; - for (expr* e : *(args[turn])) { - if (m.is_eq(e, e1, e2)) - (*uf)[turn].merge(mk_var(e1), mk_var(e2)); - else - (*preds)[turn].insert(e); - } - unsigned round = 0; - for (unsigned j = 1; j < num_args; ++j) { - ++round; - m_es.reset(); - m_mark.reset(); - - bool last = turn; - turn = !turn; - (*preds)[turn].reset(); - reset(m_uf0); - VERIFY(is_and(es[j], args[turn])); - - for (expr* e : *args[turn]) { - if (m.is_eq(e, e1, e2)) { - m_es.push_back(e1); - m_uf0.merge(mk_var(e1), mk_var(e2)); - } - else if ((*preds)[last].contains(e)) - (*preds)[turn].insert(e); - } - - if ((*preds)[turn].empty() && m_es.empty()) - return BR_FAILED; - - m_eqs.reset(); - for (expr* e : m_es) { - if (m_mark.is_marked(e)) - continue; - unsigned u = mk_var(e); - unsigned v = u; - m_roots.reset(); - do { - m_mark.mark(e); - unsigned r = (*uf)[last].find(v); - if (m_roots.find(r, e2)) - m_eqs.push_back({e, e2}); - else - m_roots.insert(r, e); - v = m_uf0.next(v); - e = mk_expr(v); - } - while (u != v); - } - reset((*uf)[turn]); - for (auto const& [e1, e2] : m_eqs) - (*uf)[turn].merge(mk_var(e1), mk_var(e2)); - if ((*preds)[turn].empty() && m_eqs.empty()) - return BR_FAILED; - } - if (m_eqs.empty()) { - result = hoist_predicates((*preds)[turn], num_args, es); - return BR_DONE; - } - // p & eqs & (or fmls) - expr_ref_vector fmls(m); - m_subst.reset(); - for (expr * p : (*preds)[turn]) { - expr* q = nullptr; - if (m.is_not(p, q)) - m_subst.insert(q, m.mk_false()); - else - m_subst.insert(p, m.mk_true()); - fmls.push_back(p); - } - bool new_eq = false; - for (auto& [a, b] : m_eqs) { - if (m.is_value(a)) - std::swap(a, b); - if (m.are_equal(a, b)) - continue; - if (m.are_distinct(a, b)) { - result = m.mk_false(); - return BR_DONE; - } - new_eq = true; - m_subst.insert(a, b); - fmls.push_back(m.mk_eq(a, b)); - } - expr_ref ors(::mk_or(m, num_args, es), m); - m_subst(ors); - fmls.push_back(ors); - result = mk_and(fmls); - TRACE("hoist", tout << ors << " => " << result << "\n";); - return new_eq ? BR_REWRITE3 : BR_DONE; -} - -unsigned hoist_rewriter::mk_var(expr* e) { - unsigned v = 0; - if (m_expr2var.find(e, v)) - return v; - m_uf1.mk_var(); - v = m_uf2.mk_var(); - SASSERT(v == m_var2expr.size()); - m_expr2var.insert(e, v); - m_var2expr.push_back(e); - return v; -} - -expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsigned num_args, expr* const* es) { - expr_ref_vector args(m), args1(m), fmls(m); - for (unsigned i = 0; i < num_args; ++i) { - VERIFY(is_and(es[i], &args1)); - fmls.reset(); - for (expr* e : args1) - if (!preds.contains(e)) - fmls.push_back(e); - args.push_back(mk_and(fmls)); - } - fmls.reset(); - fmls.push_back(mk_or(args)); - for (auto* p : preds) - fmls.push_back(p); - return mk_and(fmls); -} - - -br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { - switch (f->get_decl_kind()) { - case OP_OR: - return mk_or(num_args, args, result); - default: - return BR_FAILED; - } -} - -bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { -#if 0 - if (!args) - return m.is_and(e) || (m.is_not(e, e) && m.is_or(e)); - expr_fast_mark1 visited; - args->reset(); - args->push_back(e); - m_refs.reset(); - for (unsigned i = 0; i < args->size(); ++i) { - e = args->get(i); - if (visited.is_marked(e)) - goto drop; - m_refs.push_back(e); - visited.mark(e, true); - if (m.is_and(e)) - args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); - else if (m.is_not(e, e) && m.is_or(e)) - for (expr* arg : *to_app(e)) - args->push_back(::mk_not(m, arg)); - else - continue; - drop: - (*args)[i] = args->back(); - args->pop_back(); - --i; - } - return args->size() > 1; -#else - if (m.is_and(e)) { - if (args) { - args->reset(); - args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); - } - return true; - } - if (m.is_not(e, e) && m.is_or(e)) { - if (args) { - args->reset(); - for (expr* arg : *to_app(e)) - args->push_back(::mk_not(m, arg)); - TRACE("hoist", tout << args << " " << * args << "\n"); - } - return true; - } -#endif - return false; -} - - -void hoist_rewriter::reset(basic_union_find& uf) { - uf.reset(); - for (expr* e : m_var2expr) { - (void)e; - uf.mk_var(); - } -} diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h deleted file mode 100644 index b64325584..000000000 --- a/src/ast/rewriter/hoist_rewriter.h +++ /dev/null @@ -1,87 +0,0 @@ -/*++ -Copyright (c) 2019 Microsoft Corporation - -Module Name: - - hoist_rewriter.h - -Abstract: - - Hoist predicates over disjunctions - -Author: - - Nikolaj Bjorner (nbjorner) 2019-2-4 - -Notes: - ---*/ -#pragma once - -#include "ast/ast.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "util/params.h" -#include "util/union_find.h" -#include "util/obj_hashtable.h" - -class bool_rewriter; - -class hoist_rewriter { - ast_manager & m; - expr_ref_vector m_args1, m_args2, m_refs; - obj_hashtable m_preds1, m_preds2; - basic_union_find m_uf1, m_uf2, m_uf0; - ptr_vector m_es; - svector> m_eqs; - u_map m_roots; - expr_safe_replace m_subst; - obj_map m_expr2var; - ptr_vector m_var2expr; - expr_mark m_mark; - bool m_elim_and = false; - - bool is_and(expr* e, expr_ref_vector* args); - expr_ref mk_and(expr_ref_vector const& args); - expr_ref mk_or(expr_ref_vector const& args); - - bool is_var(expr* e) { return m_expr2var.contains(e); } - expr* mk_expr(unsigned v) { return m_var2expr[v]; } - unsigned mk_var(expr* e); - - void reset(basic_union_find& uf); - - expr_ref hoist_predicates(obj_hashtable const& p, unsigned num_args, expr* const* args); - - -public: - hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); - family_id get_fid() const { return m.get_basic_family_id(); } - bool is_eq(expr * t) const { return m.is_eq(t); } - void updt_params(params_ref const & p) {} - static void get_param_descrs(param_descrs & r) {} - br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); - br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); - void set_elim_and(bool b) { m_elim_and = b; } -}; - -struct hoist_rewriter_cfg : public default_rewriter_cfg { - hoist_rewriter m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = nullptr; - if (f->get_family_id() != m_r.get_fid()) - return BR_FAILED; - return m_r.mk_app_core(f, num, args, result); - } - hoist_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {} -}; - -class hoist_rewriter_star : public rewriter_tpl { - hoist_rewriter_cfg m_cfg; -public: - hoist_rewriter_star(ast_manager & m, params_ref const & p = params_ref()): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} -}; - From 6910a4e18c38e53afea2abe270ac9b2dec040c53 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 4 Dec 2023 00:38:06 +0000 Subject: [PATCH 397/428] Fix to_fp_signed (#7034) --- src/ast/fpa/fpa2bv_converter.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index f866e3c23..4267a6eed 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3039,7 +3039,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const // Special case: x == 0 -> +zero expr_ref c1(m), v1(m); c1 = is_zero; - v1 = pzero; // No -zero? zeros_consistent_4.smt2 requires +zero. + v1 = pzero; // No -zero (IEEE754) // Special case: x != 0 expr_ref sign_bit(m), exp_too_large(m), sig_4(m), exp_2(m), rest(m); @@ -3048,18 +3048,13 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const rest = m_bv_util.mk_extract(bv_sz - 2, 0, x); dbg_decouple("fpa2bv_to_fp_signed_rest", rest); is_neg = m.mk_eq(sign_bit, bv1_1); - neg_x = m_bv_util.mk_bv_neg(x); + neg_x = m_bv_util.mk_bv_neg(x); // overflow ok, x_abs is now unsigned. x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); + dbg_decouple("fpa2bv_to_fp_signed_x_abs", x_abs); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) // bv_sz-2 is the "1.0" bit for the rounder. - expr_ref is_max_neg(m); - is_max_neg = m.mk_and(is_neg, m.mk_eq(rest, m_bv_util.mk_numeral(0, bv_sz-1))); - dbg_decouple("fpa2bv_to_fp_signed_is_max_neg", is_max_neg); - - x_abs = m.mk_ite(is_max_neg, m_bv_util.mk_concat(bv1_1, m_bv_util.mk_numeral(0, bv_sz-1)), x_abs); - dbg_decouple("fpa2bv_to_fp_signed_x_abs", x_abs); expr_ref lz(m); mk_leading_zeros(x_abs, bv_sz, lz); @@ -3094,7 +3089,6 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed - s_exp = m.mk_ite(is_max_neg, m_bv_util.mk_bv_sub(s_exp, m_bv_util.mk_numeral(1, bv_sz)), s_exp); SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); dbg_decouple("fpa2bv_to_fp_signed_s_exp", s_exp); @@ -3109,7 +3103,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const TRACE("fpa2bv_to_fp_signed", tout << "exp worst case sz: " << exp_worst_case_sz << std::endl;); - if (exp_sz < exp_worst_case_sz) { + if (exp_sz <= exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. @@ -3188,7 +3182,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con // Special case: x == 0 -> +zero expr_ref c1(m), v1(m); c1 = is_zero; - v1 = pzero; // No nzero? + v1 = pzero; // No -zero (IEEE754) // Special case: x != 0 expr_ref exp_too_large(m), sig_4(m), exp_2(m); From 18f14921ba0a9ad764338f9284bc828078151a13 Mon Sep 17 00:00:00 2001 From: Andrey Andreyevich Bienkowski Date: Mon, 4 Dec 2023 17:32:26 +0000 Subject: [PATCH 398/428] Clarify optimizer guarantees (#7030) * Clarify optimizer guarantees (python) * Clarify optimization guarantees (OCaml) * Clarify optimizer guarantees (java) * Clarify optimizer guarantees (.net) --- src/api/dotnet/Optimize.cs | 2 +- src/api/java/Optimize.java | 2 +- src/api/ml/z3.mli | 2 +- src/api/python/z3/z3.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 0694faabe..891ed4105 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -220,7 +220,7 @@ namespace Microsoft.Z3 /// /// Check satisfiability of asserted constraints. /// Produce a model that (when the objectives are bounded and - /// don't use strict inequalities) meets the objectives. + /// don't use strict inequalities) is optimal. /// /// public Status Check(params Expr[] assumptions) diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java index d72a28f08..83833e36b 100644 --- a/src/api/java/Optimize.java +++ b/src/api/java/Optimize.java @@ -193,7 +193,7 @@ public class Optimize extends Z3Object { /** * Check satisfiability of asserted constraints. * Produce a model that (when the objectives are bounded and - * don't use strict inequalities) meets the objectives. + * don't use strict inequalities) is optimal. **/ public Status Check(Expr... assumptions) { diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 1380bd519..bb3e70875 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3481,7 +3481,7 @@ sig (** Add minimization objective. *) val minimize : optimize -> Expr.expr -> handle - (** Checks whether the assertions in the context are satisfiable and solves objectives. *) + (** Check consistency and produce optimal values. *) val check : optimize -> Solver.status (** Retrieve model from satisfiable context *) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 7551d8a20..f7a99f1c2 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -8046,7 +8046,7 @@ class Optimize(Z3PPObject): Z3_optimize_pop(self.ctx.ref(), self.optimize) def check(self, *assumptions): - """Check satisfiability while optimizing objective functions.""" + """Check consistency and produce optimal values.""" assumptions = _get_args(assumptions) num = len(assumptions) _assumptions = (Ast * num)() From 17913f3ec84215a4652df25f01cc6e64d8e2341e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Dec 2023 16:40:55 -0800 Subject: [PATCH 399/428] remove braces --- src/qe/qsat.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index a4d32b505..720614573 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -267,10 +267,9 @@ namespace qe { continue; } - for (expr* f : *a) { + for (expr* f : *a) if (!mark.is_marked(f)) - todo.push_back(f); - } + todo.push_back(f); bool is_boolop = (a->get_family_id() == m.get_basic_family_id()) && From f7415bb677107d7f2d1e14d2c2b666049664fd63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 10:30:12 -0800 Subject: [PATCH 400/428] install importlib-resources for ubuntu doc Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 1 + src/math/lp/nla_grobner.cpp | 10 ++++++---- src/sat/smt/array_axioms.cpp | 29 ++++++++++++++++------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/scripts/release.yml b/scripts/release.yml index fc7231985..b400860a7 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -133,6 +133,7 @@ stages: pool: vmImage: "ubuntu-latest" steps: + - script: pip3 install importlib-resources - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y - script: eval `opam config env`; opam install zarith ocamlfind -y diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index a95aa766d..813980238 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -399,7 +399,7 @@ namespace nla { for (auto eq : m_solver.equations()) if (propagate_linear_equations(*eq)) ++changed; -#if 0 +#if 1 for (auto eq : m_solver.equations()) if (check_missed_bound(*eq)) return true; @@ -419,15 +419,15 @@ namespace nla { if (vars.empty()) di.add(coeff, i); else { - di.set_value(t, rational::one()); + di.set_value(t, coeff); for (auto v : vars) { set_var_interval(v, s); - di.mul(coeff, s, s); - di.add(t, s, t); + di.mul(t, s, t); } if (m_mon2var.find(vars) != m_mon2var.end()) { auto v = m_mon2var.find(vars)->second; set_var_interval(v, u); + di.mul(coeff, u, u); di.intersect(t, u, t); } di.add(i, t, i); @@ -435,6 +435,8 @@ namespace nla { } if (!di.separated_from_zero(i)) return false; +// m_solver.display(verbose_stream() << "missed bound\n", e); +// exit(1); std::function f = [this](const lp::explanation& e) { new_lemma lemma(m_core, "pdd"); lemma &= e; diff --git a/src/sat/smt/array_axioms.cpp b/src/sat/smt/array_axioms.cpp index ff60bf675..0b1b55f07 100644 --- a/src/sat/smt/array_axioms.cpp +++ b/src/sat/smt/array_axioms.cpp @@ -409,6 +409,11 @@ namespace array { def1 = a.mk_default(store); def2 = a.mk_default(store->get_arg(0)); + prop |= !ctx.get_enode(def1) || !ctx.get_enode(def2); + + euf::enode* ndef1 = e_internalize(def1); + euf::enode* ndef2 = e_internalize(def2); + if (has_unitary_domain(store)) { def2 = store->get_arg(num_args - 1); } @@ -417,8 +422,8 @@ namespace array { // let A = store(B, i, v) // // Add: - // default(A) = ite(epsilon1 = i, v, default(B)) - // A[diag(i)] = B[diag(i)] + // default(A) = A[epsilon] + // default(B) = B[epsilon] // expr_ref_vector eqs(m); expr_ref_vector args1(m), args2(m); @@ -428,22 +433,20 @@ namespace array { for (unsigned i = 1; i + 1 < num_args; ++i) { expr* arg = store->get_arg(i); sort* srt = arg->get_sort(); - auto ep = mk_epsilon(srt); - eqs.push_back(m.mk_eq(ep.first, arg)); - args1.push_back(m.mk_app(ep.second, arg)); - args2.push_back(m.mk_app(ep.second, arg)); + auto [ep, d] = mk_epsilon(srt); + eqs.push_back(m.mk_eq(ep, arg)); + args1.push_back(ep); + args2.push_back(ep); } - expr_ref eq(m.mk_and(eqs), m); - def2 = m.mk_ite(eq, store->get_arg(num_args - 1), def2); app_ref sel1(m), sel2(m); sel1 = a.mk_select(args1); sel2 = a.mk_select(args2); - prop |= !ctx.get_enode(sel1) || !ctx.get_enode(sel2); - if (ctx.propagate(e_internalize(sel1), e_internalize(sel2), array_axiom())) - prop = true; + return + ctx.propagate(e_internalize(sel1), ndef1, array_axiom()) || + ctx.propagate(e_internalize(sel2), ndef2, array_axiom()); } - prop |= !ctx.get_enode(def1) || !ctx.get_enode(def2); - if (ctx.propagate(e_internalize(def1), e_internalize(def2), array_axiom())) + // default(A) == default(B) + if (ctx.propagate(ndef1, ndef2, array_axiom())) prop = true; return prop; } From de75692cb033fc557862e41efc152f0beec94b7f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 10:30:57 -0800 Subject: [PATCH 401/428] install importlib-resources for ubuntu doc Signed-off-by: Nikolaj Bjorner --- src/math/lp/nla_grobner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index 813980238..baf27a5bb 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -399,7 +399,7 @@ namespace nla { for (auto eq : m_solver.equations()) if (propagate_linear_equations(*eq)) ++changed; -#if 1 +#if 0 for (auto eq : m_solver.equations()) if (check_missed_bound(*eq)) return true; From f98b42ae4214c915cfec6260df549a66600b25eb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 10:33:29 -0800 Subject: [PATCH 402/428] install importlib-resources for ubuntu doc Signed-off-by: Nikolaj Bjorner --- src/sat/smt/array_axioms.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/array_axioms.cpp b/src/sat/smt/array_axioms.cpp index 0b1b55f07..6a08cf76e 100644 --- a/src/sat/smt/array_axioms.cpp +++ b/src/sat/smt/array_axioms.cpp @@ -443,7 +443,8 @@ namespace array { sel2 = a.mk_select(args2); return ctx.propagate(e_internalize(sel1), ndef1, array_axiom()) || - ctx.propagate(e_internalize(sel2), ndef2, array_axiom()); + ctx.propagate(e_internalize(sel2), ndef2, array_axiom()) || + prop; } // default(A) == default(B) if (ctx.propagate(ndef1, ndef2, array_axiom())) From 84a7a79e90212c14a0a4c451ab0251d9f3369455 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 17:08:01 -0800 Subject: [PATCH 403/428] fix #7037 --- src/smt/theory_datatype.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index d1216c171..cfc1f06f2 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -509,10 +509,10 @@ namespace smt { // Assuming `app` is equal to a constructor term, return the constructor enode inline enode * theory_datatype::oc_get_cstor(enode * app) { theory_var v = app->get_root()->get_th_var(get_id()); - SASSERT(v != null_theory_var); + if (v == null_theory_var) + return nullptr; v = m_find.find(v); var_data * d = m_var_data[v]; - SASSERT(d->m_constructor); return d->m_constructor; } @@ -802,8 +802,9 @@ namespace smt { return false; func_decl* con = m_util.get_accessor_constructor(f); for (enode* app : ctx.enodes_of(f)) { - enode* arg = app->get_arg(0)->get_root(); - if (is_constructor(arg) && arg->get_decl() != con) + enode* arg = app->get_arg(0); + enode* arg_con = oc_get_cstor(arg); + if (arg_con && is_constructor(arg_con) && arg_con->get_decl() != con) return true; } return false; From 4a9b38e531e86f3b92435f064ae7bea678c8486d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 17:08:17 -0800 Subject: [PATCH 404/428] clean up nla_grobner --- src/math/lp/nla_grobner.cpp | 89 ++++--------------------------------- src/math/lp/nla_grobner.h | 6 --- 2 files changed, 9 insertions(+), 86 deletions(-) diff --git a/src/math/lp/nla_grobner.cpp b/src/math/lp/nla_grobner.cpp index baf27a5bb..f9974b41c 100644 --- a/src/math/lp/nla_grobner.cpp +++ b/src/math/lp/nla_grobner.cpp @@ -59,21 +59,20 @@ namespace nla { if (m_delay_base > 0) --m_delay_base; - if (is_conflicting()) - return; - try { - if (propagate_bounds()) + + if (is_conflicting()) return; if (propagate_eqs()) return; - + if (propagate_factorization()) return; - + if (propagate_linear_equations()) return; + } catch (...) { @@ -111,17 +110,9 @@ namespace nla { return false; } - bool grobner::propagate_bounds() { - unsigned changed = 0; - for (auto eq : m_solver.equations()) - if (propagate_bounds(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) - return true; - return changed > 0; - } - bool grobner::propagate_eqs() { unsigned changed = 0; - for (auto eq : m_solver.equations()) + for (auto eq : m_solver.equations()) if (propagate_fixed(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) return true; return changed > 0; @@ -129,7 +120,7 @@ namespace nla { bool grobner::propagate_factorization() { unsigned changed = 0; - for (auto eq : m_solver.equations()) + for (auto eq : m_solver.equations()) if (propagate_factorization(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) return true; return changed > 0; @@ -165,19 +156,12 @@ namespace nla { rational d = lcm(denominator(a), denominator(b)); a *= d; b *= d; -#if 0 - c().lra.update_column_type_and_bound(v, lp::lconstraint_kind::EQ, b/a, eq.dep()); - lp::explanation exp; - explain(eq, exp); - c().add_fixed_equality(c().lra.column_to_reported_index(v), b/a, exp); -#else ineq new_eq(term(a, v), llc::EQ, b); if (c().ineq_holds(new_eq)) return false; new_lemma lemma(c(), "pdd-eq"); add_dependencies(lemma, eq); lemma |= new_eq; -#endif return true; } @@ -377,73 +361,18 @@ namespace nla { } } - bool grobner::propagate_bounds(const dd::solver::equation& e) { - return false; - // TODO - auto& di = c().m_intervals.get_dep_intervals(); - dd::pdd_interval eval(di); - eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) { - if (deps) c().m_intervals.set_var_interval(j, a); - else c().m_intervals.set_var_interval(j, a); - }; - scoped_dep_interval i(di), i_wd(di); - eval.get_interval(e.poly(), i); - return false; - } - bool grobner::propagate_linear_equations() { unsigned changed = 0; m_mon2var.clear(); for (auto const& m : c().emons()) m_mon2var[m.vars()] = m.var(); + for (auto eq : m_solver.equations()) if (propagate_linear_equations(*eq)) ++changed; -#if 0 - for (auto eq : m_solver.equations()) - if (check_missed_bound(*eq)) - return true; -#endif return changed > 0; } - - bool grobner::check_missed_bound(dd::solver::equation const& e) { - auto& di = c().m_intervals.get_dep_intervals(); - auto set_var_interval = [&](lpvar j, scoped_dep_interval& a) { - c().m_intervals.set_var_interval(j, a); - }; - scoped_dep_interval i(di), t(di), s(di), u(di); - di.set_value(i, rational::zero()); - - for (auto const& [coeff, vars] : e.poly()) { - if (vars.empty()) - di.add(coeff, i); - else { - di.set_value(t, coeff); - for (auto v : vars) { - set_var_interval(v, s); - di.mul(t, s, t); - } - if (m_mon2var.find(vars) != m_mon2var.end()) { - auto v = m_mon2var.find(vars)->second; - set_var_interval(v, u); - di.mul(coeff, u, u); - di.intersect(t, u, t); - } - di.add(i, t, i); - } - } - if (!di.separated_from_zero(i)) - return false; -// m_solver.display(verbose_stream() << "missed bound\n", e); -// exit(1); - std::function f = [this](const lp::explanation& e) { - new_lemma lemma(m_core, "pdd"); - lemma &= e; - }; - return di.check_interval_for_conflict_on_zero(i, e.dep(), f); - } - + bool grobner::propagate_linear_equations(dd::solver::equation const& e) { if (equation_is_true(e)) return false; diff --git a/src/math/lp/nla_grobner.h b/src/math/lp/nla_grobner.h index e70b5473d..be5f06136 100644 --- a/src/math/lp/nla_grobner.h +++ b/src/math/lp/nla_grobner.h @@ -35,20 +35,14 @@ namespace nla { bool is_conflicting(); bool is_conflicting(dd::solver::equation const& eq); - bool propagate_bounds(); - bool propagate_bounds(dd::solver::equation const& eq); - bool propagate_eqs(); bool propagate_fixed(dd::solver::equation const& eq); bool propagate_factorization(); bool propagate_factorization(dd::solver::equation const& eq); - bool propagate_linear_equations(); bool propagate_linear_equations(dd::solver::equation const& eq); - - bool check_missed_bound(dd::solver::equation const& eq); void add_dependencies(new_lemma& lemma, dd::solver::equation const& eq); void explain(dd::solver::equation const& eq, lp::explanation& exp); From 5e3f1d988b2435620a45906b974bdea1d605332d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 19:38:52 -0800 Subject: [PATCH 405/428] update release notes Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 5 ++++- scripts/release.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f46f32a1a..b00cb2398 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,7 +9,6 @@ Version 4.next - polysat - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. - - Light quantifier elimination based on term graphs (egraphs), and corresponding Model Based Projection for arrays and ADTs. Used by Spacer and QSAT. Version 4.12.3 ============== @@ -23,6 +22,10 @@ Version 4.12.3 - Various (ongoing) performance fixes and improvements to smt.arith.solver=6 - A working version of solver.proof.trim=true option. Proofs logs created when using sat.smt=true may be trimmed by running z3 on the generated proof log using the option solver.proof.trim=true. +- Optimizations LIA and NIA (linear integer arithmetic and non-linear integer (and real) arithmetic reasoning). + smt.arith.solver=6 is the default for most use cases. It trails smt.arith.solver=2 in some scenarios and the gap has been either removed or reduced. + smt.arith.solver=6 is complete for integrations of non-linear real arithmetic and theories, smt.arith.solver=2 is not. +- qel: Light quantifier elimination based on term graphs (egraphs), and corresponding Model Based Projection for arrays and ADTs. Used by Spacer and QSAT. Version 4.12.2 ============== diff --git a/scripts/release.yml b/scripts/release.yml index b400860a7..49679d12e 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -583,7 +583,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,0) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 389aea3330c066215289eec36911391138e8d1d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Dec 2023 19:48:43 -0800 Subject: [PATCH 406/428] update release notes, update version number Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- RELEASE_NOTES.md | 5 +++++ scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab59f18f..b98360df8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.3.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.4.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b00cb2398..fa22ae0eb 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,9 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.4 +============== + Version 4.12.3 ============== - Alpha support for polymorphism. @@ -26,6 +29,8 @@ Version 4.12.3 smt.arith.solver=6 is the default for most use cases. It trails smt.arith.solver=2 in some scenarios and the gap has been either removed or reduced. smt.arith.solver=6 is complete for integrations of non-linear real arithmetic and theories, smt.arith.solver=2 is not. - qel: Light quantifier elimination based on term graphs (egraphs), and corresponding Model Based Projection for arrays and ADTs. Used by Spacer and QSAT. +- added real-closed fields features to C API, exposed more RCF over OCaml API +- fixes to FP Version 4.12.2 ============== diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 876e0fd21..3a3f19fb2 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 3, 0) # express a default build version or pick up ci build version + set_version(4, 12, 4, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index eebff5803..3d8f6a5b7 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '3' + Patch: '4' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) diff --git a/scripts/release.yml b/scripts/release.yml index 49679d12e..08731c6e2 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.3' + ReleaseVersion: '4.12.4' stages: From 4d4359f78aaa1428487d04ce2064c2365060b7a4 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Tue, 5 Dec 2023 10:48:15 -0500 Subject: [PATCH 407/428] fix shebang syntax issue (#7044) --- scripts/update_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index ddfb31f26..d426da34a 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1,4 +1,4 @@ -# - !/usr/bin/env python +#!/usr/bin/env python ############################################ # Copyright (c) 2012 Microsoft Corporation # From 764f0d54a436bd93069778a385517e74aea47150 Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 5 Dec 2023 16:48:57 +0100 Subject: [PATCH 408/428] Overload xor operator for BoolRef (#7043) --- src/api/python/z3/z3.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index f7a99f1c2..4d92af8b9 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1593,6 +1593,9 @@ class BoolRef(ExprRef): def __or__(self, other): return Or(self, other) + + def __xor__(self, other): + return Xor(self, other) def __invert__(self): return Not(self) From 9ad4d50b5d859443e3ba004bd36c560af3673a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Tue, 5 Dec 2023 16:49:32 +0100 Subject: [PATCH 409/428] Use built-in `importlib.resources` on Python 3.9+ (#7042) Use built-in `importlib.resources` module rather than the external `importlib_resources` package on Python 3.9 and newer. The latter is only intended as a backport for old Python versions, and since modern Linux distributions may no longer support such old Python versions, they also no longer provide importlib_resources (this is the case on Gentoo). --- scripts/update_api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index d426da34a..7d3d8899f 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1831,7 +1831,10 @@ import atexit import sys, os import contextlib import ctypes -import importlib_resources +if sys.version_info >= (3, 9): + import importlib.resources as importlib_resources +else: + import importlib_resources from .z3types import * from .z3consts import * From f5ae8c324c27ae9cd1f2799e53763b306e5087a8 Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Tue, 5 Dec 2023 08:01:59 -0800 Subject: [PATCH 410/428] make a readme file Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index cf7140e5e..33d8eeb3d 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -86,6 +86,10 @@ def mk_icon(source_root): mk_dir("out/content") shutil.copy(f"{source_root}/resources/icon.jpg", "out/content/icon.jpg") +def mk_readme(source_root): + mk_dir("out/content") + shutil.copy(f"{source_root}/README.md", "out/content/README.md") + def create_nuget_spec(version, repo, branch, commit, symbols, arch): arch = f".{arch}" if arch == "x86" else "" @@ -140,6 +144,7 @@ class Env: mk_dir(self.packages) unpack(self.packages, self.symbols, self.arch) mk_targets(self.source_root) + mk_readme(self.source_root) mk_icon(self.source_root) create_nuget_spec(self.version, self.repo, self.branch, self.commit, self.symbols, self.arch) From 23fcb4376f661784729f534371ab002a82f10bcf Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Tue, 5 Dec 2023 08:03:28 -0800 Subject: [PATCH 411/428] readme Signed-off-by: NikolajBjorner --- scripts/mk_nuget_task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 33d8eeb3d..cafdc37e1 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -108,6 +108,7 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg + content/readme.md https://github.com/Z3Prover/z3 MIT From aa2e54c5a4ea8198139ecabb45fd92cb6950182f Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Tue, 5 Dec 2023 08:18:33 -0800 Subject: [PATCH 412/428] update release pipeline Signed-off-by: NikolajBjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 08731c6e2..5f049345c 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -504,7 +504,7 @@ stages: displayName: "Download Ubuntu Arm64" inputs: artifactName: 'UbuntuArm64' - targetPath: tmp + path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: "Download Doc" inputs: From 669f665f24ea74e0746e4e21c05f778e22c20bed Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Tue, 5 Dec 2023 08:19:20 -0800 Subject: [PATCH 413/428] update release pipeline Signed-off-by: NikolajBjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 5f049345c..6b4fbdb66 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -583,7 +583,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(1,0) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 7c81ee08900e43e7ec311e5502cfcaa6c0dc09b3 Mon Sep 17 00:00:00 2001 From: NikolajBjorner Date: Tue, 5 Dec 2023 09:02:08 -0800 Subject: [PATCH 414/428] fix case of README.md in nuget Signed-off-by: NikolajBjorner --- scripts/mk_nuget_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index cafdc37e1..13f3fc923 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -108,7 +108,7 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg - content/readme.md + content/README.md https://github.com/Z3Prover/z3 MIT From a9513c19989454ac4aeaa83bdb6310cf6386835d Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 5 Dec 2023 18:12:25 +0100 Subject: [PATCH 415/428] Improve BoolRef addition (#7045) --- src/api/python/z3/z3.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 4d92af8b9..18d3abc9d 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1572,7 +1572,12 @@ class BoolRef(ExprRef): return BoolSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def __add__(self, other): - return If(self, 1, 0) + If(other, 1, 0) + if isinstance(other, BoolRef): + other = If(other, 1, 0) + return If(self, 1, 0) + other + + def __radd__(self, other): + return self + other def __rmul__(self, other): return self * other From 426d7f5810bcf434182af406acccc9e5120330d3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 12:11:06 -0800 Subject: [PATCH 416/428] remove reference to readme in nuget task Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 13f3fc923..18dcb52f0 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -86,9 +86,6 @@ def mk_icon(source_root): mk_dir("out/content") shutil.copy(f"{source_root}/resources/icon.jpg", "out/content/icon.jpg") -def mk_readme(source_root): - mk_dir("out/content") - shutil.copy(f"{source_root}/README.md", "out/content/README.md") def create_nuget_spec(version, repo, branch, commit, symbols, arch): @@ -108,7 +105,6 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg - content/README.md https://github.com/Z3Prover/z3 MIT @@ -145,7 +141,6 @@ class Env: mk_dir(self.packages) unpack(self.packages, self.symbols, self.arch) mk_targets(self.source_root) - mk_readme(self.source_root) mk_icon(self.source_root) create_nuget_spec(self.version, self.repo, self.branch, self.commit, self.symbols, self.arch) From 76c05f171ae98f81f845001279691e253bc5f3da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 12:32:30 -0800 Subject: [PATCH 417/428] specify a readme file with the nuget package Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Microsoft.Z3.csproj.in | 7 +++++++ src/api/python/setup.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/api/dotnet/Microsoft.Z3.csproj.in b/src/api/dotnet/Microsoft.Z3.csproj.in index 85ab98b38..3999e724b 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj.in +++ b/src/api/dotnet/Microsoft.Z3.csproj.in @@ -7,6 +7,8 @@ Microsoft.Z3 Microsoft.Z3 + README.md + Z3 .NET Interface Z3 .NET Interface @@ -65,6 +67,11 @@ ${Z3_DOTNET_COMPILE_ITEMS} + + + + + diff --git a/src/api/python/setup.py b/src/api/python/setup.py index 1837b5bec..a20ff53a3 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -18,7 +18,7 @@ from setuptools.command.bdist_egg import bdist_egg as _bdist_egg build_env = dict(os.environ) build_env['PYTHON'] = sys.executable -build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -std=c++11" +build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -std=c++17" # determine where we're building and where sources are ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) From d566eb3df7931e8f8f32a138c8a8ab445963f24e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 13:04:25 -0800 Subject: [PATCH 418/428] include readme in package Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 4 +++- src/api/dotnet/Microsoft.Z3.csproj.in | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 3d3996792..bd8e9d918 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1736,6 +1736,7 @@ class DotNetDLLComponent(Component): true Microsoft Microsoft + README.md false Z3 is a satisfiability modulo theories solver from Microsoft Research. Copyright Microsoft Corporation. All rights reserved. @@ -1745,9 +1746,10 @@ class DotNetDLLComponent(Component): + -""" % (version, key, self.to_src_dir) +""" % (version, key, self.to_src_dir, self.src_dir) mk_dir(os.path.join(BUILD_DIR, 'dotnet')) csproj = os.path.join('dotnet', 'z3.csproj') diff --git a/src/api/dotnet/Microsoft.Z3.csproj.in b/src/api/dotnet/Microsoft.Z3.csproj.in index 3999e724b..ec136809d 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj.in +++ b/src/api/dotnet/Microsoft.Z3.csproj.in @@ -17,8 +17,8 @@ Z3 is a satisfiability modulo theories solver from Microsoft Research. .NET Interface to the Z3 Theorem Prover - Copyright (C) 2006-2019 Microsoft Corporation - Copyright (C) 2006-2019 Microsoft Corporation + Copyright (C) 2006- Microsoft Corporation + Copyright (C) 2006- Microsoft Corporation Microsoft Corporation Microsoft Corporation From 111ce017025fbfbde5641574e4986c0722ad39ea Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 13:47:05 -0800 Subject: [PATCH 419/428] update path reference to readme --- scripts/mk_util.py | 2 +- src/api/dotnet/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/api/dotnet/README.md diff --git a/scripts/mk_util.py b/scripts/mk_util.py index bd8e9d918..6b1ad95d6 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1746,7 +1746,7 @@ class DotNetDLLComponent(Component): - + """ % (version, key, self.to_src_dir, self.src_dir) diff --git a/src/api/dotnet/README.md b/src/api/dotnet/README.md new file mode 100644 index 000000000..fe614782f --- /dev/null +++ b/src/api/dotnet/README.md @@ -0,0 +1,3 @@ +# Z3 Nuget Package + +For more information see [the Z3 github page](https://github.com/z3prover/z3.git) From 156426a0cf6c5cb23ea8a63a5fd6728bb628a560 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 15:10:13 -0800 Subject: [PATCH 420/428] use / for package path Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 6b1ad95d6..0728c2cb7 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1746,10 +1746,10 @@ class DotNetDLLComponent(Component): - + -""" % (version, key, self.to_src_dir, self.src_dir) +""" % (version, key, self.to_src_dir, self.to_src_dir) mk_dir(os.path.join(BUILD_DIR, 'dotnet')) csproj = os.path.join('dotnet', 'z3.csproj') From 1d6616afaced6ce9f7924b86f03c40f4719e534f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 15:41:35 -0800 Subject: [PATCH 421/428] make var-queue a template Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.h | 2 +- src/util/var_queue.h | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 3a437855e..0361fc157 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -164,7 +164,7 @@ namespace sat { unsigned m_rephase_inc; backoff m_rephase; backoff m_reorder; - var_queue m_case_split_queue; + var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; unsigned m_search_lvl; diff --git a/src/util/var_queue.h b/src/util/var_queue.h index 7245153ca..9807e5ac2 100644 --- a/src/util/var_queue.h +++ b/src/util/var_queue.h @@ -21,20 +21,20 @@ Revision History: #include "util/heap.h" - +template class var_queue { typedef unsigned var; struct lt { - svector & m_activity; - lt(svector & act):m_activity(act) {} + ActivityVector & m_activity; + lt(ActivityVector & act):m_activity(act) {} bool operator()(var v1, var v2) const { return m_activity[v1] > m_activity[v2]; } }; heap m_queue; -public: +public: - var_queue(svector & act):m_queue(128, lt(act)) {} + var_queue(ActivityVector & act):m_queue(128, lt(act)) {} void activity_increased_eh(var v) { if (m_queue.contains(v)) @@ -90,11 +90,12 @@ public: return out; } - using const_iterator = decltype(m_queue)::const_iterator; + using const_iterator = const int *; const_iterator begin() const { return m_queue.begin(); } const_iterator end() const { return m_queue.end(); } }; -inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) { +template +inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) { return queue.display(out); } From 1fde3e9fb86dcee8a547871a7d96ffd5337e61a0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 16:16:27 -0800 Subject: [PATCH 422/428] update release Signed-off-by: Nikolaj Bjorner --- scripts/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.yml b/scripts/release.yml index 6b4fbdb66..5f049345c 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -583,7 +583,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,0) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 8111d879cdb34f8f2d1b85ace401edfb2147717e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 16:37:48 -0800 Subject: [PATCH 423/428] add README path to mk_nuget_task Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 6 ++++++ scripts/release.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 18dcb52f0..dd5659ba5 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -86,6 +86,10 @@ def mk_icon(source_root): mk_dir("out/content") shutil.copy(f"{source_root}/resources/icon.jpg", "out/content/icon.jpg") +def mk_readme(source_root): + mk_dir("out/content") + shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/icon.jpg") + def create_nuget_spec(version, repo, branch, commit, symbols, arch): @@ -105,6 +109,7 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg + content/README.md https://github.com/Z3Prover/z3 MIT @@ -142,6 +147,7 @@ class Env: unpack(self.packages, self.symbols, self.arch) mk_targets(self.source_root) mk_icon(self.source_root) + mk_readme(self.source_root) create_nuget_spec(self.version, self.repo, self.branch, self.commit, self.symbols, self.arch) def main(): diff --git a/scripts/release.yml b/scripts/release.yml index 5f049345c..6b4fbdb66 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -583,7 +583,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,1) + condition: eq(1,0) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 2c8d33851a4475605f6803663e600b22938cc18c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 16:38:35 -0800 Subject: [PATCH 424/428] add README path to mk_nuget_task Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index dd5659ba5..8f238bcc2 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -88,7 +88,7 @@ def mk_icon(source_root): def mk_readme(source_root): mk_dir("out/content") - shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/icon.jpg") + shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/README.md") From fc3a7655a5456bcad6f5f3d557db1ee73178330f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 18:06:17 -0800 Subject: [PATCH 425/428] try to put readme in root Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 8f238bcc2..44987868d 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,10 +24,11 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'x64-glibc-2.31' : ('so', 'linux-x64'), + 'x64-glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), + 'arm-glibc-2.35' : ('so', 'linux-arm64'), 'arm64-osx' : ('dylib', 'osx-arm64'), 'debian' : ('so', 'linux-x64') } @@ -88,7 +89,7 @@ def mk_icon(source_root): def mk_readme(source_root): mk_dir("out/content") - shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/content/README.md") + shutil.copy(f"{source_root}/src/api/dotnet/README.md", "out/README.md") @@ -109,7 +110,7 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg - content/README.md + README.md https://github.com/Z3Prover/z3 MIT From b3ef74c86da1915151bfc19d417190ff9379c550 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Dec 2023 18:50:23 -0800 Subject: [PATCH 426/428] remove readme for dist Signed-off-by: Nikolaj Bjorner --- scripts/mk_nuget_task.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 44987868d..841d30e2c 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -110,7 +110,6 @@ Linux Dependencies: © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover content/icon.jpg - README.md https://github.com/Z3Prover/z3 MIT @@ -148,7 +147,7 @@ class Env: unpack(self.packages, self.symbols, self.arch) mk_targets(self.source_root) mk_icon(self.source_root) - mk_readme(self.source_root) +# mk_readme(self.source_root) create_nuget_spec(self.version, self.repo, self.branch, self.commit, self.symbols, self.arch) def main(): From dce2f3d88ff7b8671bd5d843f4f3f353e7089a45 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Dec 2023 07:10:56 -0800 Subject: [PATCH 427/428] add release notes Signed-off-by: Nikolaj Bjorner --- RELEASE_NOTES.md | 4 ++++ scripts/mk_nuget_task.py | 6 ++++-- scripts/release.yml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index fa22ae0eb..f0fdc368c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,10 @@ Version 4.next Version 4.12.4 ============== +- Re-release fixing a few issues with 4.12: + - Python dependency on importlib.resources vs importlib_resources break automatic pypi installations. Supposedly fixed by conditioning dependency on Python 3.9 where the feature is built-in. + - Missing release of arm64 for Ubuntu. + - Futile attempt to streamline adding readme.md file as part of Nuget distribution. Nuget.org now requires a readme file. I was able to integrate the readme with the cmake build, but the cross-platform repackage in scripts/mk_nuget_task.py does not ingest a similar readme file with the CI pipelines. Version 4.12.3 ============== diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index 841d30e2c..ef41051d8 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -28,10 +28,12 @@ os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), - 'arm-glibc-2.35' : ('so', 'linux-arm64'), - 'arm64-osx' : ('dylib', 'osx-arm64'), 'debian' : ('so', 'linux-x64') } +# Nuget not supported for ARM +#'arm-glibc-2.35' : ('so', 'linux-arm64'), +#'arm64-osx' : ('dylib', 'osx-arm64'), + def classify_package(f, arch): diff --git a/scripts/release.yml b/scripts/release.yml index 6b4fbdb66..5f049345c 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -583,7 +583,7 @@ stages: # Enable on release: - job: PyPIPublish - condition: eq(1,0) + condition: eq(1,1) displayName: "Publish to PyPI" pool: vmImage: "ubuntu-latest" From 6afed0819c3c1ab515d7606bc4b703a4d8f9d405 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 6 Dec 2023 07:13:07 -0800 Subject: [PATCH 428/428] update minor version number Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 2 +- RELEASE_NOTES.md | 3 +++ scripts/mk_project.py | 2 +- scripts/nightly.yaml | 2 +- scripts/release.yml | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b98360df8..60531ca94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.4.0 LANGUAGES CXX) +project(Z3 VERSION 4.12.5.0 LANGUAGES CXX) ################################################################################ # Project version diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f0fdc368c..9a7bc1856 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,9 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.5 +============== + Version 4.12.4 ============== - Re-release fixing a few issues with 4.12: diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 3a3f19fb2..77bb1b680 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 4, 0) # express a default build version or pick up ci build version + set_version(4, 12, 5, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 3d8f6a5b7..2b1b4ca3d 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '4' + Patch: '5' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) diff --git a/scripts/release.yml b/scripts/release.yml index 5f049345c..76f8a8a57 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.4' + ReleaseVersion: '4.12.5' stages: