Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
math_opt_proto_utils.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <functional>
20#include <limits>
21#include <optional>
22#include <string>
23
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"
39
40namespace operations_research {
41namespace math_opt {
42namespace {
43constexpr double kInf = std::numeric_limits<double>::infinity();
44} // namespace
45
46ObjectiveBoundsProto GetObjectiveBounds(const SolveResultProto& solve_result) {
47 if (solve_result.termination().has_objective_bounds()) {
48 return solve_result.termination().objective_bounds();
49 }
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;
55}
56
57ProblemStatusProto GetProblemStatus(const SolveResultProto& solve_result) {
58 if (solve_result.termination().has_problem_status()) {
59 return solve_result.termination().problem_status();
60 }
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;
69}
70
71void RemoveSparseDoubleVectorZeros(SparseDoubleVectorProto& sparse_vector) {
72 CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
73 // Keep track of the next index that has not yet been used for a non zero
74 // value.
75 int next = 0;
76 for (const auto [id, value] : MakeView(sparse_vector)) {
77 // Se use `!(== 0.0)` here so that we keep NaN values for which both `v ==
78 // 0` and `v != 0` returns false.
79 if (!(value == 0.0)) {
80 sparse_vector.set_ids(next, id);
81 sparse_vector.set_values(next, value);
82 ++next;
83 }
84 }
85 // At the end of the iteration, `next` contains the index of the first unused
86 // index. This means it contains the number of used elements.
87 sparse_vector.mutable_ids()->Truncate(next);
88 sparse_vector.mutable_values()->Truncate(next);
89}
90
92 const SparseVectorFilterProto& filter)
93 : filter_(filter) {
94 // We only do this test in non-optimized builds.
95 if (DEBUG_MODE && filter_.filter_by_ids()) {
96 // Checks that input filtered_ids are strictly increasing.
97 // That is: for all i, ids(i) < ids(i+1).
98 // Hence here we fail if there exists i such that ids(i) >= ids(i+1).
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.";
103 }
104}
105
106SparseDoubleVectorProto FilterSparseVector(
107 const SparseDoubleVectorProto& input,
108 const SparseVectorFilterProto& filter) {
109 SparseDoubleVectorProto result;
110 SparseVectorFilterPredicate predicate(filter);
111 for (const auto [id, val] : MakeView(input)) {
112 if (predicate.AcceptsAndUpdate(id, val)) {
113 result.add_ids(id);
114 result.add_values(val);
115 }
116 }
117 return result;
118}
119
120void ApplyAllFilters(const ModelSolveParametersProto& model_solve_params,
121 SolutionProto& solution) {
122 if (model_solve_params.has_variable_values_filter() &&
123 solution.has_primal_solution()) {
124 *solution.mutable_primal_solution()->mutable_variable_values() =
125 FilterSparseVector(solution.primal_solution().variable_values(),
126 model_solve_params.variable_values_filter());
127 }
128 if (model_solve_params.has_dual_values_filter() &&
129 solution.has_dual_solution()) {
130 *solution.mutable_dual_solution()->mutable_dual_values() =
131 FilterSparseVector(solution.dual_solution().dual_values(),
132 model_solve_params.dual_values_filter());
133 }
134 if (model_solve_params.has_reduced_costs_filter() &&
135 solution.has_dual_solution()) {
136 *solution.mutable_dual_solution()->mutable_reduced_costs() =
137 FilterSparseVector(solution.dual_solution().reduced_costs(),
138 model_solve_params.reduced_costs_filter());
139 }
140}
141
142absl::flat_hash_set<CallbackEventProto> EventSet(
143 const CallbackRegistrationProto& callback_registration) {
144 // Here we don't use for-range loop since for repeated enum fields, the type
145 // used in C++ is RepeatedField<int>. Using the generated getter instead
146 // guarantees type safety.
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));
150 }
151 return events;
152}
153
154TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible,
155 const absl::string_view detail) {
156 TerminationProto result;
157 if (feasible) {
158 result.set_reason(TERMINATION_REASON_FEASIBLE);
159 } else {
160 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
161 }
162 result.set_limit(limit);
163 if (!detail.empty()) {
164 result.set_detail(std::string(detail));
165 }
166 return result;
167}
168
169TerminationProto FeasibleTermination(const LimitProto limit,
170 const absl::string_view detail) {
171 return TerminateForLimit(limit, /*feasible=*/true, detail);
172}
173
174TerminationProto NoSolutionFoundTermination(const LimitProto limit,
175 const absl::string_view detail) {
176 return TerminateForLimit(limit, /*feasible=*/false, detail);
177}
178
179TerminationProto TerminateForReason(const TerminationReasonProto reason,
180 const absl::string_view detail) {
181 TerminationProto result;
182 result.set_reason(reason);
183 if (!detail.empty()) {
184 result.set_detail(std::string(detail));
185 }
186 return result;
187}
188
189ObjectiveBoundsProto MakeTrivialBounds(const bool is_maximize) {
190 ObjectiveBoundsProto bounds;
191 bounds.set_primal_bound(is_maximize ? -kInf : +kInf);
192 bounds.set_dual_bound(is_maximize ? +kInf : -kInf);
193 return bounds;
194}
195
196namespace {
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());
201 return bounds;
202}
203} // namespace
204
205TerminationProto TerminateForReason(const bool is_maximize,
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);
214 *result.mutable_objective_bounds() = MakeTrivialBounds(is_maximize);
215 if (!detail.empty()) {
216 result.set_detail(std::string(detail));
217 }
218 return result;
219}
220
221TerminationProto OptimalTerminationProto(const double finite_primal_objective,
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);
231 if (!detail.empty()) {
232 result.set_detail(std::string(detail));
233 }
234 return result;
235}
236
237TerminationProto UnboundedTerminationProto(const bool is_maximize,
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);
246 if (!detail.empty()) {
247 result.set_detail(std::string(detail));
248 }
249 return result;
250}
251
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);
260 *result.mutable_objective_bounds() = MakeTrivialBounds(is_maximize);
261 if (dual_feasibility_status == FEASIBILITY_STATUS_FEASIBLE) {
262 result.mutable_objective_bounds()->set_dual_bound(
263 result.objective_bounds().primal_bound());
264 }
265 if (!detail.empty()) {
266 result.set_detail(std::string(detail));
267 }
268 return result;
269}
270
271TerminationProto LimitTerminationProto(
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()) {
277 return FeasibleTerminationProto(is_maximize, limit,
278 *optional_finite_primal_objective,
279 optional_dual_objective, detail);
280 }
281 return NoSolutionFoundTerminationProto(is_maximize, limit,
282 optional_dual_objective, detail);
283}
284
285TerminationProto LimitTerminationProto(
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);
294 } else {
295 result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
296 result.mutable_problem_status()->set_primal_status(
297 FEASIBILITY_STATUS_UNDETERMINED);
298 }
299 if (claim_dual_feasible_solution_exists) {
300 result.mutable_problem_status()->set_dual_status(
301 FEASIBILITY_STATUS_FEASIBLE);
302 } else {
303 result.mutable_problem_status()->set_dual_status(
304 FEASIBILITY_STATUS_UNDETERMINED);
305 }
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);
309 if (!detail.empty()) {
310 result.set_detail(std::string(detail));
311 }
312 return result;
313}
314
315TerminationProto CutoffTerminationProto(bool is_maximize,
316 const absl::string_view detail) {
318 is_maximize, LIMIT_CUTOFF, /*optional_dual_objective=*/std::nullopt,
319 detail);
320}
321
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);
332 *result.mutable_objective_bounds() = MakeTrivialBounds(is_maximize);
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);
337 }
338 result.set_limit(limit);
339 if (!detail.empty()) {
340 result.set_detail(std::string(detail));
341 }
342 return result;
343}
344
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);
356 *result.mutable_objective_bounds() = MakeTrivialBounds(is_maximize);
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);
362 }
363 result.set_limit(limit);
364 if (!detail.empty()) {
365 result.set_detail(std::string(detail));
366 }
367 return result;
368}
369
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);
380 }
381 *result.mutable_objective_bounds() = MakeTrivialBounds(is_maximize);
382 if (!detail.empty()) {
383 result.set_detail(std::string(detail));
384 }
385 return result;
386}
387
388absl::Status ModelIsSupported(const ModelProto& model,
389 const SupportedProblemStructures& support_menu,
390 const absl::string_view solver_name) {
391 const auto error_status = [solver_name](
392 const absl::string_view structure,
393 const SupportType support) -> absl::Status {
394 switch (support) {
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`";
404 }
405 };
406 if (const SupportType support = support_menu.integer_variables;
407 support != SupportType::kSupported) {
408 for (const bool is_integer : model.variables().integers()) {
409 if (is_integer) {
410 return error_status("integer variables", support);
411 }
412 }
413 }
414 if (const SupportType support = support_menu.multi_objectives;
415 support != SupportType::kSupported) {
416 if (!model.auxiliary_objectives().empty()) {
417 return error_status("multiple objectives", support);
418 }
419 }
420 if (const SupportType support = support_menu.quadratic_objectives;
421 support != SupportType::kSupported) {
422 if (!model.objective().quadratic_coefficients().row_ids().empty()) {
423 return error_status("quadratic objectives", support);
424 }
425 for (const auto& [_, objective] : model.auxiliary_objectives()) {
426 if (!objective.quadratic_coefficients().row_ids().empty()) {
427 return error_status("quadratic objectives", support);
428 }
429 }
430 }
431 if (const SupportType support = support_menu.quadratic_constraints;
432 support != SupportType::kSupported) {
433 if (!model.quadratic_constraints().empty()) {
434 return error_status("quadratic constraints", support);
435 }
436 }
437 if (const SupportType support = support_menu.second_order_cone_constraints;
438 support != SupportType::kSupported) {
439 if (!model.second_order_cone_constraints().empty()) {
440 return error_status("second-order cone constraints", support);
441 }
442 }
443 if (const SupportType support = support_menu.sos1_constraints;
444 support != SupportType::kSupported) {
445 if (!model.sos1_constraints().empty()) {
446 return error_status("sos1 constraints", support);
447 }
448 }
449 if (const SupportType support = support_menu.sos2_constraints;
450 support != SupportType::kSupported) {
451 if (!model.sos2_constraints().empty()) {
452 return error_status("sos2 constraints", support);
453 }
454 }
455 if (const SupportType support = support_menu.indicator_constraints;
456 support != SupportType::kSupported) {
457 if (!model.indicator_constraints().empty()) {
458 return error_status("indicator constraints", support);
459 }
460 }
461 return absl::OkStatus();
462}
463
464bool UpdateIsSupported(const ModelUpdateProto& update,
465 const SupportedProblemStructures& support_menu) {
466 if (support_menu.integer_variables != SupportType::kSupported) {
467 for (const bool is_integer :
468 update.variable_updates().integers().values()) {
469 if (is_integer) {
470 return false;
471 }
472 }
473 for (const bool is_integer : update.new_variables().integers()) {
474 if (is_integer) {
475 return false;
476 }
477 }
478 }
479 if (support_menu.multi_objectives != SupportType::kSupported) {
480 if (!update.auxiliary_objectives_updates()
481 .deleted_objective_ids()
482 .empty() ||
483 !update.auxiliary_objectives_updates().new_objectives().empty() ||
484 !update.auxiliary_objectives_updates().objective_updates().empty()) {
485 return false;
486 }
487 }
488 if (support_menu.quadratic_objectives != SupportType::kSupported) {
489 if (!update.objective_updates()
490 .quadratic_coefficients()
491 .row_ids()
492 .empty()) {
493 return false;
494 }
495 for (const auto& [_, new_objective] :
496 update.auxiliary_objectives_updates().new_objectives()) {
497 if (!new_objective.quadratic_coefficients().row_ids().empty()) {
498 return false;
499 }
500 }
501 for (const auto& [_, objective_update] :
502 update.auxiliary_objectives_updates().objective_updates()) {
503 if (!objective_update.quadratic_coefficients().row_ids().empty()) {
504 return false;
505 }
506 }
507 }
508 // Duck-types that the proto parameter contains fields named `new_constraints`
509 // and `deleted_constraint_ids`. This is standard for "mapped" constraints.
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();
514 };
516 if (contains_new_or_deleted_constraints(
517 update.quadratic_constraint_updates())) {
518 return false;
519 }
520 }
522 if (contains_new_or_deleted_constraints(
523 update.second_order_cone_constraint_updates())) {
524 return false;
525 }
526 }
527 if (support_menu.sos1_constraints != SupportType::kSupported) {
528 if (contains_new_or_deleted_constraints(update.sos1_constraint_updates())) {
529 return false;
530 }
531 }
532 if (support_menu.sos2_constraints != SupportType::kSupported) {
533 if (contains_new_or_deleted_constraints(update.sos2_constraint_updates())) {
534 return false;
535 }
536 }
538 if (contains_new_or_deleted_constraints(
539 update.indicator_constraint_updates())) {
540 return false;
541 }
542 }
543 return true;
544}
545
547 const ModelSolveParametersProto& model_parameters,
548 const SupportedProblemStructures& support_menu,
549 const absl::string_view solver_name) {
550 const auto validate_support = [solver_name](
551 const absl::string_view structure,
552 const SupportType support) -> absl::Status {
553 switch (support) {
555 return absl::OkStatus();
558 << structure << " is not supported as " << solver_name
559 << " does not support multiple objectives";
562 << structure
563 << " is not supported as MathOpt does not currently support "
564 << solver_name << " models with multiple objectives";
565 }
566 return absl::OkStatus();
567 };
568 if (model_parameters.has_primary_objective_parameters()) {
569 RETURN_IF_ERROR(validate_support(
570 "ModelSolveParametersProto.primary_objective_parameters",
571 support_menu.multi_objectives));
572 }
573 if (!model_parameters.auxiliary_objective_parameters().empty()) {
574 RETURN_IF_ERROR(validate_support(
575 "ModelSolveParametersProto.auxiliary_objective_parameters",
576 support_menu.multi_objectives));
577 }
578 return absl::OkStatus();
579}
580
582 SolveResultProto& solve_result_proto) {
583 *solve_result_proto.mutable_termination()->mutable_problem_status() =
584 GetProblemStatus(solve_result_proto);
585 *solve_result_proto.mutable_solve_stats()->mutable_problem_status() =
586 GetProblemStatus(solve_result_proto);
587 *solve_result_proto.mutable_termination()->mutable_objective_bounds() =
588 GetObjectiveBounds(solve_result_proto);
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());
593}
594
595} // namespace math_opt
596} // namespace operations_research
#define RETURN_IF_ERROR(expr)
SparseVectorFilterPredicate(const SparseVectorFilterProto &filter)
const bool DEBUG_MODE
Definition macros.h:26
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
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)