Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
model_builder_helper.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 <cmath>
17#include <cstdint>
18#include <functional>
19#include <limits>
20#include <memory>
21#include <optional>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/log/check.h"
27#include "absl/log/log.h"
28#include "absl/strings/match.h"
29#include "absl/strings/str_cat.h"
30#include "absl/strings/str_join.h"
35#include "ortools/linear_solver/linear_solver.pb.h"
42#if defined(USE_SCIP)
44#endif // defined(USE_SCIP)
45#if defined(USE_HIGHS)
47#endif // defined(USE_HIGHS)
48#if defined(USE_PDLP)
50#endif // defined(USE_PDLP)
54
55namespace operations_research {
56namespace mb {
57
58// ModelBuilderHelper.
59
61 const ModelBuilderHelper& other_helper) {
62 model_ = other_helper.model();
63}
64
66 const MPModelExportOptions& options) {
68 .value_or("");
69}
70
72 const MPModelExportOptions& options) {
73 return operations_research::ExportModelAsLpFormat(model_, options)
74 .value_or("");
75}
76
77bool ModelBuilderHelper::WriteToMpsFile(const std::string& filename,
78 const MPModelExportOptions& options) {
79 return WriteModelToMpsFile(filename, model_, options).ok();
80}
81
82bool ModelBuilderHelper::ReadModelFromProtoFile(const std::string& filename) {
83 if (file::GetTextProto(filename, &model_, file::Defaults()).ok() ||
84 file::GetBinaryProto(filename, &model_, file::Defaults()).ok()) {
85 return true;
86 }
87 MPModelRequest request;
88 if (file::GetTextProto(filename, &request, file::Defaults()).ok() ||
89 file::GetBinaryProto(filename, &request, file::Defaults()).ok()) {
90 model_ = request.model();
91 return true;
92 }
93 return false;
94}
95
96bool ModelBuilderHelper::WriteModelToProtoFile(const std::string& filename) {
97 if (absl::EndsWith(filename, "txt")) {
98 return file::SetTextProto(filename, model_, file::Defaults()).ok();
99 } else {
100 return file::SetBinaryProto(filename, model_, file::Defaults()).ok();
101 }
102}
103
104// See comment in the header file why we need to wrap absl::Status code with
105// code having simpler APIs.
106bool ModelBuilderHelper::ImportFromMpsString(const std::string& mps_string) {
107 absl::StatusOr<MPModelProto> model_or =
109 if (!model_or.ok()) return false;
110 model_ = model_or.value();
111 return true;
112}
113
114bool ModelBuilderHelper::ImportFromMpsFile(const std::string& mps_file) {
115 absl::StatusOr<MPModelProto> model_or =
117 if (!model_or.ok()) return false;
118 model_ = model_or.value();
119 return true;
120}
121
122bool ModelBuilderHelper::ImportFromLpString(const std::string& lp_string) {
123 absl::StatusOr<MPModelProto> model_or = ModelProtoFromLpFormat(lp_string);
124 if (!model_or.ok()) return false;
125 model_ = model_or.value();
126 return true;
127}
128
129bool ModelBuilderHelper::ImportFromLpFile(const std::string& lp_file) {
130 std::string lp_data;
131 if (!file::GetContents(lp_file, &lp_data, file::Defaults()).ok()) {
132 return false;
133 }
134 absl::StatusOr<MPModelProto> model_or = ModelProtoFromLpFormat(lp_data);
135 if (!model_or.ok()) return false;
136 model_ = model_or.value();
137 return true;
138}
139
140const MPModelProto& ModelBuilderHelper::model() const { return model_; }
141
142MPModelProto* ModelBuilderHelper::mutable_model() { return &model_; }
143
145 const int index = model_.variable_size();
146 model_.add_variable();
147 return index;
148}
149
150void ModelBuilderHelper::SetVarLowerBound(int var_index, double lb) {
151 model_.mutable_variable(var_index)->set_lower_bound(lb);
152}
153
154void ModelBuilderHelper::SetVarUpperBound(int var_index, double ub) {
155 model_.mutable_variable(var_index)->set_upper_bound(ub);
156}
157
158void ModelBuilderHelper::SetVarIntegrality(int var_index, bool is_integer) {
159 model_.mutable_variable(var_index)->set_is_integer(is_integer);
160}
161
163 double coeff) {
164 model_.mutable_variable(var_index)->set_objective_coefficient(coeff);
165}
166
167void ModelBuilderHelper::SetVarName(int var_index, const std::string& name) {
168 model_.mutable_variable(var_index)->set_name(name);
169}
170
171double ModelBuilderHelper::VarLowerBound(int var_index) const {
172 return model_.variable(var_index).lower_bound();
173}
174
175double ModelBuilderHelper::VarUpperBound(int var_index) const {
176 return model_.variable(var_index).upper_bound();
177}
178
179bool ModelBuilderHelper::VarIsIntegral(int var_index) const {
180 return model_.variable(var_index).is_integer();
181}
182
184 return model_.variable(var_index).objective_coefficient();
185}
186
187std::string ModelBuilderHelper::VarName(int var_index) const {
188 return model_.variable(var_index).name();
189}
190
192 const int index = model_.constraint_size();
193 model_.add_constraint();
194 return index;
195}
196
197void ModelBuilderHelper::SetConstraintLowerBound(int ct_index, double lb) {
198 model_.mutable_constraint(ct_index)->set_lower_bound(lb);
199}
200
201void ModelBuilderHelper::SetConstraintUpperBound(int ct_index, double ub) {
202 model_.mutable_constraint(ct_index)->set_upper_bound(ub);
203}
204
206 MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
207 ct_proto->clear_var_index();
208 ct_proto->clear_coefficient();
209}
210
211void ModelBuilderHelper::AddConstraintTerm(int ct_index, int var_index,
212 double coeff) {
213 if (coeff == 0.0) return;
214 MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
215 ct_proto->add_var_index(var_index);
216 ct_proto->add_coefficient(coeff);
217}
218
219void ModelBuilderHelper::SafeAddConstraintTerm(int ct_index, int var_index,
220 double coeff) {
221 if (coeff == 0.0) return;
222 MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
223 for (int i = 0; i < ct_proto->var_index_size(); ++i) {
224 if (ct_proto->var_index(i) == var_index) {
225 ct_proto->set_coefficient(i, coeff + ct_proto->coefficient(i));
226 return;
227 }
228 }
229 // If we reach this point, the variable does not exist in the constraint yet,
230 // so we add it to the constraint as a new term.
231 ct_proto->add_var_index(var_index);
232 ct_proto->add_coefficient(coeff);
233}
234
236 const std::string& name) {
237 model_.mutable_constraint(ct_index)->set_name(name);
238}
239
240void ModelBuilderHelper::SetConstraintCoefficient(int ct_index, int var_index,
241 double coeff) {
242 MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
243 for (int i = 0; i < ct_proto->var_index_size(); ++i) {
244 if (ct_proto->var_index(i) == var_index) {
245 ct_proto->set_coefficient(i, coeff);
246 return;
247 }
248 }
249 // If we reach this point, the variable does not exist in the constraint yet,
250 // so we add it to the constraint as a newterm.
251 ct_proto->add_var_index(var_index);
252 ct_proto->add_coefficient(coeff);
253}
254
256 return model_.constraint(ct_index).lower_bound();
257}
258
260 return model_.constraint(ct_index).upper_bound();
261}
262
263std::string ModelBuilderHelper::ConstraintName(int ct_index) const {
264 return model_.constraint(ct_index).name();
265}
266
267std::vector<int> ModelBuilderHelper::ConstraintVarIndices(int ct_index) const {
268 const MPConstraintProto& ct_proto = model_.constraint(ct_index);
269 return {ct_proto.var_index().begin(), ct_proto.var_index().end()};
270}
271
273 int ct_index) const {
274 const MPConstraintProto& ct_proto = model_.constraint(ct_index);
275 return {ct_proto.coefficient().begin(), ct_proto.coefficient().end()};
276}
277
279 const int index = model_.general_constraint_size();
280 // Create the mew constraint, and force the type to indicator ct.
281 model_.add_general_constraint()->mutable_indicator_constraint();
282 return index;
283}
284
286 const MPGeneralConstraintProto& gen = model_.general_constraint(ct_index);
287 return gen.general_constraint_case() ==
288 MPGeneralConstraintProto::kIndicatorConstraint;
289}
290
292 double lb) {
293 DCHECK(IsEnforcedConstraint(ct_index));
294 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
295 MPConstraintProto* ct_proto =
296 gen->mutable_indicator_constraint()->mutable_constraint();
297 ct_proto->set_lower_bound(lb);
298}
299
301 double ub) {
302 DCHECK(IsEnforcedConstraint(ct_index));
303 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
304 MPConstraintProto* ct_proto =
305 gen->mutable_indicator_constraint()->mutable_constraint();
306 ct_proto->set_upper_bound(ub);
307}
308
310 MPConstraintProto* ct_proto = model_.mutable_general_constraint(ct_index)
311 ->mutable_indicator_constraint()
312 ->mutable_constraint();
313 ct_proto->clear_var_index();
314 ct_proto->clear_coefficient();
315}
316
317void ModelBuilderHelper::AddEnforcedConstraintTerm(int ct_index, int var_index,
318 double coeff) {
319 DCHECK(IsEnforcedConstraint(ct_index));
320 if (coeff == 0.0) return;
321 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
322 MPConstraintProto* ct_proto =
323 gen->mutable_indicator_constraint()->mutable_constraint();
324 ct_proto->add_var_index(var_index);
325 ct_proto->add_coefficient(coeff);
326}
327
329 int var_index,
330 double coeff) {
331 DCHECK(IsEnforcedConstraint(ct_index));
332 if (coeff == 0.0) return;
333 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
334 MPConstraintProto* ct_proto =
335 gen->mutable_indicator_constraint()->mutable_constraint();
336 for (int i = 0; i < ct_proto->var_index_size(); ++i) {
337 if (ct_proto->var_index(i) == var_index) {
338 ct_proto->set_coefficient(i, coeff + ct_proto->coefficient(i));
339 return;
340 }
341 }
342 // If we reach this point, the variable does not exist in the constraint yet,
343 // so we add it to the constraint as a new term.
344 ct_proto->add_var_index(var_index);
345 ct_proto->add_coefficient(coeff);
346}
347
349 const std::string& name) {
350 model_.mutable_general_constraint(ct_index)->set_name(name);
351}
352
354 int var_index,
355 double coeff) {
356 DCHECK(IsEnforcedConstraint(ct_index));
357 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
358 MPConstraintProto* ct_proto =
359 gen->mutable_indicator_constraint()->mutable_constraint();
360 for (int i = 0; i < ct_proto->var_index_size(); ++i) {
361 if (ct_proto->var_index(i) == var_index) {
362 ct_proto->set_coefficient(i, coeff);
363 return;
364 }
365 }
366 // If we reach this point, the variable does not exist in the constraint yet,
367 // so we add it to the constraint as a new term.
368 ct_proto->add_var_index(var_index);
369 ct_proto->add_coefficient(coeff);
370}
371
373 int var_index) {
374 DCHECK(IsEnforcedConstraint(ct_index));
375 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
376 gen->mutable_indicator_constraint()->set_var_index(var_index);
377}
378
380 bool positive) {
381 DCHECK(IsEnforcedConstraint(ct_index));
382 MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index);
383 gen->mutable_indicator_constraint()->set_var_value(positive);
384}
385
387 DCHECK(IsEnforcedConstraint(ct_index));
388 return model_.general_constraint(ct_index)
389 .indicator_constraint()
390 .constraint()
391 .lower_bound();
392}
393
395 DCHECK(IsEnforcedConstraint(ct_index));
396 return model_.general_constraint(ct_index)
397 .indicator_constraint()
398 .constraint()
399 .upper_bound();
400}
401
402std::string ModelBuilderHelper::EnforcedConstraintName(int ct_index) const {
403 DCHECK(IsEnforcedConstraint(ct_index));
404 return model_.general_constraint(ct_index).name();
405}
406
408 int ct_index) const {
409 DCHECK(IsEnforcedConstraint(ct_index));
410 const MPConstraintProto& ct_proto =
411 model_.general_constraint(ct_index).indicator_constraint().constraint();
412 return {ct_proto.var_index().begin(), ct_proto.var_index().end()};
413}
414
416 int ct_index) const {
417 DCHECK(IsEnforcedConstraint(ct_index));
418 const MPConstraintProto& ct_proto =
419 model_.general_constraint(ct_index).indicator_constraint().constraint();
420 return {ct_proto.coefficient().begin(), ct_proto.coefficient().end()};
421}
422
424 DCHECK(IsEnforcedConstraint(ct_index));
425 return model_.general_constraint(ct_index).indicator_constraint().var_index();
426}
427
429 DCHECK(IsEnforcedConstraint(ct_index));
430 return model_.general_constraint(ct_index)
431 .indicator_constraint()
432 .var_value() != 0;
433}
434
435int ModelBuilderHelper::num_variables() const { return model_.variable_size(); }
436
438 return model_.constraint_size() + model_.general_constraint_size();
439}
440
441std::string ModelBuilderHelper::name() const { return model_.name(); }
442
443void ModelBuilderHelper::SetName(const std::string& name) {
444 model_.set_name(name);
445}
447 for (MPVariableProto& var : *model_.mutable_variable()) {
448 var.clear_objective_coefficient();
449 }
450}
451
452bool ModelBuilderHelper::maximize() const { return model_.maximize(); }
453
455 model_.set_maximize(maximize);
456}
457
459 return model_.objective_offset();
460}
461
463 model_.set_objective_offset(offset);
464}
465
466void ModelBuilderHelper::ClearHints() { model_.clear_solution_hint(); }
467
468void ModelBuilderHelper::AddHint(int var_index, double var_value) {
469 model_.mutable_solution_hint()->add_var_index(var_index);
470 model_.mutable_solution_hint()->add_var_value(var_value);
471}
472
473std::optional<MPSolutionResponse> ModelSolverHelper::SolveRequest(
474 const MPModelRequest& request) {
477 request.solver_type()))) {
478 return std::nullopt;
479 }
480 return SolveMPModel(request, &interrupter_);
481}
482
483namespace {
484SolveStatus MPSolverResponseStatusToSolveStatus(MPSolverResponseStatus s) {
485 switch (s) {
486 case MPSOLVER_OPTIMAL:
488 case MPSOLVER_FEASIBLE:
490 case MPSOLVER_INFEASIBLE:
492 case MPSOLVER_UNBOUNDED:
494 case MPSOLVER_ABNORMAL:
496 case MPSOLVER_NOT_SOLVED:
498 case MPSOLVER_MODEL_IS_VALID:
500 case MPSOLVER_CANCELLED_BY_USER:
502 case MPSOLVER_UNKNOWN_STATUS:
504 case MPSOLVER_MODEL_INVALID:
506 case MPSOLVER_MODEL_INVALID_SOLUTION_HINT:
508 case MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS:
510 case MPSOLVER_SOLVER_TYPE_UNAVAILABLE:
512 case MPSOLVER_INCOMPATIBLE_OPTIONS:
514 default:
516 }
517}
518} // namespace
519
520ModelSolverHelper::ModelSolverHelper(const std::string& solver_name)
521 : evaluator_(this) {
522 if (solver_name.empty()) return;
524 if (!MPSolver::ParseSolverType(solver_name, &parsed_type)) {
525 VLOG(1) << "Unsupported type " << solver_name;
526 } else {
527 solver_type_ = static_cast<MPModelRequest::SolverType>(parsed_type);
528 }
529}
530
532 if (!solver_type_.has_value()) return false;
533 if (solver_type_.value() == MPModelRequest::GLOP_LINEAR_PROGRAMMING) {
534 return true;
535 }
536#ifdef USE_PDLP
537 if (solver_type_.value() == MPModelRequest::PDLP_LINEAR_PROGRAMMING) {
538 return true;
539 }
540#endif // USE_PDLP
541 if (solver_type_.value() == MPModelRequest::SAT_INTEGER_PROGRAMMING) {
542 return true;
543 }
544#ifdef USE_SCIP
545 if (solver_type_.value() == MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING) {
546 return true;
547 }
548#endif // USE_SCIP
549#ifdef USE_HIGHS
550 if (solver_type_.value() == MPModelRequest::HIGHS_LINEAR_PROGRAMMING ||
551 solver_type_.value() == MPModelRequest::HIGHS_MIXED_INTEGER_PROGRAMMING) {
552 return true;
553 }
554#endif // USE_HIGHS
555 if (solver_type_.value() ==
556 MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING ||
557 solver_type_.value() == MPModelRequest::GUROBI_LINEAR_PROGRAMMING) {
559 }
560 if (solver_type_.value() ==
561 MPModelRequest::XPRESS_MIXED_INTEGER_PROGRAMMING ||
562 solver_type_.value() == MPModelRequest::XPRESS_LINEAR_PROGRAMMING) {
564 }
565 return false;
566}
567
569 response_.reset();
570 if (!solver_type_.has_value()) {
571 response_->set_status(
572 MPSolverResponseStatus::MPSOLVER_SOLVER_TYPE_UNAVAILABLE);
573 return;
574 }
575
576 MPModelRequest request;
577 *request.mutable_model() = model.model();
578 request.set_solver_type(solver_type_.value());
579 request.set_enable_internal_solver_output(solver_output_);
580 if (time_limit_in_second_.has_value()) {
581 request.set_solver_time_limit_seconds(time_limit_in_second_.value());
582 }
583 if (!solver_specific_parameters_.empty()) {
584 request.set_solver_specific_parameters(solver_specific_parameters_);
585 }
586 switch (solver_type_.value()) {
587 case MPModelRequest::GLOP_LINEAR_PROGRAMMING: {
588 response_ =
589 GlopSolveProto(std::move(request), &interrupt_solve_, log_callback_);
590 break;
591 }
592 case MPModelRequest::SAT_INTEGER_PROGRAMMING: {
593 response_ = SatSolveProto(std::move(request), &interrupt_solve_,
594 log_callback_, nullptr);
595 break;
596 }
597#if defined(USE_SCIP)
598 case MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING: {
599 // TODO(user): Enable log_callback support.
600 // TODO(user): Enable interrupt_solve.
601 const auto temp = ScipSolveProto(std::move(request));
602 if (temp.ok()) {
603 response_ = std::move(temp.value());
604 }
605 break;
606 }
607#endif // defined(USE_SCIP)
608#if defined(USE_PDLP)
609 case MPModelRequest::PDLP_LINEAR_PROGRAMMING: {
610 const auto temp = PdlpSolveProto(std::move(request));
611 if (temp.ok()) {
612 response_ = std::move(temp.value());
613 }
614 break;
615 }
616#endif // defined(USE_PDLP)
617 case MPModelRequest::
618 GUROBI_LINEAR_PROGRAMMING: // ABSL_FALLTHROUGH_INTENDED
619 case MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING: {
620 const auto temp = GurobiSolveProto(std::move(request));
621 if (temp.ok()) {
622 response_ = std::move(temp.value());
623 }
624 break;
625 }
626#if defined(USE_HIGHS)
627 case MPModelRequest::HIGHS_LINEAR_PROGRAMMING: // ABSL_FALLTHROUGH_INTENDED
628 case MPModelRequest::HIGHS_MIXED_INTEGER_PROGRAMMING: {
629 // TODO(user): Enable log_callback support.
630 // TODO(user): Enable interrupt_solve.
631 const auto temp = HighsSolveProto(std::move(request));
632 if (temp.ok()) {
633 response_ = std::move(temp.value());
634 }
635 break;
636 }
637#endif // defined(USE_HIGHS)
638 case MPModelRequest::
639 XPRESS_LINEAR_PROGRAMMING: // ABSL_FALLTHROUGH_INTENDED
640 case MPModelRequest::XPRESS_MIXED_INTEGER_PROGRAMMING: {
641 response_ = XPressSolveProto(request);
642 break;
643 }
644 default: {
645 response_->set_status(
646 MPSolverResponseStatus::MPSOLVER_SOLVER_TYPE_UNAVAILABLE);
647 }
648 }
649 if (response_->status() == MPSOLVER_OPTIMAL ||
650 response_->status() == MPSOLVER_FEASIBLE) {
651 model_of_last_solve_ = &model.model();
652 activities_.assign(model.num_constraints(),
653 std::numeric_limits<double>::quiet_NaN());
654 } else {
655 activities_.clear();
656 }
657}
658
660 std::function<void(const std::string&)> log_callback) {
661 log_callback_ = std::move(log_callback);
662}
663
665 MbLogCallback* log_callback) {
666 log_callback_ = [log_callback](const std::string& message) {
667 log_callback->NewMessage(message);
668 };
669}
670
671void ModelSolverHelper::ClearLogCallback() { log_callback_ = nullptr; }
672
674 interrupter_.Interrupt();
675 interrupt_solve_ = true;
676 return true;
677}
678
679bool ModelSolverHelper::has_response() const { return response_.has_value(); }
680
682 return response_.has_value() &&
683 (response_.value().status() ==
684 MPSolverResponseStatus::MPSOLVER_OPTIMAL ||
685 response_.value().status() ==
686 MPSolverResponseStatus::MPSOLVER_FEASIBLE);
687}
688
689const MPSolutionResponse& ModelSolverHelper::response() const {
690 return response_.value();
691}
692
694 if (!response_.has_value()) {
696 }
697 return MPSolverResponseStatusToSolveStatus(response_.value().status());
698}
699
701 if (!has_response()) return 0.0;
702 return response_.value().objective_value();
703}
704
706 if (!has_response()) return 0.0;
707 return response_.value().best_objective_bound();
708}
709
710double ModelSolverHelper::variable_value(int var_index) const {
711 if (!has_response()) return 0.0;
712 if (var_index >= response_.value().variable_value_size()) return 0.0;
713 return response_.value().variable_value(var_index);
714}
715
717 std::shared_ptr<LinearExpr> expr) const {
718 if (!has_response()) return 0.0;
719 evaluator_.Clear();
720 evaluator_.AddToProcess(expr, 1.0);
721 return evaluator_.Evaluate();
722}
723
724double ModelSolverHelper::reduced_cost(int var_index) const {
725 if (!has_response()) return 0.0;
726 if (var_index >= response_.value().reduced_cost_size()) return 0.0;
727 return response_.value().reduced_cost(var_index);
728}
729
730double ModelSolverHelper::dual_value(int ct_index) const {
731 if (!has_response()) return 0.0;
732 if (ct_index >= response_.value().dual_value_size()) return 0.0;
733 return response_.value().dual_value(ct_index);
734}
735
736double ModelSolverHelper::activity(int ct_index) {
737 if (!has_response() || ct_index >= activities_.size() ||
738 !model_of_last_solve_.has_value()) {
739 return 0.0;
740 }
741
742 if (std::isnan(activities_[ct_index])) {
743 const MPConstraintProto& ct_proto =
744 model_of_last_solve_.value()->constraint(ct_index);
745 double result = 0.0;
746 for (int i = 0; i < ct_proto.var_index_size(); ++i) {
747 result += response_->variable_value(ct_proto.var_index(i)) *
748 ct_proto.coefficient(i);
749 }
750 activities_[ct_index] = result;
751 }
752 return activities_[ct_index];
753}
754
756 if (!has_response()) return "";
757 return response_.value().status_str();
758}
759
761 if (!response_.has_value()) return 0.0;
762 if (!response_.value().has_solve_info()) return 0.0;
763 return response_.value().solve_info().solve_wall_time_seconds();
764}
765
767 if (!response_.has_value()) return 0.0;
768 if (!response_.value().has_solve_info()) return 0.0;
769 return response_.value().solve_info().solve_user_time_seconds();
770}
771
773 time_limit_in_second_ = limit;
774}
775
777 const std::string& solver_specific_parameters) {
778 solver_specific_parameters_ = solver_specific_parameters;
779}
780
781void ModelSolverHelper::EnableOutput(bool enabled) { solver_output_ = enabled; }
782
783// Expressions.
784std::shared_ptr<LinearExpr> LinearExpr::Term(std::shared_ptr<LinearExpr> expr,
785 double coeff) {
786 return std::make_shared<AffineExpr>(expr, coeff, 0.0);
787}
788
789std::shared_ptr<LinearExpr> LinearExpr::Affine(std::shared_ptr<LinearExpr> expr,
790 double coeff, double constant) {
791 if (coeff == 1.0 && constant == 0.0) return expr;
792 return std::make_shared<AffineExpr>(expr, coeff, constant);
793}
794
795std::shared_ptr<LinearExpr> LinearExpr::AffineCst(double value, double coeff,
796 double constant) {
797 return std::make_shared<FixedValue>(value * coeff + constant);
798}
799
800std::shared_ptr<LinearExpr> LinearExpr::Constant(double value) {
801 return std::make_shared<FixedValue>(value);
802}
803
804std::shared_ptr<LinearExpr> LinearExpr::Add(std::shared_ptr<LinearExpr> expr) {
805 std::vector<std::shared_ptr<LinearExpr>> exprs;
806 exprs.push_back(shared_from_this());
807 exprs.push_back(expr);
808 return std::make_shared<SumArray>(exprs, 0.0);
809}
810
811std::shared_ptr<LinearExpr> LinearExpr::AddFloat(double cst) {
812 if (cst == 0.0) return shared_from_this();
813 return std::make_shared<AffineExpr>(shared_from_this(), 1.0, cst);
814}
815
816std::shared_ptr<LinearExpr> LinearExpr::Sub(std::shared_ptr<LinearExpr> expr) {
817 std::vector<std::shared_ptr<LinearExpr>> exprs;
818 exprs.push_back(shared_from_this());
819 exprs.push_back(expr->MulFloat(-1.0));
820 return std::make_shared<SumArray>(exprs, 0.0);
821}
822
823std::shared_ptr<LinearExpr> LinearExpr::SubFloat(double cst) {
824 if (cst == 0.0) return shared_from_this();
825 return std::make_shared<AffineExpr>(shared_from_this(), 1.0, -cst);
826}
827
828std::shared_ptr<LinearExpr> LinearExpr::RSubFloat(double cst) {
829 return std::make_shared<AffineExpr>(shared_from_this(), -1.0, cst);
830}
831
832std::shared_ptr<LinearExpr> LinearExpr::MulFloat(double cst) {
833 if (cst == 0.0) return std::make_shared<FixedValue>(0.0);
834 if (cst == 1.0) return shared_from_this();
835 return std::make_shared<AffineExpr>(shared_from_this(), cst, 0.0);
836}
837
838std::shared_ptr<LinearExpr> LinearExpr::Neg() {
839 return std::make_shared<AffineExpr>(shared_from_this(), -1, 0);
840}
841
842// Expression visitors.
843
844void ExprVisitor::AddToProcess(std::shared_ptr<LinearExpr> expr, double coeff) {
845 to_process_.push_back(std::make_pair(expr, coeff));
846}
847
848void ExprVisitor::AddConstant(double constant) { offset_ += constant; }
849
851 to_process_.clear();
852 offset_ = 0.0;
853}
854
855void ExprFlattener::AddVarCoeff(std::shared_ptr<Variable> var, double coeff) {
856 canonical_terms_[var] += coeff;
857}
858
859double ExprFlattener::Flatten(std::vector<std::shared_ptr<Variable>>* vars,
860 std::vector<double>* coeffs) {
861 while (!to_process_.empty()) {
862 const auto [expr, coeff] = to_process_.back();
863 to_process_.pop_back();
864 expr->Visit(*this, coeff);
865 }
866
867 vars->clear();
868 coeffs->clear();
869 for (const auto& [var, coeff] : canonical_terms_) {
870 if (coeff == 0.0) continue;
871 vars->push_back(var);
872 coeffs->push_back(coeff);
873 }
874
875 return offset_;
876}
877
878void ExprEvaluator::AddVarCoeff(std::shared_ptr<Variable> var, double coeff) {
879 offset_ += coeff * helper_->variable_value(var->index());
880}
881
883 offset_ = 0.0;
884 while (!to_process_.empty()) {
885 const auto [expr, coeff] = to_process_.back();
886 to_process_.pop_back();
887 expr->Visit(*this, coeff);
888 }
889 return offset_;
890}
891
892FlatExpr::FlatExpr(std::shared_ptr<LinearExpr> expr) {
893 ExprFlattener lin;
894 lin.AddToProcess(expr, 1.0);
895 offset_ = lin.Flatten(&vars_, &coeffs_);
896}
897
898FlatExpr::FlatExpr(std::shared_ptr<LinearExpr> pos,
899 std::shared_ptr<LinearExpr> neg) {
900 ExprFlattener lin;
901 lin.AddToProcess(pos, 1.0);
902 lin.AddToProcess(neg, -1.0);
903 offset_ = lin.Flatten(&vars_, &coeffs_);
904}
905
906FlatExpr::FlatExpr(const std::vector<std::shared_ptr<Variable>>& vars,
907 const std::vector<double>& coeffs, double offset)
908 : vars_(vars), coeffs_(coeffs), offset_(offset) {}
909
910FlatExpr::FlatExpr(double offset) : offset_(offset) {}
911
912std::vector<int> FlatExpr::VarIndices() const {
913 std::vector<int> var_indices;
914 var_indices.reserve(vars_.size());
915 for (const std::shared_ptr<Variable>& var : vars_) {
916 var_indices.push_back(var->index());
917 }
918 return var_indices;
919}
920
921void FlatExpr::Visit(ExprVisitor& lin, double c) {
922 for (int i = 0; i < vars_.size(); ++i) {
923 lin.AddVarCoeff(vars_[i], coeffs_[i] * c);
924 }
925 lin.AddConstant(offset_ * c);
926}
927
928std::string FlatExpr::ToString() const {
929 if (vars_.empty()) {
930 return absl::StrCat(offset_);
931 }
932 std::string s;
933 int num_printed = 0;
934 for (int i = 0; i < vars_.size(); ++i) {
935 DCHECK_NE(coeffs_[i], 0.0);
936 ++num_printed;
937 if (num_printed > 5) {
938 absl::StrAppend(&s, " + ...");
939 break;
940 }
941 if (num_printed == 1) {
942 if (coeffs_[i] == 1.0) {
943 absl::StrAppend(&s, vars_[i]->ToString());
944 } else if (coeffs_[i] == -1.0) {
945 absl::StrAppend(&s, "-", vars_[i]->ToString());
946 } else {
947 absl::StrAppend(&s, coeffs_[i], " * ", vars_[i]->ToString());
948 }
949 } else {
950 if (coeffs_[i] == 1.0) {
951 absl::StrAppend(&s, " + ", vars_[i]->ToString());
952 } else if (coeffs_[i] == -1.0) {
953 absl::StrAppend(&s, " - ", vars_[i]->ToString());
954 } else if (coeffs_[i] > 0.0) {
955 absl::StrAppend(&s, " + ", coeffs_[i], " * ", vars_[i]->ToString());
956 } else {
957 absl::StrAppend(&s, " - ", -coeffs_[i], " * ", vars_[i]->ToString());
958 }
959 }
960 }
961 // If there are no terms, just print the offset.
962 if (num_printed == 0) {
963 return absl::StrCat(offset_);
964 }
965
966 // If there is an offset, print it.
967 if (offset_ != 0.0) {
968 if (offset_ > 0.0) {
969 absl::StrAppend(&s, " + ", offset_);
970 } else {
971 absl::StrAppend(&s, " - ", -offset_);
972 }
973 }
974 return s;
975}
976
977std::string FlatExpr::DebugString() const {
978 std::string s = absl::StrCat(
979 "FlatExpr(",
980 absl::StrJoin(vars_, ", ",
981 [](std::string* out, std::shared_ptr<Variable> expr) {
982 absl::StrAppend(out, expr->DebugString());
983 }));
984 if (offset_ != 0.0) {
985 absl::StrAppend(&s, ", offset=", offset_);
986 }
987 absl::StrAppend(&s, ")");
988 return s;
989}
990
991SumArray::SumArray(std::vector<std::shared_ptr<LinearExpr>> exprs,
992 double offset)
993 : exprs_(std::move(exprs)), offset_(offset) {}
994
995void SumArray::Visit(ExprVisitor& lin, double c) {
996 for (int i = 0; i < exprs_.size(); ++i) {
997 lin.AddToProcess(exprs_[i], c);
998 }
999 if (offset_ != 0.0) {
1000 lin.AddConstant(offset_ * c);
1001 }
1002}
1003
1004std::string SumArray::ToString() const {
1005 if (exprs_.empty()) {
1006 if (offset_ != 0.0) {
1007 return absl::StrCat(offset_);
1008 }
1009 }
1010 std::string s = "(";
1011 for (int i = 0; i < exprs_.size(); ++i) {
1012 if (i > 0) {
1013 absl::StrAppend(&s, " + ");
1014 }
1015 absl::StrAppend(&s, exprs_[i]->ToString());
1016 }
1017 if (offset_ != 0.0) {
1018 if (offset_ > 0.0) {
1019 absl::StrAppend(&s, " + ", offset_);
1020 } else {
1021 absl::StrAppend(&s, " - ", -offset_);
1022 }
1023 }
1024 absl::StrAppend(&s, ")");
1025 return s;
1026}
1027
1028std::string SumArray::DebugString() const {
1029 std::string s = absl::StrCat(
1030 "SumArray(",
1031 absl::StrJoin(exprs_, ", ",
1032 [](std::string* out, std::shared_ptr<LinearExpr> expr) {
1033 absl::StrAppend(out, expr->DebugString());
1034 }));
1035 if (offset_ != 0.0) {
1036 absl::StrAppend(&s, ", offset=", offset_);
1037 }
1038 absl::StrAppend(&s, ")");
1039 return s;
1040}
1041
1042std::shared_ptr<LinearExpr> SumArray::AddInPlace(
1043 std::shared_ptr<LinearExpr> expr) {
1044 exprs_.push_back(std::move(expr));
1045 return shared_from_this();
1046}
1047
1048std::shared_ptr<LinearExpr> SumArray::AddFloatInPlace(double cst) {
1049 offset_ += cst;
1050 return shared_from_this();
1051}
1052
1053int SumArray::num_exprs() const { return exprs_.size(); }
1054
1055double SumArray::offset() const { return offset_; }
1056
1057void FixedValue::Visit(ExprVisitor& lin, double c) {
1058 lin.AddConstant(value_ * c);
1059}
1060
1061std::string FixedValue::ToString() const { return absl::StrCat(value_); }
1062
1063std::string FixedValue::DebugString() const {
1064 return absl::StrCat("FixedValue(", value_, ")");
1065}
1066
1068 const std::vector<std::shared_ptr<LinearExpr>>& exprs,
1069 const std::vector<double>& coeffs, double offset)
1070 : exprs_(exprs.begin(), exprs.end()),
1071 coeffs_(coeffs.begin(), coeffs.end()),
1072 offset_(offset) {}
1073
1075 for (int i = 0; i < exprs_.size(); ++i) {
1076 lin.AddToProcess(exprs_[i], coeffs_[i] * c);
1077 }
1078 lin.AddConstant(offset_ * c);
1079}
1080
1081std::string WeightedSumArray::ToString() const {
1082 if (exprs_.empty()) {
1083 return absl::StrCat(offset_);
1084 }
1085 std::string s = "(";
1086 bool first_printed = true;
1087 for (int i = 0; i < exprs_.size(); ++i) {
1088 if (coeffs_[i] == 0.0) continue;
1089 if (first_printed) {
1090 first_printed = false;
1091 if (coeffs_[i] == 1.0) {
1092 absl::StrAppend(&s, exprs_[i]->ToString());
1093 } else if (coeffs_[i] == -1.0) {
1094 absl::StrAppend(&s, "-", exprs_[i]->ToString());
1095 } else {
1096 absl::StrAppend(&s, coeffs_[i], " * ", exprs_[i]->ToString());
1097 }
1098 } else {
1099 if (coeffs_[i] == 1.0) {
1100 absl::StrAppend(&s, " + ", exprs_[i]->ToString());
1101 } else if (coeffs_[i] == -1.0) {
1102 absl::StrAppend(&s, " - ", exprs_[i]->ToString());
1103 } else if (coeffs_[i] > 0.0) {
1104 absl::StrAppend(&s, " + ", coeffs_[i], " * ", exprs_[i]->ToString());
1105 } else {
1106 absl::StrAppend(&s, " - ", -coeffs_[i], " * ", exprs_[i]->ToString());
1107 }
1108 }
1109 }
1110 // If there are no terms, just print the offset.
1111 if (first_printed) {
1112 return absl::StrCat(offset_);
1113 }
1114
1115 // If there is an offset, print it.
1116 if (offset_ != 0.0) {
1117 if (offset_ > 0.0) {
1118 absl::StrAppend(&s, " + ", offset_);
1119 } else {
1120 absl::StrAppend(&s, " - ", -offset_);
1121 }
1122 }
1123 absl::StrAppend(&s, ")");
1124 return s;
1125}
1126
1128 return absl::StrCat(
1129 "WeightedSumArray([",
1130 absl::StrJoin(exprs_, ", ",
1131 [](std::string* out, std::shared_ptr<LinearExpr> e) {
1132 absl::StrAppend(out, e->DebugString());
1133 }),
1134 "], [", absl::StrJoin(coeffs_, "], "), offset_, ")");
1135}
1136
1137AffineExpr::AffineExpr(std::shared_ptr<LinearExpr> expr, double coeff,
1138 double offset)
1139 : expr_(expr), coeff_(coeff), offset_(offset) {}
1140
1141void AffineExpr::Visit(ExprVisitor& lin, double c) {
1142 lin.AddToProcess(expr_, c * coeff_);
1143 lin.AddConstant(offset_ * c);
1144}
1145
1146std::shared_ptr<LinearExpr> AffineExpr::AddFloat(double cst) {
1147 if (cst == 0.0) return shared_from_this();
1148 return LinearExpr::Affine(expr_, coeff_, offset_ + cst);
1149}
1150
1151std::shared_ptr<LinearExpr> AffineExpr::SubFloat(double cst) {
1152 if (cst == 0.0) return shared_from_this();
1153 return LinearExpr::Affine(expr_, coeff_, offset_ - cst);
1154}
1155
1156std::shared_ptr<LinearExpr> AffineExpr::RSubFloat(double cst) {
1157 return LinearExpr::Affine(expr_, -coeff_, cst - offset_);
1158}
1159
1160std::shared_ptr<LinearExpr> AffineExpr::MulFloat(double cst) {
1161 if (cst == 0.0) return std::make_shared<FixedValue>(0);
1162 if (cst == 1.0) return shared_from_this();
1163 return LinearExpr::Affine(expr_, coeff_ * cst, offset_ * cst);
1164}
1165
1166std::shared_ptr<LinearExpr> AffineExpr::Neg() {
1167 return LinearExpr::Affine(expr_, -coeff_, -offset_);
1168}
1169
1170std::string AffineExpr::ToString() const {
1171 std::string s = "(";
1172 if (coeff_ == 1.0) {
1173 absl::StrAppend(&s, expr_->ToString());
1174 } else if (coeff_ == -1.0) {
1175 absl::StrAppend(&s, "-", expr_->ToString());
1176 } else {
1177 absl::StrAppend(&s, coeff_, " * ", expr_->ToString());
1178 }
1179 if (offset_ > 0.0) {
1180 absl::StrAppend(&s, " + ", offset_);
1181 } else if (offset_ < 0.0) {
1182 absl::StrAppend(&s, " - ", -offset_);
1183 }
1184 absl::StrAppend(&s, ")");
1185 return s;
1186}
1187
1188std::string AffineExpr::DebugString() const {
1189 return absl::StrCat("AffineExpr(expr=", expr_->DebugString(),
1190 ", coeff=", coeff_, ", offset=", offset_, ")");
1191}
1192std::shared_ptr<BoundedLinearExpression> LinearExpr::Eq(
1193 std::shared_ptr<LinearExpr> rhs) {
1194 return std::make_shared<BoundedLinearExpression>(shared_from_this(), rhs, 0.0,
1195 0.0);
1196}
1197
1198std::shared_ptr<BoundedLinearExpression> LinearExpr::EqCst(double rhs) {
1199 return std::make_shared<BoundedLinearExpression>(shared_from_this(), rhs,
1200 rhs);
1201}
1202
1203std::shared_ptr<BoundedLinearExpression> LinearExpr::Le(
1204 std::shared_ptr<LinearExpr> rhs) {
1205 return std::make_shared<BoundedLinearExpression>(
1206 shared_from_this(), rhs, -std::numeric_limits<double>::infinity(), 0.0);
1207}
1208
1209std::shared_ptr<BoundedLinearExpression> LinearExpr::LeCst(double rhs) {
1210 return std::make_shared<BoundedLinearExpression>(
1211 shared_from_this(), -std::numeric_limits<double>::infinity(), rhs);
1212}
1213
1214std::shared_ptr<BoundedLinearExpression> LinearExpr::Ge(
1215 std::shared_ptr<LinearExpr> rhs) {
1216 return std::make_shared<BoundedLinearExpression>(
1217 shared_from_this(), rhs, 0.0, std::numeric_limits<double>::infinity());
1218}
1219
1220std::shared_ptr<BoundedLinearExpression> LinearExpr::GeCst(double rhs) {
1221 return std::make_shared<BoundedLinearExpression>(
1222 shared_from_this(), rhs, std::numeric_limits<double>::infinity());
1223}
1224
1225bool VariableComparator::operator()(std::shared_ptr<Variable> lhs,
1226 std::shared_ptr<Variable> rhs) const {
1227 return lhs->index() < rhs->index();
1228}
1229
1232
1234 bool is_integral)
1235 : helper_(helper) {
1236 index_ = helper_->AddVar();
1237 helper_->SetVarLowerBound(index_, lb);
1238 helper_->SetVarUpperBound(index_, ub);
1239 helper_->SetVarIntegrality(index_, is_integral);
1240}
1241
1243 bool is_integral, const std::string& name)
1244 : helper_(helper) {
1245 index_ = helper_->AddVar();
1246 helper_->SetVarLowerBound(index_, lb);
1247 helper_->SetVarUpperBound(index_, ub);
1248 helper_->SetVarIntegrality(index_, is_integral);
1249 helper_->SetVarName(index_, name);
1250}
1251
1253 bool is_integral)
1254 : helper_(helper) {
1255 index_ = helper_->AddVar();
1256 helper_->SetVarLowerBound(index_, lb);
1257 helper_->SetVarUpperBound(index_, ub);
1258 helper_->SetVarIntegrality(index_, is_integral);
1259}
1260
1262 bool is_integral, const std::string& name)
1263 : helper_(helper) {
1264 index_ = helper_->AddVar();
1265 helper_->SetVarLowerBound(index_, lb);
1266 helper_->SetVarUpperBound(index_, ub);
1267 helper_->SetVarIntegrality(index_, is_integral);
1268 helper_->SetVarName(index_, name);
1269}
1270
1271std::string Variable::ToString() const {
1272 if (!helper_->VarName(index_).empty()) {
1273 return helper_->VarName(index_);
1274 } else {
1275 return absl::StrCat("Variable(", index_, ")");
1276 }
1277}
1278
1279std::string Variable::DebugString() const {
1280 return absl::StrCat("Variable(index=", index_,
1281 ", lb=", helper_->VarLowerBound(index_),
1282 ", ub=", helper_->VarUpperBound(index_),
1283 ", is_integral=", helper_->VarIsIntegral(index_),
1284 ", name=\'", helper_->VarName(index_), "')");
1285}
1286
1287std::string Variable::name() const {
1288 const std::string& var_name = helper_->VarName(index_);
1289 if (!var_name.empty()) return var_name;
1290 return absl::StrCat("variable#", index_);
1291}
1292
1293void Variable::SetName(const std::string& name) {
1294 helper_->SetVarName(index_, name);
1295}
1296
1297double Variable::lower_bounds() const { return helper_->VarLowerBound(index_); }
1298
1300 helper_->SetVarLowerBound(index_, lb);
1301}
1302
1303double Variable::upper_bound() const { return helper_->VarUpperBound(index_); }
1304
1306 helper_->SetVarUpperBound(index_, ub);
1307}
1308
1309bool Variable::is_integral() const { return helper_->VarIsIntegral(index_); }
1310
1312 helper_->SetVarIntegrality(index_, is_integral);
1313}
1314
1316 return helper_->VarObjectiveCoefficient(index_);
1317}
1318
1320 helper_->SetVarObjectiveCoefficient(index_, coeff);
1321}
1322
1324 std::shared_ptr<LinearExpr> expr, double lower_bound, double upper_bound) {
1325 FlatExpr flat_expr(expr);
1326 vars_ = flat_expr.vars();
1327 coeffs_ = flat_expr.coeffs();
1328 lower_bound_ = lower_bound - flat_expr.offset();
1329 upper_bound_ = upper_bound - flat_expr.offset();
1330}
1331
1333 std::shared_ptr<LinearExpr> pos, std::shared_ptr<LinearExpr> neg,
1334 double lower_bound, double upper_bound) {
1335 FlatExpr flat_expr(pos, neg);
1336 vars_ = flat_expr.vars();
1337 coeffs_ = flat_expr.coeffs();
1338 lower_bound_ = lower_bound - flat_expr.offset();
1339 upper_bound_ = upper_bound - flat_expr.offset();
1340}
1341
1343 std::shared_ptr<LinearExpr> expr, int64_t lower_bound,
1344 int64_t upper_bound) {
1345 FlatExpr flat_expr(expr);
1346 vars_ = flat_expr.vars();
1347 coeffs_ = flat_expr.coeffs();
1348 lower_bound_ = lower_bound - flat_expr.offset();
1349 upper_bound_ = upper_bound - flat_expr.offset();
1350}
1351
1353 std::shared_ptr<LinearExpr> pos, std::shared_ptr<LinearExpr> neg,
1354 int64_t lower_bound, int64_t upper_bound) {
1355 FlatExpr flat_expr(pos, neg);
1356 vars_ = flat_expr.vars();
1357 coeffs_ = flat_expr.coeffs();
1358 lower_bound_ = lower_bound - flat_expr.offset();
1359 upper_bound_ = upper_bound - flat_expr.offset();
1360}
1361
1362double BoundedLinearExpression::lower_bound() const { return lower_bound_; }
1363double BoundedLinearExpression::upper_bound() const { return upper_bound_; }
1364const std::vector<std::shared_ptr<Variable>>& BoundedLinearExpression::vars()
1365 const {
1366 return vars_;
1367}
1368const std::vector<double>& BoundedLinearExpression::coeffs() const {
1369 return coeffs_;
1370}
1372 std::string s;
1373 if (vars_.empty()) {
1374 s = absl::StrCat(0.0);
1375 } else if (vars_.size() == 1) {
1376 const std::string var_name = vars_[0]->ToString();
1377 if (coeffs_[0] == 1) {
1378 s = var_name;
1379 } else if (coeffs_[0] == -1) {
1380 s = absl::StrCat("-", var_name);
1381 } else {
1382 s = absl::StrCat(coeffs_[0], " * ", var_name);
1383 }
1384 } else {
1385 s = "(";
1386 for (int i = 0; i < vars_.size(); ++i) {
1387 const std::string var_name = vars_[i]->ToString();
1388 if (i == 0) {
1389 if (coeffs_[i] == 1) {
1390 absl::StrAppend(&s, var_name);
1391 } else if (coeffs_[i] == -1) {
1392 absl::StrAppend(&s, "-", var_name);
1393 } else {
1394 absl::StrAppend(&s, coeffs_[i], " * ", var_name);
1395 }
1396 } else {
1397 if (coeffs_[i] == 1) {
1398 absl::StrAppend(&s, " + ", var_name);
1399 } else if (coeffs_[i] == -1) {
1400 absl::StrAppend(&s, " - ", var_name);
1401 } else if (coeffs_[i] > 1) {
1402 absl::StrAppend(&s, " + ", coeffs_[i], " * ", var_name);
1403 } else {
1404 absl::StrAppend(&s, " - ", -coeffs_[i], " * ", var_name);
1405 }
1406 }
1407 }
1408 absl::StrAppend(&s, ")");
1409 }
1410 if (lower_bound_ == upper_bound_) {
1411 return absl::StrCat(s, " == ", lower_bound_);
1412 } else if (lower_bound_ == -std::numeric_limits<double>::infinity()) {
1413 if (upper_bound_ == std::numeric_limits<double>::infinity()) {
1414 return absl::StrCat("-inf <= ", s, " <= inf");
1415 } else {
1416 return absl::StrCat(s, " <= ", upper_bound_);
1417 }
1418 } else if (upper_bound_ == std::numeric_limits<double>::infinity()) {
1419 return absl::StrCat(s, " >= ", lower_bound_);
1420 } else {
1421 return absl::StrCat(lower_bound_, " <= ", s, " <= ", upper_bound_);
1422 }
1423}
1424
1426 return absl::StrCat(
1427 "BoundedLinearExpression(vars=[",
1428 absl::StrJoin(vars_, ", ",
1429 [](std::string* out, std::shared_ptr<Variable> var) {
1430 absl::StrAppend(out, var->DebugString());
1431 }),
1432 "], coeffs=[", absl::StrJoin(coeffs_, ", "),
1433 "], lower_bound=", lower_bound_, ", upper_bound=", upper_bound_, ")");
1434}
1435
1436bool BoundedLinearExpression::CastToBool(bool* result) const {
1437 const bool is_zero = lower_bound_ == 0.0 && upper_bound_ == 0.0;
1438 if (is_zero) {
1439 if (vars_.empty()) {
1440 *result = true;
1441 return true;
1442 } else if (vars_.size() == 2 && coeffs_[0] + coeffs_[1] == 0 &&
1443 std::abs(coeffs_[0]) == 1) {
1444 *result = false;
1445 return true;
1446 }
1447 }
1448 return false;
1449}
1450
1451} // namespace mb
1452} // namespace operations_research
static bool SupportsProblemType(OptimizationProblemType problem_type)
static
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
static
AffineExpr(std::shared_ptr< LinearExpr > expr, double coeff, double offset)
std::string DebugString() const override
std::shared_ptr< LinearExpr > Neg() override
std::shared_ptr< LinearExpr > SubFloat(double cst) override
std::shared_ptr< LinearExpr > RSubFloat(double cst) override
std::shared_ptr< LinearExpr > AddFloat(double cst) override
void Visit(ExprVisitor &lin, double c) override
std::shared_ptr< LinearExpr > MulFloat(double cst) override
BoundedLinearExpression(std::shared_ptr< LinearExpr > expr, double lower_bound, double upper_bound)
const std::vector< std::shared_ptr< Variable > > & vars() const
void AddVarCoeff(std::shared_ptr< Variable > var, double coeff) override
double Flatten(std::vector< std::shared_ptr< Variable > > *vars, std::vector< double > *coeffs)
void AddVarCoeff(std::shared_ptr< Variable > var, double coeff) override
A visitor class to parse a floating point linear expression.
std::vector< std::pair< std::shared_ptr< LinearExpr >, double > > to_process_
void AddToProcess(std::shared_ptr< LinearExpr > expr, double coeff)
Expression visitors.
virtual void AddVarCoeff(std::shared_ptr< Variable > var, double coeff)=0
void Visit(ExprVisitor &lin, double c) override
std::string DebugString() const override
A flat linear expression sum(vars[i] * coeffs[i]) + offset.
std::string ToString() const override
void Visit(ExprVisitor &lin, double c) override
const std::vector< std::shared_ptr< Variable > > & vars() const
FlatExpr(std::shared_ptr< LinearExpr > expr)
const std::vector< double > & coeffs() const
std::vector< int > VarIndices() const
std::string DebugString() const override
std::shared_ptr< LinearExpr > Add(std::shared_ptr< LinearExpr > expr)
std::shared_ptr< BoundedLinearExpression > Le(std::shared_ptr< LinearExpr > rhs)
std::shared_ptr< LinearExpr > Sub(std::shared_ptr< LinearExpr > expr)
std::shared_ptr< BoundedLinearExpression > EqCst(double rhs)
std::shared_ptr< BoundedLinearExpression > LeCst(double rhs)
static std::shared_ptr< LinearExpr > Affine(std::shared_ptr< LinearExpr > expr, double coeff, double constant)
static std::shared_ptr< LinearExpr > Constant(double value)
static std::shared_ptr< LinearExpr > AffineCst(double value, double coeff, double constant)
std::shared_ptr< BoundedLinearExpression > Ge(std::shared_ptr< LinearExpr > rhs)
virtual std::shared_ptr< LinearExpr > Neg()
virtual std::shared_ptr< LinearExpr > RSubFloat(double cst)
virtual std::shared_ptr< LinearExpr > AddFloat(double cst)
virtual std::shared_ptr< LinearExpr > MulFloat(double cst)
static std::shared_ptr< LinearExpr > Term(std::shared_ptr< LinearExpr > expr, double coeff)
Expressions.
std::shared_ptr< BoundedLinearExpression > GeCst(double rhs)
std::shared_ptr< BoundedLinearExpression > Eq(std::shared_ptr< LinearExpr > rhs)
virtual std::shared_ptr< LinearExpr > SubFloat(double cst)
virtual void NewMessage(const std::string &message)=0
void SetEnforcedConstraintLowerBound(int ct_index, double lb)
std::vector< int > ConstraintVarIndices(int ct_index) const
std::vector< double > EnforcedConstraintCoefficients(int ct_index) const
void SetVarIntegrality(int var_index, bool is_integer)
void SetEnforcedIndicatorValue(int ct_index, bool positive)
void SafeAddEnforcedConstraintTerm(int ct_index, int var_index, double coeff)
void SetEnforcedConstraintName(int ct_index, const std::string &name)
void SetVarName(int var_index, const std::string &name)
void AddConstraintTerm(int ct_index, int var_index, double coeff)
bool WriteToMpsFile(const std::string &filename, const operations_research::MPModelExportOptions &options=MPModelExportOptions())
bool ReadModelFromProtoFile(const std::string &filename)
std::string ExportToMpsString(const operations_research::MPModelExportOptions &options=MPModelExportOptions())
void SetEnforcedConstraintCoefficient(int ct_index, int var_index, double coeff)
std::string EnforcedConstraintName(int ct_index) const
void AddEnforcedConstraintTerm(int ct_index, int var_index, double coeff)
void OverwriteModel(const ModelBuilderHelper &other_helper)
ModelBuilderHelper.
bool WriteModelToProtoFile(const std::string &filename)
void SetEnforcedIndicatorVariableIndex(int ct_index, int var_index)
void SetConstraintLowerBound(int ct_index, double lb)
bool ImportFromMpsFile(const std::string &mps_file)
void SetEnforcedConstraintUpperBound(int ct_index, double ub)
void AddHint(int var_index, double var_value)
int AddVar()
Direct low level model building API.
void SetVarObjectiveCoefficient(int var_index, double coeff)
void SetConstraintName(int ct_index, const std::string &name)
bool ImportFromMpsString(const std::string &mps_string)
bool ImportFromLpString(const std::string &lp_string)
bool ImportFromLpFile(const std::string &lp_file)
void SetConstraintUpperBound(int ct_index, double ub)
std::vector< double > ConstraintCoefficients(int ct_index) const
void SafeAddConstraintTerm(int ct_index, int var_index, double coeff)
void SetConstraintCoefficient(int ct_index, int var_index, double coeff)
std::string ExportToLpString(const operations_research::MPModelExportOptions &options=MPModelExportOptions())
std::vector< int > EnforcedConstraintVarIndices(int ct_index) const
void Solve(const ModelBuilderHelper &model)
void SetSolverSpecificParameters(const std::string &solver_specific_parameters)
ModelSolverHelper(const std::string &solver_name)
std::optional< MPSolutionResponse > SolveRequest(const MPModelRequest &request)
Only used by the CVXPY interface. Does not store the response internally.
double objective_value() const
If not defined, or no solution, they will silently return 0.
const MPSolutionResponse & response() const
void SetLogCallbackFromDirectorClass(MbLogCallback *log_callback)
double expression_value(std::shared_ptr< LinearExpr > expr) const
void SetTimeLimitInSeconds(double limit)
Solve parameters.
void SetLogCallback(std::function< void(const std::string &)> log_callback)
void Visit(ExprVisitor &lin, double c) override
std::shared_ptr< LinearExpr > AddInPlace(std::shared_ptr< LinearExpr > expr)
std::string ToString() const override
std::shared_ptr< LinearExpr > AddFloatInPlace(double cst)
std::string DebugString() const override
SumArray(std::vector< std::shared_ptr< LinearExpr > > exprs, double offset)
Variable(ModelBuilderHelper *helper, int index)
void SetName(const std::string &name)
ModelBuilderHelper * helper() const
std::string DebugString() const override
std::string ToString() const override
WeightedSumArray(const std::vector< std::shared_ptr< LinearExpr > > &exprs, const std::vector< double > &coeffs, double offset)
void Visit(ExprVisitor &lin, double c) override
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
-— Content API -—
Definition file.cc:348
absl::Status SetBinaryProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
Definition file.cc:475
absl::Status SetTextProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
Definition file.cc:447
absl::Status GetTextProto(absl::string_view file_name, google::protobuf::Message *proto, Options options)
-— Protobuf API -—
Definition file.cc:408
absl::Status GetBinaryProto(const absl::string_view file_name, google::protobuf::Message *proto, Options options)
Definition file.cc:462
Options Defaults()
Definition file.h:85
absl::StatusOr< MPModelProto > MpsFileToMPModelProto(absl::string_view mps_file)
Parses an MPS model from a file.
absl::StatusOr< MPModelProto > MpsDataToMPModelProto(absl::string_view mps_data)
Parses an MPS model from a string.
In SWIG mode, we don't want anything besides these top-level includes.
MPSolutionResponse SatSolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> logging_callback, std::function< void(const MPSolution &)> solution_callback)
absl::Status WriteModelToMpsFile(absl::string_view filename, const MPModelProto &model, const MPModelExportOptions &options)
MPSolutionResponse SolveMPModel(LazyMutableCopy< MPModelRequest > request, const SolveInterrupter *interrupter)
absl::StatusOr< MPSolutionResponse > PdlpSolveProto(LazyMutableCopy< MPModelRequest > request, const bool relax_integer_variables, const std::atomic< bool > *interrupt_solve)
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
bool GurobiIsCorrectlyInstalled()
absl::StatusOr< MPSolutionResponse > ScipSolveProto(LazyMutableCopy< MPModelRequest > request)
ClosedInterval::Iterator end(ClosedInterval interval)
MPSolutionResponse GlopSolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> logging_callback)
bool XpressIsCorrectlyInstalled()
absl::StatusOr< MPSolutionResponse > GurobiSolveProto(LazyMutableCopy< MPModelRequest > request, GRBenv *gurobi_env)
absl::StatusOr< MPSolutionResponse > HighsSolveProto(LazyMutableCopy< MPModelRequest > request)
Solve the input MIP model with the HIGHS solver.
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
ClosedInterval::Iterator begin(ClosedInterval interval)
absl::StatusOr< MPModelProto > ModelProtoFromLpFormat(absl::string_view model)
This calls ParseLp() under the hood. See below.
Definition lp_parser.cc:472
MPSolutionResponse XPressSolveProto(LazyMutableCopy< MPModelRequest > request)
Solves the input request.
STL namespace.
if(!yyg->yy_init)
Definition parser.yy.cc:966
bool operator()(std::shared_ptr< Variable > lhs, std::shared_ptr< Variable > rhs) const