Skip to content

Commit

Permalink
math_opt: Export from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Oct 9, 2023
1 parent 51ddf29 commit 67c6a5f
Show file tree
Hide file tree
Showing 24 changed files with 1,191 additions and 471 deletions.
2 changes: 1 addition & 1 deletion ortools/math_opt/constraints/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ target_sources(${NAME} PUBLIC
$<TARGET_OBJECTS:${NAME}_sos>
$<TARGET_OBJECTS:${NAME}_util>
)
install(TARGETS ${PROJECT_NAME}_math_opt_constraints EXPORT ${PROJECT_NAME}Targets)
install(TARGETS ${NAME} EXPORT ${PROJECT_NAME}Targets)
2 changes: 1 addition & 1 deletion ortools/math_opt/constraints/indicator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ target_include_directories(${NAME} PUBLIC
target_link_libraries(${NAME} PRIVATE
absl::strings
${PROJECT_NAMESPACE}::math_opt_proto)
#install(TARGETS ${PROJECT_NAME}_math_opt_constraints_indicator EXPORT ${PROJECT_NAME}Targets)
#install(TARGETS ${NAME} EXPORT ${PROJECT_NAME}Targets)
95 changes: 74 additions & 21 deletions ortools/math_opt/core/math_opt_proto_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,84 +164,136 @@ ABSL_DEPRECATED(
TerminationProto TerminateForReason(TerminationReasonProto reason,
absl::string_view detail = {});

// Returns trivial bounds.
//
// Trivial bounds are:
// * for a maximization:
// - primal_bound: -inf
// - dual_bound : +inf
// * for a minimization:
// - primal_bound: +inf
// - dual_bound : -inf
ObjectiveBoundsProto MakeTrivialBounds(bool is_maximize);

// Sets problem statuses to undetermined and sets trivial bounds independently
// of the selected reason.
// Returns a TerminationProto with the provided reason and details along with
// trivial bounds and FEASIBILITY_STATUS_UNDETERMINED statuses.
TerminationProto TerminateForReason(bool is_maximize,
TerminationReasonProto reason,
absl::string_view detail = {});

// Returns a TERMINATION_REASON_OPTIMAL termination with the provided objective
// bounds and FEASIBILITY_STATUS_FEASIBLE primal and dual statuses.
//
// finite_primal_objective must be finite for a valid TerminationProto to be
// returned.
//
// TODO(b/290359402): additionally require dual_objective to be finite.
TerminationProto OptimalTerminationProto(double finite_primal_objective,
double dual_objective,
absl::string_view detail = {});

// Returns a TERMINATION_REASON_INFEASIBLE termination with
// FEASIBILITY_STATUS_INFEASIBLE primal status and the provided dual status.
//
// It sets a trivial primal bound and a trivial dual bound based on the provided
// dual status.
//
// The convention for infeasible MIPs is that dual_feasibility_status is
// feasible (There always exist a dual feasible convex relaxation of an
// infeasible MIP).
// dual_feasibility_status must not be unspecified for a valid TerminationProto
// to be returned.
//
// dual_feasibility_status must not be FEASIBILITY_STATUS_UNSPECIFIED for a
// valid TerminationProto to be returned.
TerminationProto InfeasibleTerminationProto(
bool is_maximize, FeasibilityStatusProto dual_feasibility_status,
absl::string_view detail = {});

// Returns a TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED termination with
// FEASIBILITY_STATUS_UNDETERMINED primal status and the provided dual status
// along with trivial bounds.
//
// primal_or_dual_infeasible is set if dual_feasibility_status is
// FEASIBILITY_STATUS_UNDETERMINED.
//
// dual_feasibility_status must be infeasible or undetermined for a valid
// TerminationProto to be returned.
TerminationProto InfeasibleOrUnboundedTerminationProto(
bool is_maximize, FeasibilityStatusProto dual_feasibility_status,
absl::string_view detail = {});

// Returns a TERMINATION_REASON_UNBOUNDED termination with a
// FEASIBILITY_STATUS_FEASIBLE primal status and FEASIBILITY_STATUS_INFEASIBLE
// dual one along with corresponding trivial bounds.
TerminationProto UnboundedTerminationProto(bool is_maximize,
absl::string_view detail = {});

// Assumes dual solution exists if optional_dual_objective is set even if
// Returns a TERMINATION_REASON_NO_SOLUTION_FOUND termination with
// FEASIBILITY_STATUS_UNDETERMINED primal status.
//
// Assumes dual solution exists iff optional_dual_objective is set even if
// infinite (some solvers return feasible dual solutions without an objective
// value). optional_dual_objective should not be set when limit is LIMIT_CUTOFF
// for a valid TerminationProto to be returned (use
// LimitCutoffTerminationProto() below instead).
//
// It sets a trivial primal bound. The dual bound is either set to the
// optional_dual_objective if set, else to a trivial value.
//
// TODO(b/290359402): Consider improving to require a finite dual bound when
// dual feasible solutions are returned.
TerminationProto NoSolutionFoundTerminationProto(
bool is_maximize, LimitProto limit,
std::optional<double> optional_dual_objective = std::nullopt,
absl::string_view detail = {});

// Returns a TERMINATION_REASON_FEASIBLE termination with a
// FEASIBILITY_STATUS_FEASIBLE primal status. The dual status depends on
// optional_dual_objective.
//
// finite_primal_objective should be finite and limit should not be LIMIT_CUTOFF
// for a valid TerminationProto to be returned (use
// LimitCutoffTerminationProto() below instead).
// Assumes dual solution exists if optional_dual_objective is set even if
//
// Assumes dual solution exists iff optional_dual_objective is set even if
// infinite (some solvers return feasible dual solutions without an objective
// value)
// value). If set the dual status is set to FEASIBILITY_STATUS_FEASIBLE, else it
// is FEASIBILITY_STATUS_UNDETERMINED.
//
// It sets the primal bound based on the primal objective. The dual bound is
// either set to the optional_dual_objective if set, else to a trivial value.
//
// TODO(b/290359402): Consider improving to require a finite dual bound when
// dual feasible solutions are returned.
TerminationProto FeasibleTerminationProto(
bool is_maximize, LimitProto limit, double finite_primal_objective,
std::optional<double> optional_dual_objective = std::nullopt,
absl::string_view detail = {});

// Assumes primal solution exists if optional_finite_primal_objective is set.
// If set, optional_finite_primal_objective should be finite if set for a valid
// TerminationProto to be returned.
// Assumes dual solution exists if optional_dual_objective is set even if
// infinite (some solvers return feasible dual solutions without an objective
// value)
// TODO(b/290359402): Consider improving to require a finite dual bound when
// dual feasible solutions are returned.
// Either calls FeasibleTerminationProto() or NoSolutionFoundTerminationProto()
// based on optional_finite_primal_objective having a value.
//
// That is it assumes a primal feasible solution exists iff
// optional_finite_primal_objective has a value.
TerminationProto LimitTerminationProto(
bool is_maximize, LimitProto limit,
std::optional<double> optional_finite_primal_objective,
std::optional<double> optional_dual_objective = std::nullopt,
absl::string_view detail = {});

// Assumes primal solution exists if primal_objective is finite.
// Assumes dual solution exists if claim_dual_feasible_solution_exists is true
// even if dual_objective is infinite (some solvers return feasible dual
// solutions without an objective value). If dual_objective is finite then
// claim_dual_feasible_solution_exists must be true for a valid termination to
// be returned.
// Returns either a TERMINATION_REASON_FEASIBLE or
// TERMINATION_REASON_NO_SOLUTION_FOUND termination depending on
// primal_objective being finite or not. That is it assumes there is a primal
// feasible solution iff primal_objective is finite.
//
// If claim_dual_feasible_solution_exists is true, the dual_status is set as
// FEASIBILITY_STATUS_FEASIBLE, else FEASIBILITY_STATUS_UNDETERMINED.
//
// This function assumes dual solution exists if
// claim_dual_feasible_solution_exists is true even if dual_objective is
// infinite (some solvers return feasible dual solutions without an objective
// value). If dual_objective is finite then claim_dual_feasible_solution_exists
// must be true for a valid termination to be returned.
//
// TODO(b/290359402): Consider improving to require a finite dual bound when
// dual feasible solutions are returned.
TerminationProto LimitTerminationProto(LimitProto limit,
Expand All @@ -250,6 +302,7 @@ TerminationProto LimitTerminationProto(LimitProto limit,
bool claim_dual_feasible_solution_exists,
absl::string_view detail = {});

// Calls NoSolutionFoundTerminationProto() with LIMIT_CUTOFF LIMIT.
TerminationProto CutoffTerminationProto(bool is_maximize,
absl::string_view detail = {});

Expand Down
7 changes: 7 additions & 0 deletions ortools/math_opt/cpp/compute_infeasible_subsystem_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ struct ModelSubset {

bool empty() const { return !lower && !upper; }

friend bool operator==(const Bounds& lhs, const Bounds& rhs) {
return lhs.lower == rhs.lower && lhs.upper == rhs.upper;
}
friend bool operator!=(const Bounds& lhs, const Bounds& rhs) {
return !(lhs == rhs);
}

bool lower = false;
bool upper = false;
};
Expand Down
6 changes: 3 additions & 3 deletions ortools/math_opt/cpp/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
#include "ortools/math_opt/cpp/objective.h" // IWYU pragma: export
#include "ortools/math_opt/cpp/update_tracker.h" // IWYU pragma: export
#include "ortools/math_opt/cpp/variable_and_expressions.h" // IWYU pragma: export
#include "ortools/math_opt/model.pb.h" // IWYU pragma: export
#include "ortools/math_opt/model_update.pb.h" // IWYU pragma: export
#include "ortools/math_opt/storage/model_storage.h"
#include "ortools/math_opt/model.pb.h" // IWYU pragma: export
#include "ortools/math_opt/model_update.pb.h" // IWYU pragma: export
#include "ortools/math_opt/storage/model_storage.h" // IWYU pragma: export
#include "ortools/math_opt/storage/model_storage_types.h" // IWYU pragma: export

namespace operations_research {
Expand Down
2 changes: 1 addition & 1 deletion ortools/math_opt/io/mps_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ absl::StatusOr<std::string> ModelProtoToMps(const ModelProto& model) {
absl::StatusOr<ModelProto> ReadMpsFile(const absl::string_view filename) {
glop::MPSReader mps_reader;
MPModelProto mp_model;
RETURN_IF_ERROR(mps_reader.ParseFile(std::string(filename), &mp_model));
RETURN_IF_ERROR(mps_reader.ParseFile(filename, &mp_model));
return MPModelProtoToMathOptModel(mp_model);
}

Expand Down
86 changes: 86 additions & 0 deletions ortools/math_opt/labs/general_constraint_to_mip.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.

#include "ortools/math_opt/labs/general_constraint_to_mip.h"

#include <limits>

#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "ortools/math_opt/cpp/math_opt.h"
#include "ortools/math_opt/labs/linear_expr_util.h"

namespace operations_research::math_opt {

constexpr double kInf = std::numeric_limits<double>::infinity();

absl::Status FormulateIndicatorConstraintAsMip(
Model& model, const IndicatorConstraint indicator_constraint) {
if (indicator_constraint.storage() != model.storage()) {
return absl::InvalidArgumentError(
absl::StrFormat("Indicator constraint %s is associated with wrong "
"model (expected: %s, actual: %s)",
indicator_constraint.name(), model.name(),
indicator_constraint.storage()->name()));
}
if (!indicator_constraint.indicator_variable()) {
model.DeleteIndicatorConstraint(indicator_constraint);
return absl::OkStatus();
}
const Variable indicator_variable =
*indicator_constraint.indicator_variable();
if (!indicator_variable.is_integer() ||
indicator_variable.lower_bound() < 0 ||
indicator_variable.upper_bound() > 1) {
return absl::InvalidArgumentError(absl::StrFormat(
"In indicator constraint %s: indicator variable %s is "
"not a binary variable",
indicator_constraint.name(), indicator_variable.name()));
}
const BoundedLinearExpression implied_constraint =
indicator_constraint.ImpliedConstraint();
// One if the implied constraint should hold; zero otherwise.
const LinearExpression activated_expr =
indicator_constraint.activate_on_zero() ? 1 - indicator_variable
: indicator_variable;
if (implied_constraint.lower_bound > -kInf) {
const double expr_lower_bound = LowerBound(implied_constraint.expression);
if (expr_lower_bound == -kInf) {
return absl::InvalidArgumentError(
absl::StrFormat("In indicator constraint %s: cannot prove that "
"implied constraint is unbounded from below",
indicator_constraint.name()));
}
model.AddLinearConstraint(
implied_constraint.expression >=
expr_lower_bound + (implied_constraint.lower_bound - expr_lower_bound) *
activated_expr);
}
if (implied_constraint.upper_bound < kInf) {
const double expr_upper_bound = UpperBound(implied_constraint.expression);
if (expr_upper_bound == kInf) {
return absl::InvalidArgumentError(
absl::StrFormat("In indicator constraint %s: cannot prove that "
"implied constraint is unbounded from above",
indicator_constraint.name()));
}
model.AddLinearConstraint(
implied_constraint.expression <=
expr_upper_bound + (implied_constraint.upper_bound - expr_upper_bound) *
activated_expr);
}
model.DeleteIndicatorConstraint(indicator_constraint);
return absl::OkStatus();
}

} // namespace operations_research::math_opt
44 changes: 44 additions & 0 deletions ortools/math_opt/labs/general_constraint_to_mip.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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.

#ifndef OR_TOOLS_MATH_OPT_LABS_GENERAL_CONSTRAINT_TO_MIP_H_
#define OR_TOOLS_MATH_OPT_LABS_GENERAL_CONSTRAINT_TO_MIP_H_

#include "absl/status/status.h"
#include "ortools/math_opt/cpp/math_opt.h"

namespace operations_research::math_opt {

// Takes a `model` and an `indicator_constraint` from that same model, and
// models that constraint using mixed-integer programming (MIP). This entails
// deleting `indicator_constraint` from `model` and adding new linear
// constraints.
//
// As of 2023-10-03, this formulation is a simple big-M formulation:
//
// Indicator constraint: x = 1 --> lb ≤ <a, y> ≤ ub
// Becomes: if lb > -∞: <a, y> ≥ lb + (LowerBound(<a, y>) - lb) (1 - x)
// if ub < +∞: <a, y> ≤ ub + (UpperBound(<a, y>) - ub) (1 - x),
//
// where LowerBound() and UpperBound() are from linear_expr_util.
//
// Will return an error if `indicator_constraint` is not valid or associated
// with `model`, or if the simple bound computations are not able to prove that
// the indicator constraint is MIP representable (namely, if LowerBound() and/or
// UpperBound() return -∞ or +∞, respectively).
absl::Status FormulateIndicatorConstraintAsMip(
Model& model, IndicatorConstraint indicator_constraint);

} // namespace operations_research::math_opt

#endif // OR_TOOLS_MATH_OPT_LABS_GENERAL_CONSTRAINT_TO_MIP_H_
Loading

0 comments on commit 67c6a5f

Please sign in to comment.