math_opt: update from google3
This commit is contained in:
@@ -11,20 +11,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_python//python:proto.bzl", "py_proto_library")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
|
||||
load("@rules_python//python:proto.bzl", "py_proto_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# Features that are new or under construction have restricted access, contact
|
||||
# math-opt-dev@ if you want try using these with submitted code.
|
||||
package_group(
|
||||
name = "math_opt_allow_list",
|
||||
packages = [
|
||||
"//ortools/...",
|
||||
],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "callback_proto",
|
||||
srcs = ["callback.proto"],
|
||||
@@ -215,3 +206,27 @@ py_proto_library(
|
||||
name = "infeasible_subsystem_py_pb2",
|
||||
deps = [":infeasible_subsystem_proto"],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "rpc_proto",
|
||||
srcs = ["rpc.proto"],
|
||||
deps = [
|
||||
":callback_proto",
|
||||
":infeasible_subsystem_proto",
|
||||
":model_parameters_proto",
|
||||
":model_proto",
|
||||
":model_update_proto",
|
||||
":parameters_proto",
|
||||
":result_proto",
|
||||
],
|
||||
)
|
||||
|
||||
py_proto_library(
|
||||
name = "rpc_py_pb2",
|
||||
deps = [":rpc_proto"],
|
||||
)
|
||||
|
||||
cc_proto_library(
|
||||
name = "rpc_cc_proto",
|
||||
deps = [":rpc_proto"],
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ pybind_extension(
|
||||
srcs = ["solver.cc"],
|
||||
deps =
|
||||
select({
|
||||
"//ortools/linear_solver:use_scip": ["//ortools/math_opt/solvers:gscip_solver"],
|
||||
"//ortools/linear_solver:use_cp_sat": ["//ortools/math_opt/solvers:cp_sat_solver"],
|
||||
"//conditions:default": [],
|
||||
}) +
|
||||
select({
|
||||
@@ -32,7 +32,7 @@ pybind_extension(
|
||||
"//conditions:default": [],
|
||||
}) +
|
||||
select({
|
||||
"//ortools/linear_solver:use_cp_sat": ["//ortools/math_opt/solvers:cp_sat_solver"],
|
||||
"//ortools/linear_solver:use_scip": ["//ortools/math_opt/solvers:gscip_solver"],
|
||||
"//conditions:default": [],
|
||||
}) + [
|
||||
"//ortools/math_opt:result_cc_proto",
|
||||
|
||||
@@ -325,13 +325,13 @@ bool Termination::limit_reached() const {
|
||||
reason == TerminationReason::kNoSolutionFound;
|
||||
}
|
||||
|
||||
absl::Status Termination::ReasonIs(const TerminationReason reason) const {
|
||||
absl::Status Termination::EnsureReasonIs(TerminationReason reason) const {
|
||||
if (this->reason == reason) return absl::OkStatus();
|
||||
return util::InternalErrorBuilder()
|
||||
<< "expected termination reason '" << reason << "' but got " << *this;
|
||||
}
|
||||
|
||||
absl::Status Termination::ReasonIsAnyOf(
|
||||
absl::Status Termination::EnsureReasonIsAnyOf(
|
||||
std::initializer_list<TerminationReason> reasons) const {
|
||||
for (const TerminationReason reason : reasons) {
|
||||
if (this->reason == reason) return absl::OkStatus();
|
||||
@@ -348,11 +348,11 @@ absl::Status Termination::ReasonIsAnyOf(
|
||||
}
|
||||
|
||||
absl::Status Termination::IsOptimal() const {
|
||||
return ReasonIs(TerminationReason::kOptimal);
|
||||
return EnsureReasonIs(TerminationReason::kOptimal);
|
||||
}
|
||||
|
||||
absl::Status Termination::IsOptimalOrFeasible() const {
|
||||
return ReasonIsAnyOf(
|
||||
return EnsureReasonIsAnyOf(
|
||||
{TerminationReason::kOptimal, TerminationReason::kFeasible});
|
||||
}
|
||||
|
||||
|
||||
@@ -334,11 +334,11 @@ struct Termination {
|
||||
|
||||
// Returns an OkStatus if the reason of this `Termination` is `reason`, or an
|
||||
// `InternalError` otherwise.
|
||||
absl::Status ReasonIs(TerminationReason reason) const;
|
||||
absl::Status EnsureReasonIs(TerminationReason reason) const;
|
||||
|
||||
// Returns an OkStatus if the reason of this `Termination` is in `reasons`, or
|
||||
// an `InternalError` otherwise.
|
||||
absl::Status ReasonIsAnyOf(
|
||||
absl::Status EnsureReasonIsAnyOf(
|
||||
std::initializer_list<TerminationReason> reasons) const;
|
||||
|
||||
// Returns termination with reason kOptimal, the provided objective for both
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
"""Utility functions for normalizing proto3 message objects in Python."""
|
||||
from google3.google.protobuf import duration_pb2
|
||||
from google.protobuf import duration_pb2
|
||||
from google.protobuf import descriptor
|
||||
from google.protobuf import message
|
||||
|
||||
|
||||
61
ortools/math_opt/rpc.proto
Normal file
61
ortools/math_opt/rpc.proto
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2010-2022 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package operations_research.math_opt;
|
||||
|
||||
import "ortools/math_opt/callback.proto";
|
||||
import "ortools/math_opt/infeasible_subsystem.proto";
|
||||
import "ortools/math_opt/model.proto";
|
||||
import "ortools/math_opt/model_parameters.proto";
|
||||
import "ortools/math_opt/model_update.proto";
|
||||
import "ortools/math_opt/parameters.proto";
|
||||
import "ortools/math_opt/result.proto";
|
||||
|
||||
option java_package = "com.google.ortools.mathopt";
|
||||
option java_multiple_files = true;
|
||||
|
||||
// Request for a unary remote solve in MathOpt.
|
||||
message SolveRequest {
|
||||
// Solver type to numerically solve the problem. Note that if a solver does
|
||||
// not support a specific feautre in the model, the optimization procedure
|
||||
// won't be successful.
|
||||
SolverTypeProto solver_type = 1;
|
||||
|
||||
// A mathematical representation of the optimization problem to solve.
|
||||
ModelProto model = 2;
|
||||
|
||||
SolverInitializerProto initializer = 3;
|
||||
|
||||
// Parameters to control a single solve. The enable_output parameter is
|
||||
// handled specifically. For solvers that support messages callbacks, setting
|
||||
// it to true will have the server register a message callback. The resulting
|
||||
// messages will be returned in SolveResponse.messages. For other
|
||||
// solvers, setting enable_output to true will result in an error.
|
||||
SolveParametersProto parameters = 4;
|
||||
|
||||
// Parameters to control a single solve that are specific to the input model
|
||||
// (see SolveParametersProto for model independent parameters).
|
||||
ModelSolveParametersProto model_parameters = 5;
|
||||
}
|
||||
|
||||
// Response for a unary remote solve in MathOpt.
|
||||
message SolveResponse {
|
||||
// Description of the output of solving the model in the request.
|
||||
SolveResultProto result = 1;
|
||||
|
||||
// If SolveParametersProto.enable_output has been used, this will contain log
|
||||
// messages for solvers that support message callbacks.
|
||||
repeated string messages = 2;
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_python//python:proto.bzl", "py_proto_library")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
|
||||
load("@rules_python//python:proto.bzl", "py_proto_library")
|
||||
|
||||
package(default_visibility = ["//ortools/math_opt:__subpackages__"])
|
||||
|
||||
@@ -27,6 +27,7 @@ cc_library(
|
||||
":gscip_solver_callback",
|
||||
":message_callback_data",
|
||||
"//ortools/base:cleanup",
|
||||
"//ortools/base:linked_hash_map",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/base:protoutil",
|
||||
"//ortools/base:status_macros",
|
||||
|
||||
@@ -353,15 +353,10 @@ absl::StatusOr<glop::GlopParameters> GlopSolver::MergeSolveParameters(
|
||||
case LP_ALGORITHM_DUAL_SIMPLEX:
|
||||
result.set_use_dual_simplex(true);
|
||||
break;
|
||||
case LP_ALGORITHM_BARRIER:
|
||||
warnings.emplace_back(
|
||||
"GLOP does not support 'LP_ALGORITHM_BARRIER' value for "
|
||||
"'lp_algorithm' parameter.");
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "LPAlgorithm: "
|
||||
<< ProtoEnumToString(solve_parameters.lp_algorithm())
|
||||
<< " unknown, error setting GLOP parameters";
|
||||
warnings.emplace_back(absl::StrCat(
|
||||
"GLOP does not support the 'lp_algorithm' parameter value: ",
|
||||
ProtoEnumToString(solve_parameters.lp_algorithm())));
|
||||
}
|
||||
}
|
||||
if (!result.has_use_scaling() && !result.has_scaling_method() &&
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "absl/types/span.h"
|
||||
#include "google/protobuf/repeated_ptr_field.h"
|
||||
#include "ortools/base/cleanup.h"
|
||||
#include "ortools/base/linked_hash_map.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/protoutil.h"
|
||||
@@ -246,60 +247,17 @@ inline GScipVarType GScipVarTypeFromIsInteger(const bool is_integer) {
|
||||
return is_integer ? GScipVarType::kInteger : GScipVarType::kContinuous;
|
||||
}
|
||||
|
||||
// Used to delay the evaluation of a costly computation until the first time it
|
||||
// is actually needed.
|
||||
//
|
||||
// The typical use is when we have two independent branches that need the same
|
||||
// data but we don't want to compute these data if we don't enter any of those
|
||||
// branches.
|
||||
//
|
||||
// Usage:
|
||||
// LazyInitialized<Xxx> xxx([&]() {
|
||||
// return Xxx(...);
|
||||
// });
|
||||
//
|
||||
// if (predicate_1) {
|
||||
// ...
|
||||
// f(xxx.GetOrCreate());
|
||||
// ...
|
||||
// }
|
||||
// if (predicate_2) {
|
||||
// ...
|
||||
// f(xxx.GetOrCreate());
|
||||
// ...
|
||||
// }
|
||||
template <typename T>
|
||||
class LazyInitialized {
|
||||
public:
|
||||
// Checks that the input initializer is not nullptr.
|
||||
explicit LazyInitialized(std::function<T()> initializer)
|
||||
: initializer_(ABSL_DIE_IF_NULL(initializer)) {}
|
||||
|
||||
// Returns the value returned by initializer(), calling it the first time.
|
||||
const T& GetOrCreate() {
|
||||
if (!value_) {
|
||||
value_ = initializer_();
|
||||
}
|
||||
return *value_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::function<T()> initializer_;
|
||||
std::optional<T> value_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
SparseDoubleVectorProto FillSparseDoubleVector(
|
||||
const std::vector<int64_t>& ids_in_order,
|
||||
const absl::flat_hash_map<int64_t, T>& id_map,
|
||||
const gtl::linked_hash_map<int64_t, T>& id_map,
|
||||
const absl::flat_hash_map<T, double>& value_map,
|
||||
const SparseVectorFilterProto& filter) {
|
||||
SparseVectorFilterPredicate predicate(filter);
|
||||
SparseDoubleVectorProto result;
|
||||
for (const int64_t variable_id : ids_in_order) {
|
||||
const double value = value_map.at(id_map.at(variable_id));
|
||||
if (predicate.AcceptsAndUpdate(variable_id, value)) {
|
||||
result.add_ids(variable_id);
|
||||
for (const auto [mathopt_id, scip_id] : id_map) {
|
||||
const double value = value_map.at(scip_id);
|
||||
if (predicate.AcceptsAndUpdate(mathopt_id, value)) {
|
||||
result.add_ids(mathopt_id);
|
||||
result.add_values(value);
|
||||
}
|
||||
}
|
||||
@@ -989,15 +947,6 @@ absl::StatusOr<SolveResultProto> GScipSolver::CreateSolveResultProto(
|
||||
}
|
||||
};
|
||||
|
||||
LazyInitialized<std::vector<int64_t>> sorted_variables([&]() {
|
||||
std::vector<int64_t> sorted;
|
||||
sorted.reserve(variables_.size());
|
||||
for (const auto& entry : variables_) {
|
||||
sorted.emplace_back(entry.first);
|
||||
}
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
return sorted;
|
||||
});
|
||||
CHECK_EQ(gscip_result.solutions.size(), gscip_result.objective_values.size());
|
||||
for (int i = 0; i < gscip_result.solutions.size(); ++i) {
|
||||
// GScip ensures the solutions are returned best objective first.
|
||||
@@ -1009,14 +958,13 @@ absl::StatusOr<SolveResultProto> GScipSolver::CreateSolveResultProto(
|
||||
solution->mutable_primal_solution();
|
||||
primal_solution->set_objective_value(gscip_result.objective_values[i]);
|
||||
primal_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
|
||||
*primal_solution->mutable_variable_values() = FillSparseDoubleVector(
|
||||
sorted_variables.GetOrCreate(), variables_, gscip_result.solutions[i],
|
||||
model_parameters.variable_values_filter());
|
||||
*primal_solution->mutable_variable_values() =
|
||||
FillSparseDoubleVector(variables_, gscip_result.solutions[i],
|
||||
model_parameters.variable_values_filter());
|
||||
}
|
||||
if (!gscip_result.primal_ray.empty()) {
|
||||
*solve_result.add_primal_rays()->mutable_variable_values() =
|
||||
FillSparseDoubleVector(sorted_variables.GetOrCreate(), variables_,
|
||||
gscip_result.primal_ray,
|
||||
FillSparseDoubleVector(variables_, gscip_result.primal_ray,
|
||||
model_parameters.variable_values_filter());
|
||||
}
|
||||
ASSIGN_OR_RETURN(*solve_result.mutable_termination(),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/linked_hash_map.h"
|
||||
#include "ortools/gscip/gscip.h"
|
||||
#include "ortools/gscip/gscip.pb.h"
|
||||
#include "ortools/gscip/gscip_event_handler.h"
|
||||
@@ -195,8 +196,9 @@ class GScipSolver : public SolverInterface {
|
||||
absl::Status RegisterHandlers();
|
||||
|
||||
const std::unique_ptr<GScip> gscip_;
|
||||
|
||||
InterruptEventHandler interrupt_event_handler_;
|
||||
absl::flat_hash_map<int64_t, SCIP_VAR*> variables_;
|
||||
gtl::linked_hash_map<int64_t, SCIP_VAR*> variables_;
|
||||
bool has_quadratic_objective_ = false;
|
||||
absl::flat_hash_map<int64_t, SCIP_CONS*> linear_constraints_;
|
||||
absl::flat_hash_map<int64_t, SCIP_CONS*> quadratic_constraints_;
|
||||
|
||||
@@ -891,6 +891,7 @@ absl::StatusOr<SolveResultProto> GurobiSolver::ExtractSolveResultProto(
|
||||
GetBestDualBound(solution_and_claims.solutions));
|
||||
solution_claims = solution_and_claims.solution_claims;
|
||||
|
||||
// TODO(b/195295177): Add tests for rays in unbounded MIPs
|
||||
RETURN_IF_ERROR(FillRays(model_parameters, solution_claims, result));
|
||||
|
||||
for (SolutionProto& solution : solution_and_claims.solutions) {
|
||||
@@ -2929,7 +2930,9 @@ absl::StatusOr<SolveResultProto> GurobiSolver::Solve(
|
||||
RETURN_IF_ERROR(
|
||||
UpdateInt32ListAttribute(model_parameters.branching_priorities(),
|
||||
GRB_INT_ATTR_BRANCHPRIORITY, variables_map_));
|
||||
RETURN_IF_ERROR(SetMultiObjectiveTolerances(model_parameters));
|
||||
if (is_multi_objective_mode()) {
|
||||
RETURN_IF_ERROR(SetMultiObjectiveTolerances(model_parameters));
|
||||
}
|
||||
|
||||
// Here we register the callback when we either have a user callback or a
|
||||
// local interrupter. The rationale for doing so when we have only an
|
||||
|
||||
@@ -379,6 +379,8 @@ class GurobiSolver : public SolverInterface {
|
||||
// `.constraint_index` is set and refers to the Gurobi linear constraint index
|
||||
// for a slack constraint just added to the model such that:
|
||||
// `expression` == `.variable_index`.
|
||||
// TODO(b/267310257): Use this for linear constraint slacks, and maybe move it
|
||||
// up the stack to a bridge.
|
||||
absl::StatusOr<VariableEqualToExpression>
|
||||
CreateSlackVariableEqualToExpression(const LinearExpressionProto& expression);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user