From bcf774e65a1be5eea2e45ac58a4542aee9cfc44c Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Wed, 6 Aug 2025 10:53:04 +0200 Subject: [PATCH] math_opt: backport from main --- .../math_opt/constraints/indicator/BUILD.bazel | 3 +++ .../math_opt/constraints/quadratic/BUILD.bazel | 3 +++ .../constraints/second_order_cone/BUILD.bazel | 3 +++ ortools/math_opt/constraints/sos/BUILD.bazel | 3 +++ ortools/math_opt/constraints/util/BUILD.bazel | 3 +++ ortools/math_opt/core/BUILD.bazel | 4 +++- ortools/math_opt/cpp/BUILD.bazel | 2 ++ ortools/math_opt/elemental/python/BUILD.bazel | 2 +- ortools/math_opt/python/BUILD.bazel | 2 +- ortools/math_opt/python/ipc/BUILD.bazel | 2 +- ortools/math_opt/python/testing/BUILD.bazel | 2 +- .../math_opt/solver_tests/test_models_test.cc | 8 ++++++++ ortools/math_opt/solvers/highs_solver.cc | 16 +++++++++++++++- ortools/math_opt/solvers/highs_solver_test.cc | 17 +++++++++++++---- ortools/math_opt/solvers/xpress/BUILD.bazel | 2 ++ 15 files changed, 62 insertions(+), 10 deletions(-) diff --git a/ortools/math_opt/constraints/indicator/BUILD.bazel b/ortools/math_opt/constraints/indicator/BUILD.bazel index 9b3c1cfc6a..e49dad14af 100644 --- a/ortools/math_opt/constraints/indicator/BUILD.bazel +++ b/ortools/math_opt/constraints/indicator/BUILD.bazel @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( diff --git a/ortools/math_opt/constraints/quadratic/BUILD.bazel b/ortools/math_opt/constraints/quadratic/BUILD.bazel index 1a395834e2..5c283ede8e 100644 --- a/ortools/math_opt/constraints/quadratic/BUILD.bazel +++ b/ortools/math_opt/constraints/quadratic/BUILD.bazel @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( diff --git a/ortools/math_opt/constraints/second_order_cone/BUILD.bazel b/ortools/math_opt/constraints/second_order_cone/BUILD.bazel index 7f564322ac..3bd3630aca 100644 --- a/ortools/math_opt/constraints/second_order_cone/BUILD.bazel +++ b/ortools/math_opt/constraints/second_order_cone/BUILD.bazel @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( diff --git a/ortools/math_opt/constraints/sos/BUILD.bazel b/ortools/math_opt/constraints/sos/BUILD.bazel index e5c55a0630..9573e96039 100644 --- a/ortools/math_opt/constraints/sos/BUILD.bazel +++ b/ortools/math_opt/constraints/sos/BUILD.bazel @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( diff --git a/ortools/math_opt/constraints/util/BUILD.bazel b/ortools/math_opt/constraints/util/BUILD.bazel index 4aada4db68..dc015a8b8d 100644 --- a/ortools/math_opt/constraints/util/BUILD.bazel +++ b/ortools/math_opt/constraints/util/BUILD.bazel @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( diff --git a/ortools/math_opt/core/BUILD.bazel b/ortools/math_opt/core/BUILD.bazel index dd45ee0207..2e23d864e8 100644 --- a/ortools/math_opt/core/BUILD.bazel +++ b/ortools/math_opt/core/BUILD.bazel @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") + package(default_visibility = ["//ortools/math_opt:__subpackages__"]) cc_library( @@ -62,9 +64,9 @@ cc_library( deps = [ "//ortools/base:linked_hash_map", "//ortools/base:status_macros", + "//ortools/base:string_view_migration", "//ortools/math_opt:model_cc_proto", "//ortools/math_opt:model_update_cc_proto", - "//ortools/base:string_view_migration", "@abseil-cpp//absl/algorithm:container", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/log:check", diff --git a/ortools/math_opt/cpp/BUILD.bazel b/ortools/math_opt/cpp/BUILD.bazel index 4c609e3a3a..7a82d9736a 100644 --- a/ortools/math_opt/cpp/BUILD.bazel +++ b/ortools/math_opt/cpp/BUILD.bazel @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") + # External users should depend only on ":math_opt" and include # "math_opt.h". Hence other libraries are private. package(default_visibility = [ diff --git a/ortools/math_opt/elemental/python/BUILD.bazel b/ortools/math_opt/elemental/python/BUILD.bazel index 7d290b43ba..207be61169 100644 --- a/ortools/math_opt/elemental/python/BUILD.bazel +++ b/ortools/math_opt/elemental/python/BUILD.bazel @@ -14,7 +14,7 @@ load("@pip_deps//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") load("@rules_python//python:py_library.bzl", "py_library") -load("@rules_python//python:py_test.bzl", "py_test") +load("@rules_python//python:py_test.bzl", "py_test") genrule( name = "generated_enums", diff --git a/ortools/math_opt/python/BUILD.bazel b/ortools/math_opt/python/BUILD.bazel index fb0ce15240..63b12001c7 100644 --- a/ortools/math_opt/python/BUILD.bazel +++ b/ortools/math_opt/python/BUILD.bazel @@ -13,7 +13,7 @@ load("@pip_deps//:requirements.bzl", "requirement") load("@rules_python//python:py_library.bzl", "py_library") -load("@rules_python//python:py_test.bzl", "py_test") +load("@rules_python//python:py_test.bzl", "py_test") # External users should depend only on ":mathopt" and import "mathopt". # Hence other libraries are private. diff --git a/ortools/math_opt/python/ipc/BUILD.bazel b/ortools/math_opt/python/ipc/BUILD.bazel index 79d3dd9911..6b271dd68b 100644 --- a/ortools/math_opt/python/ipc/BUILD.bazel +++ b/ortools/math_opt/python/ipc/BUILD.bazel @@ -12,7 +12,7 @@ # limitations under the License. load("@pip_deps//:requirements.bzl", "requirement") -load("@rules_python//python:defs.bzl", "py_library") +load("@rules_python//python:py_library.bzl", "py_library") py_library( name = "remote_http_solve", diff --git a/ortools/math_opt/python/testing/BUILD.bazel b/ortools/math_opt/python/testing/BUILD.bazel index 374bf0332a..467db47bdc 100644 --- a/ortools/math_opt/python/testing/BUILD.bazel +++ b/ortools/math_opt/python/testing/BUILD.bazel @@ -13,7 +13,7 @@ load("@pip_deps//:requirements.bzl", "requirement") load("@rules_python//python:py_library.bzl", "py_library") -load("@rules_python//python:py_test.bzl", "py_test") +load("@rules_python//python:py_test.bzl", "py_test") package(default_visibility = ["//ortools/math_opt:__subpackages__"]) diff --git a/ortools/math_opt/solver_tests/test_models_test.cc b/ortools/math_opt/solver_tests/test_models_test.cc index 9c751ce1a2..6fbb74312c 100644 --- a/ortools/math_opt/solver_tests/test_models_test.cc +++ b/ortools/math_opt/solver_tests/test_models_test.cc @@ -24,21 +24,25 @@ namespace operations_research::math_opt { using ::testing::status::IsOkAndHolds; +#if defined(USE_SCIP) TEST(SmallModelTest, Integer) { const std::unique_ptr model = SmallModel(/*integer=*/true); EXPECT_THAT(Solve(*model, SolverType::kGscip), IsOkAndHolds(IsOptimal(9.0))); } +#endif // USE_SCIP TEST(SmallModelTest, Continuous) { const std::unique_ptr model = SmallModel(/*integer=*/false); EXPECT_THAT(Solve(*model, SolverType::kGlop), IsOkAndHolds(IsOptimal(12.0))); } +#if defined(USE_SCIP) TEST(DenseIndependentSetTest, Integer) { const std::unique_ptr model = DenseIndependentSet(/*integer=*/true); EXPECT_THAT(Solve(*model, SolverType::kGscip), IsOkAndHolds(IsOptimal(7.0))); } +#endif // USE_SCIP TEST(DenseIndependentSetTest, Continuous) { const std::unique_ptr model = @@ -47,6 +51,7 @@ TEST(DenseIndependentSetTest, Continuous) { IsOkAndHolds(IsOptimal(10.0 * (5 + 4 + 3) / 2.0))); } +#if defined(USE_SCIP) TEST(DenseIndependentSetHint5Test, HintIsFeasibleWithObjective5) { const std::unique_ptr model = DenseIndependentSet(/*integer=*/true, 5); ModelSolveParameters model_params; @@ -58,12 +63,15 @@ TEST(DenseIndependentSetHint5Test, HintIsFeasibleWithObjective5) { } EXPECT_THAT(Solve(*model, SolverType::kGscip), IsOkAndHolds(IsOptimal(5.0))); } +#endif // USE_SCIP +#if defined(USE_SCIP) TEST(IndependentSetCompleteGraphTest, Integer) { const std::unique_ptr model = IndependentSetCompleteGraph(/*integer=*/true); EXPECT_THAT(Solve(*model, SolverType::kGscip), IsOkAndHolds(IsOptimal(1.0))); } +#endif // USE_SCIP TEST(IndependentSetCompleteGraphTest, Continuous) { const std::unique_ptr model = diff --git a/ortools/math_opt/solvers/highs_solver.cc b/ortools/math_opt/solvers/highs_solver.cc index 25f66f1e7f..42e30f9033 100644 --- a/ortools/math_opt/solvers/highs_solver.cc +++ b/ortools/math_opt/solvers/highs_solver.cc @@ -14,7 +14,6 @@ // Unimplemented features: // * Quadratic objective // * TODO(b/272767311): initial basis, more precise returned basis. -// * Starting solution // * TODO(b/271104776): Returning rays #include "ortools/math_opt/solvers/highs_solver.h" @@ -925,6 +924,21 @@ absl::StatusOr HighsSolver::Solve( return absl::OkStatus(); }; + if (model_parameters.solution_hints_size() > 0) { + // Take the first solution hint and set the solution. + const SolutionHintProto& hint = model_parameters.solution_hints(0); + HighsInt num_entries = hint.variable_values().ids_size(); + std::vector index(num_entries); + std::vector value(num_entries); + size_t i = 0; + for (const auto [id, val] : MakeView(hint.variable_values())) { + index[i] = variable_data_.at(id).index; + value[i] = val; + ++i; + } + RETURN_IF_ERROR(ToStatus(highs_->setSolution(num_entries, index.data(), value.data()))); + } + RETURN_IF_ERROR(ListInvertedBounds().ToStatus()); // TODO(b/271595607): delete this code once we upgrade HiGHS, if HiGHS does // return a proper infeasibility status for models with empty integer bounds. diff --git a/ortools/math_opt/solvers/highs_solver_test.cc b/ortools/math_opt/solvers/highs_solver_test.cc index ccb276129b..6c3805bdda 100644 --- a/ortools/math_opt/solvers/highs_solver_test.cc +++ b/ortools/math_opt/solvers/highs_solver_test.cc @@ -162,11 +162,20 @@ INSTANTIATE_TEST_SUITE_P(HighsLpModelSolveParametersTest, Values(LpModelSolveParametersTestParameters( SolverType::kHighs, /*exact_zeros=*/true, /*supports_duals=*/true, - /*supports_primal_only_warm_starts=*/false))); + /*supports_primal_only_warm_starts=*/true))); -// MIP hint appears to be supported by Highs::setSolution, this is not yet -// implemented. -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MipSolutionHintTest); +SolutionHintTestParams MakeHighsSolutionHintParams() { + SolveParameters solve_params; + solve_params.presolve = Emphasis::kOff; + (*solve_params.highs.mutable_int_options())["mip_max_nodes"] = 0; + std::string hint_message_regex = + "Attempting to find feasible solution by " + "solving MIP for user-supplied values of"; + return SolutionHintTestParams(SolverType::kHighs, solve_params, std::nullopt, + hint_message_regex); +} +INSTANTIATE_TEST_SUITE_P(HighsSolutionHintTest, MipSolutionHintTest, + Values(MakeHighsSolutionHintParams())); // HiGHS does not support branching priority. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BranchPrioritiesTest); diff --git a/ortools/math_opt/solvers/xpress/BUILD.bazel b/ortools/math_opt/solvers/xpress/BUILD.bazel index b339b3b6e7..5b18b11f2c 100644 --- a/ortools/math_opt/solvers/xpress/BUILD.bazel +++ b/ortools/math_opt/solvers/xpress/BUILD.bazel @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@rules_cc//cc:cc_library.bzl", "cc_library") + # Code specific to XPRESS used in xpress_solver.cc. package(default_visibility = ["//ortools/math_opt/solvers:__subpackages__"])