24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
26#include "absl/status/status.h"
27#include "absl/strings/string_view.h"
30#include "ortools/math_opt/callback.pb.h"
32#include "ortools/math_opt/model.pb.h"
33#include "ortools/math_opt/model_parameters.pb.h"
34#include "ortools/math_opt/model_update.pb.h"
35#include "ortools/math_opt/result.pb.h"
36#include "ortools/math_opt/solution.pb.h"
37#include "ortools/math_opt/sparse_containers.pb.h"
42constexpr double kInf = std::numeric_limits<double>::infinity();
46 if (solve_result.termination().has_objective_bounds()) {
47 return solve_result.termination().objective_bounds();
49 ObjectiveBoundsProto objective_bounds;
50 objective_bounds.set_primal_bound(
51 solve_result.solve_stats().best_primal_bound());
52 objective_bounds.set_dual_bound(solve_result.solve_stats().best_dual_bound());
53 return objective_bounds;
57 if (solve_result.termination().has_problem_status()) {
58 return solve_result.termination().problem_status();
60 ProblemStatusProto problem_status;
61 problem_status.set_primal_status(
62 solve_result.solve_stats().problem_status().primal_status());
63 problem_status.set_dual_status(
64 solve_result.solve_stats().problem_status().dual_status());
65 problem_status.set_primal_or_dual_infeasible(
66 solve_result.solve_stats().problem_status().primal_or_dual_infeasible());
67 return problem_status;
71 CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
78 if (!(
value == 0.0)) {
79 sparse_vector.set_ids(
next,
id);
86 sparse_vector.mutable_ids()->Truncate(
next);
87 sparse_vector.mutable_values()->Truncate(
next);
91 const SparseVectorFilterProto& filter)
98 const auto& ids = filter_.filtered_ids();
99 CHECK(std::adjacent_find(ids.begin(), ids.end(),
100 std::greater_equal<int64_t>()) == ids.end())
101 <<
"The input filter.filtered_ids must be strictly increasing.";
106 const SparseDoubleVectorProto&
input,
107 const SparseVectorFilterProto& filter) {
108 SparseDoubleVectorProto result;
113 result.add_values(val);
121 if (model_solve_params.has_variable_values_filter() &&
123 *
solution.mutable_primal_solution()->mutable_variable_values() =
125 model_solve_params.variable_values_filter());
127 if (model_solve_params.has_dual_values_filter() &&
129 *
solution.mutable_dual_solution()->mutable_dual_values() =
131 model_solve_params.dual_values_filter());
133 if (model_solve_params.has_reduced_costs_filter() &&
135 *
solution.mutable_dual_solution()->mutable_reduced_costs() =
137 model_solve_params.reduced_costs_filter());
142 const CallbackRegistrationProto& callback_registration) {
146 absl::flat_hash_set<CallbackEventProto> events;
147 for (
int i = 0; i < callback_registration.request_registration_size(); ++i) {
148 events.emplace(callback_registration.request_registration(i));
154 const absl::string_view
detail) {
155 TerminationProto result;
157 result.set_reason(TERMINATION_REASON_FEASIBLE);
159 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
161 result.set_limit(limit);
163 result.set_detail(std::string(
detail));
169 const absl::string_view
detail) {
174 const absl::string_view
detail) {
179 const absl::string_view
detail) {
180 TerminationProto result;
181 result.set_reason(reason);
183 result.set_detail(std::string(
detail));
189 ObjectiveBoundsProto bounds;
190 bounds.set_primal_bound(is_maximize ? -
kInf : +
kInf);
191 bounds.set_dual_bound(is_maximize ? +
kInf : -
kInf);
196ObjectiveBoundsProto MakeUnboundedBounds(
const bool is_maximize) {
197 ObjectiveBoundsProto bounds;
198 bounds.set_primal_bound(is_maximize ? +
kInf : -
kInf);
199 bounds.set_dual_bound(bounds.primal_bound());
205 const TerminationReasonProto reason,
206 const absl::string_view
detail) {
207 TerminationProto result;
208 result.set_reason(reason);
209 result.mutable_problem_status()->set_primal_status(
210 FEASIBILITY_STATUS_UNDETERMINED);
211 result.mutable_problem_status()->set_dual_status(
212 FEASIBILITY_STATUS_UNDETERMINED);
215 result.set_detail(std::string(
detail));
221 const double dual_objective,
222 const absl::string_view
detail) {
223 TerminationProto result;
224 result.set_reason(TERMINATION_REASON_OPTIMAL);
225 result.mutable_objective_bounds()->set_primal_bound(finite_primal_objective);
226 result.mutable_objective_bounds()->set_dual_bound(dual_objective);
227 result.mutable_problem_status()->set_primal_status(
228 FEASIBILITY_STATUS_FEASIBLE);
229 result.mutable_problem_status()->set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
231 result.set_detail(std::string(
detail));
237 const absl::string_view
detail) {
238 TerminationProto result;
239 result.set_reason(TERMINATION_REASON_UNBOUNDED);
240 result.mutable_problem_status()->set_primal_status(
241 FEASIBILITY_STATUS_FEASIBLE);
242 result.mutable_problem_status()->set_dual_status(
243 FEASIBILITY_STATUS_INFEASIBLE);
244 *result.mutable_objective_bounds() = MakeUnboundedBounds(is_maximize);
246 result.set_detail(std::string(
detail));
252 bool is_maximize,
const FeasibilityStatusProto dual_feasibility_status,
253 const absl::string_view
detail) {
254 TerminationProto result;
255 result.set_reason(TERMINATION_REASON_INFEASIBLE);
256 result.mutable_problem_status()->set_primal_status(
257 FEASIBILITY_STATUS_INFEASIBLE);
258 result.mutable_problem_status()->set_dual_status(dual_feasibility_status);
260 if (dual_feasibility_status == FEASIBILITY_STATUS_FEASIBLE) {
261 result.mutable_objective_bounds()->set_dual_bound(
262 result.objective_bounds().primal_bound());
265 result.set_detail(std::string(
detail));
271 const bool is_maximize,
const LimitProto limit,
272 const std::optional<double> optional_finite_primal_objective,
273 const std::optional<double> optional_dual_objective,
274 const absl::string_view
detail) {
275 if (optional_finite_primal_objective.has_value()) {
277 *optional_finite_primal_objective,
278 optional_dual_objective,
detail);
281 optional_dual_objective,
detail);
285 LimitProto limit,
const double primal_objective,
286 const double dual_objective,
const bool claim_dual_feasible_solution_exists,
287 const absl::string_view
detail) {
288 TerminationProto result;
289 if (std::isfinite(primal_objective)) {
290 result.set_reason(TERMINATION_REASON_FEASIBLE);
291 result.mutable_problem_status()->set_primal_status(
292 FEASIBILITY_STATUS_FEASIBLE);
294 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
295 result.mutable_problem_status()->set_primal_status(
296 FEASIBILITY_STATUS_UNDETERMINED);
298 if (claim_dual_feasible_solution_exists) {
299 result.mutable_problem_status()->set_dual_status(
300 FEASIBILITY_STATUS_FEASIBLE);
302 result.mutable_problem_status()->set_dual_status(
303 FEASIBILITY_STATUS_UNDETERMINED);
305 result.mutable_objective_bounds()->set_primal_bound(primal_objective);
306 result.mutable_objective_bounds()->set_dual_bound(dual_objective);
307 result.set_limit(limit);
309 result.set_detail(std::string(
detail));
315 const absl::string_view
detail) {
317 is_maximize, LIMIT_CUTOFF, std::nullopt,
322 const bool is_maximize,
const LimitProto limit,
323 const std::optional<double> optional_dual_objective,
324 const absl::string_view
detail) {
325 TerminationProto result;
326 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
327 result.mutable_problem_status()->set_primal_status(
328 FEASIBILITY_STATUS_UNDETERMINED);
329 result.mutable_problem_status()->set_dual_status(
330 FEASIBILITY_STATUS_UNDETERMINED);
332 if (optional_dual_objective.has_value()) {
333 result.mutable_objective_bounds()->set_dual_bound(*optional_dual_objective);
334 result.mutable_problem_status()->set_dual_status(
335 FEASIBILITY_STATUS_FEASIBLE);
337 result.set_limit(limit);
339 result.set_detail(std::string(
detail));
345 const bool is_maximize,
const LimitProto limit,
346 const double primal_objective,
347 const std::optional<double> optional_dual_objective,
348 const absl::string_view
detail) {
349 TerminationProto result;
350 result.set_reason(TERMINATION_REASON_FEASIBLE);
351 result.mutable_problem_status()->set_primal_status(
352 FEASIBILITY_STATUS_FEASIBLE);
353 result.mutable_problem_status()->set_dual_status(
354 FEASIBILITY_STATUS_UNDETERMINED);
356 result.mutable_objective_bounds()->set_primal_bound(primal_objective);
357 if (optional_dual_objective.has_value()) {
358 result.mutable_objective_bounds()->set_dual_bound(*optional_dual_objective);
359 result.mutable_problem_status()->set_dual_status(
360 FEASIBILITY_STATUS_FEASIBLE);
362 result.set_limit(limit);
364 result.set_detail(std::string(
detail));
370 bool is_maximize,
const FeasibilityStatusProto dual_feasibility_status,
371 const absl::string_view
detail) {
372 TerminationProto result;
373 result.set_reason(TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED);
374 result.mutable_problem_status()->set_primal_status(
375 FEASIBILITY_STATUS_UNDETERMINED);
376 result.mutable_problem_status()->set_dual_status(dual_feasibility_status);
377 if (dual_feasibility_status == FEASIBILITY_STATUS_UNDETERMINED) {
378 result.mutable_problem_status()->set_primal_or_dual_infeasible(
true);
382 result.set_detail(std::string(
detail));
389 const absl::string_view solver_name) {
390 const auto error_status = [solver_name](
391 const absl::string_view structure,
396 << solver_name <<
" does not support " << structure;
399 <<
"MathOpt does not currently support " << solver_name
400 <<
" models with " << structure;
402 LOG(FATAL) <<
"Unexpected call with `kSupported`";
407 for (
const bool is_integer :
model.variables().integers()) {
409 return error_status(
"integer variables", support);
415 if (!
model.auxiliary_objectives().empty()) {
416 return error_status(
"multiple objectives", support);
421 if (!
model.objective().quadratic_coefficients().row_ids().empty()) {
422 return error_status(
"quadratic objectives", support);
424 for (
const auto& [_, objective] :
model.auxiliary_objectives()) {
425 if (!objective.quadratic_coefficients().row_ids().empty()) {
426 return error_status(
"quadratic objectives", support);
432 if (!
model.quadratic_constraints().empty()) {
433 return error_status(
"quadratic constraints", support);
438 if (!
model.second_order_cone_constraints().empty()) {
439 return error_status(
"second-order cone constraints", support);
444 if (!
model.sos1_constraints().empty()) {
445 return error_status(
"sos1 constraints", support);
450 if (!
model.sos2_constraints().empty()) {
451 return error_status(
"sos2 constraints", support);
456 if (!
model.indicator_constraints().empty()) {
457 return error_status(
"indicator constraints", support);
460 return absl::OkStatus();
466 for (
const bool is_integer :
467 update.variable_updates().integers().values()) {
472 for (
const bool is_integer : update.new_variables().integers()) {
479 if (!update.auxiliary_objectives_updates()
480 .deleted_objective_ids()
482 !update.auxiliary_objectives_updates().new_objectives().empty() ||
483 !update.auxiliary_objectives_updates().objective_updates().empty()) {
488 if (!update.objective_updates()
489 .quadratic_coefficients()
494 for (
const auto& [_, new_objective] :
495 update.auxiliary_objectives_updates().new_objectives()) {
496 if (!new_objective.quadratic_coefficients().row_ids().empty()) {
500 for (
const auto& [_, objective_update] :
501 update.auxiliary_objectives_updates().objective_updates()) {
502 if (!objective_update.quadratic_coefficients().row_ids().empty()) {
509 const auto contains_new_or_deleted_constraints =
510 [](
const auto& constraint_update) {
511 return !constraint_update.new_constraints().empty() ||
512 !constraint_update.deleted_constraint_ids().empty();
515 if (contains_new_or_deleted_constraints(
516 update.quadratic_constraint_updates())) {
521 if (contains_new_or_deleted_constraints(
522 update.second_order_cone_constraint_updates())) {
527 if (contains_new_or_deleted_constraints(update.sos1_constraint_updates())) {
532 if (contains_new_or_deleted_constraints(update.sos2_constraint_updates())) {
537 if (contains_new_or_deleted_constraints(
538 update.indicator_constraint_updates())) {
546 SolveResultProto& solve_result_proto) {
547 *solve_result_proto.mutable_termination()->mutable_problem_status() =
549 *solve_result_proto.mutable_solve_stats()->mutable_problem_status() =
551 *solve_result_proto.mutable_termination()->mutable_objective_bounds() =
553 solve_result_proto.mutable_solve_stats()->set_best_primal_bound(
554 solve_result_proto.termination().objective_bounds().primal_bound());
555 solve_result_proto.mutable_solve_stats()->set_best_dual_bound(
556 solve_result_proto.termination().objective_bounds().dual_bound());
SparseVectorFilterPredicate(const SparseVectorFilterProto &filter)
bool AcceptsAndUpdate(int64_t id, const Value &value)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
TerminationProto FeasibleTermination(const LimitProto limit, const absl::string_view detail)
void UpgradeSolveResultProtoForStatsMigration(SolveResultProto &solve_result_proto)
TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible, const absl::string_view detail)
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Returns the callback_registration.request_registration as a set of enums.
TerminationProto OptimalTerminationProto(const double finite_primal_objective, const double dual_objective, const absl::string_view detail)
TerminationProto LimitTerminationProto(const bool is_maximize, const LimitProto limit, const std::optional< double > optional_finite_primal_objective, const std::optional< double > optional_dual_objective, const absl::string_view detail)
TerminationProto InfeasibleOrUnboundedTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
bool UpdateIsSupported(const ModelUpdateProto &update, const SupportedProblemStructures &support_menu)
ProblemStatusProto GetProblemStatus(const SolveResultProto &solve_result)
TerminationProto FeasibleTerminationProto(const bool is_maximize, const LimitProto limit, const double primal_objective, const std::optional< double > optional_dual_objective, const absl::string_view detail)
ObjectiveBoundsProto GetObjectiveBounds(const SolveResultProto &solve_result)
TerminationProto NoSolutionFoundTerminationProto(const bool is_maximize, const LimitProto limit, const std::optional< double > optional_dual_objective, const absl::string_view detail)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
TerminationProto CutoffTerminationProto(bool is_maximize, const absl::string_view detail)
Calls NoSolutionFoundTerminationProto() with LIMIT_CUTOFF LIMIT.
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
TerminationProto InfeasibleTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
SparseDoubleVectorProto FilterSparseVector(const SparseDoubleVectorProto &input, const SparseVectorFilterProto &filter)
TerminationProto UnboundedTerminationProto(const bool is_maximize, const absl::string_view detail)
void RemoveSparseDoubleVectorZeros(SparseDoubleVectorProto &sparse_vector)
void ApplyAllFilters(const ModelSolveParametersProto &model_solve_params, SolutionProto &solution)
ObjectiveBoundsProto MakeTrivialBounds(const bool is_maximize)
In SWIG mode, we don't want anything besides these top-level includes.
StatusBuilder UnimplementedErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
static int input(yyscan_t yyscanner)
SupportType second_order_cone_constraints
SupportType sos2_constraints
SupportType quadratic_objectives
SupportType multi_objectives
SupportType integer_variables
SupportType sos1_constraints
SupportType quadratic_constraints
SupportType indicator_constraints