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 
14 #include "ortools/sat/lp_utils.h"
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"
26 #include "ortools/base/int_type.h"
28 #include "ortools/base/logging.h"
29 #include "ortools/glop/lp_solver.h"
34 #include "ortools/sat/integer.h"
35 #include "ortools/sat/sat_base.h"
36 #include "ortools/util/fp_utils.h"
37 
38 namespace operations_research {
39 namespace sat {
40 
41 using glop::ColIndex;
42 using glop::Fractional;
43 using glop::kInfinity;
44 using glop::RowIndex;
45 
49 
50 namespace {
51 
52 void 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 
62 void 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 
100 std::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.
119 int 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 
138 namespace {
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.
144 double 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 
182 void 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) {
206  MPConstraintProto* ct = mp_model->mutable_constraint(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 
235 std::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 
480 namespace {
481 
482 // We use a class to reuse the temporary memory.
483 struct 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 
501 namespace {
502 
503 double 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 
516 ConstraintProto* 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"));
792  ct_neg->add_enforcement_literal(
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"));
811  ct_neg->add_enforcement_literal(
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 =
904  ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
905 
906  // Display the objective error/scaling.
907  SOLVER_LOG(
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 =
1016  ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
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  }
1148  lp->SetConstraintBounds(
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 &&
1203  status != glop::ProblemStatus::IMPRECISE &&
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
#define CHECK(condition)
Definition: base/logging.h:491
bool ConvertBinaryMPModelProtoToBooleanProblem(const MPModelProto &mp_model, LinearBooleanProblem *problem)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
int64_t bound
bool SolveLpAndUseSolutionForSatAssignmentPreference(const glop::LinearProgram &lp, SatSolver *sat_solver, double max_time_in_seconds)
void add_coefficients(::PROTOBUF_NAMESPACE_ID::int64 value)
int64_t min
Definition: alldiff_cst.cc:139
bool SolveLpAndUseIntegerVariableToStartLNS(const glop::LinearProgram &lp, LinearBooleanProblem *problem)
void set_upper_bound(::PROTOBUF_NAMESPACE_ID::int64 value)
const ::operations_research::MPGeneralConstraintProto & general_constraint(int index) const
::PROTOBUF_NAMESPACE_ID::int64 coefficients(int index) const
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:9275
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:326
void set_lower_bound(::PROTOBUF_NAMESPACE_ID::int64 value)
::operations_research::sat::LinearObjective * mutable_objective()
const int FATAL
Definition: log_severity.h:32
const ::operations_research::sat::LinearBooleanConstraint & constraints(int index) const
std::vector< double > coefficients
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#define VLOG(verboselevel)
Definition: base/logging.h:979
::PROTOBUF_NAMESPACE_ID::int32 num_variables() const
std::vector< double > lower_bounds
const std::string name
const int ERROR
Definition: log_severity.h:32
const std::string & name() const
::PROTOBUF_NAMESPACE_ID::int32 var_index(int index) const
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
void SetVariableType(ColIndex col, VariableType type)
Definition: lp_data.cc:236
void SetVariableName(ColIndex col, absl::string_view name)
Definition: lp_data.cc:232
const ::operations_research::sat::LinearObjective & objective() const
#define LOG(severity)
Definition: base/logging.h:416
ColIndex col
Definition: markowitz.cc:183
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:6839
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:309
void SetObjectiveOffset(Fractional objective_offset)
Definition: lp_data.cc:331
void RemoveNearZeroTerms(const SatParameters &params, MPModelProto *mp_model, SolverLogger *logger)
::operations_research::sat::IntegerVariableProto * add_variables()
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
double max_scaling_factor
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_literals()
Definition: cp_model.pb.h:6857
const ::operations_research::MPConstraintProto & constraint(int index) const
double max_relative_coeff_error
int FindRationalFactor(double x, int limit, double tolerance)
bool ConvertMPModelProtoToCpModelProto(const SatParameters &params, const MPModelProto &mp_model, CpModelProto *cp_model, SolverLogger *logger)
const DenseRow & variable_values() const
Definition: lp_solver.h:100
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:116
void set_num_variables(::PROTOBUF_NAMESPACE_ID::int32 value)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
int FixVariablesFromSat(const SatSolver &solver, glop::LinearProgram *lp)
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
void set_name(ArgT0 &&arg0, ArgT... args)
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
Definition: cp_model.pb.h:9366
int64_t max
Definition: alldiff_cst.cc:140
::operations_research::MPConstraintProto * mutable_constraint(int index)
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
int64_t ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
Definition: fp_utils.cc:200
void set_name(ArgT0 &&arg0, ArgT... args)
const int WARNING
Definition: log_severity.h:31
::operations_research::sat::BoolArgumentProto * mutable_bool_and()
Definition: cp_model.pb.h:9440
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
const ::operations_research::MPVariableProto & variable(int index) const
::PROTOBUF_NAMESPACE_ID::int32 literals(int index) const
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::sat::ConstraintProto * add_constraints()
double wanted_precision
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:6788
const double kInfinity
Definition: lp_types.h:84
const std::string & name() const
const std::string & var_names(int index) const
void set_name(ArgT0 &&arg0, ArgT... args)
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define LOG_IF(severity, condition)
Definition: base/logging.h:475
#define DCHECK(condition)
Definition: base/logging.h:885
void SetAssignmentPreference(Literal literal, double weight)
Definition: sat_solver.h:151
void set_name(ArgT0 &&arg0, ArgT... args)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
void add_coefficients(::PROTOBUF_NAMESPACE_ID::int64 value)
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:249
void set_name(ArgT0 &&arg0, ArgT... args)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:82
::operations_research::MPVariableProto * mutable_variable(int index)
::operations_research::sat::LinearBooleanConstraint * add_constraints()
void SetMaximizationProblem(bool maximize)
Definition: lp_data.cc:343
double max_relative_rhs_error
Collection of objects used to extend the Constraint Solver library.
::operations_research::sat::CpObjectiveProto * mutable_objective()
int64_t scaling_target
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
std::vector< double > upper_bounds
IntVar * var
Definition: expr_array.cc:1874
const Trail & LiteralTrail() const
Definition: sat_solver.h:362
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
Definition: cp_model.pb.h:6777
void ConvertBooleanProblemToLinearProgram(const LinearBooleanProblem &problem, glop::LinearProgram *lp)
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:132
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:317
::PROTOBUF_NAMESPACE_ID::int32 mip_max_activity_exponent() const
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
void SetConstraintName(RowIndex row, absl::string_view name)
Definition: lp_data.cc:245
int64_t value
Literal literal
Definition: optimization.cc:85
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
const AssignmentInfo & Info(BooleanVariable var) const
Definition: sat_base.h:382
const Constraint * ct
std::vector< int > var_indices
const int INFO
Definition: log_severity.h:31