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