math_opt: Export from google3

This commit is contained in:
Corentin Le Molgat
2024-11-08 17:36:50 +01:00
parent b924e12220
commit a70cc56969
31 changed files with 268 additions and 44 deletions

View File

@@ -226,6 +226,8 @@ std::function<int(GRBenv** envP)> GRBemptyenv = nullptr;
std::function<int(GRBenv** envP, const char* logfilename)> GRBloadenv = nullptr;
std::function<int(GRBenv* env)> GRBstartenv = nullptr;
std::function<GRBenv*(GRBmodel* model)> GRBgetenv = nullptr;
std::function<GRBenv*(GRBmodel* model, int num)> GRBgetmultiobjenv = nullptr;
std::function<GRBenv*(GRBmodel* model)> GRBdiscardmultiobjenvs = nullptr;
std::function<void(GRBenv* env)> GRBfreeenv = nullptr;
std::function<const char*(GRBenv* env)> GRBgeterrormsg = nullptr;
std::function<void(int* majorP, int* minorP, int* technicalP)> GRBversion =
@@ -337,6 +339,10 @@ void LoadGurobiFunctions(DynamicLibrary* gurobi_dynamic_library) {
gurobi_dynamic_library->GetFunction(&GRBgetstrparaminfo,
"GRBgetstrparaminfo");
gurobi_dynamic_library->GetFunction(&GRBgetenv, "GRBgetenv");
gurobi_dynamic_library->GetFunction(&GRBgetmultiobjenv,
"GRBgetmultiobjenv");
gurobi_dynamic_library->GetFunction(&GRBdiscardmultiobjenvs,
"GRBdiscardmultiobjenvs");
gurobi_dynamic_library->GetFunction(&GRBfreeenv, "GRBfreeenv");
gurobi_dynamic_library->GetFunction(&GRBgeterrormsg, "GRBgeterrormsg");
gurobi_dynamic_library->GetFunction(&GRBversion, "GRBversion");

View File

@@ -723,6 +723,8 @@ extern std::function<int(GRBenv *envP, const char *paramname, int *valueP, int *
extern std::function<int(GRBenv *envP, const char *paramname, double *valueP, double *minP, double *maxP, double *defP)> GRBgetdblparaminfo;
extern std::function<int(GRBenv *envP, const char *paramname, char *valueP, char *defP)> GRBgetstrparaminfo;
extern std::function<GRBenv *(GRBmodel *model)> GRBgetenv;
extern std::function<GRBenv*(GRBmodel* model, int num)> GRBgetmultiobjenv;
extern std::function<GRBenv*(GRBmodel* model)> GRBdiscardmultiobjenvs;
extern std::function<void(GRBenv *env)> GRBfreeenv;
extern std::function<const char *(GRBenv *env)> GRBgeterrormsg;
extern std::function<void(int *majorP, int *minorP, int *technicalP)> GRBversion;

View File

@@ -11,9 +11,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_python//python:proto.bzl", "py_proto_library")
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
package(default_visibility = ["//visibility:public"])
@@ -64,6 +64,7 @@ proto_library(
deps = [
":solution_proto",
":sparse_containers_proto",
"@com_google_protobuf//:duration_proto",
],
)

View File

@@ -79,7 +79,7 @@ class QuadraticConstraint {
// The quadratic expression will have a zero offset, even if the constraint
// was created with a non-zero one. For example:
//
// const LinearConstraint c =
// const QuadraticConstraint c =
// model.AddQuadraticConstraint(3.2 <= x*x + 1.0 <= 4.2);
//
// // Here `e` will contain 3.2 - 1.0 <= x*x <= 4.2 - 1.0.

View File

@@ -24,6 +24,7 @@ cc_library(
"//ortools/math_opt/cpp:variable_and_expressions",
"//ortools/math_opt/storage:linear_expression_data",
"//ortools/math_opt/storage:model_storage",
"//ortools/math_opt/storage:model_storage_types",
"@com_google_absl//absl/strings",
],
)

View File

@@ -24,9 +24,11 @@
#include "absl/strings/string_view.h"
#include "ortools/base/strong_int.h"
#include "ortools/math_opt/constraints/second_order_cone/storage.h" // IWYU pragma: keep (`AtomicConstraintTraits<SecondOrderConeConstraintId>`)
#include "ortools/math_opt/constraints/util/model_util.h"
#include "ortools/math_opt/cpp/variable_and_expressions.h"
#include "ortools/math_opt/storage/model_storage.h"
#include "ortools/math_opt/storage/model_storage_types.h"
namespace operations_research::math_opt {

View File

@@ -27,6 +27,7 @@
#include "absl/strings/string_view.h"
#include "ortools/base/logging.h"
#include "ortools/base/status_builder.h"
#include "ortools/base/status_macros.h"
#include "ortools/math_opt/callback.pb.h"
#include "ortools/math_opt/core/sparse_vector_view.h"
#include "ortools/math_opt/model.pb.h"
@@ -542,6 +543,41 @@ bool UpdateIsSupported(const ModelUpdateProto& update,
return true;
}
absl::Status ModelSolveParametersAreSupported(
const ModelSolveParametersProto& model_parameters,
const SupportedProblemStructures& support_menu,
const absl::string_view solver_name) {
const auto validate_support = [solver_name](
const absl::string_view structure,
const SupportType support) -> absl::Status {
switch (support) {
case SupportType::kSupported:
return absl::OkStatus();
case SupportType::kNotSupported:
return util::InvalidArgumentErrorBuilder()
<< structure << " is not supported as " << solver_name
<< " does not support multiple objectives";
case SupportType::kNotImplemented:
return util::UnimplementedErrorBuilder()
<< structure
<< " is not supported as MathOpt does not currently support "
<< solver_name << " models with multiple objectives";
}
return absl::OkStatus();
};
if (model_parameters.has_primary_objective_parameters()) {
RETURN_IF_ERROR(validate_support(
"ModelSolveParametersProto.primary_objective_parameters",
support_menu.multi_objectives));
}
if (!model_parameters.auxiliary_objective_parameters().empty()) {
RETURN_IF_ERROR(validate_support(
"ModelSolveParametersProto.auxiliary_objective_parameters",
support_menu.multi_objectives));
}
return absl::OkStatus();
}
void UpgradeSolveResultProtoForStatsMigration(
SolveResultProto& solve_result_proto) {
*solve_result_proto.mutable_termination()->mutable_problem_status() =

View File

@@ -343,6 +343,14 @@ absl::Status ModelIsSupported(const ModelProto& model,
bool UpdateIsSupported(const ModelUpdateProto& update,
const SupportedProblemStructures& support_menu);
// Returns an InvalidArgumentError (respectively, UnimplementedError) if a
// problem structure is present in `model_parameters` and not supported (resp.,
// not yet implemented) according to `support_menu`.
absl::Status ModelSolveParametersAreSupported(
const ModelSolveParametersProto& model_parameters,
const SupportedProblemStructures& support_menu,
absl::string_view solver_name);
void UpgradeSolveResultProtoForStatsMigration(
SolveResultProto& solve_result_proto);

View File

@@ -84,6 +84,7 @@ cc_library(
"//ortools/math_opt/constraints/indicator:indicator_constraint",
"//ortools/math_opt/constraints/quadratic:quadratic_constraint",
"//ortools/math_opt/constraints/second_order_cone:second_order_cone_constraint",
"//ortools/math_opt/constraints/second_order_cone:storage",
"//ortools/math_opt/constraints/sos:sos1_constraint",
"//ortools/math_opt/constraints/sos:sos2_constraint",
"//ortools/math_opt/constraints/util:model_util",
@@ -277,6 +278,7 @@ cc_library(
":solution",
":sparse_containers",
":variable_and_expressions",
"//ortools/base:protoutil",
"//ortools/base:status_macros",
"//ortools/math_opt:model_parameters_cc_proto",
"//ortools/math_opt:solution_cc_proto",
@@ -285,8 +287,10 @@ cc_library(
"//ortools/util:status_macros",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/time",
"@com_google_protobuf//:protobuf",
],
)
@@ -316,6 +320,7 @@ cc_library(
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/types:span",
"@com_google_protobuf//:protobuf",
],
)

View File

@@ -77,7 +77,8 @@ absl::Status BoundsMapProtoToCpp(
const google::protobuf::Map<int64_t, ModelSubsetProto::Bounds>& source,
absl::flat_hash_map<K, ModelSubset::Bounds>& target,
const ModelStorage* const model,
bool (ModelStorage::*const contains_strong_id)(typename K::IdType id) const,
bool (ModelStorage::* const contains_strong_id)(typename K::IdType id)
const,
const absl::string_view object_name) {
for (const auto& [raw_id, bounds_proto] : source) {
const typename K::IdType strong_id(raw_id);
@@ -95,7 +96,8 @@ template <typename K>
absl::Status RepeatedIdsProtoToCpp(
const google::protobuf::RepeatedField<int64_t>& source,
absl::flat_hash_set<K>& target, const ModelStorage* const model,
bool (ModelStorage::*const contains_strong_id)(typename K::IdType id) const,
bool (ModelStorage::* const contains_strong_id)(typename K::IdType id)
const,
const absl::string_view object_name) {
for (const int64_t raw_id : source) {
const typename K::IdType strong_id(raw_id);

View File

@@ -228,11 +228,11 @@ std::optional<E> EnumFromString(absl::string_view str);
//
// It calls EnumToOptString(), printing the returned value if not nullopt. When
// nullopt it prints the enum numeric value instead.
template <typename E,
// We must use enable_if here to prevent this overload to be selected
// for other types than ones that implement Enum<E>.
typename = std::enable_if_t<Enum<E>::kIsImplemented>>
std::ostream& operator<<(std::ostream& out, const E value) {
// We must use enable_if here to prevent this overload to be selected
// for other types than ones that implement Enum<E>.
template <typename E>
std::enable_if_t<Enum<E>::kIsImplemented, std::ostream&> operator<<(
std::ostream& out, const E value) {
const std::optional<absl::string_view> opt_str = EnumToOptString(value);
if (opt_str.has_value()) {
out << *opt_str;

View File

@@ -22,6 +22,7 @@
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/span.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_ptr_field.h"
#include "ortools/base/logging.h"
@@ -50,7 +51,7 @@ class PrinterMessageCallbackImpl {
const std::string prefix_;
};
void PushBack(const std::vector<std::string>& messages,
void PushBack(absl::Span<const std::string> messages,
std::vector<std::string>* const sink) {
sink->insert(sink->end(), messages.begin(), messages.end());
}

View File

@@ -32,6 +32,7 @@
#include "ortools/math_opt/constraints/indicator/indicator_constraint.h"
#include "ortools/math_opt/constraints/quadratic/quadratic_constraint.h"
#include "ortools/math_opt/constraints/second_order_cone/second_order_cone_constraint.h"
#include "ortools/math_opt/constraints/second_order_cone/storage.h"
#include "ortools/math_opt/constraints/sos/sos1_constraint.h"
#include "ortools/math_opt/constraints/sos/sos2_constraint.h"
#include "ortools/math_opt/constraints/util/model_util.h"
@@ -261,7 +262,7 @@ void Model::SetObjective(const Objective objective,
}
}
void Model::AddToObjective(Objective objective,
void Model::AddToObjective(const Objective objective,
const LinearExpression& expression) {
CheckModel(objective.storage());
CheckOptionalModel(expression.storage());
@@ -272,6 +273,28 @@ void Model::AddToObjective(Objective objective,
}
}
std::vector<Variable> Model::NonzeroVariablesInLinearObjective(
const Objective objective) const {
CheckModel(objective.storage());
std::vector<Variable> result;
result.reserve(storage()->num_linear_objective_terms(objective.typed_id()));
for (const auto [var_id, unused] :
storage()->linear_objective(objective.typed_id())) {
result.push_back(Variable(storage(), var_id));
}
return result;
}
std::vector<Variable> Model::NonzeroVariablesInQuadraticObjective() const {
std::vector<Variable> result;
for (const auto& [var_id1, var_id2, unused] :
storage()->quadratic_objective_terms(kPrimaryObjectiveId)) {
result.push_back(Variable(storage(), var_id1));
result.push_back(Variable(storage(), var_id2));
}
return result;
}
ModelProto Model::ExportModel(const bool remove_names) const {
return storage()->ExportModel(remove_names);
}

View File

@@ -21,7 +21,6 @@
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <vector>
#include "absl/log/check.h"
@@ -162,7 +161,7 @@ class Model {
std::unique_ptr<Model> Clone(
std::optional<absl::string_view> new_name = std::nullopt) const;
inline const std::string& name() const;
inline absl::string_view name() const;
//////////////////////////////////////////////////////////////////////////////
// Variable methods
@@ -217,7 +216,7 @@ class Model {
inline Variable variable(VariableId id) const;
// Returns the variable name.
inline const std::string& name(Variable variable) const;
inline absl::string_view name(Variable variable) const;
// Sets a variable lower bound.
inline void set_lower_bound(Variable variable, double lower_bound);
@@ -318,7 +317,7 @@ class Model {
inline LinearConstraint linear_constraint(LinearConstraintId id) const;
// Returns the linear constraint name.
inline const std::string& name(LinearConstraint constraint) const;
inline absl::string_view name(LinearConstraint constraint) const;
// Sets a linear constraint lower bound.
inline void set_lower_bound(LinearConstraint constraint, double lower_bound);
@@ -750,6 +749,13 @@ class Model {
// Prefer set_maximize() and set_minimize() above for more readable code.
inline void set_is_maximize(bool is_maximize);
// Returns all variables that have a nonzero coefficient in the linear part of
// the primary objective. Result is not sorted.
inline std::vector<Variable> NonzeroVariablesInLinearObjective() const;
// Returns all variables that have a nonzero coefficient in the quadratic part
// of the primary objective. Result is not sorted.
std::vector<Variable> NonzeroVariablesInQuadraticObjective() const;
//////////////////////////////////////////////////////////////////////////////
// Auxiliary objective methods
//
@@ -847,6 +853,11 @@ class Model {
// Prefer set_maximize() and set_minimize() above for more readable code.
inline void set_is_maximize(Objective objective, bool is_maximize);
// Returns all variables that have a nonzero coefficient in the linear part of
// the `objective`. Result is not sorted.
std::vector<Variable> NonzeroVariablesInLinearObjective(
Objective objective) const;
// Returns a proto representation of the optimization model.
//
// See FromModelProto() to build a Model from a proto.
@@ -922,7 +933,7 @@ class Model {
// ------------------------------- Variables -----------------------------------
const std::string& Model::name() const { return storage()->name(); }
absl::string_view Model::name() const { return storage()->name(); }
Variable Model::AddVariable(const absl::string_view name) {
return Variable(storage(), storage()->AddVariable(name));
@@ -978,7 +989,7 @@ Variable Model::variable(const VariableId id) const {
return Variable(storage(), id);
}
const std::string& Model::name(const Variable variable) const {
absl::string_view Model::name(const Variable variable) const {
CheckModel(variable.storage());
return storage()->variable_name(variable.typed_id());
}
@@ -1080,7 +1091,7 @@ LinearConstraint Model::linear_constraint(const LinearConstraintId id) const {
return LinearConstraint(storage(), id);
}
const std::string& Model::name(const LinearConstraint constraint) const {
absl::string_view Model::name(const LinearConstraint constraint) const {
CheckModel(constraint.storage());
return storage()->linear_constraint_name(constraint.typed_id());
}
@@ -1491,6 +1502,10 @@ void Model::set_is_maximize(const bool is_maximize) {
storage()->set_is_maximize(kPrimaryObjectiveId, is_maximize);
}
std::vector<Variable> Model::NonzeroVariablesInLinearObjective() const {
return NonzeroVariablesInLinearObjective(primary_objective());
}
// -------------------------- Auxiliary objectives -----------------------------
Objective Model::AddAuxiliaryObjective(const int64_t priority,

View File

@@ -20,9 +20,12 @@
#include <utility>
#include "absl/algorithm/container.h"
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/time/time.h"
#include "google/protobuf/repeated_field.h"
#include "ortools/base/protoutil.h"
#include "ortools/base/status_macros.h"
#include "ortools/math_opt/cpp/linear_constraint.h"
#include "ortools/math_opt/cpp/model.h"
@@ -137,8 +140,8 @@ ModelSolveParameters::SolutionHint::FromProto(
};
}
ObjectiveParametersProto ModelSolveParameters::ObjectiveParameters::Proto()
const {
absl::StatusOr<ObjectiveParametersProto>
ModelSolveParameters::ObjectiveParameters::Proto() const {
ObjectiveParametersProto params;
if (objective_degradation_absolute_tolerance) {
params.set_objective_degradation_absolute_tolerance(
@@ -148,10 +151,14 @@ ObjectiveParametersProto ModelSolveParameters::ObjectiveParameters::Proto()
params.set_objective_degradation_relative_tolerance(
*objective_degradation_relative_tolerance);
}
if (time_limit < absl::InfiniteDuration()) {
RETURN_IF_ERROR(util_time::EncodeGoogleApiProto(
time_limit, params.mutable_time_limit()));
}
return params;
}
ModelSolveParameters::ObjectiveParameters
absl::StatusOr<ModelSolveParameters::ObjectiveParameters>
ModelSolveParameters::ObjectiveParameters::FromProto(
const ObjectiveParametersProto& proto) {
ObjectiveParameters result;
@@ -163,11 +170,18 @@ ModelSolveParameters::ObjectiveParameters::FromProto(
result.objective_degradation_relative_tolerance =
proto.objective_degradation_relative_tolerance();
}
if (proto.has_time_limit()) {
OR_ASSIGN_OR_RETURN3(result.time_limit,
util_time::DecodeGoogleApiProto(proto.time_limit()),
_ << "invalid time_limit");
} else {
result.time_limit = absl::InfiniteDuration();
}
return result;
}
// TODO: b/315974557 - Return an error if a RepeatedField is too long.
ModelSolveParametersProto ModelSolveParameters::Proto() const {
absl::StatusOr<ModelSolveParametersProto> ModelSolveParameters::Proto() const {
ModelSolveParametersProto ret;
*ret.mutable_variable_values_filter() = variable_values_filter.Proto();
*ret.mutable_dual_values_filter() = dual_values_filter.Proto();
@@ -195,11 +209,15 @@ ModelSolveParametersProto ModelSolveParameters::Proto() const {
}
}
for (const auto& [objective, params] : objective_parameters) {
if (objective.id()) {
(*ret.mutable_auxiliary_objective_parameters())[*objective.id()] =
params.Proto();
if (objective.id().has_value()) {
OR_ASSIGN_OR_RETURN3(
((*ret.mutable_auxiliary_objective_parameters())[*objective.id()]),
params.Proto(),
_ << "invalid parameters for objective " << *objective.id());
} else {
*ret.mutable_primary_objective_parameters() = params.Proto();
OR_ASSIGN_OR_RETURN3(*ret.mutable_primary_objective_parameters(),
params.Proto(),
_ << "invalid parameters for primary objective");
}
}
if (!lazy_linear_constraints.empty()) {
@@ -253,9 +271,13 @@ absl::StatusOr<ModelSolveParameters> ModelSolveParameters::FromProto(
VariableValuesFromProto(model.storage(), proto.branching_priorities()),
_ << "invalid branching_priorities");
if (proto.has_primary_objective_parameters()) {
OR_ASSIGN_OR_RETURN3(
auto primary_objective_params,
ObjectiveParameters::FromProto(proto.primary_objective_parameters()),
_ << "invalid primary_objective_parameters");
result.objective_parameters.try_emplace(
Objective::Primary(model.storage()),
ObjectiveParameters::FromProto(proto.primary_objective_parameters()));
std::move(primary_objective_params));
}
for (const auto& [id, aux_obj_params_proto] :
proto.auxiliary_objective_parameters()) {
@@ -264,9 +286,13 @@ absl::StatusOr<ModelSolveParameters> ModelSolveParameters::FromProto(
<< "invalid auxiliary_objective_parameters with id: " << id
<< ", objective not in the model";
}
OR_ASSIGN_OR_RETURN3(
auto aux_obj_params,
ObjectiveParameters::FromProto(aux_obj_params_proto),
_ << "invalid auxiliary_objective_parameters with id: " << id);
result.objective_parameters.try_emplace(
Objective::Auxiliary(model.storage(), AuxiliaryObjectiveId{id}),
ObjectiveParameters::FromProto(aux_obj_params_proto));
std::move(aux_obj_params));
}
for (int64_t lin_con : proto.lazy_linear_constraint_ids()) {
if (!model.has_linear_constraint(lin_con)) {

View File

@@ -27,6 +27,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/time/time.h"
#include "ortools/math_opt/cpp/linear_constraint.h"
#include "ortools/math_opt/cpp/map_filter.h" // IWYU pragma: export
#include "ortools/math_opt/cpp/model.h"
@@ -184,10 +185,21 @@ struct ModelSolveParameters {
// If set, must be nonnegative.
std::optional<double> objective_degradation_relative_tolerance;
// Returns the proto equivalent of this object.
ObjectiveParametersProto Proto() const;
// Maximum time a solver should spend on optimizing this particular
// objective (or infinite if not set).
//
// Note that this does not supersede the global time limit in
// SolveParametersProto.time_limit; both will be enforced when set.
//
// This value is not a hard limit, solve time may slightly exceed this
// value.
absl::Duration time_limit = absl::InfiniteDuration();
static ObjectiveParameters FromProto(const ObjectiveParametersProto& proto);
// Returns the proto equivalent of this object.
absl::StatusOr<ObjectiveParametersProto> Proto() const;
static absl::StatusOr<ObjectiveParameters> FromProto(
const ObjectiveParametersProto& proto);
};
// Parameters for individual objectives in a multi-objective model.
ObjectiveMap<ObjectiveParameters> objective_parameters;
@@ -209,7 +221,7 @@ struct ModelSolveParameters {
//
// The caller should use CheckModelStorage() as this function does not check
// internal consistency of the referenced variables and constraints.
ModelSolveParametersProto Proto() const;
absl::StatusOr<ModelSolveParametersProto> Proto() const;
// Returns the ModelSolveParameters corresponding to this proto and the given
// model.

View File

@@ -78,9 +78,11 @@ absl::StatusOr<SolveResult> CallSolve(
};
}
ASSIGN_OR_RETURN(ModelSolveParametersProto model_parameters,
arguments.model_parameters.Proto());
const absl::StatusOr<SolveResultProto> solve_result_proto = solver.Solve(
{.parameters = arguments.parameters.Proto(),
.model_parameters = arguments.model_parameters.Proto(),
.model_parameters = std::move(model_parameters),
.message_callback = arguments.message_callback,
.callback_registration = arguments.callback_registration.Proto(),
.user_cb = std::move(cb),

View File

@@ -30,13 +30,17 @@ namespace operations_research::math_opt {
// parameters are hints and may be ignored by the remote server (in particular
// in case of solve in a local subprocess, for example).
//
// When using RemoteSolve() and RemoteComputeInfeasibleSubsystem(), these hints
// are mostly optional as some defaults will be computed based on the other
// parameters.
// When using:
// - RemoteSolve(),
// - RemoteComputeInfeasibleSubsystem(),
// - XxxRemoteStreamingSolve(),
// - XxxRemoteStreamingComputeInfeasibleSubsystem(),
// these hints are recommended but optional. When they are not provided,
// resource usage will be estimated based on other parameters.
//
// When using RemoteStreamingSolve() these hints are used to dimension the
// resources available during the execution of every action; thus it is
// recommended to set them.
// When using NewXxxRemoteStreamingIncrementalSolver() these hints are used to
// dimension the resources available during the execution of every action; thus
// it is recommended to set them.
//
struct SolverResources {
// The number of solver threads that are expected to actually execute in

View File

@@ -17,6 +17,7 @@ syntax = "proto3";
package operations_research.math_opt;
import "google/protobuf/duration.proto";
import "ortools/math_opt/solution.proto";
import "ortools/math_opt/sparse_containers.proto";
@@ -89,6 +90,15 @@ message ObjectiveParametersProto {
//
// If set, must be nonnegative.
optional double objective_degradation_relative_tolerance = 8;
// Maximum time a solver should spend on optimizing this particular objective
// (or infinite if not set).
//
// Note that this does not supersede the global time limit in
// SolveParametersProto.time_limit; both will be enforced when set.
//
// This value is not a hard limit, solve time may slightly exceed this value.
google.protobuf.Duration time_limit = 9;
}
// TODO(b/183628247): follow naming convention in fields below.

View File

@@ -317,6 +317,7 @@ cc_library(
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
],
# Make sure the tests are included when using --dynamic_mode=off.
alwayslink = 1,

View File

@@ -110,7 +110,8 @@ ortools_cxx_library(
TESTING
)
# In CMake or-tools is linked with all enable solvers so this test won't work.
# In CMake, or-tools is linked with all solvers enable at configure time so this
# test won't work...
#ortools_cxx_test(
# NAME
# ${_PREFIX}_unregistered_solver_test

View File

@@ -15,10 +15,12 @@
#include <memory>
#include <ostream>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
#include "ortools/base/gmock.h"
#include "ortools/base/status_macros.h"

View File

@@ -30,6 +30,7 @@ cc_library(
"//ortools/gurobi:environment",
"//ortools/gurobi/isv_public:gurobi_isv",
"//ortools/math_opt/solvers:gurobi_cc_proto",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/log:die_if_null",
"@com_google_absl//absl/memory",

View File

@@ -777,6 +777,23 @@ absl::Status Gurobi::ResetParameters() {
return ToStatus(GRBresetparams(model_env_));
}
absl::Status Gurobi::SetMultiObjectiveDoubleParam(const char* const name,
const int obj_index,
const double value) {
ASSIGN_OR_RETURN(GRBenv* const obj_env, GetMultiObjectiveEnv(obj_index));
return util::StatusBuilder(ToStatus(GRBsetdblparam(obj_env, name, value)))
<< " for objective index: " << obj_index;
}
absl::StatusOr<double> Gurobi::GetMultiObjectiveDoubleParam(
const char* const name, const int obj_index) {
ASSIGN_OR_RETURN(GRBenv* const obj_env, GetMultiObjectiveEnv(obj_index));
double result;
RETURN_IF_ERROR(ToStatus(GRBgetdblparam(obj_env, name, &result)))
<< " for objective index: " << obj_index;
return result;
}
void Gurobi::Terminate() { GRBterminate(gurobi_model_); }
Gurobi::CallbackContext::CallbackContext(Gurobi* const gurobi,
@@ -842,4 +859,15 @@ absl::StatusOr<double> Gurobi::CallbackContext::CbSolution(
return result;
}
absl::StatusOr<GRBenv*> Gurobi::GetMultiObjectiveEnv(
const int obj_index) const {
GRBenv* const obj_env = GRBgetmultiobjenv(gurobi_model_, obj_index);
if (obj_env == nullptr) {
return util::InvalidArgumentErrorBuilder()
<< "Failed to get objective environment for objective index: "
<< obj_index;
}
return obj_env;
}
} // namespace operations_research::math_opt

View File

@@ -556,6 +556,20 @@ class Gurobi {
// Calls GRBresetparams().
absl::Status ResetParameters();
//////////////////////////////////////////////////////////////////////////////
// Multi-objective Parameters
//////////////////////////////////////////////////////////////////////////////
// Calls GRBsetdblparam() on the environment associated with the
// `obj_index`-th objective.
absl::Status SetMultiObjectiveDoubleParam(const char* name, int obj_index,
double value);
// Calls GRBgetdblparam() on the environment associated with the
// `obj_index`-th objective.
absl::StatusOr<double> GetMultiObjectiveDoubleParam(const char* name,
int obj_index);
// Typically not needed.
GRBmodel* model() const { return gurobi_model_; }
@@ -570,6 +584,7 @@ class Gurobi {
// optional_owned_primary_env can be null, primary_env cannot.
static absl::StatusOr<std::unique_ptr<Gurobi>> New(
GRBenvUniquePtr optional_owned_primary_env, GRBenv* primary_env);
absl::StatusOr<GRBenv*> GetMultiObjectiveEnv(int obj_index) const;
const GRBenvUniquePtr owned_primary_env_;
// Invariant: Not null.

View File

@@ -203,6 +203,7 @@ cc_library(
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",

View File

@@ -38,7 +38,7 @@ namespace operations_research::math_opt {
// const std::pair<XXX, std::unique_ptr<UpdateData>>&
// The returned iterator will be over non-const references to Field as read off
// the UpdateData values.
template <typename UpdateData, typename V, V UpdateData::*Field,
template <typename UpdateData, typename V, V UpdateData::* Field,
typename BaseIter>
class UpdateDataFieldIterator {
public:

View File

@@ -15,6 +15,7 @@
#include <cstdint>
#include <memory>
#include <new>
#include <optional>
#include <string>
#include <utility>
@@ -23,6 +24,7 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/check.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"

View File

@@ -164,7 +164,7 @@ class ModelStorage {
// considered invalid when solving.
//
// See ApplyUpdateProto() for dealing with subsequent updates.
static absl::StatusOr<std::unique_ptr<ModelStorage>> FromModelProto(
static absl::StatusOr<std::unique_ptr<ModelStorage> > FromModelProto(
const ModelProto& model_proto);
// Creates an empty minimization problem.
@@ -326,7 +326,7 @@ class ModelStorage {
// The {linear constraint, variable, coefficient} tuples with nonzero linear
// constraint matrix coefficients.
inline std::vector<std::tuple<LinearConstraintId, VariableId, double>>
inline std::vector<std::tuple<LinearConstraintId, VariableId, double> >
linear_constraint_matrix() const;
// Returns the variables with nonzero coefficients in a linear constraint.
@@ -399,7 +399,7 @@ class ModelStorage {
// are ordered such that .first <= .second. All values are nonempty.
//
// TODO(b/233630053) do no allocate the result, expose an iterator API.
inline std::vector<std::tuple<VariableId, VariableId, double>>
inline std::vector<std::tuple<VariableId, VariableId, double> >
quadratic_objective_terms(ObjectiveId id) const;
//////////////////////////////////////////////////////////////////////////////

View File

@@ -194,12 +194,15 @@ cc_library(
":ids_validator",
":solution_validator",
":sparse_vector_validator",
"//ortools/base:protoutil",
"//ortools/base:status_macros",
"//ortools/math_opt:model_parameters_cc_proto",
"//ortools/math_opt:sparse_containers_cc_proto",
"//ortools/math_opt/core:model_summary",
"//ortools/math_opt/core:sparse_vector_view",
"//ortools/util:status_macros",
"@com_google_absl//absl/status",
"@com_google_absl//absl/time",
"@com_google_protobuf//:protobuf",
],
)

View File

@@ -16,7 +16,9 @@
#include <cstdint>
#include "absl/status/status.h"
#include "absl/time/time.h"
#include "google/protobuf/repeated_field.h"
#include "ortools/base/protoutil.h"
#include "ortools/base/status_builder.h"
#include "ortools/base/status_macros.h"
#include "ortools/math_opt/core/model_summary.h"
@@ -26,6 +28,7 @@
#include "ortools/math_opt/validators/ids_validator.h"
#include "ortools/math_opt/validators/solution_validator.h"
#include "ortools/math_opt/validators/sparse_vector_validator.h"
#include "ortools/util/status_macros.h"
namespace operations_research {
namespace math_opt {
@@ -80,6 +83,17 @@ absl::Status ValidateObjectiveParameters(
"tolerance = "
<< parameters.objective_degradation_relative_tolerance() << " < 0";
}
{
OR_ASSIGN_OR_RETURN3(
const absl::Duration time_limit,
util_time::DecodeGoogleApiProto(parameters.time_limit()),
_ << "invalid ObjectiveParametersProto.time_limit");
if (time_limit < absl::ZeroDuration()) {
return util::InvalidArgumentErrorBuilder()
<< "ObjectiveParametersProto.time_limit = " << time_limit
<< " < 0";
}
}
return absl::OkStatus();
}