Skip to content

Commit

Permalink
Export math_opt from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Jul 13, 2023
1 parent 944fd97 commit a29d1ee
Show file tree
Hide file tree
Showing 29 changed files with 415 additions and 2,707 deletions.
1 change: 1 addition & 0 deletions ortools/math_opt/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ proto_library(
deps = [
":solution_proto",
"//ortools/gscip:gscip_proto",
"//ortools/pdlp:solve_log_proto",
"@com_google_protobuf//:duration_proto",
],
)
Expand Down
4 changes: 2 additions & 2 deletions ortools/math_opt/cpp/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ cc_library(
":linear_constraint",
":solution",
":variable_and_expressions",
"//ortools/base",
"//ortools/base:protoutil",
"//ortools/base:status_macros",
"//ortools/gscip:gscip_cc_proto",
Expand All @@ -180,7 +179,6 @@ cc_library(
"//ortools/port:proto_utils",
"//ortools/util:fp_roundtrip_conv",
"//ortools/util:status_macros",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
Expand Down Expand Up @@ -368,6 +366,7 @@ cc_library(
"//ortools/math_opt:parameters_cc_proto",
"//ortools/math_opt/solvers:gurobi_cc_proto",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
],
)

Expand All @@ -385,6 +384,7 @@ cc_library(
"//ortools/math_opt:parameters_cc_proto",
"//ortools/math_opt/solvers:glpk_cc_proto",
"//ortools/math_opt/solvers:gurobi_cc_proto",
"//ortools/math_opt/solvers:highs_cc_proto",
"//ortools/port:proto_utils",
"//ortools/sat:sat_parameters_cc_proto",
"//ortools/util:status_macros",
Expand Down
1 change: 0 additions & 1 deletion ortools/math_opt/cpp/solution.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "ortools/math_opt/cpp/basis_status.h"
#include "ortools/math_opt/cpp/enums.h" // IWYU pragma: export
#include "ortools/math_opt/cpp/linear_constraint.h"
Expand Down
42 changes: 39 additions & 3 deletions ortools/math_opt/cpp/solve_result.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
#include <utility>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/base/protoutil.h"
#include "ortools/base/status_macros.h"
#include "ortools/math_opt/cpp/linear_constraint.h"
Expand Down Expand Up @@ -170,6 +169,37 @@ bool Termination::limit_reached() const {
reason == TerminationReason::kNoSolutionFound;
}

absl::Status Termination::ReasonIs(const TerminationReason reason) const {
if (this->reason == reason) return absl::OkStatus();
return util::InternalErrorBuilder()
<< "expected termination reason '" << reason << "' but got " << *this;
}

absl::Status Termination::ReasonIsAnyOf(
std::initializer_list<TerminationReason> reasons) const {
for (const TerminationReason reason : reasons) {
if (this->reason == reason) return absl::OkStatus();
}
return util::InternalErrorBuilder()
<< "expected termination reason in {"
<< absl::StrJoin(reasons, ", ",
[](std::string* out, TerminationReason reason) {
absl::StrAppend(out, "'");
absl::StreamFormatter()(out, reason);
absl::StrAppend(out, "'");
})
<< "} but got " << *this;
}

absl::Status Termination::IsOptimal() const {
return ReasonIs(TerminationReason::kOptimal);
}

absl::Status Termination::IsOptimalOrFeasible() const {
return ReasonIsAnyOf(
{TerminationReason::kOptimal, TerminationReason::kFeasible});
}

absl::StatusOr<Termination> Termination::FromProto(
const TerminationProto& termination_proto) {
const std::optional<TerminationReason> reason =
Expand Down Expand Up @@ -327,6 +357,9 @@ absl::StatusOr<SolveResultProto> SolveResult::Proto() const {
if (gscip_solver_specific_output.ByteSizeLong() > 0) {
*result.mutable_gscip_output() = gscip_solver_specific_output;
}
if (pdlp_solver_specific_output.ByteSizeLong() > 0) {
*result.mutable_pdlp_output() = pdlp_solver_specific_output;
}
return result;
}

Expand Down Expand Up @@ -365,6 +398,9 @@ absl::StatusOr<SolveResult> SolveResult::FromProto(
case SolveResultProto::kGscipOutput:
result.gscip_solver_specific_output = solve_result_proto.gscip_output();
return result;
case SolveResultProto::kPdlpOutput:
result.pdlp_solver_specific_output = solve_result_proto.pdlp_output();
return result;
case SolveResultProto::SOLVER_SPECIFIC_OUTPUT_NOT_SET:
return result;
}
Expand Down
71 changes: 59 additions & 12 deletions ortools/math_opt/cpp/solve_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef OR_TOOLS_MATH_OPT_CPP_SOLVE_RESULT_H_
#define OR_TOOLS_MATH_OPT_CPP_SOLVE_RESULT_H_

#include <initializer_list>
#include <optional>
#include <ostream>
#include <string>
Expand All @@ -25,8 +26,6 @@

#include "absl/status/statusor.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/gscip/gscip.pb.h"
#include "ortools/math_opt/cpp/enums.h" // IWYU pragma: export
#include "ortools/math_opt/cpp/linear_constraint.h"
Expand Down Expand Up @@ -103,23 +102,26 @@ struct SolveStats {
// contract is clarified.

// Solver claims the optimal value is equal or better (smaller for
// minimization and larger for maximization) than best_primal_bound:
// minimization and larger for maximization) than best_primal_bound up to the
// solvers primal feasibility tolerance (see warning below):
// * best_primal_bound is trivial (+inf for minimization and -inf
// maximization) when the solver does not claim to have such bound. This
// may happen for some solvers (e.g., PDLP, typically continuous solvers)
// even when returning optimal (solver could terminate with slightly
// infeasible primal solutions).
// maximization) when the solver does not claim to have such bound.
// * best_primal_bound can be closer to the optimal value than the objective
// of the best primal feasible solution. In particular, best_primal_bound
// may be non-trivial even when no primal feasible solutions are returned.
// * best_dual_bound is always better (smaller for minimization and larger
// for maximization) than best_primal_bound.
// Warning: The precise claim is that there exists a primal solution that:
// * is numerically feasible (i.e. feasible up to the solvers tolerance), and
// * has an objective value best_primal_bound.
// This numerically feasible solution could be slightly infeasible, in which
// case best_primal_bound could be strictly better than the optimal value.
// Translating a primal feasibility tolerance to a tolerance on
// best_primal_bound is non-trivial, specially when the feasibility tolerance
// is relatively large (e.g. when solving with PDLP).
double best_primal_bound = 0.0;

// Solver claims the optimal value is equal or worse (larger for
// minimization and smaller for maximization) than best_dual_bound:
// * best_dual_bound is always better (smaller for minimization and larger
// for maximization) than best_primal_bound.
// minimization and smaller for maximization) than best_dual_bound up to the
// solvers dual feasibility tolerance (see warning below):
// * best_dual_bound is trivial (-inf for minimization and +inf
// maximization) when the solver does not claim to have such bound.
// Similarly to best_primal_bound, this may happen for some solvers even
Expand All @@ -129,6 +131,28 @@ struct SolveStats {
// value than the objective of the best dual feasible solution. For MIP
// one of the first non-trivial values for best_dual_bound is often the
// optimal value of the LP relaxation of the MIP.
// * best_dual_bound should be better (smaller for minimization and larger
// for maximization) than best_primal_bound up to the solvers tolerances
// (see warning below).
// Warning:
// * For continuous problems, the precise claim is that there exists a
// dual solution that:
// * is numerically feasible (i.e. feasible up to the solvers tolerance),
// and
// * has an objective value best_dual_bound.
// This numerically feasible solution could be slightly infeasible, in
// which case best_dual_bound could be strictly worse than the optimal
// value and best_primal_bound. Similar to the primal case, translating a
// dual feasibility tolerance to a tolerance on best_dual_bound is
// non-trivial, specially when the feasibility tolerance is relatively
// large. However, some solvers provide a corrected version of
// best_dual_bound that can be numerically safer. This corrected version
// can be accessed through the solver's specific output (e.g. for PDLP,
// pdlp_output.convergence_information.corrected_dual_objective).
// * For MIP solvers, best_dual_bound may be associated to a dual solution
// for some continuous relaxation (e.g. LP relaxation), but it is often a
// complex consequence of the solvers execution and is typically more
// imprecise than the bounds reported by LP solvers.
double best_dual_bound = 0.0;

// Feasibility statuses for primal and dual problems.
Expand Down Expand Up @@ -285,6 +309,27 @@ struct Termination {
// kNoSolutionFound, and limit is not empty).
bool limit_reached() const;

// Returns an OkStatus if the reason of this `Termination` is
// `TerminationReason::kOptimal` or `TerminationReason::kFeasible`, or an
// `InternalError` otherwise.
absl::Status IsOptimalOrFeasible() const;

// Returns an OkStatus if the reason of this `Termination` is
// `TerminationReason::kOptimal`, or an `InternalError` otherwise.
//
// In most use cases, at least for MIPs, `IsOptimalOrFeasible` should be used
// instead.
absl::Status IsOptimal() const;

// Returns an OkStatus if the reason of this `Termination` is `reason`, or an
// `InternalError` otherwise.
absl::Status ReasonIs(TerminationReason reason) const;

// Returns an OkStatus if the reason of this `Termination` is in `reasons`, or
// an `InternalError` otherwise.
absl::Status ReasonIsAnyOf(
std::initializer_list<TerminationReason> reasons) const;

// Sets the reason to kFeasible
static Termination Feasible(Limit limit, std::string detail = {});

Expand Down Expand Up @@ -340,6 +385,8 @@ struct SolveResult {

// Solver specific output from Gscip. Only populated if Gscip is used.
GScipOutput gscip_solver_specific_output;
// Solver specific output from Pdlp. Only populated if Pdlp is used.
SolveResultProto::PdlpOutput pdlp_solver_specific_output;

// Returns the SolveResult equivalent of solve_result_proto.
//
Expand Down
49 changes: 41 additions & 8 deletions ortools/math_opt/result.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ syntax = "proto3";
package operations_research.math_opt;

import "google/protobuf/duration.proto";
import "ortools/pdlp/solve_log.proto";
import "ortools/gscip/gscip.proto";
import "ortools/math_opt/solution.proto";

Expand Down Expand Up @@ -79,21 +80,26 @@ message SolveStatsProto {
google.protobuf.Duration solve_time = 1;

// Solver claims the optimal value is equal or better (smaller for
// minimization and larger for maximization) than best_primal_bound:
// minimization and larger for maximization) than best_primal_bound up to the
// solvers primal feasibility tolerance (see warning below):
// * best_primal_bound is trivial (+inf for minimization and -inf
// maximization) when the solver does not claim to have such bound. This
// may happen for some solvers (e.g. PDLP, typically continuous solvers)
// even when returning optimal (solver could terminate with slightly
// infeasible primal solutions).
// maximization) when the solver does not claim to have such bound.
// * best_primal_bound can be closer to the optimal value than the objective
// of the best primal feasible solution. In particular, best_primal_bound
// may be non-trivial even when no primal feasible solutions are returned.
// Warning: The precise claim is that there exists a primal solution that:
// * is numerically feasible (i.e. feasible up to the solvers tolerance), and
// * has an objective value best_primal_bound.
// This numerically feasible solution could be slightly infeasible, in which
// case best_primal_bound could be strictly better than the optimal value.
// Translating a primal feasibility tolerance to a tolerance on
// best_primal_bound is non-trivial, specially when the feasibility tolerance
// is relatively large (e.g. when solving with PDLP).
double best_primal_bound = 2;

// Solver claims the optimal value is equal or worse (larger for
// minimization and smaller for maximization) than best_dual_bound:
// * best_dual_bound is always better (smaller for minimization and larger
// for maximization) than best_primal_bound.
// minimization and smaller for maximization) than best_dual_bound up to the
// solvers dual feasibility tolerance (see warning below):
// * best_dual_bound is trivial (-inf for minimization and +inf
// maximization) when the solver does not claim to have such bound.
// Similarly to best_primal_bound, this may happen for some solvers even
Expand All @@ -103,6 +109,28 @@ message SolveStatsProto {
// value than the objective of the best dual feasible solution. For MIP
// one of the first non-trivial values for best_dual_bound is often the
// optimal value of the LP relaxation of the MIP.
// * best_dual_bound should be better (smaller for minimization and larger
// for maximization) than best_primal_bound up to the solvers tolerances
// (see warning below).
// Warning:
// * For continuous problems, the precise claim is that there exists a
// dual solution that:
// * is numerically feasible (i.e. feasible up to the solvers tolerance),
// and
// * has an objective value best_dual_bound.
// This numerically feasible solution could be slightly infeasible, in
// which case best_dual_bound could be strictly worse than the optimal
// value and best_primal_bound. Similar to the primal case, translating a
// dual feasibility tolerance to a tolerance on best_dual_bound is
// non-trivial, specially when the feasibility tolerance is relatively
// large. However, some solvers provide a corrected version of
// best_dual_bound that can be numerically safer. This corrected version
// can be accessed through the solver's specific output (e.g. for PDLP,
// pdlp_output.convergence_information.corrected_dual_objective).
// * For MIP solvers, best_dual_bound may be associated to a dual solution
// for some continuous relaxation (e.g. LP relaxation), but it is often a
// complex consequence of the solvers execution and is typically more
// imprecise than the bounds reported by LP solvers.
double best_dual_bound = 3;

// Feasibility statuses for primal and dual problems.
Expand Down Expand Up @@ -284,8 +312,13 @@ message SolveResultProto {
// Statistics on the solve process, e.g. running time, iterations.
SolveStatsProto solve_stats = 6;

message PdlpOutput {
pdlp.ConvergenceInformation convergence_information = 1;
}

oneof solver_specific_output {
GScipOutput gscip_output = 7;
PdlpOutput pdlp_output = 9;
}

reserved 1; // Deleted fields.
Expand Down
Loading

0 comments on commit a29d1ee

Please sign in to comment.