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"
31#include "ortools/math_opt/callback.pb.h"
33#include "ortools/math_opt/model.pb.h"
34#include "ortools/math_opt/model_parameters.pb.h"
35#include "ortools/math_opt/model_update.pb.h"
36#include "ortools/math_opt/result.pb.h"
37#include "ortools/math_opt/solution.pb.h"
38#include "ortools/math_opt/sparse_containers.pb.h"
43constexpr double kInf = std::numeric_limits<double>::infinity();
47 if (solve_result.termination().has_objective_bounds()) {
48 return solve_result.termination().objective_bounds();
50 ObjectiveBoundsProto objective_bounds;
51 objective_bounds.set_primal_bound(
52 solve_result.solve_stats().best_primal_bound());
53 objective_bounds.set_dual_bound(solve_result.solve_stats().best_dual_bound());
54 return objective_bounds;
58 if (solve_result.termination().has_problem_status()) {
59 return solve_result.termination().problem_status();
61 ProblemStatusProto problem_status;
62 problem_status.set_primal_status(
63 solve_result.solve_stats().problem_status().primal_status());
64 problem_status.set_dual_status(
65 solve_result.solve_stats().problem_status().dual_status());
66 problem_status.set_primal_or_dual_infeasible(
67 solve_result.solve_stats().problem_status().primal_or_dual_infeasible());
68 return problem_status;
72 CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
76 for (
const auto [
id, value] :
MakeView(sparse_vector)) {
79 if (!(value == 0.0)) {
80 sparse_vector.set_ids(next,
id);
81 sparse_vector.set_values(next, value);
87 sparse_vector.mutable_ids()->Truncate(next);
88 sparse_vector.mutable_values()->Truncate(next);
92 const SparseVectorFilterProto& filter)
99 const auto& ids = filter_.filtered_ids();
100 CHECK(std::adjacent_find(ids.begin(), ids.end(),
101 std::greater_equal<int64_t>()) == ids.end())
102 <<
"The input filter.filtered_ids must be strictly increasing.";
107 const SparseDoubleVectorProto&
input,
108 const SparseVectorFilterProto& filter) {
109 SparseDoubleVectorProto result;
114 result.add_values(val);
122 if (model_solve_params.has_variable_values_filter() &&
124 *
solution.mutable_primal_solution()->mutable_variable_values() =
126 model_solve_params.variable_values_filter());
128 if (model_solve_params.has_dual_values_filter() &&
130 *
solution.mutable_dual_solution()->mutable_dual_values() =
132 model_solve_params.dual_values_filter());
134 if (model_solve_params.has_reduced_costs_filter() &&
136 *
solution.mutable_dual_solution()->mutable_reduced_costs() =
138 model_solve_params.reduced_costs_filter());
143 const CallbackRegistrationProto& callback_registration) {
147 absl::flat_hash_set<CallbackEventProto> events;
148 for (
int i = 0; i < callback_registration.request_registration_size(); ++i) {
149 events.emplace(callback_registration.request_registration(i));
155 const absl::string_view
detail) {
156 TerminationProto result;
158 result.set_reason(TERMINATION_REASON_FEASIBLE);
160 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
162 result.set_limit(limit);
164 result.set_detail(std::string(
detail));
170 const absl::string_view
detail) {
175 const absl::string_view
detail) {
180 const absl::string_view
detail) {
181 TerminationProto result;
182 result.set_reason(reason);
184 result.set_detail(std::string(
detail));
190 ObjectiveBoundsProto bounds;
191 bounds.set_primal_bound(is_maximize ? -
kInf : +
kInf);
192 bounds.set_dual_bound(is_maximize ? +
kInf : -
kInf);
197ObjectiveBoundsProto MakeUnboundedBounds(
const bool is_maximize) {
198 ObjectiveBoundsProto bounds;
199 bounds.set_primal_bound(is_maximize ? +
kInf : -
kInf);
200 bounds.set_dual_bound(bounds.primal_bound());
206 const TerminationReasonProto reason,
207 const absl::string_view
detail) {
208 TerminationProto result;
209 result.set_reason(reason);
210 result.mutable_problem_status()->set_primal_status(
211 FEASIBILITY_STATUS_UNDETERMINED);
212 result.mutable_problem_status()->set_dual_status(
213 FEASIBILITY_STATUS_UNDETERMINED);
216 result.set_detail(std::string(
detail));
222 const double dual_objective,
223 const absl::string_view
detail) {
224 TerminationProto result;
225 result.set_reason(TERMINATION_REASON_OPTIMAL);
226 result.mutable_objective_bounds()->set_primal_bound(finite_primal_objective);
227 result.mutable_objective_bounds()->set_dual_bound(dual_objective);
228 result.mutable_problem_status()->set_primal_status(
229 FEASIBILITY_STATUS_FEASIBLE);
230 result.mutable_problem_status()->set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
232 result.set_detail(std::string(
detail));
238 const absl::string_view
detail) {
239 TerminationProto result;
240 result.set_reason(TERMINATION_REASON_UNBOUNDED);
241 result.mutable_problem_status()->set_primal_status(
242 FEASIBILITY_STATUS_FEASIBLE);
243 result.mutable_problem_status()->set_dual_status(
244 FEASIBILITY_STATUS_INFEASIBLE);
245 *result.mutable_objective_bounds() = MakeUnboundedBounds(is_maximize);
247 result.set_detail(std::string(
detail));
253 bool is_maximize,
const FeasibilityStatusProto dual_feasibility_status,
254 const absl::string_view
detail) {
255 TerminationProto result;
256 result.set_reason(TERMINATION_REASON_INFEASIBLE);
257 result.mutable_problem_status()->set_primal_status(
258 FEASIBILITY_STATUS_INFEASIBLE);
259 result.mutable_problem_status()->set_dual_status(dual_feasibility_status);
261 if (dual_feasibility_status == FEASIBILITY_STATUS_FEASIBLE) {
262 result.mutable_objective_bounds()->set_dual_bound(
263 result.objective_bounds().primal_bound());
266 result.set_detail(std::string(
detail));
272 const bool is_maximize,
const LimitProto limit,
273 const std::optional<double> optional_finite_primal_objective,
274 const std::optional<double> optional_dual_objective,
275 const absl::string_view
detail) {
276 if (optional_finite_primal_objective.has_value()) {
278 *optional_finite_primal_objective,
279 optional_dual_objective,
detail);
282 optional_dual_objective,
detail);
286 LimitProto limit,
const double primal_objective,
287 const double dual_objective,
const bool claim_dual_feasible_solution_exists,
288 const absl::string_view
detail) {
289 TerminationProto result;
290 if (std::isfinite(primal_objective)) {
291 result.set_reason(TERMINATION_REASON_FEASIBLE);
292 result.mutable_problem_status()->set_primal_status(
293 FEASIBILITY_STATUS_FEASIBLE);
295 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
296 result.mutable_problem_status()->set_primal_status(
297 FEASIBILITY_STATUS_UNDETERMINED);
299 if (claim_dual_feasible_solution_exists) {
300 result.mutable_problem_status()->set_dual_status(
301 FEASIBILITY_STATUS_FEASIBLE);
303 result.mutable_problem_status()->set_dual_status(
304 FEASIBILITY_STATUS_UNDETERMINED);
306 result.mutable_objective_bounds()->set_primal_bound(primal_objective);
307 result.mutable_objective_bounds()->set_dual_bound(dual_objective);
308 result.set_limit(limit);
310 result.set_detail(std::string(
detail));
316 const absl::string_view
detail) {
318 is_maximize, LIMIT_CUTOFF, std::nullopt,
323 const bool is_maximize,
const LimitProto limit,
324 const std::optional<double> optional_dual_objective,
325 const absl::string_view
detail) {
326 TerminationProto result;
327 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
328 result.mutable_problem_status()->set_primal_status(
329 FEASIBILITY_STATUS_UNDETERMINED);
330 result.mutable_problem_status()->set_dual_status(
331 FEASIBILITY_STATUS_UNDETERMINED);
333 if (optional_dual_objective.has_value()) {
334 result.mutable_objective_bounds()->set_dual_bound(*optional_dual_objective);
335 result.mutable_problem_status()->set_dual_status(
336 FEASIBILITY_STATUS_FEASIBLE);
338 result.set_limit(limit);
340 result.set_detail(std::string(
detail));
346 const bool is_maximize,
const LimitProto limit,
347 const double primal_objective,
348 const std::optional<double> optional_dual_objective,
349 const absl::string_view
detail) {
350 TerminationProto result;
351 result.set_reason(TERMINATION_REASON_FEASIBLE);
352 result.mutable_problem_status()->set_primal_status(
353 FEASIBILITY_STATUS_FEASIBLE);
354 result.mutable_problem_status()->set_dual_status(
355 FEASIBILITY_STATUS_UNDETERMINED);
357 result.mutable_objective_bounds()->set_primal_bound(primal_objective);
358 if (optional_dual_objective.has_value()) {
359 result.mutable_objective_bounds()->set_dual_bound(*optional_dual_objective);
360 result.mutable_problem_status()->set_dual_status(
361 FEASIBILITY_STATUS_FEASIBLE);
363 result.set_limit(limit);
365 result.set_detail(std::string(
detail));
371 bool is_maximize,
const FeasibilityStatusProto dual_feasibility_status,
372 const absl::string_view
detail) {
373 TerminationProto result;
374 result.set_reason(TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED);
375 result.mutable_problem_status()->set_primal_status(
376 FEASIBILITY_STATUS_UNDETERMINED);
377 result.mutable_problem_status()->set_dual_status(dual_feasibility_status);
378 if (dual_feasibility_status == FEASIBILITY_STATUS_UNDETERMINED) {
379 result.mutable_problem_status()->set_primal_or_dual_infeasible(
true);
383 result.set_detail(std::string(
detail));
390 const absl::string_view solver_name) {
391 const auto error_status = [solver_name](
392 const absl::string_view structure,
397 << solver_name <<
" does not support " << structure;
400 <<
"MathOpt does not currently support " << solver_name
401 <<
" models with " << structure;
403 LOG(FATAL) <<
"Unexpected call with `kSupported`";
408 for (
const bool is_integer : model.variables().integers()) {
410 return error_status(
"integer variables", support);
416 if (!model.auxiliary_objectives().empty()) {
417 return error_status(
"multiple objectives", support);
422 if (!model.objective().quadratic_coefficients().row_ids().empty()) {
423 return error_status(
"quadratic objectives", support);
425 for (
const auto& [_, objective] : model.auxiliary_objectives()) {
426 if (!objective.quadratic_coefficients().row_ids().empty()) {
427 return error_status(
"quadratic objectives", support);
433 if (!model.quadratic_constraints().empty()) {
434 return error_status(
"quadratic constraints", support);
439 if (!model.second_order_cone_constraints().empty()) {
440 return error_status(
"second-order cone constraints", support);
445 if (!model.sos1_constraints().empty()) {
446 return error_status(
"sos1 constraints", support);
451 if (!model.sos2_constraints().empty()) {
452 return error_status(
"sos2 constraints", support);
457 if (!model.indicator_constraints().empty()) {
458 return error_status(
"indicator constraints", support);
461 return absl::OkStatus();
467 for (
const bool is_integer :
468 update.variable_updates().integers().values()) {
473 for (
const bool is_integer : update.new_variables().integers()) {
480 if (!update.auxiliary_objectives_updates()
481 .deleted_objective_ids()
483 !update.auxiliary_objectives_updates().new_objectives().empty() ||
484 !update.auxiliary_objectives_updates().objective_updates().empty()) {
489 if (!update.objective_updates()
490 .quadratic_coefficients()
495 for (
const auto& [_, new_objective] :
496 update.auxiliary_objectives_updates().new_objectives()) {
497 if (!new_objective.quadratic_coefficients().row_ids().empty()) {
501 for (
const auto& [_, objective_update] :
502 update.auxiliary_objectives_updates().objective_updates()) {
503 if (!objective_update.quadratic_coefficients().row_ids().empty()) {
510 const auto contains_new_or_deleted_constraints =
511 [](
const auto& constraint_update) {
512 return !constraint_update.new_constraints().empty() ||
513 !constraint_update.deleted_constraint_ids().empty();
516 if (contains_new_or_deleted_constraints(
517 update.quadratic_constraint_updates())) {
522 if (contains_new_or_deleted_constraints(
523 update.second_order_cone_constraint_updates())) {
528 if (contains_new_or_deleted_constraints(update.sos1_constraint_updates())) {
533 if (contains_new_or_deleted_constraints(update.sos2_constraint_updates())) {
538 if (contains_new_or_deleted_constraints(
539 update.indicator_constraint_updates())) {
547 const ModelSolveParametersProto& model_parameters,
549 const absl::string_view solver_name) {
550 const auto validate_support = [solver_name](
551 const absl::string_view structure,
555 return absl::OkStatus();
558 << structure <<
" is not supported as " << solver_name
559 <<
" does not support multiple objectives";
563 <<
" is not supported as MathOpt does not currently support "
564 << solver_name <<
" models with multiple objectives";
566 return absl::OkStatus();
568 if (model_parameters.has_primary_objective_parameters()) {
570 "ModelSolveParametersProto.primary_objective_parameters",
573 if (!model_parameters.auxiliary_objective_parameters().empty()) {
575 "ModelSolveParametersProto.auxiliary_objective_parameters",
578 return absl::OkStatus();
582 SolveResultProto& solve_result_proto) {
583 *solve_result_proto.mutable_termination()->mutable_problem_status() =
585 *solve_result_proto.mutable_solve_stats()->mutable_problem_status() =
587 *solve_result_proto.mutable_termination()->mutable_objective_bounds() =
589 solve_result_proto.mutable_solve_stats()->set_best_primal_bound(
590 solve_result_proto.termination().objective_bounds().primal_bound());
591 solve_result_proto.mutable_solve_stats()->set_best_dual_bound(
592 solve_result_proto.termination().objective_bounds().dual_bound());
#define RETURN_IF_ERROR(expr)
SparseVectorFilterPredicate(const SparseVectorFilterProto &filter)
bool AcceptsAndUpdate(int64_t id, const Value &value)
An object oriented wrapper for quadratic constraints in ModelStorage.
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::Status ModelSolveParametersAreSupported(const ModelSolveParametersProto &model_parameters, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
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.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
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