Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
lp_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 <limits>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "absl/log/check.h"
25#include "absl/status/status.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/string_view.h"
28#include "absl/types/span.h"
47
48namespace operations_research {
49namespace sat {
50
51using glop::ColIndex;
53using glop::kInfinity;
54using glop::RowIndex;
55
56using operations_research::MPConstraintProto;
57using operations_research::MPModelProto;
58using operations_research::MPVariableProto;
59
60namespace {
61
62void ScaleConstraint(absl::Span<const double> var_scaling,
63 MPConstraintProto* mp_constraint) {
64 const int num_terms = mp_constraint->coefficient_size();
65 for (int i = 0; i < num_terms; ++i) {
66 const int var_index = mp_constraint->var_index(i);
67 mp_constraint->set_coefficient(
68 i, mp_constraint->coefficient(i) / var_scaling[var_index]);
69 }
70}
71
72void ApplyVarScaling(absl::Span<const double> var_scaling,
73 MPModelProto* mp_model) {
74 const int num_variables = mp_model->variable_size();
75 for (int i = 0; i < num_variables; ++i) {
76 const double scaling = var_scaling[i];
77 const MPVariableProto& mp_var = mp_model->variable(i);
78 const double old_lb = mp_var.lower_bound();
79 const double old_ub = mp_var.upper_bound();
80 const double old_obj = mp_var.objective_coefficient();
81 mp_model->mutable_variable(i)->set_lower_bound(old_lb * scaling);
82 mp_model->mutable_variable(i)->set_upper_bound(old_ub * scaling);
83 mp_model->mutable_variable(i)->set_objective_coefficient(old_obj / scaling);
84
85 // TODO(user): Make bounds of integer variable integer.
86 }
87 for (MPConstraintProto& mp_constraint : *mp_model->mutable_constraint()) {
88 ScaleConstraint(var_scaling, &mp_constraint);
89 }
90 for (MPGeneralConstraintProto& general_constraint :
91 *mp_model->mutable_general_constraint()) {
92 switch (general_constraint.general_constraint_case()) {
94 ScaleConstraint(var_scaling,
95 general_constraint.mutable_indicator_constraint()
96 ->mutable_constraint());
97 break;
100 // These constraints have only Boolean variables and no constants. They
101 // don't need scaling.
102 break;
103 default:
104 LOG(FATAL) << "Scaling unsupported for general constraint of type "
105 << general_constraint.general_constraint_case();
106 }
107 }
108}
109
110} // namespace
111
112std::vector<double> ScaleContinuousVariables(double scaling, double max_bound,
113 MPModelProto* mp_model) {
114 const int num_variables = mp_model->variable_size();
115 std::vector<double> var_scaling(num_variables, 1.0);
116 for (int i = 0; i < num_variables; ++i) {
117 if (mp_model->variable(i).is_integer()) continue;
118 if (max_bound == std::numeric_limits<double>::infinity()) {
119 var_scaling[i] = scaling;
120 continue;
121 }
122 const double lb = mp_model->variable(i).lower_bound();
123 const double ub = mp_model->variable(i).upper_bound();
124 const double magnitude = std::max(std::abs(lb), std::abs(ub));
125 if (magnitude == 0 || magnitude > max_bound) continue;
126 var_scaling[i] = std::min(scaling, max_bound / magnitude);
127 }
128 ApplyVarScaling(var_scaling, mp_model);
129 return var_scaling;
130}
131
132// This uses the best rational approximation of x via continuous fractions.
133// It is probably not the best implementation, but according to the unit test,
134// it seems to do the job.
135int64_t FindRationalFactor(double x, int64_t limit, double tolerance) {
136 const double initial_x = x;
137 x = std::abs(x);
138 x -= std::floor(x);
139 int64_t current_q = 1;
140 int64_t prev_q = 0;
141 while (current_q < limit) {
142 const double q = static_cast<double>(current_q);
143 const double qx = q * initial_x;
144 const double qtolerance = q * tolerance;
145 if (std::abs(qx - std::round(qx)) < qtolerance) {
146 return current_q;
147 }
148 x = 1 / x;
149 const double floored_x = std::floor(x);
150 if (floored_x >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
151 return 0;
152 }
153 const int64_t new_q =
154 CapAdd(prev_q, CapProd(static_cast<int64_t>(floored_x), current_q));
155 prev_q = current_q;
156 current_q = new_q;
157 x -= std::floor(x);
158 }
159 return 0;
160}
161
162namespace {
163
164// Returns a factor such that factor * var only need to take integer values to
165// satisfy the given constraint. Return 0.0 if we didn't find such factor.
166//
167// Precondition: var must be the only non-integer in the given constraint.
168double GetIntegralityMultiplier(const MPModelProto& mp_model,
169 absl::Span<const double> var_scaling, int var,
170 int ct_index, double tolerance) {
171 DCHECK(!mp_model.variable(var).is_integer());
172 const MPConstraintProto& ct = mp_model.constraint(ct_index);
173 double multiplier = 1.0;
174 double var_coeff = 0.0;
175 const double max_multiplier = 1e4;
176 for (int i = 0; i < ct.var_index().size(); ++i) {
177 if (var == ct.var_index(i)) {
178 var_coeff = ct.coefficient(i);
179 continue;
180 }
181
182 DCHECK(mp_model.variable(ct.var_index(i)).is_integer());
183 // This actually compute the smallest multiplier to make all other
184 // terms in the constraint integer.
185 const double coeff =
186 multiplier * ct.coefficient(i) / var_scaling[ct.var_index(i)];
187 multiplier *=
188 FindRationalFactor(coeff, /*limit=*/100, multiplier * tolerance);
189 if (multiplier == 0 || multiplier > max_multiplier) return 0.0;
190 }
191 DCHECK_NE(var_coeff, 0.0);
192
193 // Makes sure that the constraint bound is infinite or integer.
194 for (const double bound : {ct.lower_bound(), ct.upper_bound()}) {
195 if (!std::isfinite(bound)) continue;
196
197 const double scaled_bound = multiplier * bound;
198 multiplier *=
199 FindRationalFactor(scaled_bound, /*limit=*/100, multiplier * tolerance);
200 if (multiplier == 0 || multiplier > max_multiplier) return 0.0;
201 }
202 return std::abs(multiplier * var_coeff);
203}
204
205} // namespace
206
208 MPModelProto* mp_model,
209 SolverLogger* logger) {
210 const int num_variables = mp_model->variable_size();
211 const double tolerance = params.mip_wanted_precision();
212 int64_t num_changes = 0;
213 for (int i = 0; i < num_variables; ++i) {
214 const MPVariableProto& mp_var = mp_model->variable(i);
215 if (!mp_var.is_integer()) continue;
216
217 const double lb = mp_var.lower_bound();
218 const double new_lb = std::isfinite(lb) ? std::ceil(lb - tolerance) : lb;
219 if (lb != new_lb) {
220 ++num_changes;
221 mp_model->mutable_variable(i)->set_lower_bound(new_lb);
222 }
223
224 const double ub = mp_var.upper_bound();
225 const double new_ub = std::isfinite(ub) ? std::floor(ub + tolerance) : ub;
226 if (ub != new_ub) {
227 ++num_changes;
228 mp_model->mutable_variable(i)->set_upper_bound(new_ub);
229 }
230
231 if (new_ub < new_lb) {
232 SOLVER_LOG(logger, "Empty domain for integer variable #", i, ": [", lb,
233 ",", ub, "]");
234 return false;
235 }
236 }
237 return true;
238}
239
240void ChangeLargeBoundsToInfinity(double max_magnitude, MPModelProto* mp_model,
241 SolverLogger* logger) {
242 const int num_variables = mp_model->variable_size();
243 int64_t num_variable_bounds_pushed_to_infinity = 0;
244 const double infinity = std::numeric_limits<double>::infinity();
245 for (int i = 0; i < num_variables; ++i) {
246 MPVariableProto* mp_var = mp_model->mutable_variable(i);
247 const double lb = mp_var->lower_bound();
248 if (std::isfinite(lb) && lb < -max_magnitude) {
249 ++num_variable_bounds_pushed_to_infinity;
250 mp_var->set_lower_bound(-infinity);
251 }
252
253 const double ub = mp_var->upper_bound();
254 if (std::isfinite(ub) && ub > max_magnitude) {
255 ++num_variable_bounds_pushed_to_infinity;
256 mp_var->set_upper_bound(infinity);
257 }
258 }
259
260 if (num_variable_bounds_pushed_to_infinity > 0) {
261 SOLVER_LOG(logger, "Pushed ", num_variable_bounds_pushed_to_infinity,
262 " variable bounds to +/-infinity");
263 }
264
265 const int num_constraints = mp_model->constraint_size();
266 int64_t num_constraint_bounds_pushed_to_infinity = 0;
267
268 for (int i = 0; i < num_constraints; ++i) {
269 MPConstraintProto* mp_ct = mp_model->mutable_constraint(i);
270 const double lb = mp_ct->lower_bound();
271 if (std::isfinite(lb) && lb < -max_magnitude) {
272 ++num_constraint_bounds_pushed_to_infinity;
273 mp_ct->set_lower_bound(-infinity);
274 }
275
276 const double ub = mp_ct->upper_bound();
277 if (std::isfinite(ub) && ub > max_magnitude) {
278 ++num_constraint_bounds_pushed_to_infinity;
279 mp_ct->set_upper_bound(infinity);
280 }
281 }
282
283 for (int i = 0; i < mp_model->general_constraint_size(); ++i) {
286 continue;
287 }
288
292 const double lb = mp_ct->lower_bound();
293 if (std::isfinite(lb) && lb < -max_magnitude) {
294 ++num_constraint_bounds_pushed_to_infinity;
295 mp_ct->set_lower_bound(-infinity);
296 }
297
298 const double ub = mp_ct->upper_bound();
299 if (std::isfinite(ub) && ub > max_magnitude) {
300 ++num_constraint_bounds_pushed_to_infinity;
301 mp_ct->set_upper_bound(infinity);
302 }
303 }
304
305 if (num_constraint_bounds_pushed_to_infinity > 0) {
306 SOLVER_LOG(logger, "Pushed ", num_constraint_bounds_pushed_to_infinity,
307 " constraint bounds to +/-infinity");
308 }
309}
310
311void RemoveNearZeroTerms(const SatParameters& params, MPModelProto* mp_model,
312 SolverLogger* logger) {
313 // Having really low bounds or rhs can be problematic. We set them to zero.
314 int num_dropped = 0;
315 double max_dropped = 0.0;
316 const double drop = params.mip_drop_tolerance();
317 const int num_variables = mp_model->variable_size();
318 for (int i = 0; i < num_variables; ++i) {
319 MPVariableProto* var = mp_model->mutable_variable(i);
320 if (var->lower_bound() != 0.0 && std::abs(var->lower_bound()) < drop) {
321 ++num_dropped;
322 max_dropped = std::max(max_dropped, std::abs(var->lower_bound()));
323 var->set_lower_bound(0.0);
324 }
325 if (var->upper_bound() != 0.0 && std::abs(var->upper_bound()) < drop) {
326 ++num_dropped;
327 max_dropped = std::max(max_dropped, std::abs(var->upper_bound()));
328 var->set_upper_bound(0.0);
329 }
330 }
331 const int num_constraints = mp_model->constraint_size();
332 for (int i = 0; i < num_constraints; ++i) {
333 MPConstraintProto* ct = mp_model->mutable_constraint(i);
334 if (ct->lower_bound() != 0.0 && std::abs(ct->lower_bound()) < drop) {
335 ++num_dropped;
336 max_dropped = std::max(max_dropped, std::abs(ct->lower_bound()));
337 ct->set_lower_bound(0.0);
338 }
339 if (ct->upper_bound() != 0.0 && std::abs(ct->upper_bound()) < drop) {
340 ++num_dropped;
341 max_dropped = std::max(max_dropped, std::abs(ct->upper_bound()));
342 ct->set_upper_bound(0.0);
343 }
344 }
345 if (num_dropped > 0) {
346 SOLVER_LOG(logger, "Set to zero ", num_dropped,
347 " variable or constraint bounds with largest magnitude ",
348 max_dropped);
349 }
350
351 // Compute for each variable its current maximum magnitude. Note that we will
352 // only scale variable with a coefficient >= 1, so it is safe to use this
353 // bound.
354 std::vector<double> max_bounds(num_variables);
355 for (int i = 0; i < num_variables; ++i) {
356 double value = std::abs(mp_model->variable(i).lower_bound());
357 value = std::max(value, std::abs(mp_model->variable(i).upper_bound()));
358 value = std::min(value, params.mip_max_bound());
359 max_bounds[i] = value;
360 }
361
362 // Note that when a variable is fixed to zero, the code here remove all its
363 // coefficients. But we do not count them here.
364 double largest_removed = 0.0;
365
366 // We want the maximum absolute error while setting coefficients to zero to
367 // not exceed our mip wanted precision. So for a binary variable we might set
368 // to zero coefficient around 1e-7. But for large domain, we need lower coeff
369 // than that, around 1e-12 with the default params.mip_max_bound(). This also
370 // depends on the size of the constraint.
371 int64_t num_removed = 0;
372 for (int c = 0; c < num_constraints; ++c) {
373 MPConstraintProto* ct = mp_model->mutable_constraint(c);
374 int new_size = 0;
375 const int size = ct->var_index().size();
376 if (size == 0) continue;
377 const double threshold =
378 params.mip_wanted_precision() / static_cast<double>(size);
379 for (int i = 0; i < size; ++i) {
380 const int var = ct->var_index(i);
381 const double coeff = ct->coefficient(i);
382 if (std::abs(coeff) * max_bounds[var] < threshold) {
383 if (max_bounds[var] != 0) {
384 largest_removed = std::max(largest_removed, std::abs(coeff));
385 }
386 continue;
387 }
388 ct->set_var_index(new_size, var);
389 ct->set_coefficient(new_size, coeff);
390 ++new_size;
391 }
392 num_removed += size - new_size;
393 ct->mutable_var_index()->Truncate(new_size);
394 ct->mutable_coefficient()->Truncate(new_size);
395 }
396
397 // We also do the same for the objective coefficient.
398 if (num_variables > 0) {
399 const double threshold =
400 params.mip_wanted_precision() / static_cast<double>(num_variables);
401 for (int var = 0; var < num_variables; ++var) {
402 const double coeff = mp_model->variable(var).objective_coefficient();
403 if (coeff == 0.0) continue;
404 if (std::abs(coeff) * max_bounds[var] < threshold) {
405 ++num_removed;
406 if (max_bounds[var] != 0) {
407 largest_removed = std::max(largest_removed, std::abs(coeff));
408 }
410 }
411 }
412 }
413
414 if (num_removed > 0) {
415 SOLVER_LOG(logger, "Removed ", num_removed,
416 " near zero terms with largest magnitude of ", largest_removed,
417 ".");
418 }
419}
420
422 const MPModelProto& mp_model,
423 SolverLogger* logger) {
424 // Abort if there is constraint type we don't currently support.
425 for (const MPGeneralConstraintProto& general_constraint :
426 mp_model.general_constraint()) {
427 switch (general_constraint.general_constraint_case()) {
429 break;
431 break;
433 break;
434 default:
435 SOLVER_LOG(logger, "General constraints of type ",
436 general_constraint.general_constraint_case(),
437 " are not supported.");
438 return false;
439 }
440 }
441
442 // Abort if finite variable bounds or objective is too large.
443 const double threshold = params.mip_max_valid_magnitude();
444 const int num_variables = mp_model.variable_size();
445 for (int i = 0; i < num_variables; ++i) {
446 const MPVariableProto& var = mp_model.variable(i);
447 if ((std::isfinite(var.lower_bound()) &&
448 std::abs(var.lower_bound()) > threshold) ||
449 (std::isfinite(var.upper_bound()) &&
450 std::abs(var.upper_bound()) > threshold)) {
451 SOLVER_LOG(logger, "Variable bounds are too large [", var.lower_bound(),
452 ",", var.upper_bound(), "]");
453 return false;
454 }
455 if (std::abs(var.objective_coefficient()) > threshold) {
456 SOLVER_LOG(logger, "Objective coefficient is too large: ",
458 return false;
459 }
460 }
461
462 // Abort if finite constraint bounds or coefficients are too large.
463 const int num_constraints = mp_model.constraint_size();
464 for (int c = 0; c < num_constraints; ++c) {
465 const MPConstraintProto& ct = mp_model.constraint(c);
466 if ((std::isfinite(ct.lower_bound()) &&
467 std::abs(ct.lower_bound()) > threshold) ||
468 (std::isfinite(ct.upper_bound()) &&
469 std::abs(ct.upper_bound()) > threshold)) {
470 SOLVER_LOG(logger, "Constraint bounds are too large [", ct.lower_bound(),
471 ",", ct.upper_bound(), "]");
472 return false;
473 }
474 for (const double coeff : ct.coefficient()) {
475 if (std::abs(coeff) > threshold) {
476 SOLVER_LOG(logger, "Constraint coefficient is too large: ", coeff);
477 return false;
478 }
479 }
480 }
481
482 return true;
483}
484
485std::vector<double> DetectImpliedIntegers(MPModelProto* mp_model,
486 SolverLogger* logger) {
487 const int num_variables = mp_model->variable_size();
488 std::vector<double> var_scaling(num_variables, 1.0);
489
490 int initial_num_integers = 0;
491 for (int i = 0; i < num_variables; ++i) {
492 if (mp_model->variable(i).is_integer()) ++initial_num_integers;
493 }
494 VLOG(1) << "Initial num integers: " << initial_num_integers;
495
496 // We will process all equality constraints with exactly one non-integer.
497 const double tolerance = 1e-6;
498 std::vector<int> constraint_queue;
499
500 const int num_constraints = mp_model->constraint_size();
501 std::vector<int> constraint_to_num_non_integer(num_constraints, 0);
502 std::vector<std::vector<int>> var_to_constraints(num_variables);
503 for (int i = 0; i < num_constraints; ++i) {
504 const MPConstraintProto& mp_constraint = mp_model->constraint(i);
505
506 for (const int var : mp_constraint.var_index()) {
507 if (!mp_model->variable(var).is_integer()) {
508 var_to_constraints[var].push_back(i);
509 constraint_to_num_non_integer[i]++;
510 }
511 }
512 if (constraint_to_num_non_integer[i] == 1) {
513 constraint_queue.push_back(i);
514 }
515 }
516 VLOG(1) << "Initial constraint queue: " << constraint_queue.size() << " / "
517 << num_constraints;
518
519 int num_detected = 0;
520 double max_scaling = 0.0;
521 auto scale_and_mark_as_integer = [&](int var, double scaling) mutable {
522 CHECK_NE(var, -1);
523 CHECK(!mp_model->variable(var).is_integer());
524 CHECK_EQ(var_scaling[var], 1.0);
525 if (scaling != 1.0) {
526 VLOG(2) << "Scaled " << var << " by " << scaling;
527 }
528
529 ++num_detected;
530 max_scaling = std::max(max_scaling, scaling);
531
532 // Scale the variable right away and mark it as implied integer.
533 // Note that the constraints will be scaled later.
534 var_scaling[var] = scaling;
535 mp_model->mutable_variable(var)->set_is_integer(true);
536
537 // Update the queue of constraints with a single non-integer.
538 for (const int ct_index : var_to_constraints[var]) {
539 constraint_to_num_non_integer[ct_index]--;
540 if (constraint_to_num_non_integer[ct_index] == 1) {
541 constraint_queue.push_back(ct_index);
542 }
543 }
544 };
545
546 int num_fail_due_to_rhs = 0;
547 int num_fail_due_to_large_multiplier = 0;
548 int num_processed_constraints = 0;
549 while (!constraint_queue.empty()) {
550 const int top_ct_index = constraint_queue.back();
551 constraint_queue.pop_back();
552
553 // The non integer variable was already made integer by one other
554 // constraint.
555 if (constraint_to_num_non_integer[top_ct_index] == 0) continue;
556
557 // Ignore non-equality here.
558 const MPConstraintProto& ct = mp_model->constraint(top_ct_index);
559 if (ct.lower_bound() + tolerance < ct.upper_bound()) continue;
560
561 ++num_processed_constraints;
562
563 // This will be set to the unique non-integer term of this constraint.
564 int var = -1;
565 double var_coeff;
566
567 // We are looking for a "multiplier" so that the unique non-integer term
568 // in this constraint (i.e. var * var_coeff) times this multiplier is an
569 // integer.
570 //
571 // If this is set to zero or becomes too large, we fail to detect a new
572 // implied integer and ignore this constraint.
573 double multiplier = 1.0;
574 const double max_multiplier = 1e4;
575
576 for (int i = 0; i < ct.var_index().size(); ++i) {
577 if (!mp_model->variable(ct.var_index(i)).is_integer()) {
578 CHECK_EQ(var, -1);
579 var = ct.var_index(i);
580 var_coeff = ct.coefficient(i);
581 } else {
582 // This actually compute the smallest multiplier to make all other
583 // terms in the constraint integer.
584 const double coeff =
585 multiplier * ct.coefficient(i) / var_scaling[ct.var_index(i)];
586 multiplier *=
587 FindRationalFactor(coeff, /*limit=*/100, multiplier * tolerance);
588 if (multiplier == 0 || multiplier > max_multiplier) {
589 break;
590 }
591 }
592 }
593
594 if (multiplier == 0 || multiplier > max_multiplier) {
595 ++num_fail_due_to_large_multiplier;
596 continue;
597 }
598
599 // These "rhs" fail could be handled by shifting the variable.
600 const double rhs = ct.lower_bound();
601 if (std::abs(std::round(rhs * multiplier) - rhs * multiplier) >
602 tolerance * multiplier) {
603 ++num_fail_due_to_rhs;
604 continue;
605 }
606
607 // We want to multiply the variable so that it is integer. We know that
608 // coeff * multiplier is an integer, so we just multiply by that.
609 //
610 // But if a variable appear in more than one equality, we want to find the
611 // smallest integrality factor! See diameterc-msts-v40a100d5i.mps
612 // for an instance of this.
613 double best_scaling = std::abs(var_coeff * multiplier);
614 for (const int ct_index : var_to_constraints[var]) {
615 if (ct_index == top_ct_index) continue;
616 if (constraint_to_num_non_integer[ct_index] != 1) continue;
617
618 // Ignore non-equality here.
619 const MPConstraintProto& ct = mp_model->constraint(top_ct_index);
620 if (ct.lower_bound() + tolerance < ct.upper_bound()) continue;
621
622 const double multiplier = GetIntegralityMultiplier(
623 *mp_model, var_scaling, var, ct_index, tolerance);
624 if (multiplier != 0.0 && multiplier < best_scaling) {
625 best_scaling = multiplier;
626 }
627 }
628
629 scale_and_mark_as_integer(var, best_scaling);
630 }
631
632 // Process continuous variables that only appear as the unique non integer
633 // in a set of non-equality constraints.
634 //
635 // Note that turning to integer such variable cannot in turn trigger new
636 // integer detection, so there is no point doing that in a loop.
637 int num_in_inequalities = 0;
638 int num_to_be_handled = 0;
639 for (int var = 0; var < num_variables; ++var) {
640 if (mp_model->variable(var).is_integer()) continue;
641
642 // This should be presolved and not happen.
643 if (var_to_constraints[var].empty()) continue;
644
645 bool ok = true;
646 for (const int ct_index : var_to_constraints[var]) {
647 if (constraint_to_num_non_integer[ct_index] != 1) {
648 ok = false;
649 break;
650 }
651 }
652 if (!ok) continue;
653
654 std::vector<double> scaled_coeffs;
655 for (const int ct_index : var_to_constraints[var]) {
656 const double multiplier = GetIntegralityMultiplier(
657 *mp_model, var_scaling, var, ct_index, tolerance);
658 if (multiplier == 0.0) {
659 ok = false;
660 break;
661 }
662 scaled_coeffs.push_back(multiplier);
663 }
664 if (!ok) continue;
665
666 // The situation is a bit tricky here, we have a bunch of coeffs c_i, and we
667 // know that X * c_i can take integer value without changing the constraint
668 // i meaning.
669 //
670 // For now we take the min, and scale only if all c_i / min are integer.
671 double scaling = scaled_coeffs[0];
672 for (const double c : scaled_coeffs) {
673 scaling = std::min(scaling, c);
674 }
675 CHECK_GT(scaling, 0.0);
676 for (const double c : scaled_coeffs) {
677 const double fraction = c / scaling;
678 if (std::abs(std::round(fraction) - fraction) > tolerance) {
679 ok = false;
680 break;
681 }
682 }
683 if (!ok) {
684 // TODO(user): be smarter! we should be able to handle these cases.
685 ++num_to_be_handled;
686 continue;
687 }
688
689 // Tricky, we also need the bound of the scaled variable to be integer.
690 for (const double bound : {mp_model->variable(var).lower_bound(),
691 mp_model->variable(var).upper_bound()}) {
692 if (!std::isfinite(bound)) continue;
693 if (std::abs(std::round(bound * scaling) - bound * scaling) >
694 tolerance * scaling) {
695 ok = false;
696 break;
697 }
698 }
699 if (!ok) {
700 // TODO(user): If we scale more we migth be able to turn it into an
701 // integer.
702 ++num_to_be_handled;
703 continue;
704 }
705
706 ++num_in_inequalities;
707 scale_and_mark_as_integer(var, scaling);
708 }
709 VLOG(1) << "num_new_integer: " << num_detected
710 << " num_processed_constraints: " << num_processed_constraints
711 << " num_rhs_fail: " << num_fail_due_to_rhs
712 << " num_multiplier_fail: " << num_fail_due_to_large_multiplier;
713
714 if (num_to_be_handled > 0) {
715 SOLVER_LOG(logger, "Missed ", num_to_be_handled,
716 " potential implied integer.");
717 }
718
719 const int num_integers = initial_num_integers + num_detected;
720 SOLVER_LOG(logger, "Num integers: ", num_integers, "/", num_variables,
721 " (implied: ", num_detected,
722 " in_inequalities: ", num_in_inequalities,
723 " max_scaling: ", max_scaling, ")",
724 (num_integers == num_variables ? " [IP] " : " [MIP] "));
725
726 ApplyVarScaling(var_scaling, mp_model);
727 return var_scaling;
728}
729
731 const MPConstraintProto& mp_constraint, CpModelProto* cp_model) {
732 absl::Span<const IntegerVariableProto* const> var_domains =
733 absl::MakeSpan(cp_model->variables());
734 const absl::Status status = ScaleAndAddConstraint(
735 absl::MakeConstSpan(mp_constraint.var_index()),
736 absl::MakeConstSpan(mp_constraint.coefficient()),
737 mp_constraint.lower_bound(), mp_constraint.upper_bound(),
738 mp_constraint.name(), var_domains, cp_model->add_constraints());
739 if (!status.ok()) {
740 return absl::InvalidArgumentError(
741 absl::StrCat("Scaling factor of zero while scaling constraint: ",
742 ProtobufShortDebugString(mp_constraint)));
743 }
744 return absl::OkStatus();
745}
746
748 absl::Span<const int> vars, absl::Span<const double> coeffs,
749 double lower_bound, double upper_bound, absl::string_view name,
750 absl::Span<const IntegerVariableProto* const> var_domains,
751 ConstraintProto* constraint) {
752 if (keep_names && !name.empty()) constraint->set_name(name);
753 auto* arg = constraint->mutable_linear();
754
755 // First scale the coefficients of the constraints so that the constraint
756 // sum can always be computed without integer overflow.
757 var_indices.clear();
758 coefficients.clear();
759 lower_bounds.clear();
760 upper_bounds.clear();
761 const int num_coeffs = vars.size();
762 for (int i = 0; i < num_coeffs; ++i) {
763 const auto& var_proto = var_domains[vars[i]];
764 const int64_t lb = var_proto->domain(0);
765 const int64_t ub = var_proto->domain(var_proto->domain_size() - 1);
766 if (lb == 0 && ub == 0) continue;
767
768 const double coeff = coeffs[i];
769 if (coeff == 0.0) continue;
770
771 var_indices.push_back(vars[i]);
772 coefficients.push_back(coeff);
773 lower_bounds.push_back(lb);
774 upper_bounds.push_back(ub);
775 }
776
777 double relative_coeff_error;
778 double scaled_sum_error;
779 const double scaling_factor = FindBestScalingAndComputeErrors(
781 wanted_precision, &relative_coeff_error, &scaled_sum_error);
782 if (scaling_factor == 0.0) {
783 return absl::InvalidArgumentError(
784 "Scaling factor of zero while scaling constraint");
785 }
786
787 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
789 std::max(relative_coeff_error, max_relative_coeff_error);
790 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
791 min_scaling_factor = std::min(scaling_factor / gcd, min_scaling_factor);
792
793 for (int i = 0; i < coefficients.size(); ++i) {
794 const double scaled_value = coefficients[i] * scaling_factor;
795 const int64_t value = static_cast<int64_t>(std::round(scaled_value)) / gcd;
796 if (value != 0) {
797 arg->add_vars(var_indices[i]);
798 arg->add_coeffs(value);
799 }
800 }
802 std::max(max_absolute_rhs_error, scaled_sum_error / scaling_factor);
803
804 // We relax the constraint bound by the absolute value of the wanted_precision
805 // before scaling. Note that this is needed because now that the scaled
806 // constraint activity is integer, we will floor/ceil these bound.
807 //
808 // It might make more sense to use a relative precision here for large bounds,
809 // but absolute is usually what is used in the MIP world. Also if the problem
810 // was a pure integer problem, and a user asked for sum == 10k, we want to
811 // stay exact here.
812 const Fractional lb = lower_bound - wanted_precision;
813 const Fractional ub = upper_bound + wanted_precision;
814
815 // Add the constraint bounds. Because we are sure the scaled constraint fit
816 // on an int64_t, if the scaled bounds are too large, the constraint is either
817 // always true or always false.
818 const Fractional scaled_lb = std::ceil(lb * scaling_factor);
819 if (lb == kInfinity || scaled_lb >= std::numeric_limits<int64_t>::max()) {
820 // Corner case: infeasible model.
821 arg->add_domain(std::numeric_limits<int64_t>::max());
822 } else if (lb == -kInfinity ||
823 scaled_lb <= std::numeric_limits<int64_t>::min()) {
824 arg->add_domain(std::numeric_limits<int64_t>::min());
825 } else {
826 arg->add_domain(CeilRatio(IntegerValue(static_cast<int64_t>(scaled_lb)),
827 IntegerValue(gcd))
828 .value());
829 }
830
831 const Fractional scaled_ub = std::floor(ub * scaling_factor);
832 if (ub == -kInfinity || scaled_ub <= std::numeric_limits<int64_t>::min()) {
833 // Corner case: infeasible model.
834 arg->add_domain(std::numeric_limits<int64_t>::min());
835 } else if (ub == kInfinity ||
836 scaled_ub >= std::numeric_limits<int64_t>::max()) {
837 arg->add_domain(std::numeric_limits<int64_t>::max());
838 } else {
839 arg->add_domain(FloorRatio(IntegerValue(static_cast<int64_t>(scaled_ub)),
840 IntegerValue(gcd))
841 .value());
842 }
843
844 return absl::OkStatus();
845}
846
847namespace {
848
849bool ConstraintIsAlwaysTrue(const MPConstraintProto& mp_constraint) {
850 return mp_constraint.lower_bound() == -kInfinity &&
851 mp_constraint.upper_bound() == kInfinity;
852}
853
854// TODO(user): unit test this.
855double FindFractionalScaling(absl::Span<const double> coefficients,
856 double tolerance) {
857 double multiplier = 1.0;
858 for (const double coeff : coefficients) {
859 multiplier *= FindRationalFactor(multiplier * coeff, /*limit=*/1e8,
860 multiplier * tolerance);
861 if (multiplier == 0.0) break;
862 }
863 return multiplier;
864}
865
866} // namespace
867
869 absl::Span<const double> coefficients,
870 absl::Span<const double> lower_bounds,
871 absl::Span<const double> upper_bounds, int64_t max_absolute_activity,
872 double wanted_absolute_activity_precision, double* relative_coeff_error,
873 double* scaled_sum_error) {
874 // Starts by computing the highest possible factor.
875 double scaling_factor = GetBestScalingOfDoublesToInt64(
876 coefficients, lower_bounds, upper_bounds, max_absolute_activity);
877 if (scaling_factor == 0.0) return scaling_factor;
878
879 // Returns the smallest factor of the form 2^i that gives us a relative sum
880 // error of wanted_absolute_activity_precision and still make sure we will
881 // have no integer overflow.
882 //
883 // Important: the loop is written in such a way that ComputeScalingErrors()
884 // is called on the last factor.
885 //
886 // TODO(user): Make this faster.
887 double x = std::min(scaling_factor, 1.0);
888 for (; x <= scaling_factor; x *= 2) {
889 ComputeScalingErrors(coefficients, lower_bounds, upper_bounds, x,
890 relative_coeff_error, scaled_sum_error);
891 if (*scaled_sum_error < wanted_absolute_activity_precision * x) break;
892
893 // This could happen if we always have enough precision.
894 if (x == scaling_factor) break;
895 }
896 scaling_factor = x;
897 DCHECK(std::isfinite(scaling_factor));
898
899 // Because we deal with an approximate input, scaling with a power of 2 might
900 // not be the best choice. It is also possible user used rational coeff and
901 // then converted them to double (1/2, 1/3, 4/5, etc...). This scaling will
902 // recover such rational input and might result in a smaller overall
903 // coefficient which is good.
904 //
905 // Note that if our current precisions is already above the requested one,
906 // we choose integer scaling if we get a better precision.
907 const double integer_factor = FindFractionalScaling(coefficients, 1e-8);
908 DCHECK(std::isfinite(integer_factor));
909 if (integer_factor != 0 && integer_factor < scaling_factor) {
910 double local_relative_coeff_error;
911 double local_scaled_sum_error;
912 ComputeScalingErrors(coefficients, lower_bounds, upper_bounds,
913 integer_factor, &local_relative_coeff_error,
914 &local_scaled_sum_error);
915 if (local_scaled_sum_error * scaling_factor <=
916 *scaled_sum_error * integer_factor ||
917 local_scaled_sum_error <
918 wanted_absolute_activity_precision * integer_factor) {
919 *relative_coeff_error = local_relative_coeff_error;
920 *scaled_sum_error = local_scaled_sum_error;
921 scaling_factor = integer_factor;
922 }
923 }
924
925 DCHECK(std::isfinite(scaling_factor));
926 return scaling_factor;
927}
928
930 const MPModelProto& mp_model,
931 CpModelProto* cp_model,
932 SolverLogger* logger) {
933 CHECK(cp_model != nullptr);
934 cp_model->Clear();
935 cp_model->set_name(mp_model.name());
936
937 // To make sure we cannot have integer overflow, we use this bound for any
938 // unbounded variable.
939 //
940 // TODO(user): This could be made larger if needed, so be smarter if we have
941 // MIP problem that we cannot "convert" because of this. Note however than we
942 // cannot go that much further because we need to make sure we will not run
943 // into overflow if we add a big linear combination of such variables. It
944 // should always be possible for a user to scale its problem so that all
945 // relevant quantities are a couple of millions. A LP/MIP solver have a
946 // similar condition in disguise because problem with a difference of more
947 // than 6 magnitudes between the variable values will likely run into numeric
948 // trouble.
949 const int64_t kMaxVariableBound =
950 static_cast<int64_t>(params.mip_max_bound());
951
952 int num_truncated_bounds = 0;
953 int num_small_domains = 0;
954 const int64_t kSmallDomainSize = 1000;
955 const double kWantedPrecision = params.mip_wanted_precision();
956
957 // Add the variables.
958 const int num_variables = mp_model.variable_size();
959 const bool keep_names = !params.ignore_names();
960 for (int i = 0; i < num_variables; ++i) {
961 const MPVariableProto& mp_var = mp_model.variable(i);
962 IntegerVariableProto* cp_var = cp_model->add_variables();
963 if (keep_names) cp_var->set_name(mp_var.name());
964
965 // Deal with the corner case of a domain far away from zero.
966 //
967 // TODO(user): We could avoid these cases by shifting the domain of
968 // all variables to contain zero. This should also lead to a better scaling,
969 // but it has some complications with integer variables and require some
970 // post-solve.
971 if (mp_var.lower_bound() > static_cast<double>(kMaxVariableBound) ||
972 mp_var.upper_bound() < static_cast<double>(-kMaxVariableBound)) {
973 SOLVER_LOG(logger, "Error: variable ", mp_var,
974 " is outside [-mip_max_bound..mip_max_bound]");
975 return false;
976 }
977
978 // Note that we must process the lower bound first.
979 for (const bool lower : {true, false}) {
980 const double bound = lower ? mp_var.lower_bound() : mp_var.upper_bound();
981 if (std::abs(bound) + kWantedPrecision >=
982 static_cast<double>(kMaxVariableBound)) {
983 ++num_truncated_bounds;
984 cp_var->add_domain(bound < 0 ? -kMaxVariableBound : kMaxVariableBound);
985 continue;
986 }
987
988 // Note that the cast is "perfect" because we forbid large values.
989 cp_var->add_domain(
990 static_cast<int64_t>(lower ? std::ceil(bound - kWantedPrecision)
991 : std::floor(bound + kWantedPrecision)));
992 }
993
994 if (cp_var->domain(0) > cp_var->domain(1)) {
995 LOG(WARNING) << "Variable #" << i << " cannot take integer value. "
996 << ProtobufShortDebugString(mp_var);
997 return false;
998 }
999
1000 // Notify if a continuous variable has a small domain as this is likely to
1001 // make an all integer solution far from a continuous one.
1002 if (!mp_var.is_integer()) {
1003 const double diff = mp_var.upper_bound() - mp_var.lower_bound();
1004 if (diff > kWantedPrecision && diff < kSmallDomainSize) {
1005 ++num_small_domains;
1006 }
1007 }
1008 }
1009
1010 if (num_truncated_bounds > 0) {
1011 SOLVER_LOG(logger, "Warning: ", num_truncated_bounds,
1012 " bounds were truncated to ", kMaxVariableBound, ".");
1013 }
1014 if (num_small_domains > 0) {
1015 SOLVER_LOG(logger, "Warning: ", num_small_domains,
1016 " continuous variable domain with fewer than ", kSmallDomainSize,
1017 " values.");
1018 }
1019
1020 ConstraintScaler scaler;
1021 const int64_t kScalingTarget = int64_t{1}
1022 << params.mip_max_activity_exponent();
1023 scaler.wanted_precision = kWantedPrecision;
1024 scaler.scaling_target = kScalingTarget;
1025 scaler.keep_names = keep_names;
1026
1027 // Add the constraints. We scale each of them individually.
1028 for (const MPConstraintProto& mp_constraint : mp_model.constraint()) {
1029 if (ConstraintIsAlwaysTrue(mp_constraint)) continue;
1030
1031 const absl::Status status =
1032 scaler.ScaleAndAddConstraint(mp_constraint, cp_model);
1033 if (!status.ok()) {
1034 SOLVER_LOG(logger, "Error while scaling constraint. ", status.message());
1035 return false;
1036 }
1037 }
1038 for (const MPGeneralConstraintProto& general_constraint :
1039 mp_model.general_constraint()) {
1040 switch (general_constraint.general_constraint_case()) {
1042 const auto& indicator_constraint =
1043 general_constraint.indicator_constraint();
1044 const MPConstraintProto& mp_constraint =
1045 indicator_constraint.constraint();
1046 if (ConstraintIsAlwaysTrue(mp_constraint)) continue;
1047
1048 const int new_ct_index = cp_model->constraints().size();
1049 const absl::Status status =
1050 scaler.ScaleAndAddConstraint(mp_constraint, cp_model);
1051 if (!status.ok()) {
1052 SOLVER_LOG(logger, "Error while scaling constraint. ",
1053 status.message());
1054 return false;
1055 }
1056
1057 // Add the indicator.
1058 const int var = indicator_constraint.var_index();
1059 const int value = indicator_constraint.var_value();
1060 cp_model->mutable_constraints(new_ct_index)
1061 ->add_enforcement_literal(value == 1 ? var : NegatedRef(var));
1062 break;
1063 }
1065 const auto& and_constraint = general_constraint.and_constraint();
1066 absl::string_view name = general_constraint.name();
1067
1068 ConstraintProto* ct_pos = cp_model->add_constraints();
1069 ct_pos->set_name(name.empty() ? "" : absl::StrCat(name, "_pos"));
1070 ct_pos->add_enforcement_literal(and_constraint.resultant_var_index());
1071 *ct_pos->mutable_bool_and()->mutable_literals() =
1072 and_constraint.var_index();
1073
1074 ConstraintProto* ct_neg = cp_model->add_constraints();
1075 ct_neg->set_name(name.empty() ? "" : absl::StrCat(name, "_neg"));
1077 NegatedRef(and_constraint.resultant_var_index()));
1078 for (const int var_index : and_constraint.var_index()) {
1079 ct_neg->mutable_bool_or()->add_literals(NegatedRef(var_index));
1080 }
1081 break;
1082 }
1084 const auto& or_constraint = general_constraint.or_constraint();
1085 absl::string_view name = general_constraint.name();
1086
1087 ConstraintProto* ct_pos = cp_model->add_constraints();
1088 ct_pos->set_name(name.empty() ? "" : absl::StrCat(name, "_pos"));
1089 ct_pos->add_enforcement_literal(or_constraint.resultant_var_index());
1090 *ct_pos->mutable_bool_or()->mutable_literals() =
1091 or_constraint.var_index();
1092
1093 ConstraintProto* ct_neg = cp_model->add_constraints();
1094 ct_neg->set_name(name.empty() ? "" : absl::StrCat(name, "_neg"));
1096 NegatedRef(or_constraint.resultant_var_index()));
1097 for (const int var_index : or_constraint.var_index()) {
1098 ct_neg->mutable_bool_and()->add_literals(NegatedRef(var_index));
1099 }
1100 break;
1101 }
1102 default:
1103 LOG(ERROR) << "Can't convert general constraints of type "
1104 << general_constraint.general_constraint_case()
1105 << " to CpModelProto.";
1106 return false;
1107 }
1108 }
1109
1110 // Display the error/scaling on the constraints.
1111 SOLVER_LOG(logger, "Maximum constraint coefficient relative error: ",
1113 SOLVER_LOG(logger, "Maximum constraint worst-case activity error: ",
1116 ? " [Potentially IMPRECISE]"
1117 : ""));
1118 SOLVER_LOG(logger, "Constraint scaling factor range: [",
1119 scaler.min_scaling_factor, ", ", scaler.max_scaling_factor, "]");
1120
1121 // Since cp_model support a floating point objective, we use that. This will
1122 // allow us to scale the objective a bit later so we can potentially do more
1123 // domain reduction first.
1124 auto* float_objective = cp_model->mutable_floating_point_objective();
1125 float_objective->set_maximize(mp_model.maximize());
1126 float_objective->set_offset(mp_model.objective_offset());
1127 for (int i = 0; i < num_variables; ++i) {
1128 const MPVariableProto& mp_var = mp_model.variable(i);
1129 if (mp_var.objective_coefficient() != 0.0) {
1130 float_objective->add_vars(i);
1131 float_objective->add_coeffs(mp_var.objective_coefficient());
1132 }
1133 }
1134
1135 // If the objective is fixed to zero, we consider there is none.
1136 if (float_objective->offset() == 0 && float_objective->vars().empty()) {
1138 }
1139 return true;
1140}
1141
1142namespace {
1143
1144int AppendSumOfLiteral(absl::Span<const int> literals, MPConstraintProto* out) {
1145 int shift = 0;
1146 for (const int ref : literals) {
1147 if (ref >= 0) {
1148 out->add_coefficient(1);
1149 out->add_var_index(ref);
1150 } else {
1151 out->add_coefficient(-1);
1152 out->add_var_index(PositiveRef(ref));
1153 ++shift;
1154 }
1155 }
1156 return shift;
1157}
1158
1159} // namespace
1160
1162 MPModelProto* output) {
1163 CHECK(output != nullptr);
1164 output->Clear();
1165
1166 // Copy variables.
1167 const int num_vars = input.variables().size();
1168 for (int v = 0; v < num_vars; ++v) {
1169 if (input.variables(v).domain().size() != 2) {
1170 VLOG(1) << "Cannot convert "
1171 << ProtobufShortDebugString(input.variables(v));
1172 return false;
1173 }
1174
1175 MPVariableProto* var = output->add_variable();
1176 var->set_is_integer(true);
1177 var->set_lower_bound(input.variables(v).domain(0));
1178 var->set_upper_bound(input.variables(v).domain(1));
1179 }
1180
1181 // Copy integer or float objective.
1182 if (input.has_objective()) {
1183 double factor = input.objective().scaling_factor();
1184 if (factor == 0.0) factor = 1.0;
1185 const int num_terms = input.objective().vars().size();
1186 for (int i = 0; i < num_terms; ++i) {
1187 const int var = input.objective().vars(i);
1188 if (var < 0) return false;
1189 CHECK_EQ(output->variable(var).objective_coefficient(), 0.0);
1191 factor * input.objective().coeffs(i));
1192 }
1193 output->set_objective_offset(factor * input.objective().offset());
1194 if (factor < 0) {
1195 output->set_maximize(true);
1196 }
1197 } else if (input.has_floating_point_objective()) {
1198 const int num_terms = input.floating_point_objective().vars().size();
1199 for (int i = 0; i < num_terms; ++i) {
1200 const int var = input.floating_point_objective().vars(i);
1201 if (var < 0) return false;
1202 CHECK_EQ(output->variable(var).objective_coefficient(), 0.0);
1204 input.floating_point_objective().coeffs(i));
1205 }
1206 output->set_objective_offset(input.floating_point_objective().offset());
1207 }
1208 if (output->objective_offset() == 0.0) {
1209 output->clear_objective_offset();
1210 }
1211
1212 // Copy constraint.
1213 const int num_constraints = input.constraints().size();
1214 std::vector<int> tmp_literals;
1215 for (int c = 0; c < num_constraints; ++c) {
1216 const ConstraintProto& ct = input.constraints(c);
1217 if (!ct.enforcement_literal().empty() &&
1220 // TODO(user): Support more constraints with enforcement.
1221 VLOG(1) << "Cannot convert constraint: " << ProtobufDebugString(ct);
1222 return false;
1223 }
1224 switch (ct.constraint_case()) {
1226 MPConstraintProto* out = output->add_constraint();
1227 const int shift = AppendSumOfLiteral(ct.exactly_one().literals(), out);
1228 out->set_lower_bound(1 - shift);
1229 out->set_upper_bound(1 - shift);
1230 break;
1231 }
1233 MPConstraintProto* out = output->add_constraint();
1234 const int shift = AppendSumOfLiteral(ct.at_most_one().literals(), out);
1236 out->set_upper_bound(1 - shift);
1237 break;
1238 }
1240 MPConstraintProto* out = output->add_constraint();
1241 const int shift = AppendSumOfLiteral(ct.bool_or().literals(), out);
1242 out->set_lower_bound(1 - shift);
1244 break;
1245 }
1247 tmp_literals.clear();
1248 for (const int ref : ct.enforcement_literal()) {
1249 tmp_literals.push_back(NegatedRef(ref));
1250 }
1251 for (const int ref : ct.bool_and().literals()) {
1252 MPConstraintProto* out = output->add_constraint();
1253 tmp_literals.push_back(ref);
1254 const int shift = AppendSumOfLiteral(tmp_literals, out);
1255 out->set_lower_bound(1 - shift);
1257 tmp_literals.pop_back();
1258 }
1259 break;
1260 }
1262 if (ct.linear().domain().size() != 2) {
1263 VLOG(1) << "Cannot convert constraint: "
1265 return false;
1266 }
1267
1268 // Compute min/max activity.
1269 int64_t min_activity = 0;
1270 int64_t max_activity = 0;
1271 const int num_terms = ct.linear().vars().size();
1272 for (int i = 0; i < num_terms; ++i) {
1273 const int var = ct.linear().vars(i);
1274 if (var < 0) return false;
1275 DCHECK_EQ(input.variables(var).domain().size(), 2);
1276 const int64_t coeff = ct.linear().coeffs(i);
1277 if (coeff > 0) {
1278 min_activity += coeff * input.variables(var).domain(0);
1279 max_activity += coeff * input.variables(var).domain(1);
1280 } else {
1281 min_activity += coeff * input.variables(var).domain(1);
1282 max_activity += coeff * input.variables(var).domain(0);
1283 }
1284 }
1285
1286 if (ct.enforcement_literal().empty()) {
1287 MPConstraintProto* out_ct = output->add_constraint();
1288 if (min_activity < ct.linear().domain(0)) {
1289 out_ct->set_lower_bound(ct.linear().domain(0));
1290 } else {
1291 out_ct->set_lower_bound(-kInfinity);
1292 }
1293 if (max_activity > ct.linear().domain(1)) {
1294 out_ct->set_upper_bound(ct.linear().domain(1));
1295 } else {
1296 out_ct->set_upper_bound(kInfinity);
1297 }
1298 for (int i = 0; i < num_terms; ++i) {
1299 const int var = ct.linear().vars(i);
1300 if (var < 0) return false;
1301 out_ct->add_var_index(var);
1302 out_ct->add_coefficient(ct.linear().coeffs(i));
1303 }
1304 break;
1305 }
1306
1307 std::vector<MPConstraintProto*> out_cts;
1308 if (ct.linear().domain(1) < max_activity) {
1309 MPConstraintProto* high_out_ct = output->add_constraint();
1310 high_out_ct->set_lower_bound(-kInfinity);
1311 int64_t ub = ct.linear().domain(1);
1312 const int64_t coeff = max_activity - ct.linear().domain(1);
1313 for (const int lit : ct.enforcement_literal()) {
1314 if (RefIsPositive(lit)) {
1315 // term <= ub + coeff * (1 - enf);
1316 high_out_ct->add_var_index(lit);
1317 high_out_ct->add_coefficient(coeff);
1318 ub += coeff;
1319 } else {
1320 high_out_ct->add_var_index(PositiveRef(lit));
1321 high_out_ct->add_coefficient(-coeff);
1322 }
1323 }
1324 high_out_ct->set_upper_bound(ub);
1325 out_cts.push_back(high_out_ct);
1326 }
1327 if (ct.linear().domain(0) > min_activity) {
1328 MPConstraintProto* low_out_ct = output->add_constraint();
1329 low_out_ct->set_upper_bound(kInfinity);
1330 int64_t lb = ct.linear().domain(0);
1331 int64_t coeff = min_activity - ct.linear().domain(0);
1332 for (const int lit : ct.enforcement_literal()) {
1333 if (RefIsPositive(lit)) {
1334 // term >= lb + coeff * (1 - enf)
1335 low_out_ct->add_var_index(lit);
1336 low_out_ct->add_coefficient(coeff);
1337 lb += coeff;
1338 } else {
1339 low_out_ct->add_var_index(PositiveRef(lit));
1340 low_out_ct->add_coefficient(-coeff);
1341 }
1342 }
1343 low_out_ct->set_lower_bound(lb);
1344 out_cts.push_back(low_out_ct);
1345 }
1346 for (MPConstraintProto* out_ct : out_cts) {
1347 for (int i = 0; i < num_terms; ++i) {
1348 const int var = ct.linear().vars(i);
1349 if (var < 0) return false;
1350 out_ct->add_var_index(var);
1351 out_ct->add_coefficient(ct.linear().coeffs(i));
1352 }
1353 }
1354 break;
1355 }
1356 default:
1357 VLOG(1) << "Cannot convert constraint: " << ProtobufDebugString(ct);
1358 return false;
1359 }
1360 }
1361
1362 return true;
1363}
1364
1366 absl::Span<const std::pair<int, double>> objective,
1367 double objective_offset, bool maximize,
1368 CpModelProto* cp_model, SolverLogger* logger) {
1369 // Make sure the objective is currently empty.
1370 cp_model->clear_objective();
1371
1372 // We filter constant terms and compute some needed quantities.
1373 std::vector<int> var_indices;
1374 std::vector<double> coefficients;
1375 std::vector<double> lower_bounds;
1376 std::vector<double> upper_bounds;
1377 double min_magnitude = std::numeric_limits<double>::infinity();
1378 double max_magnitude = 0.0;
1379 double l1_norm = 0.0;
1380 for (const auto& [var, coeff] : objective) {
1381 const auto& var_proto = cp_model->variables(var);
1382 const int64_t lb = var_proto.domain(0);
1383 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
1384 if (lb == ub) {
1385 if (lb != 0) objective_offset += lb * coeff;
1386 continue;
1387 }
1388 var_indices.push_back(var);
1389 coefficients.push_back(coeff);
1390 lower_bounds.push_back(lb);
1391 upper_bounds.push_back(ub);
1392
1393 min_magnitude = std::min(min_magnitude, std::abs(coeff));
1394 max_magnitude = std::max(max_magnitude, std::abs(coeff));
1395 l1_norm += std::abs(coeff);
1396 }
1397
1398 if (coefficients.empty() && objective_offset == 0.0) return true;
1399
1400 if (!coefficients.empty()) {
1401 const double average_magnitude =
1402 l1_norm / static_cast<double>(coefficients.size());
1403 SOLVER_LOG(logger, "[Scaling] Floating point objective has ",
1404 coefficients.size(), " terms with magnitude in [", min_magnitude,
1405 ", ", max_magnitude, "] average = ", average_magnitude);
1406 }
1407
1408 // These are the parameters used for scaling the objective.
1409 const int64_t max_absolute_activity = int64_t{1}
1410 << params.mip_max_activity_exponent();
1411 const double wanted_precision =
1412 std::max(params.mip_wanted_precision(), params.absolute_gap_limit());
1413
1414 double relative_coeff_error;
1415 double scaled_sum_error;
1416 const double scaling_factor = FindBestScalingAndComputeErrors(
1417 coefficients, lower_bounds, upper_bounds, max_absolute_activity,
1418 wanted_precision, &relative_coeff_error, &scaled_sum_error);
1419 if (scaling_factor == 0.0) {
1420 LOG(ERROR) << "Scaling factor of zero while scaling objective! This "
1421 "likely indicate an infinite coefficient in the objective.";
1422 return false;
1423 }
1424
1425 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
1426
1427 // Display the objective error/scaling.
1428 SOLVER_LOG(logger, "[Scaling] Objective coefficient relative error: ",
1429 relative_coeff_error);
1430 SOLVER_LOG(logger, "[Scaling] Objective worst-case absolute error: ",
1431 scaled_sum_error / scaling_factor);
1432 SOLVER_LOG(logger,
1433 "[Scaling] Objective scaling factor: ", scaling_factor / gcd);
1434
1435 if (scaled_sum_error / scaling_factor > wanted_precision) {
1436 SOLVER_LOG(logger,
1437 "[Scaling] Warning: the worst-case absolute error is greater "
1438 "than the wanted precision (",
1439 wanted_precision,
1440 "). Try to increase mip_max_activity_exponent (default = ",
1442 ") or reduced your variables range and/or objective "
1443 "coefficient. We will continue the solve, but the final "
1444 "objective value might be off.");
1445 }
1446
1447 // Note that here we set the scaling factor for the inverse operation of
1448 // getting the "true" objective value from the scaled one. Hence the
1449 // inverse.
1450 auto* objective_proto = cp_model->mutable_objective();
1451 const int64_t mult = maximize ? -1 : 1;
1452 objective_proto->set_offset(objective_offset * scaling_factor / gcd * mult);
1453 objective_proto->set_scaling_factor(1.0 / scaling_factor * gcd * mult);
1454 for (int i = 0; i < coefficients.size(); ++i) {
1455 const int64_t value =
1456 static_cast<int64_t>(std::round(coefficients[i] * scaling_factor)) /
1457 gcd;
1458 if (value != 0) {
1459 objective_proto->add_vars(var_indices[i]);
1460 objective_proto->add_coeffs(value * mult);
1461 }
1462 }
1463
1464 if (scaled_sum_error == 0.0) {
1465 objective_proto->set_scaling_was_exact(true);
1466 }
1467
1468 return true;
1469}
1470
1472 LinearBooleanProblem* problem) {
1473 CHECK(problem != nullptr);
1474 problem->Clear();
1475 problem->set_name(mp_model.name());
1476 const int num_variables = mp_model.variable_size();
1477 problem->set_num_variables(num_variables);
1478
1479 // Test if the variables are binary variables.
1480 // Add constraints for the fixed variables.
1481 for (int var_id(0); var_id < num_variables; ++var_id) {
1482 const MPVariableProto& mp_var = mp_model.variable(var_id);
1483 problem->add_var_names(mp_var.name());
1484
1485 // This will be changed to false as soon as we detect the variable to be
1486 // non-binary. This is done this way so we can display a nice error message
1487 // before aborting the function and returning false.
1488 bool is_binary = mp_var.is_integer();
1489
1490 const Fractional lb = mp_var.lower_bound();
1491 const Fractional ub = mp_var.upper_bound();
1492 if (lb <= -1.0) is_binary = false;
1493 if (ub >= 2.0) is_binary = false;
1494 if (is_binary) {
1495 // 4 cases.
1496 if (lb <= 0.0 && ub >= 1.0) {
1497 // Binary variable. Ok.
1498 } else if (lb <= 1.0 && ub >= 1.0) {
1499 // Fixed variable at 1.
1500 LinearBooleanConstraint* constraint = problem->add_constraints();
1501 constraint->set_lower_bound(1);
1502 constraint->set_upper_bound(1);
1503 constraint->add_literals(var_id + 1);
1504 constraint->add_coefficients(1);
1505 } else if (lb <= 0.0 && ub >= 0.0) {
1506 // Fixed variable at 0.
1507 LinearBooleanConstraint* constraint = problem->add_constraints();
1508 constraint->set_lower_bound(0);
1509 constraint->set_upper_bound(0);
1510 constraint->add_literals(var_id + 1);
1511 constraint->add_coefficients(1);
1512 } else {
1513 // No possible integer value!
1514 is_binary = false;
1515 }
1516 }
1517
1518 // Abort if the variable is not binary.
1519 if (!is_binary) {
1520 LOG(WARNING) << "The variable #" << var_id << " with name "
1521 << mp_var.name() << " is not binary. "
1522 << "lb: " << lb << " ub: " << ub;
1523 return false;
1524 }
1525 }
1526
1527 // Variables needed to scale the double coefficients into int64_t.
1528 const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
1529 double max_relative_error = 0.0;
1530 double max_bound_error = 0.0;
1531 double max_scaling_factor = 0.0;
1532 double relative_error = 0.0;
1533 double scaling_factor = 0.0;
1534 std::vector<double> coefficients;
1535
1536 // Add all constraints.
1537 for (const MPConstraintProto& mp_constraint : mp_model.constraint()) {
1538 LinearBooleanConstraint* constraint = problem->add_constraints();
1539 constraint->set_name(mp_constraint.name());
1540
1541 // First scale the coefficients of the constraints.
1542 coefficients.clear();
1543 const int num_coeffs = mp_constraint.coefficient_size();
1544 for (int i = 0; i < num_coeffs; ++i) {
1545 coefficients.push_back(mp_constraint.coefficient(i));
1546 }
1547 GetBestScalingOfDoublesToInt64(coefficients, kInt64Max, &scaling_factor,
1548 &relative_error);
1549 const int64_t gcd =
1550 ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
1551 max_relative_error = std::max(relative_error, max_relative_error);
1552 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
1553
1554 double bound_error = 0.0;
1555 for (int i = 0; i < num_coeffs; ++i) {
1556 const double scaled_value = mp_constraint.coefficient(i) * scaling_factor;
1557 bound_error += std::abs(round(scaled_value) - scaled_value);
1558 const int64_t value = static_cast<int64_t>(round(scaled_value)) / gcd;
1559 if (value != 0) {
1560 constraint->add_literals(mp_constraint.var_index(i) + 1);
1561 constraint->add_coefficients(value);
1562 }
1563 }
1564 max_bound_error = std::max(max_bound_error, bound_error);
1565
1566 // Add the bounds. Note that we do not pass them to
1567 // GetBestScalingOfDoublesToInt64() because we know that the sum of absolute
1568 // coefficients of the constraint fit on an int64_t. If one of the scaled
1569 // bound overflows, we don't care by how much because in this case the
1570 // constraint is just trivial or unsatisfiable.
1571 const Fractional lb = mp_constraint.lower_bound();
1572 if (lb != -kInfinity) {
1573 if (lb * scaling_factor > static_cast<double>(kInt64Max)) {
1574 LOG(WARNING) << "A constraint is trivially unsatisfiable.";
1575 return false;
1576 }
1577 if (lb * scaling_factor > -static_cast<double>(kInt64Max)) {
1578 // Otherwise, the constraint is not needed.
1579 constraint->set_lower_bound(
1580 static_cast<int64_t>(round(lb * scaling_factor - bound_error)) /
1581 gcd);
1582 }
1583 }
1584 const Fractional ub = mp_constraint.upper_bound();
1585 if (ub != kInfinity) {
1586 if (ub * scaling_factor < -static_cast<double>(kInt64Max)) {
1587 LOG(WARNING) << "A constraint is trivially unsatisfiable.";
1588 return false;
1589 }
1590 if (ub * scaling_factor < static_cast<double>(kInt64Max)) {
1591 // Otherwise, the constraint is not needed.
1592 constraint->set_upper_bound(
1593 static_cast<int64_t>(round(ub * scaling_factor + bound_error)) /
1594 gcd);
1595 }
1596 }
1597 }
1598
1599 // Display the error/scaling without taking into account the objective first.
1600 LOG(INFO) << "Maximum constraint relative error: " << max_relative_error;
1601 LOG(INFO) << "Maximum constraint bound error: " << max_bound_error;
1602 LOG(INFO) << "Maximum constraint scaling factor: " << max_scaling_factor;
1603
1604 // Add the objective.
1605 coefficients.clear();
1606 for (int var_id = 0; var_id < num_variables; ++var_id) {
1607 const MPVariableProto& mp_var = mp_model.variable(var_id);
1608 coefficients.push_back(mp_var.objective_coefficient());
1609 }
1610 GetBestScalingOfDoublesToInt64(coefficients, kInt64Max, &scaling_factor,
1611 &relative_error);
1612 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
1613 max_relative_error = std::max(relative_error, max_relative_error);
1614
1615 // Display the objective error/scaling.
1616 LOG(INFO) << "objective relative error: " << relative_error;
1617 LOG(INFO) << "objective scaling factor: " << scaling_factor / gcd;
1618
1619 LinearObjective* objective = problem->mutable_objective();
1620 objective->set_offset(mp_model.objective_offset() * scaling_factor / gcd);
1621
1622 // Note that here we set the scaling factor for the inverse operation of
1623 // getting the "true" objective value from the scaled one. Hence the inverse.
1624 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
1625 for (int var_id = 0; var_id < num_variables; ++var_id) {
1626 const MPVariableProto& mp_var = mp_model.variable(var_id);
1627 const int64_t value =
1628 static_cast<int64_t>(
1629 round(mp_var.objective_coefficient() * scaling_factor)) /
1630 gcd;
1631 if (value != 0) {
1632 objective->add_literals(var_id + 1);
1633 objective->add_coefficients(value);
1634 }
1635 }
1636
1637 // If the problem was a maximization one, we need to modify the objective.
1638 if (mp_model.maximize()) ChangeOptimizationDirection(problem);
1639
1640 // Test the precision of the conversion.
1641 const double kRelativeTolerance = 1e-8;
1642 if (max_relative_error > kRelativeTolerance) {
1643 LOG(WARNING) << "The relative error during double -> int64_t conversion "
1644 << "is too high!";
1645 return false;
1646 }
1647 return true;
1648}
1649
1651 glop::LinearProgram* lp) {
1652 lp->Clear();
1653 for (int i = 0; i < problem.num_variables(); ++i) {
1654 const ColIndex col = lp->CreateNewVariable();
1656 lp->SetVariableBounds(col, 0.0, 1.0);
1657 }
1658
1659 // Variables name are optional.
1660 if (problem.var_names_size() != 0) {
1661 CHECK_EQ(problem.var_names_size(), problem.num_variables());
1662 for (int i = 0; i < problem.num_variables(); ++i) {
1663 lp->SetVariableName(ColIndex(i), problem.var_names(i));
1664 }
1665 }
1666
1667 for (const LinearBooleanConstraint& constraint : problem.constraints()) {
1668 const RowIndex constraint_index = lp->CreateNewConstraint();
1669 lp->SetConstraintName(constraint_index, constraint.name());
1670 double sum = 0.0;
1671 for (int i = 0; i < constraint.literals_size(); ++i) {
1672 const int literal = constraint.literals(i);
1673 const double coeff = constraint.coefficients(i);
1674 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1675 if (literal < 0) {
1676 sum += coeff;
1677 lp->SetCoefficient(constraint_index, variable_index, -coeff);
1678 } else {
1679 lp->SetCoefficient(constraint_index, variable_index, coeff);
1680 }
1681 }
1683 constraint_index,
1684 constraint.has_lower_bound() ? constraint.lower_bound() - sum
1685 : -kInfinity,
1686 constraint.has_upper_bound() ? constraint.upper_bound() - sum
1687 : kInfinity);
1688 }
1689
1690 // Objective.
1691 {
1692 double sum = 0.0;
1693 const LinearObjective& objective = problem.objective();
1694 const double scaling_factor = objective.scaling_factor();
1695 for (int i = 0; i < objective.literals_size(); ++i) {
1696 const int literal = objective.literals(i);
1697 const double coeff =
1698 static_cast<double>(objective.coefficients(i)) * scaling_factor;
1699 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1700 if (literal < 0) {
1701 sum += coeff;
1702 lp->SetObjectiveCoefficient(variable_index, -coeff);
1703 } else {
1704 lp->SetObjectiveCoefficient(variable_index, coeff);
1705 }
1706 }
1707 lp->SetObjectiveOffset((sum + objective.offset()) * scaling_factor);
1708 lp->SetMaximizationProblem(scaling_factor < 0);
1709 }
1710
1711 lp->CleanUp();
1712}
1713
1715 const CpModelProto& model_proto_with_floating_point_objective,
1716 const CpObjectiveProto& integer_objective,
1717 const int64_t inner_integer_objective_lower_bound) {
1718 // Create an LP with the correct variable domain.
1720 const CpModelProto& proto = model_proto_with_floating_point_objective;
1721 for (int i = 0; i < proto.variables().size(); ++i) {
1722 const auto& domain = proto.variables(i).domain();
1723 lp.SetVariableBounds(lp.CreateNewVariable(), static_cast<double>(domain[0]),
1724 static_cast<double>(domain[domain.size() - 1]));
1725 }
1726
1727 // Add the original problem floating point objective.
1728 // This is user given, so we do need to deal with duplicate entries.
1729 const FloatObjectiveProto& float_obj = proto.floating_point_objective();
1730 lp.SetObjectiveOffset(float_obj.offset());
1731 lp.SetMaximizationProblem(float_obj.maximize());
1732 for (int i = 0; i < float_obj.vars().size(); ++i) {
1733 const glop::ColIndex col(float_obj.vars(i));
1734 const double old_value = lp.objective_coefficients()[col];
1735 lp.SetObjectiveCoefficient(col, old_value + float_obj.coeffs(i));
1736 }
1737
1738 // Add a single constraint "integer_objective >= lower_bound".
1739 const glop::RowIndex ct = lp.CreateNewConstraint();
1741 ct, static_cast<double>(inner_integer_objective_lower_bound),
1742 std::numeric_limits<double>::infinity());
1743 for (int i = 0; i < integer_objective.vars().size(); ++i) {
1744 lp.SetCoefficient(ct, glop::ColIndex(integer_objective.vars(i)),
1745 static_cast<double>(integer_objective.coeffs(i)));
1746 }
1747
1748 lp.CleanUp();
1749
1750 // This should be fast. However, in case of numerical difficulties, we bound
1751 // the number of iterations.
1752 glop::LPSolver solver;
1753 glop::GlopParameters glop_parameters;
1754 glop_parameters.set_max_number_of_iterations(100 * proto.variables().size());
1755 glop_parameters.set_change_status_to_imprecise(false);
1756 solver.SetParameters(glop_parameters);
1757 const glop::ProblemStatus& status = solver.Solve(lp);
1758 if (status == glop::ProblemStatus::OPTIMAL) {
1759 return solver.GetObjectiveValue();
1760 }
1761
1762 // Error. Hoperfully this shouldn't happen.
1763 return float_obj.maximize() ? std::numeric_limits<double>::infinity()
1764 : -std::numeric_limits<double>::infinity();
1765}
1766
1767} // namespace sat
1768} // namespace operations_research
void set_var_index(int index, ::int32_t value)
::google::protobuf::RepeatedField< double > *PROTOBUF_NONNULL mutable_coefficient()
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_var_index()
const ::std::string & name() const
void set_coefficient(int index, double value)
GeneralConstraintCase general_constraint_case() const
::operations_research::MPIndicatorConstraint *PROTOBUF_NONNULL mutable_indicator_constraint()
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint()
const ::operations_research::MPVariableProto & variable(int index) const
const ::operations_research::MPGeneralConstraintProto & general_constraint(int index) const
::operations_research::MPConstraintProto *PROTOBUF_NONNULL add_constraint()
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
::operations_research::MPVariableProto *PROTOBUF_NONNULL add_variable()
const ::operations_research::MPConstraintProto & constraint(int index) const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
::operations_research::MPVariableProto *PROTOBUF_NONNULL mutable_variable(int index)
const ::std::string & name() const
::operations_research::MPGeneralConstraintProto *PROTOBUF_NONNULL mutable_general_constraint(int index)
const ::std::string & name() const
void set_max_number_of_iterations(::int64_t value)
Fractional GetObjectiveValue() const
Definition lp_solver.cc:528
void SetParameters(const GlopParameters &parameters)
Definition lp_solver.cc:127
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition lp_solver.cc:145
const DenseRow & objective_coefficients() const
Definition lp_data.h:230
void SetConstraintName(RowIndex row, absl::string_view name)
Definition lp_data.cc:254
void SetObjectiveOffset(Fractional objective_offset)
Definition lp_data.cc:340
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition lp_data.cc:335
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition lp_data.cc:258
void SetVariableType(ColIndex col, VariableType type)
Definition lp_data.cc:245
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition lp_data.cc:318
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition lp_data.cc:326
void SetVariableName(ColIndex col, absl::string_view name)
Definition lp_data.cc:241
void SetMaximizationProblem(bool maximize)
Definition lp_data.cc:352
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_literals()
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_and()
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
const ::operations_research::sat::BoolArgumentProto & bool_and() const
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_or()
const ::operations_research::sat::BoolArgumentProto & bool_or() const
::operations_research::sat::LinearConstraintProto *PROTOBUF_NONNULL mutable_linear()
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::FloatObjectiveProto *PROTOBUF_NONNULL mutable_floating_point_objective()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL mutable_constraints(int index)
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::FloatObjectiveProto & floating_point_objective() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL add_variables()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL add_constraints()
::operations_research::sat::CpObjectiveProto *PROTOBUF_NONNULL mutable_objective()
void set_name(Arg_ &&arg, Args_... args)
void set_name(Arg_ &&arg, Args_... args)
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::LinearBooleanConstraint *PROTOBUF_NONNULL add_constraints()
const ::operations_research::sat::LinearBooleanConstraint & constraints(int index) const
const ::operations_research::sat::LinearObjective & objective() const
::std::string *PROTOBUF_NONNULL add_var_names()
const ::std::string & var_names(int index) const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
::operations_research::sat::LinearObjective *PROTOBUF_NONNULL mutable_objective()
void set_coefficient(int index, double value)
const ::operations_research::MPVariableProto & variable(int index) const
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
::operations_research::MPVariableProto *PROTOBUF_NONNULL mutable_variable(int index)
::operations_research::MPGeneralConstraintProto *PROTOBUF_NONNULL mutable_general_constraint(int index)
constexpr Fractional kInfinity
Definition lp_types.h:87
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
double FindBestScalingAndComputeErrors(absl::Span< const double > coefficients, absl::Span< const double > lower_bounds, absl::Span< const double > upper_bounds, int64_t max_absolute_activity, double wanted_absolute_activity_precision, double *relative_coeff_error, double *scaled_sum_error)
Definition lp_utils.cc:868
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool ConvertMPModelProtoToCpModelProto(const SatParameters &params, const MPModelProto &mp_model, CpModelProto *cp_model, SolverLogger *logger)
Definition lp_utils.cc:929
int64_t FindRationalFactor(double x, int64_t limit, double tolerance)
Definition lp_utils.cc:135
void ConvertBooleanProblemToLinearProgram(const LinearBooleanProblem &problem, glop::LinearProgram *lp)
Definition lp_utils.cc:1650
bool MakeBoundsOfIntegerVariablesInteger(const SatParameters &params, MPModelProto *mp_model, SolverLogger *logger)
Definition lp_utils.cc:207
void ChangeLargeBoundsToInfinity(double max_magnitude, MPModelProto *mp_model, SolverLogger *logger)
Definition lp_utils.cc:240
bool ScaleAndSetObjective(const SatParameters &params, absl::Span< const std::pair< int, double > > objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
Definition lp_utils.cc:1365
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
Definition lp_utils.cc:485
void RemoveNearZeroTerms(const SatParameters &params, MPModelProto *mp_model, SolverLogger *logger)
Definition lp_utils.cc:311
bool ConvertBinaryMPModelProtoToBooleanProblem(const MPModelProto &mp_model, LinearBooleanProblem *problem)
Definition lp_utils.cc:1471
double ComputeTrueObjectiveLowerBound(const CpModelProto &model_proto_with_floating_point_objective, const CpObjectiveProto &integer_objective, const int64_t inner_integer_objective_lower_bound)
Definition lp_utils.cc:1714
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
Definition lp_utils.cc:1161
constexpr Fractional kInfinity
Definition lp_types.h:87
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
Definition lp_utils.cc:112
bool MPModelProtoValidationBeforeConversion(const SatParameters &params, const MPModelProto &mp_model, SolverLogger *logger)
Definition lp_utils.cc:421
OR-Tools root namespace.
int64_t CapAdd(int64_t x, int64_t y)
double GetBestScalingOfDoublesToInt64(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, int64_t max_absolute_sum)
Definition fp_utils.cc:184
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:46
int64_t CapProd(int64_t x, int64_t y)
void ComputeScalingErrors(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, double scaling_factor, double *max_relative_coeff_error, double *max_scaled_sum_error)
Definition fp_utils.cc:175
std::string ProtobufDebugString(const P &message)
Definition proto_utils.h:31
int64_t ComputeGcdOfRoundedDoubles(absl::Span< const double > x, double scaling_factor)
Definition fp_utils.cc:207
static int input(yyscan_t yyscanner)
absl::Status ScaleAndAddConstraint(absl::Span< const int > vars, absl::Span< const double > coeffs, double lower_bound, double upper_bound, absl::string_view name, absl::Span< const IntegerVariableProto *const > var_domains, ConstraintProto *constraint)
Definition lp_utils.cc:747
#define SOLVER_LOG(logger,...)
Definition logging.h:114