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