OR-Tools  9.1
sat/lp_utils.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <stdlib.h>
17
18#include <algorithm>
19#include <cmath>
20#include <cstdint>
21#include <limits>
22#include <string>
23#include <vector>
24
25#include "absl/strings/str_cat.h"
34#include "ortools/sat/integer.h"
37
38namespace operations_research {
39namespace sat {
40
41using glop::ColIndex;
43using glop::kInfinity;
44using glop::RowIndex;
45
49
50namespace {
51
52void ScaleConstraint(const std::vector<double>& var_scaling,
53 MPConstraintProto* mp_constraint) {
54 const int num_terms = mp_constraint->coefficient_size();
55 for (int i = 0; i < num_terms; ++i) {
56 const int var_index = mp_constraint->var_index(i);
57 mp_constraint->set_coefficient(
58 i, mp_constraint->coefficient(i) / var_scaling[var_index]);
59 }
60}
61
62void ApplyVarScaling(const std::vector<double> var_scaling,
63 MPModelProto* mp_model) {
64 const int num_variables = mp_model->variable_size();
65 for (int i = 0; i < num_variables; ++i) {
66 const double scaling = var_scaling[i];
67 const MPVariableProto& mp_var = mp_model->variable(i);
68 const double old_lb = mp_var.lower_bound();
69 const double old_ub = mp_var.upper_bound();
70 const double old_obj = mp_var.objective_coefficient();
71 mp_model->mutable_variable(i)->set_lower_bound(old_lb * scaling);
72 mp_model->mutable_variable(i)->set_upper_bound(old_ub * scaling);
73 mp_model->mutable_variable(i)->set_objective_coefficient(old_obj / scaling);
74 }
75 for (MPConstraintProto& mp_constraint : *mp_model->mutable_constraint()) {
76 ScaleConstraint(var_scaling, &mp_constraint);
77 }
78 for (MPGeneralConstraintProto& general_constraint :
79 *mp_model->mutable_general_constraint()) {
80 switch (general_constraint.general_constraint_case()) {
82 ScaleConstraint(var_scaling,
83 general_constraint.mutable_indicator_constraint()
84 ->mutable_constraint());
85 break;
88 // These constraints have only Boolean variables and no constants. They
89 // don't need scaling.
90 break;
91 default:
92 LOG(FATAL) << "Scaling unsupported for general constraint of type "
93 << general_constraint.general_constraint_case();
94 }
95 }
96}
97
98} // namespace
99
100std::vector<double> ScaleContinuousVariables(double scaling, double max_bound,
101 MPModelProto* mp_model) {
102 const int num_variables = mp_model->variable_size();
103 std::vector<double> var_scaling(num_variables, 1.0);
104 for (int i = 0; i < num_variables; ++i) {
105 if (mp_model->variable(i).is_integer()) continue;
106 const double lb = mp_model->variable(i).lower_bound();
107 const double ub = mp_model->variable(i).upper_bound();
108 const double magnitude = std::max(std::abs(lb), std::abs(ub));
109 if (magnitude == 0 || magnitude > max_bound) continue;
110 var_scaling[i] = std::min(scaling, max_bound / magnitude);
111 }
112 ApplyVarScaling(var_scaling, mp_model);
113 return var_scaling;
114}
115
116// This uses the best rational approximation of x via continuous fractions. It
117// is probably not the best implementation, but according to the unit test, it
118// seems to do the job.
119int FindRationalFactor(double x, int limit, double tolerance) {
120 const double initial_x = x;
121 x = std::abs(x);
122 x -= std::floor(x);
123 int q = 1;
124 int prev_q = 0;
125 while (q < limit) {
126 if (std::abs(q * initial_x - std::round(q * initial_x)) < q * tolerance) {
127 return q;
128 }
129 x = 1 / x;
130 const int new_q = prev_q + static_cast<int>(std::floor(x)) * q;
131 prev_q = q;
132 q = new_q;
133 x -= std::floor(x);
134 }
135 return 0;
136}
137
138namespace {
139
140// Returns a factor such that factor * var only need to take integer values to
141// satisfy the given constraint. Return 0.0 if we didn't find such factor.
142//
143// Precondition: var must be the only non-integer in the given constraint.
144double GetIntegralityMultiplier(const MPModelProto& mp_model,
145 const std::vector<double>& var_scaling, int var,
146 int ct_index, double tolerance) {
147 DCHECK(!mp_model.variable(var).is_integer());
148 const MPConstraintProto& ct = mp_model.constraint(ct_index);
149 double multiplier = 1.0;
150 double var_coeff = 0.0;
151 const double max_multiplier = 1e4;
152 for (int i = 0; i < ct.var_index().size(); ++i) {
153 if (var == ct.var_index(i)) {
154 var_coeff = ct.coefficient(i);
155 continue;
156 }
157
158 DCHECK(mp_model.variable(ct.var_index(i)).is_integer());
159 // This actually compute the smallest multiplier to make all other
160 // terms in the constraint integer.
161 const double coeff =
162 multiplier * ct.coefficient(i) / var_scaling[ct.var_index(i)];
163 multiplier *=
164 FindRationalFactor(coeff, /*limit=*/100, multiplier * tolerance);
165 if (multiplier == 0 || multiplier > max_multiplier) return 0.0;
166 }
167 DCHECK_NE(var_coeff, 0.0);
168
169 // The constraint bound need to be infinite or integer.
170 for (const double bound : {ct.lower_bound(), ct.upper_bound()}) {
171 if (!std::isfinite(bound)) continue;
172 if (std::abs(std::round(bound * multiplier) - bound * multiplier) >
173 tolerance * multiplier) {
174 return 0.0;
175 }
176 }
177 return std::abs(multiplier * var_coeff);
178}
179
180} // namespace
181
182void RemoveNearZeroTerms(const SatParameters& params, MPModelProto* mp_model,
183 SolverLogger* logger) {
184 const int num_variables = mp_model->variable_size();
185
186 // Compute for each variable its current maximum magnitude. Note that we will
187 // only scale variable with a coefficient >= 1, so it is safe to use this
188 // bound.
189 std::vector<double> max_bounds(num_variables);
190 for (int i = 0; i < num_variables; ++i) {
191 double value = std::abs(mp_model->variable(i).lower_bound());
192 value = std::max(value, std::abs(mp_model->variable(i).upper_bound()));
193 value = std::min(value, params.mip_max_bound());
194 max_bounds[i] = value;
195 }
196
197 // We want the maximum absolute error while setting coefficients to zero to
198 // not exceed our mip wanted precision. So for a binary variable we might set
199 // to zero coefficient around 1e-7. But for large domain, we need lower coeff
200 // than that, around 1e-12 with the default params.mip_max_bound(). This also
201 // depends on the size of the constraint.
202 int64_t num_removed = 0;
203 double largest_removed = 0.0;
204 const int num_constraints = mp_model->constraint_size();
205 for (int c = 0; c < num_constraints; ++c) {
207 int new_size = 0;
208 const int size = ct->var_index().size();
209 if (size == 0) continue;
210 const double threshold =
211 params.mip_wanted_precision() / static_cast<double>(size);
212 for (int i = 0; i < size; ++i) {
213 const int var = ct->var_index(i);
214 const double coeff = ct->coefficient(i);
215 if (std::abs(coeff) * max_bounds[var] < threshold) {
216 largest_removed = std::max(largest_removed, std::abs(coeff));
217 continue;
218 }
219 ct->set_var_index(new_size, var);
220 ct->set_coefficient(new_size, coeff);
221 ++new_size;
222 }
223 num_removed += size - new_size;
224 ct->mutable_var_index()->Truncate(new_size);
225 ct->mutable_coefficient()->Truncate(new_size);
226 }
227
228 if (num_removed > 0) {
229 SOLVER_LOG(logger, "Removed ", num_removed,
230 " near zero terms with largest magnitude of ", largest_removed,
231 ".");
232 }
233}
234
235std::vector<double> DetectImpliedIntegers(MPModelProto* mp_model,
236 SolverLogger* logger) {
237 const int num_variables = mp_model->variable_size();
238 std::vector<double> var_scaling(num_variables, 1.0);
239
240 int initial_num_integers = 0;
241 for (int i = 0; i < num_variables; ++i) {
242 if (mp_model->variable(i).is_integer()) ++initial_num_integers;
243 }
244 VLOG(1) << "Initial num integers: " << initial_num_integers;
245
246 // We will process all equality constraints with exactly one non-integer.
247 const double tolerance = 1e-6;
248 std::vector<int> constraint_queue;
249
250 const int num_constraints = mp_model->constraint_size();
251 std::vector<int> constraint_to_num_non_integer(num_constraints, 0);
252 std::vector<std::vector<int>> var_to_constraints(num_variables);
253 for (int i = 0; i < num_constraints; ++i) {
254 const MPConstraintProto& mp_constraint = mp_model->constraint(i);
255
256 for (const int var : mp_constraint.var_index()) {
257 if (!mp_model->variable(var).is_integer()) {
258 var_to_constraints[var].push_back(i);
259 constraint_to_num_non_integer[i]++;
260 }
261 }
262 if (constraint_to_num_non_integer[i] == 1) {
263 constraint_queue.push_back(i);
264 }
265 }
266 VLOG(1) << "Initial constraint queue: " << constraint_queue.size() << " / "
267 << num_constraints;
268
269 int num_detected = 0;
270 double max_scaling = 0.0;
271 auto scale_and_mark_as_integer = [&](int var, double scaling) mutable {
272 CHECK_NE(var, -1);
273 CHECK(!mp_model->variable(var).is_integer());
274 CHECK_EQ(var_scaling[var], 1.0);
275 if (scaling != 1.0) {
276 VLOG(2) << "Scaled " << var << " by " << scaling;
277 }
278
279 ++num_detected;
280 max_scaling = std::max(max_scaling, scaling);
281
282 // Scale the variable right away and mark it as implied integer.
283 // Note that the constraints will be scaled later.
284 var_scaling[var] = scaling;
285 mp_model->mutable_variable(var)->set_is_integer(true);
286
287 // Update the queue of constraints with a single non-integer.
288 for (const int ct_index : var_to_constraints[var]) {
289 constraint_to_num_non_integer[ct_index]--;
290 if (constraint_to_num_non_integer[ct_index] == 1) {
291 constraint_queue.push_back(ct_index);
292 }
293 }
294 };
295
296 int num_fail_due_to_rhs = 0;
297 int num_fail_due_to_large_multiplier = 0;
298 int num_processed_constraints = 0;
299 while (!constraint_queue.empty()) {
300 const int top_ct_index = constraint_queue.back();
301 constraint_queue.pop_back();
302
303 // The non integer variable was already made integer by one other
304 // constraint.
305 if (constraint_to_num_non_integer[top_ct_index] == 0) continue;
306
307 // Ignore non-equality here.
308 const MPConstraintProto& ct = mp_model->constraint(top_ct_index);
309 if (ct.lower_bound() + tolerance < ct.upper_bound()) continue;
310
311 ++num_processed_constraints;
312
313 // This will be set to the unique non-integer term of this constraint.
314 int var = -1;
315 double var_coeff;
316
317 // We are looking for a "multiplier" so that the unique non-integer term
318 // in this constraint (i.e. var * var_coeff) times this multiplier is an
319 // integer.
320 //
321 // If this is set to zero or becomes too large, we fail to detect a new
322 // implied integer and ignore this constraint.
323 double multiplier = 1.0;
324 const double max_multiplier = 1e4;
325
326 for (int i = 0; i < ct.var_index().size(); ++i) {
327 if (!mp_model->variable(ct.var_index(i)).is_integer()) {
328 CHECK_EQ(var, -1);
329 var = ct.var_index(i);
330 var_coeff = ct.coefficient(i);
331 } else {
332 // This actually compute the smallest multiplier to make all other
333 // terms in the constraint integer.
334 const double coeff =
335 multiplier * ct.coefficient(i) / var_scaling[ct.var_index(i)];
336 multiplier *=
337 FindRationalFactor(coeff, /*limit=*/100, multiplier * tolerance);
338 if (multiplier == 0 || multiplier > max_multiplier) {
339 break;
340 }
341 }
342 }
343
344 if (multiplier == 0 || multiplier > max_multiplier) {
345 ++num_fail_due_to_large_multiplier;
346 continue;
347 }
348
349 // These "rhs" fail could be handled by shifting the variable.
350 const double rhs = ct.lower_bound();
351 if (std::abs(std::round(rhs * multiplier) - rhs * multiplier) >
352 tolerance * multiplier) {
353 ++num_fail_due_to_rhs;
354 continue;
355 }
356
357 // We want to multiply the variable so that it is integer. We know that
358 // coeff * multiplier is an integer, so we just multiply by that.
359 //
360 // But if a variable appear in more than one equality, we want to find the
361 // smallest integrality factor! See diameterc-msts-v40a100d5i.mps
362 // for an instance of this.
363 double best_scaling = std::abs(var_coeff * multiplier);
364 for (const int ct_index : var_to_constraints[var]) {
365 if (ct_index == top_ct_index) continue;
366 if (constraint_to_num_non_integer[ct_index] != 1) continue;
367
368 // Ignore non-equality here.
369 const MPConstraintProto& ct = mp_model->constraint(top_ct_index);
370 if (ct.lower_bound() + tolerance < ct.upper_bound()) continue;
371
372 const double multiplier = GetIntegralityMultiplier(
373 *mp_model, var_scaling, var, ct_index, tolerance);
374 if (multiplier != 0.0 && multiplier < best_scaling) {
375 best_scaling = multiplier;
376 }
377 }
378
379 scale_and_mark_as_integer(var, best_scaling);
380 }
381
382 // Process continuous variables that only appear as the unique non integer
383 // in a set of non-equality constraints.
384 //
385 // Note that turning to integer such variable cannot in turn trigger new
386 // integer detection, so there is no point doing that in a loop.
387 int num_in_inequalities = 0;
388 int num_to_be_handled = 0;
389 for (int var = 0; var < num_variables; ++var) {
390 if (mp_model->variable(var).is_integer()) continue;
391
392 // This should be presolved and not happen.
393 if (var_to_constraints[var].empty()) continue;
394
395 bool ok = true;
396 for (const int ct_index : var_to_constraints[var]) {
397 if (constraint_to_num_non_integer[ct_index] != 1) {
398 ok = false;
399 break;
400 }
401 }
402 if (!ok) continue;
403
404 std::vector<double> scaled_coeffs;
405 for (const int ct_index : var_to_constraints[var]) {
406 const double multiplier = GetIntegralityMultiplier(
407 *mp_model, var_scaling, var, ct_index, tolerance);
408 if (multiplier == 0.0) {
409 ok = false;
410 break;
411 }
412 scaled_coeffs.push_back(multiplier);
413 }
414 if (!ok) continue;
415
416 // The situation is a bit tricky here, we have a bunch of coeffs c_i, and we
417 // know that X * c_i can take integer value without changing the constraint
418 // i meaning.
419 //
420 // For now we take the min, and scale only if all c_i / min are integer.
421 double scaling = scaled_coeffs[0];
422 for (const double c : scaled_coeffs) {
423 scaling = std::min(scaling, c);
424 }
425 CHECK_GT(scaling, 0.0);
426 for (const double c : scaled_coeffs) {
427 const double fraction = c / scaling;
428 if (std::abs(std::round(fraction) - fraction) > tolerance) {
429 ok = false;
430 break;
431 }
432 }
433 if (!ok) {
434 // TODO(user): be smarter! we should be able to handle these cases.
435 ++num_to_be_handled;
436 continue;
437 }
438
439 // Tricky, we also need the bound of the scaled variable to be integer.
440 for (const double bound : {mp_model->variable(var).lower_bound(),
441 mp_model->variable(var).upper_bound()}) {
442 if (!std::isfinite(bound)) continue;
443 if (std::abs(std::round(bound * scaling) - bound * scaling) >
444 tolerance * scaling) {
445 ok = false;
446 break;
447 }
448 }
449 if (!ok) {
450 // TODO(user): If we scale more we migth be able to turn it into an
451 // integer.
452 ++num_to_be_handled;
453 continue;
454 }
455
456 ++num_in_inequalities;
457 scale_and_mark_as_integer(var, scaling);
458 }
459 VLOG(1) << "num_new_integer: " << num_detected
460 << " num_processed_constraints: " << num_processed_constraints
461 << " num_rhs_fail: " << num_fail_due_to_rhs
462 << " num_multiplier_fail: " << num_fail_due_to_large_multiplier;
463
464 if (num_to_be_handled > 0) {
465 SOLVER_LOG(logger, "Missed ", num_to_be_handled,
466 " potential implied integer.");
467 }
468
469 const int num_integers = initial_num_integers + num_detected;
470 SOLVER_LOG(logger, "Num integers: ", num_integers, "/", num_variables,
471 " (implied: ", num_detected,
472 " in_inequalities: ", num_in_inequalities,
473 " max_scaling: ", max_scaling, ")",
474 (num_integers == num_variables ? " [IP] " : " [MIP] "));
475
476 ApplyVarScaling(var_scaling, mp_model);
477 return var_scaling;
478}
479
480namespace {
481
482// We use a class to reuse the temporary memory.
483struct ConstraintScaler {
484 // Scales an individual constraint.
485 ConstraintProto* AddConstraint(const MPModelProto& mp_model,
486 const MPConstraintProto& mp_constraint,
487 CpModelProto* cp_model);
488
491 double max_scaling_factor = 0.0;
492
493 double wanted_precision = 1e-6;
494 int64_t scaling_target = int64_t{1} << 50;
495 std::vector<int> var_indices;
496 std::vector<double> coefficients;
497 std::vector<double> lower_bounds;
498 std::vector<double> upper_bounds;
499};
500
501namespace {
502
503double FindFractionalScaling(const std::vector<double>& coefficients,
504 double tolerance) {
505 double multiplier = 1.0;
506 for (const double coeff : coefficients) {
507 multiplier *=
508 FindRationalFactor(coeff, /*limit=*/1e8, multiplier * tolerance);
509 if (multiplier == 0.0) break;
510 }
511 return multiplier;
512}
513
514} // namespace
515
516ConstraintProto* ConstraintScaler::AddConstraint(
517 const MPModelProto& mp_model, const MPConstraintProto& mp_constraint,
518 CpModelProto* cp_model) {
519 if (mp_constraint.lower_bound() == -kInfinity &&
520 mp_constraint.upper_bound() == kInfinity) {
521 return nullptr;
522 }
523
524 auto* constraint = cp_model->add_constraints();
525 constraint->set_name(mp_constraint.name());
526 auto* arg = constraint->mutable_linear();
527
528 // First scale the coefficients of the constraints so that the constraint
529 // sum can always be computed without integer overflow.
530 var_indices.clear();
531 coefficients.clear();
532 lower_bounds.clear();
533 upper_bounds.clear();
534 const int num_coeffs = mp_constraint.coefficient_size();
535 for (int i = 0; i < num_coeffs; ++i) {
536 const auto& var_proto = cp_model->variables(mp_constraint.var_index(i));
537 const int64_t lb = var_proto.domain(0);
538 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
539 if (lb == 0 && ub == 0) continue;
540
541 const double coeff = mp_constraint.coefficient(i);
542 if (coeff == 0.0) continue;
543
544 var_indices.push_back(mp_constraint.var_index(i));
545 coefficients.push_back(coeff);
546 lower_bounds.push_back(lb);
547 upper_bounds.push_back(ub);
548 }
549
550 // We compute the worst case error relative to the magnitude of the bounds.
551 Fractional lb = mp_constraint.lower_bound();
552 Fractional ub = mp_constraint.upper_bound();
553 const double ct_norm = std::max(1.0, std::min(std::abs(lb), std::abs(ub)));
554
555 double scaling_factor = GetBestScalingOfDoublesToInt64(
557 if (scaling_factor == 0.0) {
558 // TODO(user): Report error properly instead of ignoring constraint. Note
559 // however that this likely indicate a coefficient of inf in the constraint,
560 // so we should probably abort before reaching here.
561 LOG(DFATAL) << "Scaling factor of zero while scaling constraint: "
562 << mp_constraint.ShortDebugString();
563 return nullptr;
564 }
565
566 // Returns the smallest factor of the form 2^i that gives us a relative sum
567 // error of wanted_precision and still make sure we will have no integer
568 // overflow.
569 //
570 // TODO(user): Make this faster.
571 double x = std::min(scaling_factor, 1.0);
572 double relative_coeff_error;
573 double scaled_sum_error;
574 for (; x <= scaling_factor; x *= 2) {
576 &relative_coeff_error, &scaled_sum_error);
577 if (scaled_sum_error < wanted_precision * x * ct_norm) break;
578 }
579 scaling_factor = x;
580
581 // Because we deal with an approximate input, scaling with a power of 2 might
582 // not be the best choice. It is also possible user used rational coeff and
583 // then converted them to double (1/2, 1/3, 4/5, etc...). This scaling will
584 // recover such rational input and might result in a smaller overall
585 // coefficient which is good.
586 const double integer_factor = FindFractionalScaling(coefficients, 1e-8);
587 if (integer_factor != 0 && integer_factor < scaling_factor) {
589 &relative_coeff_error, &scaled_sum_error);
590 if (scaled_sum_error < wanted_precision * integer_factor * ct_norm) {
591 scaling_factor = integer_factor;
592 }
593 }
594
595 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
597 std::max(relative_coeff_error, max_relative_coeff_error);
598 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
599
600 // We do not relax the constraint bound if all variables are integer and
601 // we made no error at all during our scaling.
602 bool relax_bound = scaled_sum_error > 0;
603
604 for (int i = 0; i < coefficients.size(); ++i) {
605 const double scaled_value = coefficients[i] * scaling_factor;
606 const int64_t value = static_cast<int64_t>(std::round(scaled_value)) / gcd;
607 if (value != 0) {
608 if (!mp_model.variable(var_indices[i]).is_integer()) {
609 relax_bound = true;
610 }
611 arg->add_vars(var_indices[i]);
612 arg->add_coeffs(value);
613 }
614 }
616 max_relative_rhs_error, scaled_sum_error / (scaling_factor * ct_norm));
617
618 // Add the constraint bounds. Because we are sure the scaled constraint fit
619 // on an int64_t, if the scaled bounds are too large, the constraint is either
620 // always true or always false.
621 if (relax_bound) {
622 lb -= std::max(std::abs(lb), 1.0) * wanted_precision;
623 }
624 const Fractional scaled_lb = std::ceil(lb * scaling_factor);
625 if (lb == kInfinity || scaled_lb >= std::numeric_limits<int64_t>::max()) {
626 // Corner case: infeasible model.
627 arg->add_domain(std::numeric_limits<int64_t>::max());
628 } else if (lb == -kInfinity ||
629 scaled_lb <= std::numeric_limits<int64_t>::min()) {
630 arg->add_domain(std::numeric_limits<int64_t>::min());
631 } else {
632 arg->add_domain(CeilRatio(IntegerValue(static_cast<int64_t>(scaled_lb)),
633 IntegerValue(gcd))
634 .value());
635 }
636
637 if (relax_bound) {
638 ub += std::max(std::abs(ub), 1.0) * wanted_precision;
639 }
640 const Fractional scaled_ub = std::floor(ub * scaling_factor);
641 if (ub == -kInfinity || scaled_ub <= std::numeric_limits<int64_t>::min()) {
642 // Corner case: infeasible model.
643 arg->add_domain(std::numeric_limits<int64_t>::min());
644 } else if (ub == kInfinity ||
645 scaled_ub >= std::numeric_limits<int64_t>::max()) {
646 arg->add_domain(std::numeric_limits<int64_t>::max());
647 } else {
648 arg->add_domain(FloorRatio(IntegerValue(static_cast<int64_t>(scaled_ub)),
649 IntegerValue(gcd))
650 .value());
651 }
652
653 return constraint;
654}
655
656} // namespace
657
659 const MPModelProto& mp_model,
660 CpModelProto* cp_model,
661 SolverLogger* logger) {
662 CHECK(cp_model != nullptr);
663 cp_model->Clear();
664 cp_model->set_name(mp_model.name());
665
666 // To make sure we cannot have integer overflow, we use this bound for any
667 // unbounded variable.
668 //
669 // TODO(user): This could be made larger if needed, so be smarter if we have
670 // MIP problem that we cannot "convert" because of this. Note however than we
671 // cannot go that much further because we need to make sure we will not run
672 // into overflow if we add a big linear combination of such variables. It
673 // should always be possible for a user to scale its problem so that all
674 // relevant quantities are a couple of millions. A LP/MIP solver have a
675 // similar condition in disguise because problem with a difference of more
676 // than 6 magnitudes between the variable values will likely run into numeric
677 // trouble.
678 const int64_t kMaxVariableBound =
679 static_cast<int64_t>(params.mip_max_bound());
680
681 int num_truncated_bounds = 0;
682 int num_small_domains = 0;
683 const int64_t kSmallDomainSize = 1000;
684 const double kWantedPrecision = params.mip_wanted_precision();
685
686 // Add the variables.
687 const int num_variables = mp_model.variable_size();
688 for (int i = 0; i < num_variables; ++i) {
689 const MPVariableProto& mp_var = mp_model.variable(i);
690 IntegerVariableProto* cp_var = cp_model->add_variables();
691 cp_var->set_name(mp_var.name());
692
693 // Deal with the corner case of a domain far away from zero.
694 //
695 // TODO(user): We should deal with these case by shifting the domain so
696 // that it includes zero instead of just fixing the variable. But that is a
697 // bit of work as it requires some postsolve.
698 if (mp_var.lower_bound() > kMaxVariableBound) {
699 // Fix var to its lower bound.
700 ++num_truncated_bounds;
701 const int64_t value =
702 static_cast<int64_t>(std::round(mp_var.lower_bound()));
703 cp_var->add_domain(value);
704 cp_var->add_domain(value);
705 continue;
706 } else if (mp_var.upper_bound() < -kMaxVariableBound) {
707 // Fix var to its upper_bound.
708 ++num_truncated_bounds;
709 const int64_t value =
710 static_cast<int64_t>(std::round(mp_var.upper_bound()));
711 cp_var->add_domain(value);
712 cp_var->add_domain(value);
713 continue;
714 }
715
716 // Note that we must process the lower bound first.
717 for (const bool lower : {true, false}) {
718 const double bound = lower ? mp_var.lower_bound() : mp_var.upper_bound();
719 if (std::abs(bound) >= kMaxVariableBound) {
720 ++num_truncated_bounds;
721 cp_var->add_domain(bound < 0 ? -kMaxVariableBound : kMaxVariableBound);
722 continue;
723 }
724
725 // Note that the cast is "perfect" because we forbid large values.
726 cp_var->add_domain(
727 static_cast<int64_t>(lower ? std::ceil(bound - kWantedPrecision)
728 : std::floor(bound + kWantedPrecision)));
729 }
730
731 if (cp_var->domain(0) > cp_var->domain(1)) {
732 LOG(WARNING) << "Variable #" << i << " cannot take integer value. "
733 << mp_var.ShortDebugString();
734 return false;
735 }
736
737 // Notify if a continuous variable has a small domain as this is likely to
738 // make an all integer solution far from a continuous one.
739 if (!mp_var.is_integer() && cp_var->domain(0) != cp_var->domain(1) &&
740 cp_var->domain(1) - cp_var->domain(0) < kSmallDomainSize) {
741 ++num_small_domains;
742 }
743 }
744
745 LOG_IF(WARNING, num_truncated_bounds > 0)
746 << num_truncated_bounds << " bounds were truncated to "
747 << kMaxVariableBound << ".";
748 LOG_IF(WARNING, num_small_domains > 0)
749 << num_small_domains << " continuous variable domain with fewer than "
750 << kSmallDomainSize << " values.";
751
752 ConstraintScaler scaler;
753 const int64_t kScalingTarget = int64_t{1}
754 << params.mip_max_activity_exponent();
755 scaler.wanted_precision = kWantedPrecision;
756 scaler.scaling_target = kScalingTarget;
757
758 // Add the constraints. We scale each of them individually.
759 for (const MPConstraintProto& mp_constraint : mp_model.constraint()) {
760 scaler.AddConstraint(mp_model, mp_constraint, cp_model);
761 }
762 for (const MPGeneralConstraintProto& general_constraint :
763 mp_model.general_constraint()) {
764 switch (general_constraint.general_constraint_case()) {
766 const auto& indicator_constraint =
767 general_constraint.indicator_constraint();
768 const MPConstraintProto& mp_constraint =
769 indicator_constraint.constraint();
771 scaler.AddConstraint(mp_model, mp_constraint, cp_model);
772 if (ct == nullptr) continue;
773
774 // Add the indicator.
775 const int var = indicator_constraint.var_index();
776 const int value = indicator_constraint.var_value();
777 ct->add_enforcement_literal(value == 1 ? var : NegatedRef(var));
778 break;
779 }
781 const auto& and_constraint = general_constraint.and_constraint();
782 const std::string& name = general_constraint.name();
783
784 ConstraintProto* ct_pos = cp_model->add_constraints();
785 ct_pos->set_name(name.empty() ? "" : absl::StrCat(name, "_pos"));
786 ct_pos->add_enforcement_literal(and_constraint.resultant_var_index());
787 *ct_pos->mutable_bool_and()->mutable_literals() =
788 and_constraint.var_index();
789
790 ConstraintProto* ct_neg = cp_model->add_constraints();
791 ct_neg->set_name(name.empty() ? "" : absl::StrCat(name, "_neg"));
793 NegatedRef(and_constraint.resultant_var_index()));
794 for (const int var_index : and_constraint.var_index()) {
795 ct_neg->mutable_bool_or()->add_literals(NegatedRef(var_index));
796 }
797 break;
798 }
800 const auto& or_constraint = general_constraint.or_constraint();
801 const std::string& name = general_constraint.name();
802
803 ConstraintProto* ct_pos = cp_model->add_constraints();
804 ct_pos->set_name(name.empty() ? "" : absl::StrCat(name, "_pos"));
805 ct_pos->add_enforcement_literal(or_constraint.resultant_var_index());
806 *ct_pos->mutable_bool_or()->mutable_literals() =
807 or_constraint.var_index();
808
809 ConstraintProto* ct_neg = cp_model->add_constraints();
810 ct_neg->set_name(name.empty() ? "" : absl::StrCat(name, "_neg"));
812 NegatedRef(or_constraint.resultant_var_index()));
813 for (const int var_index : or_constraint.var_index()) {
814 ct_neg->mutable_bool_and()->add_literals(NegatedRef(var_index));
815 }
816 break;
817 }
818 default:
819 LOG(ERROR) << "Can't convert general constraints of type "
820 << general_constraint.general_constraint_case()
821 << " to CpModelProto.";
822 return false;
823 }
824 }
825
826 // Display the error/scaling on the constraints.
827 SOLVER_LOG(logger, "Maximum constraint coefficient relative error: ",
828 scaler.max_relative_coeff_error);
829 SOLVER_LOG(logger, "Maximum constraint worst-case activity relative error: ",
830 scaler.max_relative_rhs_error,
831 (scaler.max_relative_rhs_error > params.mip_check_precision()
832 ? " [Potentially IMPRECISE]"
833 : ""));
834 SOLVER_LOG(logger,
835 "Maximum constraint scaling factor: ", scaler.max_scaling_factor);
836
837 // Add the objective.
838 std::vector<int> var_indices;
839 std::vector<double> coefficients;
840 std::vector<double> lower_bounds;
841 std::vector<double> upper_bounds;
842 double min_magnitude = std::numeric_limits<double>::infinity();
843 double max_magnitude = 0.0;
844 double l1_norm = 0.0;
845 for (int i = 0; i < num_variables; ++i) {
846 const MPVariableProto& mp_var = mp_model.variable(i);
847 if (mp_var.objective_coefficient() == 0.0) continue;
848
849 const auto& var_proto = cp_model->variables(i);
850 const int64_t lb = var_proto.domain(0);
851 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
852 if (lb == 0 && ub == 0) continue;
853
854 var_indices.push_back(i);
855 coefficients.push_back(mp_var.objective_coefficient());
856 lower_bounds.push_back(lb);
857 upper_bounds.push_back(ub);
858 min_magnitude = std::min(min_magnitude, std::abs(coefficients.back()));
859 max_magnitude = std::max(max_magnitude, std::abs(coefficients.back()));
860 l1_norm += std::abs(coefficients.back());
861 }
862 if (!coefficients.empty()) {
863 const double average_magnitude =
864 l1_norm / static_cast<double>(coefficients.size());
865 SOLVER_LOG(logger, "Objective magnitude in [", min_magnitude, ", ",
866 max_magnitude, "] average = ", average_magnitude);
867 }
868 if (!coefficients.empty() || mp_model.objective_offset() != 0.0) {
869 double scaling_factor = GetBestScalingOfDoublesToInt64(
870 coefficients, lower_bounds, upper_bounds, kScalingTarget);
871 if (scaling_factor == 0.0) {
872 LOG(ERROR) << "Scaling factor of zero while scaling objective! This "
873 "likely indicate an infinite coefficient in the objective.";
874 return false;
875 }
876
877 // Returns the smallest factor of the form 2^i that gives us an absolute
878 // error of kWantedPrecision and still make sure we will have no integer
879 // overflow.
880 //
881 // TODO(user): Make this faster.
882 double x = std::min(scaling_factor, 1.0);
883 double relative_coeff_error;
884 double scaled_sum_error;
885 for (; x <= scaling_factor; x *= 2) {
887 &relative_coeff_error, &scaled_sum_error);
888 if (scaled_sum_error < kWantedPrecision * x) break;
889 }
890 scaling_factor = x;
891
892 // Same remark as for the constraint.
893 // TODO(user): Extract common code.
894 const double integer_factor = FindFractionalScaling(coefficients, 1e-8);
895 if (integer_factor != 0 && integer_factor < scaling_factor) {
897 &relative_coeff_error, &scaled_sum_error);
898 if (scaled_sum_error < kWantedPrecision * integer_factor) {
899 scaling_factor = integer_factor;
900 }
901 }
902
903 const int64_t gcd =
905
906 // Display the objective error/scaling.
908 logger, "Objective coefficient relative error: ", relative_coeff_error,
909 (relative_coeff_error > params.mip_check_precision() ? " [IMPRECISE]"
910 : ""));
911 SOLVER_LOG(logger, "Objective worst-case absolute error: ",
912 scaled_sum_error / scaling_factor);
913 SOLVER_LOG(logger, "Objective scaling factor: ", scaling_factor / gcd);
914
915 // Note that here we set the scaling factor for the inverse operation of
916 // getting the "true" objective value from the scaled one. Hence the
917 // inverse.
918 auto* objective = cp_model->mutable_objective();
919 const int mult = mp_model.maximize() ? -1 : 1;
920 objective->set_offset(mp_model.objective_offset() * scaling_factor / gcd *
921 mult);
922 objective->set_scaling_factor(1.0 / scaling_factor * gcd * mult);
923 for (int i = 0; i < coefficients.size(); ++i) {
924 const int64_t value =
925 static_cast<int64_t>(std::round(coefficients[i] * scaling_factor)) /
926 gcd;
927 if (value != 0) {
928 objective->add_vars(var_indices[i]);
929 objective->add_coeffs(value * mult);
930 }
931 }
932 }
933
934 return true;
935}
936
938 LinearBooleanProblem* problem) {
939 CHECK(problem != nullptr);
940 problem->Clear();
941 problem->set_name(mp_model.name());
942 const int num_variables = mp_model.variable_size();
943 problem->set_num_variables(num_variables);
944
945 // Test if the variables are binary variables.
946 // Add constraints for the fixed variables.
947 for (int var_id(0); var_id < num_variables; ++var_id) {
948 const MPVariableProto& mp_var = mp_model.variable(var_id);
949 problem->add_var_names(mp_var.name());
950
951 // This will be changed to false as soon as we detect the variable to be
952 // non-binary. This is done this way so we can display a nice error message
953 // before aborting the function and returning false.
954 bool is_binary = mp_var.is_integer();
955
956 const Fractional lb = mp_var.lower_bound();
957 const Fractional ub = mp_var.upper_bound();
958 if (lb <= -1.0) is_binary = false;
959 if (ub >= 2.0) is_binary = false;
960 if (is_binary) {
961 // 4 cases.
962 if (lb <= 0.0 && ub >= 1.0) {
963 // Binary variable. Ok.
964 } else if (lb <= 1.0 && ub >= 1.0) {
965 // Fixed variable at 1.
966 LinearBooleanConstraint* constraint = problem->add_constraints();
967 constraint->set_lower_bound(1);
968 constraint->set_upper_bound(1);
969 constraint->add_literals(var_id + 1);
970 constraint->add_coefficients(1);
971 } else if (lb <= 0.0 && ub >= 0.0) {
972 // Fixed variable at 0.
973 LinearBooleanConstraint* constraint = problem->add_constraints();
974 constraint->set_lower_bound(0);
975 constraint->set_upper_bound(0);
976 constraint->add_literals(var_id + 1);
977 constraint->add_coefficients(1);
978 } else {
979 // No possible integer value!
980 is_binary = false;
981 }
982 }
983
984 // Abort if the variable is not binary.
985 if (!is_binary) {
986 LOG(WARNING) << "The variable #" << var_id << " with name "
987 << mp_var.name() << " is not binary. "
988 << "lb: " << lb << " ub: " << ub;
989 return false;
990 }
991 }
992
993 // Variables needed to scale the double coefficients into int64_t.
994 const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
995 double max_relative_error = 0.0;
996 double max_bound_error = 0.0;
997 double max_scaling_factor = 0.0;
998 double relative_error = 0.0;
999 double scaling_factor = 0.0;
1000 std::vector<double> coefficients;
1001
1002 // Add all constraints.
1003 for (const MPConstraintProto& mp_constraint : mp_model.constraint()) {
1004 LinearBooleanConstraint* constraint = problem->add_constraints();
1005 constraint->set_name(mp_constraint.name());
1006
1007 // First scale the coefficients of the constraints.
1008 coefficients.clear();
1009 const int num_coeffs = mp_constraint.coefficient_size();
1010 for (int i = 0; i < num_coeffs; ++i) {
1011 coefficients.push_back(mp_constraint.coefficient(i));
1012 }
1013 GetBestScalingOfDoublesToInt64(coefficients, kInt64Max, &scaling_factor,
1014 &relative_error);
1015 const int64_t gcd =
1017 max_relative_error = std::max(relative_error, max_relative_error);
1018 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
1019
1020 double bound_error = 0.0;
1021 for (int i = 0; i < num_coeffs; ++i) {
1022 const double scaled_value = mp_constraint.coefficient(i) * scaling_factor;
1023 bound_error += std::abs(round(scaled_value) - scaled_value);
1024 const int64_t value = static_cast<int64_t>(round(scaled_value)) / gcd;
1025 if (value != 0) {
1026 constraint->add_literals(mp_constraint.var_index(i) + 1);
1027 constraint->add_coefficients(value);
1028 }
1029 }
1030 max_bound_error = std::max(max_bound_error, bound_error);
1031
1032 // Add the bounds. Note that we do not pass them to
1033 // GetBestScalingOfDoublesToInt64() because we know that the sum of absolute
1034 // coefficients of the constraint fit on an int64_t. If one of the scaled
1035 // bound overflows, we don't care by how much because in this case the
1036 // constraint is just trivial or unsatisfiable.
1037 const Fractional lb = mp_constraint.lower_bound();
1038 if (lb != -kInfinity) {
1039 if (lb * scaling_factor > static_cast<double>(kInt64Max)) {
1040 LOG(WARNING) << "A constraint is trivially unsatisfiable.";
1041 return false;
1042 }
1043 if (lb * scaling_factor > -static_cast<double>(kInt64Max)) {
1044 // Otherwise, the constraint is not needed.
1045 constraint->set_lower_bound(
1046 static_cast<int64_t>(round(lb * scaling_factor - bound_error)) /
1047 gcd);
1048 }
1049 }
1050 const Fractional ub = mp_constraint.upper_bound();
1051 if (ub != kInfinity) {
1052 if (ub * scaling_factor < -static_cast<double>(kInt64Max)) {
1053 LOG(WARNING) << "A constraint is trivially unsatisfiable.";
1054 return false;
1055 }
1056 if (ub * scaling_factor < static_cast<double>(kInt64Max)) {
1057 // Otherwise, the constraint is not needed.
1058 constraint->set_upper_bound(
1059 static_cast<int64_t>(round(ub * scaling_factor + bound_error)) /
1060 gcd);
1061 }
1062 }
1063 }
1064
1065 // Display the error/scaling without taking into account the objective first.
1066 LOG(INFO) << "Maximum constraint relative error: " << max_relative_error;
1067 LOG(INFO) << "Maximum constraint bound error: " << max_bound_error;
1068 LOG(INFO) << "Maximum constraint scaling factor: " << max_scaling_factor;
1069
1070 // Add the objective.
1071 coefficients.clear();
1072 for (int var_id = 0; var_id < num_variables; ++var_id) {
1073 const MPVariableProto& mp_var = mp_model.variable(var_id);
1074 coefficients.push_back(mp_var.objective_coefficient());
1075 }
1076 GetBestScalingOfDoublesToInt64(coefficients, kInt64Max, &scaling_factor,
1077 &relative_error);
1078 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
1079 max_relative_error = std::max(relative_error, max_relative_error);
1080
1081 // Display the objective error/scaling.
1082 LOG(INFO) << "objective relative error: " << relative_error;
1083 LOG(INFO) << "objective scaling factor: " << scaling_factor / gcd;
1084
1085 LinearObjective* objective = problem->mutable_objective();
1086 objective->set_offset(mp_model.objective_offset() * scaling_factor / gcd);
1087
1088 // Note that here we set the scaling factor for the inverse operation of
1089 // getting the "true" objective value from the scaled one. Hence the inverse.
1090 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
1091 for (int var_id = 0; var_id < num_variables; ++var_id) {
1092 const MPVariableProto& mp_var = mp_model.variable(var_id);
1093 const int64_t value =
1094 static_cast<int64_t>(
1095 round(mp_var.objective_coefficient() * scaling_factor)) /
1096 gcd;
1097 if (value != 0) {
1098 objective->add_literals(var_id + 1);
1099 objective->add_coefficients(value);
1100 }
1101 }
1102
1103 // If the problem was a maximization one, we need to modify the objective.
1104 if (mp_model.maximize()) ChangeOptimizationDirection(problem);
1105
1106 // Test the precision of the conversion.
1107 const double kRelativeTolerance = 1e-8;
1108 if (max_relative_error > kRelativeTolerance) {
1109 LOG(WARNING) << "The relative error during double -> int64_t conversion "
1110 << "is too high!";
1111 return false;
1112 }
1113 return true;
1114}
1115
1117 glop::LinearProgram* lp) {
1118 lp->Clear();
1119 for (int i = 0; i < problem.num_variables(); ++i) {
1120 const ColIndex col = lp->CreateNewVariable();
1122 lp->SetVariableBounds(col, 0.0, 1.0);
1123 }
1124
1125 // Variables name are optional.
1126 if (problem.var_names_size() != 0) {
1127 CHECK_EQ(problem.var_names_size(), problem.num_variables());
1128 for (int i = 0; i < problem.num_variables(); ++i) {
1129 lp->SetVariableName(ColIndex(i), problem.var_names(i));
1130 }
1131 }
1132
1133 for (const LinearBooleanConstraint& constraint : problem.constraints()) {
1134 const RowIndex constraint_index = lp->CreateNewConstraint();
1135 lp->SetConstraintName(constraint_index, constraint.name());
1136 double sum = 0.0;
1137 for (int i = 0; i < constraint.literals_size(); ++i) {
1138 const int literal = constraint.literals(i);
1139 const double coeff = constraint.coefficients(i);
1140 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1141 if (literal < 0) {
1142 sum += coeff;
1143 lp->SetCoefficient(constraint_index, variable_index, -coeff);
1144 } else {
1145 lp->SetCoefficient(constraint_index, variable_index, coeff);
1146 }
1147 }
1149 constraint_index,
1150 constraint.has_lower_bound() ? constraint.lower_bound() - sum
1151 : -kInfinity,
1152 constraint.has_upper_bound() ? constraint.upper_bound() - sum
1153 : kInfinity);
1154 }
1155
1156 // Objective.
1157 {
1158 double sum = 0.0;
1159 const LinearObjective& objective = problem.objective();
1160 const double scaling_factor = objective.scaling_factor();
1161 for (int i = 0; i < objective.literals_size(); ++i) {
1162 const int literal = objective.literals(i);
1163 const double coeff =
1164 static_cast<double>(objective.coefficients(i)) * scaling_factor;
1165 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1166 if (literal < 0) {
1167 sum += coeff;
1168 lp->SetObjectiveCoefficient(variable_index, -coeff);
1169 } else {
1170 lp->SetObjectiveCoefficient(variable_index, coeff);
1171 }
1172 }
1173 lp->SetObjectiveOffset((sum + objective.offset()) * scaling_factor);
1174 lp->SetMaximizationProblem(scaling_factor < 0);
1175 }
1176
1177 lp->CleanUp();
1178}
1179
1181 int num_fixed_variables = 0;
1182 const Trail& trail = solver.LiteralTrail();
1183 for (int i = 0; i < trail.Index(); ++i) {
1184 const BooleanVariable var = trail[i].Variable();
1185 const int value = trail[i].IsPositive() ? 1.0 : 0.0;
1186 if (trail.Info(var).level == 0) {
1187 ++num_fixed_variables;
1188 lp->SetVariableBounds(ColIndex(var.value()), value, value);
1189 }
1190 }
1191 return num_fixed_variables;
1192}
1193
1195 const glop::LinearProgram& lp, SatSolver* sat_solver,
1196 double max_time_in_seconds) {
1197 glop::LPSolver solver;
1198 glop::GlopParameters glop_parameters;
1199 glop_parameters.set_max_time_in_seconds(max_time_in_seconds);
1200 solver.SetParameters(glop_parameters);
1201 const glop::ProblemStatus& status = solver.Solve(lp);
1202 if (status != glop::ProblemStatus::OPTIMAL &&
1205 return false;
1206 }
1207 for (ColIndex col(0); col < lp.num_variables(); ++col) {
1208 const Fractional& value = solver.variable_values()[col];
1209 sat_solver->SetAssignmentPreference(
1210 Literal(BooleanVariable(col.value()), round(value) == 1),
1211 1 - std::abs(value - round(value)));
1212 }
1213 return true;
1214}
1215
1217 LinearBooleanProblem* problem) {
1218 glop::LPSolver solver;
1219 const glop::ProblemStatus& status = solver.Solve(lp);
1220 if (status != glop::ProblemStatus::OPTIMAL &&
1222 return false;
1223 int num_variable_fixed = 0;
1224 for (ColIndex col(0); col < lp.num_variables(); ++col) {
1225 const Fractional tolerance = 1e-5;
1226 const Fractional& value = solver.variable_values()[col];
1227 if (value > 1 - tolerance) {
1228 ++num_variable_fixed;
1229 LinearBooleanConstraint* constraint = problem->add_constraints();
1230 constraint->set_lower_bound(1);
1231 constraint->set_upper_bound(1);
1232 constraint->add_coefficients(1);
1233 constraint->add_literals(col.value() + 1);
1234 } else if (value < tolerance) {
1235 ++num_variable_fixed;
1236 LinearBooleanConstraint* constraint = problem->add_constraints();
1237 constraint->set_lower_bound(0);
1238 constraint->set_upper_bound(0);
1239 constraint->add_coefficients(1);
1240 constraint->add_literals(col.value() + 1);
1241 }
1242 }
1243 LOG(INFO) << "LNS with " << num_variable_fixed << " fixed variables.";
1244 return true;
1245}
1246
1247} // namespace sat
1248} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define LOG_IF(severity, condition)
Definition: base/logging.h:475
#define CHECK(condition)
Definition: base/logging.h:491
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
#define LOG(severity)
Definition: base/logging.h:416
#define DCHECK(condition)
Definition: base/logging.h:885
#define VLOG(verboselevel)
Definition: base/logging.h:979
::PROTOBUF_NAMESPACE_ID::int32 var_index(int index) const
::operations_research::MPConstraintProto * mutable_constraint(int index)
const ::operations_research::MPConstraintProto & constraint(int index) const
const std::string & name() const
const ::operations_research::MPVariableProto & variable(int index) const
::operations_research::MPVariableProto * mutable_variable(int index)
const ::operations_research::MPGeneralConstraintProto & general_constraint(int index) const
const std::string & name() const
const DenseRow & variable_values() const
Definition: lp_solver.h:100
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:132
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:116
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:249
void SetConstraintName(RowIndex row, absl::string_view name)
Definition: lp_data.cc:245
void SetObjectiveOffset(Fractional objective_offset)
Definition: lp_data.cc:331
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:317
void SetVariableName(ColIndex col, absl::string_view name)
Definition: lp_data.cc:232
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:309
void SetVariableType(ColIndex col, VariableType type)
Definition: lp_data.cc:236
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:326
void SetMaximizationProblem(bool maximize)
Definition: lp_data.cc:343
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_literals()
Definition: cp_model.pb.h:6973
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:6955
void set_name(ArgT0 &&arg0, ArgT... args)
::operations_research::sat::BoolArgumentProto * mutable_bool_and()
Definition: cp_model.pb.h:9556
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:9391
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
Definition: cp_model.pb.h:9482
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
void set_name(ArgT0 &&arg0, ArgT... args)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
::operations_research::sat::ConstraintProto * add_constraints()
::operations_research::sat::IntegerVariableProto * add_variables()
::operations_research::sat::CpObjectiveProto * mutable_objective()
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:6904
void set_name(ArgT0 &&arg0, ArgT... args)
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
Definition: cp_model.pb.h:6893
void add_coefficients(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_upper_bound(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_name(ArgT0 &&arg0, ArgT... args)
void set_lower_bound(::PROTOBUF_NAMESPACE_ID::int64 value)
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::sat::LinearBooleanConstraint * add_constraints()
void set_name(ArgT0 &&arg0, ArgT... args)
void set_num_variables(::PROTOBUF_NAMESPACE_ID::int32 value)
const ::operations_research::sat::LinearObjective & objective() const
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
::PROTOBUF_NAMESPACE_ID::int32 num_variables() const
const ::operations_research::sat::LinearBooleanConstraint & constraints(int index) const
const std::string & var_names(int index) const
::operations_research::sat::LinearObjective * mutable_objective()
void add_coefficients(::PROTOBUF_NAMESPACE_ID::int64 value)
::PROTOBUF_NAMESPACE_ID::int64 coefficients(int index) const
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
::PROTOBUF_NAMESPACE_ID::int32 literals(int index) const
::PROTOBUF_NAMESPACE_ID::int32 mip_max_activity_exponent() const
const Trail & LiteralTrail() const
Definition: sat_solver.h:362
void SetAssignmentPreference(Literal literal, double weight)
Definition: sat_solver.h:151
const AssignmentInfo & Info(BooleanVariable var) const
Definition: sat_base.h:382
const std::string name
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double lower_bound
const int WARNING
Definition: log_severity.h:31
const int INFO
Definition: log_severity.h:31
const int ERROR
Definition: log_severity.h:32
const int FATAL
Definition: log_severity.h:32
ColIndex col
Definition: markowitz.cc:183
const double kInfinity
Definition: lp_types.h:84
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
bool SolveLpAndUseSolutionForSatAssignmentPreference(const glop::LinearProgram &lp, SatSolver *sat_solver, double max_time_in_seconds)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:82
void ConvertBooleanProblemToLinearProgram(const LinearBooleanProblem &problem, glop::LinearProgram *lp)
bool ConvertBinaryMPModelProtoToBooleanProblem(const MPModelProto &mp_model, LinearBooleanProblem *problem)
void RemoveNearZeroTerms(const SatParameters &params, MPModelProto *mp_model, SolverLogger *logger)
bool ConvertMPModelProtoToCpModelProto(const SatParameters &params, const MPModelProto &mp_model, CpModelProto *cp_model, SolverLogger *logger)
bool SolveLpAndUseIntegerVariableToStartLNS(const glop::LinearProgram &lp, LinearBooleanProblem *problem)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
int FixVariablesFromSat(const SatSolver &solver, glop::LinearProgram *lp)
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
int FindRationalFactor(double x, int limit, double tolerance)
Collection of objects used to extend the Constraint Solver library.
void ComputeScalingErrors(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, double scaling_factor, double *max_relative_coeff_error, double *max_scaled_sum_error)
Definition: fp_utils.cc:170
int64_t ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
Definition: fp_utils.cc:200
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64_t max_absolute_sum)
Definition: fp_utils.cc:179
Literal literal
Definition: optimization.cc:85
int64_t bound
double max_scaling_factor
int64_t scaling_target
double max_relative_coeff_error
std::vector< double > lower_bounds
double wanted_precision
std::vector< int > var_indices
std::vector< double > upper_bounds
std::vector< double > coefficients
double max_relative_rhs_error
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63