OR-Tools  8.2
cp_model_solver.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 
15 
16 #include <algorithm>
17 #include <atomic>
18 #include <cmath>
19 #include <functional>
20 #include <limits>
21 #include <map>
22 #include <memory>
23 #include <set>
24 #include <utility>
25 #include <vector>
26 
27 #include "ortools/base/cleanup.h"
29 #include "ortools/sat/intervals.h"
30 
31 #if !defined(__PORTABLE_PLATFORM__)
32 #include "absl/synchronization/notification.h"
33 #include "google/protobuf/text_format.h"
34 #include "ortools/base/file.h"
35 #include "ortools/util/sigint.h"
36 #endif // __PORTABLE_PLATFORM__
37 
38 #include "absl/container/flat_hash_set.h"
39 #include "absl/memory/memory.h"
40 #include "absl/status/status.h"
41 #include "absl/strings/str_cat.h"
42 #include "absl/strings/str_format.h"
43 #include "absl/strings/str_join.h"
44 #include "absl/synchronization/mutex.h"
46 #include "ortools/base/int_type.h"
48 #include "ortools/base/logging.h"
49 #include "ortools/base/map_util.h"
51 #include "ortools/base/timer.h"
55 #include "ortools/sat/circuit.h"
56 #include "ortools/sat/clause.h"
65 #include "ortools/sat/cuts.h"
68 #include "ortools/sat/integer.h"
75 #include "ortools/sat/probing.h"
76 #include "ortools/sat/rins.h"
77 #include "ortools/sat/sat_base.h"
80 #include "ortools/sat/sat_solver.h"
82 #include "ortools/sat/subsolver.h"
86 
87 #if defined(_MSC_VER)
88 ABSL_FLAG(std::string, cp_model_dump_prefix, ".\\",
89  "Prefix filename for all dumped files");
90 #else
91 ABSL_FLAG(std::string, cp_model_dump_prefix, "/tmp/",
92  "Prefix filename for all dumped files");
93 #endif
94 ABSL_FLAG(bool, cp_model_dump_models, false,
95  "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
96  "protos (original model, presolved model, mapping model) in text "
97  "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
98  "mapping_model}.pbtxt.");
99 
100 ABSL_FLAG(bool, cp_model_dump_lns, false,
101  "DEBUG ONLY. When set to true, solve will dump all "
102  "lns models proto in text format to "
103  "'FLAGS_cp_model_dump_prefix'lns_xxx.pbtxt.");
104 
105 ABSL_FLAG(bool, cp_model_dump_response, false,
106  "DEBUG ONLY. If true, the final response of each solve will be "
107  "dumped to 'FLAGS_cp_model_dump_prefix'response.pbtxt");
108 
109 ABSL_FLAG(std::string, cp_model_params, "",
110  "This is interpreted as a text SatParameters proto. The "
111  "specified fields will override the normal ones for all solves.");
112 
113 ABSL_FLAG(std::string, drat_output, "",
114  "If non-empty, a proof in DRAT format will be written to this file. "
115  "This will only be used for pure-SAT problems.");
116 
117 ABSL_FLAG(bool, drat_check, false,
118  "If true, a proof in DRAT format will be stored in memory and "
119  "checked if the problem is UNSAT. This will only be used for "
120  "pure-SAT problems.");
121 
122 ABSL_FLAG(double, max_drat_time_in_seconds,
123  std::numeric_limits<double>::infinity(),
124  "Maximum time in seconds to check the DRAT proof. This will only "
125  "be used is the drat_check flag is enabled.");
126 
127 ABSL_FLAG(bool, cp_model_check_intermediate_solutions, false,
128  "When true, all intermediate solutions found by the solver will be "
129  "checked. This can be expensive, therefore it is off by default.");
130 
131 namespace operations_research {
132 namespace sat {
133 
134 namespace {
135 
136 // Makes the string fit in one line by cutting it in the middle if necessary.
137 std::string Summarize(const std::string& input) {
138  if (input.size() < 105) return input;
139  const int half = 50;
140  return absl::StrCat(input.substr(0, half), " ... ",
141  input.substr(input.size() - half, half));
142 }
143 
144 } // namespace.
145 
146 // =============================================================================
147 // Public API.
148 // =============================================================================
149 
150 std::string CpModelStats(const CpModelProto& model_proto) {
151  std::map<std::string, int> num_constraints_by_name;
152  std::map<std::string, int> num_reif_constraints_by_name;
153  std::map<std::string, int> name_to_num_literals;
154  std::map<std::string, int> name_to_num_terms;
155  for (const ConstraintProto& ct : model_proto.constraints()) {
156  std::string name = ConstraintCaseName(ct.constraint_case());
157 
158  // We split the linear constraints into 3 buckets has it gives more insight
159  // on the type of problem we are facing.
160  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
161  if (ct.linear().vars_size() == 1) name += "1";
162  if (ct.linear().vars_size() == 2) name += "2";
163  if (ct.linear().vars_size() == 3) name += "3";
164  if (ct.linear().vars_size() > 3) name += "N";
165  }
166 
167  num_constraints_by_name[name]++;
168  if (!ct.enforcement_literal().empty()) {
169  num_reif_constraints_by_name[name]++;
170  }
171 
172  // For pure Boolean constraints, we also display the total number of literal
173  // involved as this gives a good idea of the problem size.
174  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
175  name_to_num_literals[name] += ct.bool_or().literals().size();
176  } else if (ct.constraint_case() ==
177  ConstraintProto::ConstraintCase::kBoolAnd) {
178  name_to_num_literals[name] += ct.bool_and().literals().size();
179  } else if (ct.constraint_case() ==
180  ConstraintProto::ConstraintCase::kAtMostOne) {
181  name_to_num_literals[name] += ct.at_most_one().literals().size();
182  } else if (ct.constraint_case() ==
183  ConstraintProto::ConstraintCase::kExactlyOne) {
184  name_to_num_literals[name] += ct.exactly_one().literals().size();
185  }
186 
187  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
188  ct.linear().vars_size() > 3) {
189  name_to_num_terms[name] += ct.linear().vars_size();
190  }
191  }
192 
193  int num_constants = 0;
194  std::set<int64> constant_values;
195  std::map<Domain, int> num_vars_per_domains;
196  for (const IntegerVariableProto& var : model_proto.variables()) {
197  if (var.domain_size() == 2 && var.domain(0) == var.domain(1)) {
198  ++num_constants;
199  constant_values.insert(var.domain(0));
200  } else {
201  num_vars_per_domains[ReadDomainFromProto(var)]++;
202  }
203  }
204 
205  std::string result;
206  if (model_proto.has_objective()) {
207  absl::StrAppend(&result, "Optimization model '", model_proto.name(),
208  "':\n");
209  } else {
210  absl::StrAppend(&result, "Satisfaction model '", model_proto.name(),
211  "':\n");
212  }
213 
214  for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
215  absl::StrAppend(
216  &result, "Search strategy: on ", strategy.variables_size(),
217  " variables, ",
218  ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
219  strategy.variable_selection_strategy()),
220  ", ",
221  ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
222  strategy.domain_reduction_strategy()),
223  "\n");
224  }
225 
226  const std::string objective_string =
227  model_proto.has_objective()
228  ? absl::StrCat(" (", model_proto.objective().vars_size(),
229  " in objective)")
230  : "";
231  absl::StrAppend(&result, "#Variables: ", model_proto.variables_size(),
232  objective_string, "\n");
233  if (num_vars_per_domains.size() < 100) {
234  for (const auto& entry : num_vars_per_domains) {
235  const std::string temp = absl::StrCat(" - ", entry.second, " in ",
236  entry.first.ToString(), "\n");
237  absl::StrAppend(&result, Summarize(temp));
238  }
239  } else {
240  int64 max_complexity = 0;
241  int64 min = kint64max;
242  int64 max = kint64min;
243  for (const auto& entry : num_vars_per_domains) {
244  min = std::min(min, entry.first.Min());
245  max = std::max(max, entry.first.Max());
246  max_complexity = std::max(max_complexity,
247  static_cast<int64>(entry.first.NumIntervals()));
248  }
249  absl::StrAppend(&result, " - ", num_vars_per_domains.size(),
250  " different domains in [", min, ",", max,
251  "] with a largest complexity of ", max_complexity, ".\n");
252  }
253 
254  if (num_constants > 0) {
255  const std::string temp =
256  absl::StrCat(" - ", num_constants, " constants in {",
257  absl::StrJoin(constant_values, ","), "} \n");
258  absl::StrAppend(&result, Summarize(temp));
259  }
260 
261  std::vector<std::string> constraints;
262  constraints.reserve(num_constraints_by_name.size());
263  for (const auto& entry : num_constraints_by_name) {
264  const std::string& name = entry.first;
265  constraints.push_back(absl::StrCat("#", name, ": ", entry.second));
266  if (gtl::ContainsKey(num_reif_constraints_by_name, name)) {
267  absl::StrAppend(&constraints.back(),
268  " (#enforced: ", num_reif_constraints_by_name[name], ")");
269  }
270  if (gtl::ContainsKey(name_to_num_literals, name)) {
271  absl::StrAppend(&constraints.back(),
272  " (#literals: ", name_to_num_literals[name], ")");
273  }
274  if (gtl::ContainsKey(name_to_num_terms, name)) {
275  absl::StrAppend(&constraints.back(),
276  " (#terms: ", name_to_num_terms[name], ")");
277  }
278  }
279  std::sort(constraints.begin(), constraints.end());
280  absl::StrAppend(&result, absl::StrJoin(constraints, "\n"));
281 
282  return result;
283 }
284 
285 std::string CpSolverResponseStats(const CpSolverResponse& response,
286  bool has_objective) {
287  std::string result;
288  absl::StrAppend(&result, "CpSolverResponse:");
289  absl::StrAppend(&result, "\nstatus: ",
290  ProtoEnumToString<CpSolverStatus>(response.status()));
291 
292  if (has_objective && response.status() != CpSolverStatus::INFEASIBLE) {
293  absl::StrAppendFormat(&result, "\nobjective: %.16g",
294  response.objective_value());
295  absl::StrAppendFormat(&result, "\nbest_bound: %.16g",
296  response.best_objective_bound());
297  } else {
298  absl::StrAppend(&result, "\nobjective: NA");
299  absl::StrAppend(&result, "\nbest_bound: NA");
300  }
301 
302  absl::StrAppend(&result, "\nbooleans: ", response.num_booleans());
303  absl::StrAppend(&result, "\nconflicts: ", response.num_conflicts());
304  absl::StrAppend(&result, "\nbranches: ", response.num_branches());
305 
306  // TODO(user): This is probably better named "binary_propagation", but we just
307  // output "propagations" to be consistent with sat/analyze.sh.
308  absl::StrAppend(&result,
309  "\npropagations: ", response.num_binary_propagations());
310  absl::StrAppend(
311  &result, "\ninteger_propagations: ", response.num_integer_propagations());
312 
313  absl::StrAppend(&result, "\nrestarts: ", response.num_restarts());
314  absl::StrAppend(&result, "\nlp_iterations: ", response.num_lp_iterations());
315  absl::StrAppend(&result, "\nwalltime: ", response.wall_time());
316  absl::StrAppend(&result, "\nusertime: ", response.user_time());
317  absl::StrAppend(&result,
318  "\ndeterministic_time: ", response.deterministic_time());
319  absl::StrAppend(&result, "\nprimal_integral: ", response.primal_integral());
320  absl::StrAppend(&result, "\n");
321  return result;
322 }
323 
324 namespace {
325 
326 void FillSolutionInResponse(const CpModelProto& model_proto, const Model& model,
327  CpSolverResponse* response) {
328  response->clear_solution();
329  response->clear_solution_lower_bounds();
330  response->clear_solution_upper_bounds();
331 
332  auto* mapping = model.Get<CpModelMapping>();
333  auto* trail = model.Get<Trail>();
334  auto* integer_trail = model.Get<IntegerTrail>();
335 
336  std::vector<int64> solution;
337  for (int i = 0; i < model_proto.variables_size(); ++i) {
338  if (mapping->IsInteger(i)) {
339  const IntegerVariable var = mapping->Integer(i);
340  if (integer_trail->IsCurrentlyIgnored(var)) {
341  // This variable is "ignored" so it may not be fixed, simply use
342  // the current lower bound. Any value in its domain should lead to
343  // a feasible solution.
344  solution.push_back(model.Get(LowerBound(var)));
345  } else {
346  if (model.Get(LowerBound(var)) != model.Get(UpperBound(var))) {
347  solution.clear();
348  break;
349  }
350  solution.push_back(model.Get(Value(var)));
351  }
352  } else {
353  DCHECK(mapping->IsBoolean(i));
354  const Literal literal = mapping->Literal(i);
355  if (trail->Assignment().LiteralIsAssigned(literal)) {
356  solution.push_back(model.Get(Value(literal)));
357  } else {
358  solution.clear();
359  break;
360  }
361  }
362  }
363 
364  if (!solution.empty()) {
365  if (DEBUG_MODE ||
366  absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
367  // TODO(user): Checks against initial model.
369  }
370  for (const int64 value : solution) response->add_solution(value);
371  } else {
372  // Not all variables are fixed.
373  // We fill instead the lb/ub of each variables.
374  const auto& assignment = trail->Assignment();
375  for (int i = 0; i < model_proto.variables_size(); ++i) {
376  if (mapping->IsBoolean(i)) {
377  if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) {
378  const int value = model.Get(Value(mapping->Literal(i)));
379  response->add_solution_lower_bounds(value);
380  response->add_solution_upper_bounds(value);
381  } else {
382  response->add_solution_lower_bounds(0);
383  response->add_solution_upper_bounds(1);
384  }
385  } else {
386  response->add_solution_lower_bounds(
387  model.Get(LowerBound(mapping->Integer(i))));
388  response->add_solution_upper_bounds(
389  model.Get(UpperBound(mapping->Integer(i))));
390  }
391  }
392  }
393 }
394 
395 namespace {
396 
397 IntegerVariable GetOrCreateVariableWithTightBound(
398  const std::vector<std::pair<IntegerVariable, int64>>& terms, Model* model) {
399  if (terms.empty()) return model->Add(ConstantIntegerVariable(0));
400  if (terms.size() == 1 && terms.front().second == 1) {
401  return terms.front().first;
402  }
403  if (terms.size() == 1 && terms.front().second == -1) {
404  return NegationOf(terms.front().first);
405  }
406 
407  int64 sum_min = 0;
408  int64 sum_max = 0;
409  for (const std::pair<IntegerVariable, int64> var_coeff : terms) {
410  const int64 min_domain = model->Get(LowerBound(var_coeff.first));
411  const int64 max_domain = model->Get(UpperBound(var_coeff.first));
412  const int64 coeff = var_coeff.second;
413  const int64 prod1 = min_domain * coeff;
414  const int64 prod2 = max_domain * coeff;
415  sum_min += std::min(prod1, prod2);
416  sum_max += std::max(prod1, prod2);
417  }
418  return model->Add(NewIntegerVariable(sum_min, sum_max));
419 }
420 
421 IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
422  const std::vector<std::pair<IntegerVariable, int64>>& terms, Model* model) {
423  if (terms.empty()) return model->Add(ConstantIntegerVariable(0));
424  if (terms.size() == 1 && terms.front().second == 1) {
425  return terms.front().first;
426  }
427  if (terms.size() == 1 && terms.front().second == -1) {
428  return NegationOf(terms.front().first);
429  }
430 
431  // Create a new variable and link it with the linear terms.
432  const IntegerVariable new_var =
433  GetOrCreateVariableWithTightBound(terms, model);
434  std::vector<IntegerVariable> vars;
435  std::vector<int64> coeffs;
436  for (const auto& term : terms) {
437  vars.push_back(term.first);
438  coeffs.push_back(term.second);
439  }
440  vars.push_back(new_var);
441  coeffs.push_back(-1);
442  model->Add(WeightedSumLowerOrEqual(vars, coeffs, 0));
443  return new_var;
444 }
445 
446 void TryToAddCutGenerators(const CpModelProto& model_proto,
447  const ConstraintProto& ct, Model* m,
448  LinearRelaxation* relaxation) {
449  const int linearization_level =
450  m->GetOrCreate<SatParameters>()->linearization_level();
451  auto* mapping = m->GetOrCreate<CpModelMapping>();
452  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit &&
453  linearization_level > 1) {
454  std::vector<int> tails(ct.circuit().tails().begin(),
455  ct.circuit().tails().end());
456  std::vector<int> heads(ct.circuit().heads().begin(),
457  ct.circuit().heads().end());
458  std::vector<Literal> literals = mapping->Literals(ct.circuit().literals());
459  const int num_nodes = ReindexArcs(&tails, &heads);
460 
461  relaxation->cut_generators.push_back(
462  CreateStronglyConnectedGraphCutGenerator(num_nodes, tails, heads,
463  literals, m));
464  }
465  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kRoutes &&
466  linearization_level > 1) {
467  std::vector<int> tails(ct.routes().tails().begin(),
468  ct.routes().tails().end());
469  std::vector<int> heads(ct.routes().heads().begin(),
470  ct.routes().heads().end());
471  std::vector<Literal> literals = mapping->Literals(ct.routes().literals());
472 
473  int num_nodes = 0;
474  for (int i = 0; i < ct.routes().tails_size(); ++i) {
475  num_nodes = std::max(num_nodes, 1 + ct.routes().tails(i));
476  num_nodes = std::max(num_nodes, 1 + ct.routes().heads(i));
477  }
478  if (ct.routes().demands().empty() || ct.routes().capacity() == 0) {
479  relaxation->cut_generators.push_back(
480  CreateStronglyConnectedGraphCutGenerator(num_nodes, tails, heads,
481  literals, m));
482  } else {
483  const std::vector<int64> demands(ct.routes().demands().begin(),
484  ct.routes().demands().end());
485  relaxation->cut_generators.push_back(
486  CreateCVRPCutGenerator(num_nodes, tails, heads, literals, demands,
487  ct.routes().capacity(), m));
488  }
489  }
490  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntProd) {
491  if (HasEnforcementLiteral(ct)) return;
492  if (ct.int_prod().vars_size() != 2) return;
493 
494  // Constraint is z == x * y.
495 
496  IntegerVariable z = mapping->Integer(ct.int_prod().target());
497  IntegerVariable x = mapping->Integer(ct.int_prod().vars(0));
498  IntegerVariable y = mapping->Integer(ct.int_prod().vars(1));
499 
500  IntegerTrail* const integer_trail = m->GetOrCreate<IntegerTrail>();
501  IntegerValue x_lb = integer_trail->LowerBound(x);
502  IntegerValue x_ub = integer_trail->UpperBound(x);
503  IntegerValue y_lb = integer_trail->LowerBound(y);
504  IntegerValue y_ub = integer_trail->UpperBound(y);
505 
506  if (x == y) {
507  // We currently only support variables with non-negative domains.
508  if (x_lb < 0 && x_ub > 0) return;
509 
510  // Change the sigh of x if its domain is non-positive.
511  if (x_ub <= 0) {
512  x = NegationOf(x);
513  }
514 
515  relaxation->cut_generators.push_back(CreateSquareCutGenerator(z, x, m));
516  } else {
517  // We currently only support variables with non-negative domains.
518  if (x_lb < 0 && x_ub > 0) return;
519  if (y_lb < 0 && y_ub > 0) return;
520 
521  // Change signs to return to the case where all variables are a domain
522  // with non negative values only.
523  if (x_ub <= 0) {
524  x = NegationOf(x);
525  z = NegationOf(z);
526  }
527  if (y_ub <= 0) {
528  y = NegationOf(y);
529  z = NegationOf(z);
530  }
531 
532  relaxation->cut_generators.push_back(
534  }
535  }
536  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kAllDiff) {
537  if (linearization_level < 2) return;
538  if (HasEnforcementLiteral(ct)) return;
539  const int num_vars = ct.all_diff().vars_size();
540  if (num_vars <= m->GetOrCreate<SatParameters>()->max_all_diff_cut_size()) {
541  std::vector<IntegerVariable> vars =
542  mapping->Integers(ct.all_diff().vars());
543  relaxation->cut_generators.push_back(
545  }
546  }
547 
548  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCumulative) {
549  if (linearization_level < 2) return;
550  if (HasEnforcementLiteral(ct)) return;
551 
552  std::vector<IntegerVariable> demands =
553  mapping->Integers(ct.cumulative().demands());
554  std::vector<IntervalVariable> intervals =
555  mapping->Intervals(ct.cumulative().intervals());
556  const IntegerVariable capacity =
557  mapping->Integer(ct.cumulative().capacity());
558  relaxation->cut_generators.push_back(
560  m));
561  relaxation->cut_generators.push_back(
562  CreateCumulativeCutGenerator(intervals, capacity, demands, m));
563  }
564 
565  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kNoOverlap) {
566  if (linearization_level < 2) return;
567  if (HasEnforcementLiteral(ct)) return;
568  std::vector<IntervalVariable> intervals =
569  mapping->Intervals(ct.no_overlap().intervals());
570  relaxation->cut_generators.push_back(
571  CreateNoOverlapCutGenerator(intervals, m));
572  relaxation->cut_generators.push_back(
574  }
575 
576  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinMax) {
577  if (!m->GetOrCreate<SatParameters>()->add_lin_max_cuts()) return;
578  if (HasEnforcementLiteral(ct)) return;
579 
580  // TODO(user): Support linearization of general target expression.
581  if (ct.lin_max().target().vars_size() != 1) return;
582  if (ct.lin_max().target().coeffs(0) != 1) return;
583 
584  const IntegerVariable target =
585  mapping->Integer(ct.lin_max().target().vars(0));
586  std::vector<LinearExpression> exprs;
587  exprs.reserve(ct.lin_max().exprs_size());
588  for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
589  // Note: Cut generator requires all expressions to contain only positive
590  // vars.
591  exprs.push_back(
592  PositiveVarExpr(GetExprFromProto(ct.lin_max().exprs(i), *mapping)));
593  }
594 
595  // Add initial big-M linear relaxation.
596  // z_vars[i] == 1 <=> target = exprs[i].
597  const std::vector<IntegerVariable> z_vars =
598  AppendLinMaxRelaxation(target, exprs, m, relaxation);
599 
600  if (linearization_level >= 2) {
601  relaxation->cut_generators.push_back(
602  CreateLinMaxCutGenerator(target, exprs, z_vars, m));
603  }
604  }
605 }
606 
607 } // namespace
608 
609 LinearRelaxation ComputeLinearRelaxation(const CpModelProto& model_proto,
610  int linearization_level, Model* m) {
611  LinearRelaxation relaxation;
612 
613  // Linearize the constraints.
614  absl::flat_hash_set<int> used_integer_variable;
615 
616  auto* mapping = m->GetOrCreate<CpModelMapping>();
617  auto* encoder = m->GetOrCreate<IntegerEncoder>();
618  auto* trail = m->GetOrCreate<Trail>();
619  for (const auto& ct : model_proto.constraints()) {
620  // Make sure the literals from a circuit constraint always have a view.
621  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) {
622  for (const int ref : ct.circuit().literals()) {
623  const Literal l = mapping->Literal(ref);
624  if (!encoder->LiteralOrNegationHasView(l)) {
626  }
627  }
628  }
629 
630  // For now, we skip any constraint with literals that do not have an integer
631  // view. Ideally it should be up to the constraint to decide if creating a
632  // view is worth it.
633  //
634  // TODO(user): It should be possible to speed this up if needed.
635  const IndexReferences refs = GetReferencesUsedByConstraint(ct);
636  bool ok = true;
637  for (const int literal_ref : refs.literals) {
638  const Literal literal = mapping->Literal(literal_ref);
639  if (trail->Assignment().LiteralIsAssigned(literal)) {
640  // Create a view to the constant 0 or 1.
642  } else if (!encoder->LiteralOrNegationHasView(literal)) {
643  ok = false;
644  break;
645  }
646  }
647  if (!ok) continue;
648 
649  TryToLinearizeConstraint(model_proto, ct, m, linearization_level,
650  &relaxation);
651  TryToAddCutGenerators(model_proto, ct, m, &relaxation);
652  }
653 
654  // Linearize the encoding of variable that are fully encoded in the proto.
655  int num_full_encoding_relaxations = 0;
656  int num_partial_encoding_relaxations = 0;
657  for (int i = 0; i < model_proto.variables_size(); ++i) {
658  if (mapping->IsBoolean(i)) continue;
659 
660  const IntegerVariable var = mapping->Integer(i);
661  if (m->Get(IsFixed(var))) continue;
662 
663  // TODO(user): This different encoding for the partial variable might be
664  // better (less LP constraints), but we do need more investigation to
665  // decide.
666  if (/* DISABLES CODE */ (false)) {
667  AppendPartialEncodingRelaxation(var, *m, &relaxation);
668  continue;
669  }
670 
671  if (encoder->VariableIsFullyEncoded(var)) {
672  if (AppendFullEncodingRelaxation(var, *m, &relaxation)) {
673  ++num_full_encoding_relaxations;
674  continue;
675  }
676  }
677 
678  // Even if the variable is fully encoded, sometimes not all its associated
679  // literal have a view (if they are not part of the original model for
680  // instance).
681  //
682  // TODO(user): Should we add them to the LP anyway? this isn't clear as
683  // we can sometimes create a lot of Booleans like this.
684  const int old = relaxation.linear_constraints.size();
686  if (relaxation.linear_constraints.size() > old) {
687  ++num_partial_encoding_relaxations;
688  }
689  }
690 
691  // Linearize the at most one constraints. Note that we transform them
692  // into maximum "at most one" first and we removes redundant ones.
693  m->GetOrCreate<BinaryImplicationGraph>()->TransformIntoMaxCliques(
694  &relaxation.at_most_ones);
695  for (const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
696  if (at_most_one.empty()) continue;
697 
698  LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(1));
699  for (const Literal literal : at_most_one) {
700  // Note that it is okay to simply ignore the literal if it has no
701  // integer view.
702  const bool unused ABSL_ATTRIBUTE_UNUSED =
703  lc.AddLiteralTerm(literal, IntegerValue(1));
704  }
705  relaxation.linear_constraints.push_back(lc.Build());
706  }
707 
708  // We converted all at_most_one to LP constraints, so we need to clear them
709  // so that we don't do extra work in the connected component computation.
710  relaxation.at_most_ones.clear();
711 
712  // Remove size one LP constraints, they are not useful.
713  {
714  int new_size = 0;
715  for (int i = 0; i < relaxation.linear_constraints.size(); ++i) {
716  if (relaxation.linear_constraints[i].vars.size() <= 1) continue;
717  std::swap(relaxation.linear_constraints[new_size++],
718  relaxation.linear_constraints[i]);
719  }
720  relaxation.linear_constraints.resize(new_size);
721  }
722 
723  VLOG(3) << "num_full_encoding_relaxations: " << num_full_encoding_relaxations;
724  VLOG(3) << "num_partial_encoding_relaxations: "
725  << num_partial_encoding_relaxations;
726  VLOG(3) << relaxation.linear_constraints.size()
727  << " constraints in the LP relaxation.";
728  VLOG(3) << relaxation.cut_generators.size() << " cuts generators.";
729  return relaxation;
730 }
731 
732 // Adds one LinearProgrammingConstraint per connected component of the model.
733 IntegerVariable AddLPConstraints(const CpModelProto& model_proto,
734  int linearization_level, Model* m) {
735  const LinearRelaxation relaxation =
736  ComputeLinearRelaxation(model_proto, linearization_level, m);
737 
738  // The bipartite graph of LP constraints might be disconnected:
739  // make a partition of the variables into connected components.
740  // Constraint nodes are indexed by [0..num_lp_constraints),
741  // variable nodes by [num_lp_constraints..num_lp_constraints+num_variables).
742  //
743  // TODO(user): look into biconnected components.
744  const int num_lp_constraints = relaxation.linear_constraints.size();
745  const int num_lp_cut_generators = relaxation.cut_generators.size();
746  const int num_integer_variables =
747  m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
749  components.SetNumberOfNodes(num_lp_constraints + num_lp_cut_generators +
750  num_integer_variables);
751  auto get_constraint_index = [](int ct_index) { return ct_index; };
752  auto get_cut_generator_index = [num_lp_constraints](int cut_index) {
753  return num_lp_constraints + cut_index;
754  };
755  auto get_var_index = [num_lp_constraints,
756  num_lp_cut_generators](IntegerVariable var) {
757  return num_lp_constraints + num_lp_cut_generators + var.value();
758  };
759  for (int i = 0; i < num_lp_constraints; i++) {
760  for (const IntegerVariable var : relaxation.linear_constraints[i].vars) {
761  components.AddEdge(get_constraint_index(i), get_var_index(var));
762  }
763  }
764  for (int i = 0; i < num_lp_cut_generators; ++i) {
765  for (const IntegerVariable var : relaxation.cut_generators[i].vars) {
766  components.AddEdge(get_cut_generator_index(i), get_var_index(var));
767  }
768  }
769 
770  // Add edges for at most ones that we do not statically add to the LP.
771  //
772  // TODO(user): Because we currently add every at_most_ones (and we clear it)
773  // this code is unused outside of experiments.
774  for (const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
775  LinearConstraintBuilder builder(m, kMinIntegerValue, IntegerValue(1));
776  for (const Literal literal : at_most_one) {
777  // Note that it is okay to simply ignore the literal if it has no
778  // integer view.
779  const bool unused ABSL_ATTRIBUTE_UNUSED =
780  builder.AddLiteralTerm(literal, IntegerValue(1));
781  }
782  LinearConstraint lc = builder.Build();
783  for (int i = 1; i < lc.vars.size(); ++i) {
784  components.AddEdge(get_var_index(lc.vars[0]), get_var_index(lc.vars[i]));
785  }
786  }
787 
788  const int num_components = components.GetNumberOfComponents();
789  std::vector<int> component_sizes(num_components, 0);
790  const std::vector<int> index_to_component = components.GetComponentIds();
791  for (int i = 0; i < num_lp_constraints; i++) {
792  ++component_sizes[index_to_component[get_constraint_index(i)]];
793  }
794  for (int i = 0; i < num_lp_cut_generators; i++) {
795  ++component_sizes[index_to_component[get_cut_generator_index(i)]];
796  }
797 
798  // Make sure any constraint that touch the objective is not discarded even
799  // if it is the only one in its component. This is important to propagate
800  // as much as possible the objective bound by using any bounds the LP give
801  // us on one of its components. This is critical on the zephyrus problems for
802  // instance.
803  auto* mapping = m->GetOrCreate<CpModelMapping>();
804  for (int i = 0; i < model_proto.objective().coeffs_size(); ++i) {
805  const IntegerVariable var =
806  mapping->Integer(model_proto.objective().vars(i));
807  ++component_sizes[index_to_component[get_var_index(var)]];
808  }
809 
810  // Dispatch every constraint to its LinearProgrammingConstraint.
811  std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
812  nullptr);
813  std::vector<std::vector<LinearConstraint>> component_to_constraints(
814  num_components);
815  for (int i = 0; i < num_lp_constraints; i++) {
816  const int c = index_to_component[get_constraint_index(i)];
817  if (component_sizes[c] <= 1) continue;
818  component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
819  if (lp_constraints[c] == nullptr) {
820  lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
821  }
822  // Load the constraint.
823  lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
824  }
825 
826  // Dispatch every cut generator to its LinearProgrammingConstraint.
827  for (int i = 0; i < num_lp_cut_generators; i++) {
828  const int c = index_to_component[get_cut_generator_index(i)];
829  if (lp_constraints[c] == nullptr) {
830  lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
831  }
832  lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
833  }
834 
835  // Register "generic" clique (i.e. at most one) cut generator.
836  const SatParameters& params = *(m->GetOrCreate<SatParameters>());
837  if (params.add_clique_cuts() && params.linearization_level() > 1) {
838  for (LinearProgrammingConstraint* lp : lp_constraints) {
839  if (lp == nullptr) continue;
840  lp->AddCutGenerator(CreateCliqueCutGenerator(lp->integer_variables(), m));
841  }
842  }
843 
844  if (params.add_knapsack_cuts() && params.linearization_level() > 1) {
845  for (int c = 0; c < num_components; ++c) {
846  if (component_to_constraints[c].empty()) continue;
847  lp_constraints[c]->AddCutGenerator(CreateKnapsackCoverCutGenerator(
848  component_to_constraints[c], lp_constraints[c]->integer_variables(),
849  m));
850  }
851  }
852 
853  // Add the objective.
854  std::vector<std::vector<std::pair<IntegerVariable, int64>>>
855  component_to_cp_terms(num_components);
856  std::vector<std::pair<IntegerVariable, int64>> top_level_cp_terms;
857  int num_components_containing_objective = 0;
858  if (model_proto.has_objective()) {
859  // First pass: set objective coefficients on the lp constraints, and store
860  // the cp terms in one vector per component.
861  for (int i = 0; i < model_proto.objective().coeffs_size(); ++i) {
862  const IntegerVariable var =
863  mapping->Integer(model_proto.objective().vars(i));
864  const int64 coeff = model_proto.objective().coeffs(i);
865  const int c = index_to_component[get_var_index(var)];
866  if (lp_constraints[c] != nullptr) {
867  lp_constraints[c]->SetObjectiveCoefficient(var, IntegerValue(coeff));
868  component_to_cp_terms[c].push_back(std::make_pair(var, coeff));
869  } else {
870  // Component is too small. We still need to store the objective term.
871  top_level_cp_terms.push_back(std::make_pair(var, coeff));
872  }
873  }
874  // Second pass: Build the cp sub-objectives per component.
875  for (int c = 0; c < num_components; ++c) {
876  if (component_to_cp_terms[c].empty()) continue;
877  const IntegerVariable sub_obj_var =
878  GetOrCreateVariableGreaterOrEqualToSumOf(component_to_cp_terms[c], m);
879  top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
880  lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
881  num_components_containing_objective++;
882  }
883  }
884 
885  const IntegerVariable main_objective_var =
886  model_proto.has_objective()
887  ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
889 
890  // Register LP constraints. Note that this needs to be done after all the
891  // constraints have been added.
892  for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
893  if (lp_constraint == nullptr) continue;
894  lp_constraint->RegisterWith(m);
895  VLOG(3) << "LP constraint: " << lp_constraint->DimensionString() << ".";
896  }
897 
898  VLOG(3) << top_level_cp_terms.size()
899  << " terms in the main objective linear equation ("
900  << num_components_containing_objective << " from LP constraints).";
901  return main_objective_var;
902 }
903 
904 } // namespace
905 
906 // Used by NewFeasibleSolutionObserver to register observers.
909  std::vector<std::function<void(const CpSolverResponse& response)>> observers;
910 };
911 
912 std::function<void(Model*)> NewFeasibleSolutionObserver(
913  const std::function<void(const CpSolverResponse& response)>& observer) {
914  return [=](Model* model) {
915  model->GetOrCreate<SolutionObservers>()->observers.push_back(observer);
916  };
917 }
918 
919 #if !defined(__PORTABLE_PLATFORM__)
920 // TODO(user): Support it on android.
921 std::function<SatParameters(Model*)> NewSatParameters(
922  const std::string& params) {
923  sat::SatParameters parameters;
924  if (!params.empty()) {
925  CHECK(google::protobuf::TextFormat::ParseFromString(params, &parameters))
926  << params;
927  }
929 }
930 #endif // __PORTABLE_PLATFORM__
931 
932 std::function<SatParameters(Model*)> NewSatParameters(
933  const sat::SatParameters& parameters) {
934  return [=](Model* model) {
935  // Tricky: It is important to initialize the model parameters before any
936  // of the solver object are created, so that by default they use the given
937  // parameters.
938  //
939  // TODO(user): A notable exception to this is the TimeLimit which is
940  // currently not initializing itself from the SatParameters in the model. It
941  // will also starts counting from the time of its creation. It will be good
942  // to find a solution that is less error prone.
943  *model->GetOrCreate<SatParameters>() = parameters;
944  return parameters;
945  };
946 }
947 
948 namespace {
949 
950 // Registers a callback that will export variables bounds fixed at level 0 of
951 // the search. This should not be registered to a LNS search.
952 void RegisterVariableBoundsLevelZeroExport(
953  const CpModelProto& model_proto, SharedBoundsManager* shared_bounds_manager,
954  Model* model) {
955  CHECK(shared_bounds_manager != nullptr);
956  int saved_trail_index = 0;
957  const auto broadcast_level_zero_bounds =
958  [&model_proto, saved_trail_index, model, shared_bounds_manager](
959  const std::vector<IntegerVariable>& modified_vars) mutable {
960  CpModelMapping* const mapping = model->GetOrCreate<CpModelMapping>();
961 
962  std::vector<int> model_variables;
963  std::vector<int64> new_lower_bounds;
964  std::vector<int64> new_upper_bounds;
965  absl::flat_hash_set<int> visited_variables;
966 
967  // Inspect the modified IntegerVariables.
968  auto* integer_trail = model->Get<IntegerTrail>();
969  for (const IntegerVariable& var : modified_vars) {
970  const IntegerVariable positive_var = PositiveVariable(var);
971  const int model_var =
972  mapping->GetProtoVariableFromIntegerVariable(positive_var);
973  if (model_var == -1 || visited_variables.contains(model_var)) {
974  // TODO(user): I don't think we should see the same model_var twice
975  // here so maybe we don't need the visited_variables.contains()
976  // part.
977  continue;
978  }
979 
980  visited_variables.insert(model_var);
981  const int64 new_lb =
982  integer_trail->LevelZeroLowerBound(positive_var).value();
983  const int64 new_ub =
984  integer_trail->LevelZeroUpperBound(positive_var).value();
985  // TODO(user): We could imagine an API based on atomic<int64>
986  // that could preemptively check if this new bounds are improving.
987  model_variables.push_back(model_var);
988  new_lower_bounds.push_back(new_lb);
989  new_upper_bounds.push_back(new_ub);
990  }
991 
992  // Inspect the newly modified Booleans.
993  auto* trail = model->Get<Trail>();
994  for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
995  const Literal fixed_literal = (*trail)[saved_trail_index];
996  const int model_var = mapping->GetProtoVariableFromBooleanVariable(
997  fixed_literal.Variable());
998  if (model_var == -1 || visited_variables.contains(model_var)) {
999  // If the variable is already visited, it should mean that this
1000  // Boolean also has an IntegerVariable view, and we should already
1001  // have set its bound correctly.
1002  continue;
1003  }
1004 
1005  visited_variables.insert(model_var);
1006  model_variables.push_back(model_var);
1007  if (fixed_literal.IsPositive()) {
1008  new_lower_bounds.push_back(1);
1009  new_upper_bounds.push_back(1);
1010  } else {
1011  new_lower_bounds.push_back(0);
1012  new_upper_bounds.push_back(0);
1013  }
1014  }
1015 
1016  if (!model_variables.empty()) {
1017  shared_bounds_manager->ReportPotentialNewBounds(
1018  model_proto, model->Name(), model_variables, new_lower_bounds,
1019  new_upper_bounds);
1020  }
1021 
1022  // If we are not in interleave_search we synchronize right away.
1023  if (!model->Get<SatParameters>()->interleave_search()) {
1024  shared_bounds_manager->Synchronize();
1025  }
1026  };
1027 
1028  model->GetOrCreate<GenericLiteralWatcher>()
1029  ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
1030 }
1031 
1032 // Registers a callback to import new variables bounds stored in the
1033 // shared_bounds_manager. These bounds are imported at level 0 of the search
1034 // in the linear scan minimize function.
1035 void RegisterVariableBoundsLevelZeroImport(
1036  const CpModelProto& model_proto, SharedBoundsManager* shared_bounds_manager,
1037  Model* model) {
1038  CHECK(shared_bounds_manager != nullptr);
1039  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
1040  CpModelMapping* const mapping = model->GetOrCreate<CpModelMapping>();
1041  const int id = shared_bounds_manager->RegisterNewId();
1042 
1043  const auto& import_level_zero_bounds = [&model_proto, shared_bounds_manager,
1044  model, integer_trail, id, mapping]() {
1045  std::vector<int> model_variables;
1046  std::vector<int64> new_lower_bounds;
1047  std::vector<int64> new_upper_bounds;
1048  shared_bounds_manager->GetChangedBounds(
1049  id, &model_variables, &new_lower_bounds, &new_upper_bounds);
1050  bool new_bounds_have_been_imported = false;
1051  for (int i = 0; i < model_variables.size(); ++i) {
1052  const int model_var = model_variables[i];
1053  // This can happen if a boolean variables is forced to have an
1054  // integer view in one thread, and not in another thread.
1055  if (!mapping->IsInteger(model_var)) continue;
1056  const IntegerVariable var = mapping->Integer(model_var);
1057  const IntegerValue new_lb(new_lower_bounds[i]);
1058  const IntegerValue new_ub(new_upper_bounds[i]);
1059  const IntegerValue old_lb = integer_trail->LowerBound(var);
1060  const IntegerValue old_ub = integer_trail->UpperBound(var);
1061  const bool changed_lb = new_lb > old_lb;
1062  const bool changed_ub = new_ub < old_ub;
1063  if (!changed_lb && !changed_ub) continue;
1064 
1065  new_bounds_have_been_imported = true;
1066  if (VLOG_IS_ON(3)) {
1067  const IntegerVariableProto& var_proto =
1068  model_proto.variables(model_var);
1069  const std::string& var_name =
1070  var_proto.name().empty()
1071  ? absl::StrCat("anonymous_var(", model_var, ")")
1072  : var_proto.name();
1073  LOG(INFO) << " '" << model->Name() << "' imports new bounds for "
1074  << var_name << ": from [" << old_lb << ", " << old_ub
1075  << "] to [" << new_lb << ", " << new_ub << "]";
1076  }
1077 
1078  if (changed_lb &&
1079  !integer_trail->Enqueue(IntegerLiteral::GreaterOrEqual(var, new_lb),
1080  {}, {})) {
1081  return false;
1082  }
1083  if (changed_ub &&
1084  !integer_trail->Enqueue(IntegerLiteral::LowerOrEqual(var, new_ub), {},
1085  {})) {
1086  return false;
1087  }
1088  }
1089  if (new_bounds_have_been_imported &&
1090  !model->GetOrCreate<SatSolver>()->FinishPropagation()) {
1091  return false;
1092  }
1093  return true;
1094  };
1095  model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1096  import_level_zero_bounds);
1097 }
1098 
1099 // Registers a callback that will report improving objective best bound.
1100 // It will be called each time new objective bound are propagated at level zero.
1101 void RegisterObjectiveBestBoundExport(
1102  IntegerVariable objective_var,
1103  SharedResponseManager* shared_response_manager, Model* model) {
1104  auto* integer_trail = model->Get<IntegerTrail>();
1105  const auto broadcast_objective_lower_bound =
1106  [objective_var, integer_trail, shared_response_manager,
1107  model](const std::vector<IntegerVariable>& unused) {
1108  shared_response_manager->UpdateInnerObjectiveBounds(
1109  model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
1110  integer_trail->LevelZeroUpperBound(objective_var));
1111  // If we are not in interleave_search we synchronize right away.
1112  if (!model->Get<SatParameters>()->interleave_search()) {
1113  shared_response_manager->Synchronize();
1114  }
1115  };
1116  model->GetOrCreate<GenericLiteralWatcher>()
1117  ->RegisterLevelZeroModifiedVariablesCallback(
1118  broadcast_objective_lower_bound);
1119 }
1120 
1121 // Registers a callback to import new objective bounds. It will be called each
1122 // time the search main loop is back to level zero. Note that it the presence of
1123 // assumptions, this will not happen until the set of assumptions is changed.
1124 void RegisterObjectiveBoundsImport(
1125  SharedResponseManager* shared_response_manager, Model* model) {
1126  auto* solver = model->GetOrCreate<SatSolver>();
1127  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
1128  auto* objective = model->GetOrCreate<ObjectiveDefinition>();
1129  const std::string name = model->Name();
1130  const auto import_objective_bounds = [name, solver, integer_trail, objective,
1131  shared_response_manager]() {
1132  if (solver->AssumptionLevel() != 0) return true;
1133  bool propagate = false;
1134 
1135  const IntegerValue external_lb =
1136  shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1137  const IntegerValue current_lb =
1138  integer_trail->LowerBound(objective->objective_var);
1139  if (external_lb > current_lb) {
1140  if (!integer_trail->Enqueue(IntegerLiteral::GreaterOrEqual(
1141  objective->objective_var, external_lb),
1142  {}, {})) {
1143  return false;
1144  }
1145  propagate = true;
1146  }
1147 
1148  const IntegerValue external_ub =
1149  shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1150  const IntegerValue current_ub =
1151  integer_trail->UpperBound(objective->objective_var);
1152  if (external_ub < current_ub) {
1153  if (!integer_trail->Enqueue(IntegerLiteral::LowerOrEqual(
1154  objective->objective_var, external_ub),
1155  {}, {})) {
1156  return false;
1157  }
1158  propagate = true;
1159  }
1160 
1161  if (!propagate) return true;
1162 
1163  VLOG(2) << "'" << name << "' imports objective bounds: external ["
1164  << objective->ScaleIntegerObjective(external_lb) << ", "
1165  << objective->ScaleIntegerObjective(external_ub) << "], current ["
1166  << objective->ScaleIntegerObjective(current_lb) << ", "
1167  << objective->ScaleIntegerObjective(current_ub) << "]";
1168 
1169  return solver->FinishPropagation();
1170  };
1171 
1172  model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1173  import_objective_bounds);
1174 }
1175 
1176 void LoadBaseModel(const CpModelProto& model_proto,
1177  SharedResponseManager* shared_response_manager,
1178  Model* model) {
1179  CHECK(shared_response_manager != nullptr);
1180  auto* sat_solver = model->GetOrCreate<SatSolver>();
1181 
1182  // Simple function for the few places where we do "return unsat()".
1183  const auto unsat = [shared_response_manager, sat_solver, model] {
1184  sat_solver->NotifyThatModelIsUnsat();
1185  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1186  absl::StrCat(model->Name(), " [loading]"));
1187  };
1188 
1189  // We will add them all at once after model_proto is loaded.
1190  model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1191 
1192  auto* mapping = model->GetOrCreate<CpModelMapping>();
1193  const SatParameters& parameters = *(model->GetOrCreate<SatParameters>());
1194  const bool view_all_booleans_as_integers =
1195  (parameters.linearization_level() >= 2) ||
1196  (parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1197  model_proto.search_strategy().empty());
1198  mapping->CreateVariables(model_proto, view_all_booleans_as_integers, model);
1199  mapping->DetectOptionalVariables(model_proto, model);
1200  mapping->DetectAndLoadBooleanSymmetries(model_proto, model);
1201  mapping->ExtractEncoding(model_proto, model);
1202  mapping->PropagateEncodingFromEquivalenceRelations(model_proto, model);
1203 
1204  // Check the model is still feasible before continuing.
1205  if (sat_solver->IsModelUnsat()) return unsat();
1206 
1207  // Force some variables to be fully encoded.
1209 
1210  // Load the constraints.
1211  std::set<std::string> unsupported_types;
1212  int num_ignored_constraints = 0;
1213  for (const ConstraintProto& ct : model_proto.constraints()) {
1214  if (mapping->ConstraintIsAlreadyLoaded(&ct)) {
1215  ++num_ignored_constraints;
1216  continue;
1217  }
1218 
1219  if (!LoadConstraint(ct, model)) {
1220  unsupported_types.insert(ConstraintCaseName(ct.constraint_case()));
1221  continue;
1222  }
1223 
1224  // We propagate after each new Boolean constraint but not the integer
1225  // ones. So we call FinishPropagation() manually here.
1226  //
1227  // Note that we only do that in debug mode as this can be really slow on
1228  // certain types of problems with millions of constraints.
1229  if (DEBUG_MODE) {
1230  if (sat_solver->FinishPropagation()) {
1231  Trail* trail = model->GetOrCreate<Trail>();
1232  const int old_num_fixed = trail->Index();
1233  if (trail->Index() > old_num_fixed) {
1234  VLOG(3) << "Constraint fixed " << trail->Index() - old_num_fixed
1235  << " Boolean variable(s): " << ProtobufDebugString(ct);
1236  }
1237  }
1238  }
1239  if (sat_solver->IsModelUnsat()) {
1240  VLOG(2) << "UNSAT during extraction (after adding '"
1241  << ConstraintCaseName(ct.constraint_case()) << "'). "
1242  << ProtobufDebugString(ct);
1243  break;
1244  }
1245  }
1246  if (num_ignored_constraints > 0) {
1247  VLOG(3) << num_ignored_constraints << " constraints were skipped.";
1248  }
1249  if (!unsupported_types.empty()) {
1250  VLOG(1) << "There is unsupported constraints types in this model: ";
1251  for (const std::string& type : unsupported_types) {
1252  VLOG(1) << " - " << type;
1253  }
1254  return unsat();
1255  }
1256 
1257  model->GetOrCreate<IntegerEncoder>()
1258  ->AddAllImplicationsBetweenAssociatedLiterals();
1259  if (!sat_solver->FinishPropagation()) return unsat();
1260 }
1261 
1262 void LoadFeasibilityPump(const CpModelProto& model_proto,
1263  SharedResponseManager* shared_response_manager,
1264  Model* model) {
1265  CHECK(shared_response_manager != nullptr);
1266 
1267  LoadBaseModel(model_proto, shared_response_manager, model);
1268 
1269  auto* mapping = model->GetOrCreate<CpModelMapping>();
1270  const SatParameters& parameters = *(model->GetOrCreate<SatParameters>());
1271  if (parameters.linearization_level() == 0) return;
1272 
1273  // Add linear constraints to Feasibility Pump.
1274  const LinearRelaxation relaxation = ComputeLinearRelaxation(
1275  model_proto, parameters.linearization_level(), model);
1276  const int num_lp_constraints = relaxation.linear_constraints.size();
1277  if (num_lp_constraints == 0) return;
1278  auto* feasibility_pump = model->GetOrCreate<FeasibilityPump>();
1279  for (int i = 0; i < num_lp_constraints; i++) {
1280  feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1281  }
1282 
1283  if (model_proto.has_objective()) {
1284  for (int i = 0; i < model_proto.objective().coeffs_size(); ++i) {
1285  const IntegerVariable var =
1286  mapping->Integer(model_proto.objective().vars(i));
1287  const int64 coeff = model_proto.objective().coeffs(i);
1288  feasibility_pump->SetObjectiveCoefficient(var, IntegerValue(coeff));
1289  }
1290  }
1291 }
1292 
1293 // Loads a CpModelProto inside the given model.
1294 // This should only be called once on a given 'Model' class.
1295 //
1296 // TODO(user): move to cp_model_loader.h/.cc
1297 void LoadCpModel(const CpModelProto& model_proto,
1298  SharedResponseManager* shared_response_manager, Model* model) {
1299  CHECK(shared_response_manager != nullptr);
1300  auto* sat_solver = model->GetOrCreate<SatSolver>();
1301 
1302  LoadBaseModel(model_proto, shared_response_manager, model);
1303 
1304  // Simple function for the few places where we do "return unsat()".
1305  const auto unsat = [shared_response_manager, sat_solver, model] {
1306  sat_solver->NotifyThatModelIsUnsat();
1307  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1308  absl::StrCat(model->Name(), " [loading]"));
1309  };
1310 
1311  auto* mapping = model->GetOrCreate<CpModelMapping>();
1312  const SatParameters& parameters = *(model->GetOrCreate<SatParameters>());
1313 
1314  // Auto detect "at least one of" constraints in the PrecedencesPropagator.
1315  // Note that we do that before we finish loading the problem (objective and
1316  // LP relaxation), because propagation will be faster at this point and it
1317  // should be enough for the purpose of this auto-detection.
1318  if (model->Mutable<PrecedencesPropagator>() != nullptr &&
1319  parameters.auto_detect_greater_than_at_least_one_of()) {
1320  model->Mutable<PrecedencesPropagator>()
1321  ->AddGreaterThanAtLeastOneOfConstraints(model);
1322  if (!sat_solver->FinishPropagation()) return unsat();
1323  }
1324 
1325  // TODO(user): This should be done in the presolve instead.
1326  // TODO(user): We don't have a good deterministic time on all constraints,
1327  // so this might take more time than wanted.
1328  if (parameters.cp_model_probing_level() > 1) {
1329  Prober* prober = model->GetOrCreate<Prober>();
1330  prober->ProbeBooleanVariables(/*deterministic_time_limit=*/1.0);
1331  if (model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1332  return unsat();
1333  }
1334  if (!model->GetOrCreate<BinaryImplicationGraph>()
1335  ->ComputeTransitiveReduction()) {
1336  return unsat();
1337  }
1338  }
1339 
1340  // Create an objective variable and its associated linear constraint if
1341  // needed.
1342  IntegerVariable objective_var = kNoIntegerVariable;
1343  if (parameters.linearization_level() > 0) {
1344  // Linearize some part of the problem and register LP constraint(s).
1345  objective_var =
1346  AddLPConstraints(model_proto, parameters.linearization_level(), model);
1347  } else if (model_proto.has_objective()) {
1348  const CpObjectiveProto& obj = model_proto.objective();
1349  std::vector<std::pair<IntegerVariable, int64>> terms;
1350  terms.reserve(obj.vars_size());
1351  for (int i = 0; i < obj.vars_size(); ++i) {
1352  terms.push_back(
1353  std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1354  }
1355  if (parameters.optimize_with_core()) {
1356  objective_var = GetOrCreateVariableWithTightBound(terms, model);
1357  } else {
1358  objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms, model);
1359  }
1360  }
1361 
1362  // Create the objective definition inside the Model so that it can be accessed
1363  // by the heuristics than needs it.
1364  if (objective_var != kNoIntegerVariable) {
1365  const CpObjectiveProto& objective_proto = model_proto.objective();
1366  auto* objective_definition = model->GetOrCreate<ObjectiveDefinition>();
1367 
1368  objective_definition->scaling_factor = objective_proto.scaling_factor();
1369  if (objective_definition->scaling_factor == 0.0) {
1370  objective_definition->scaling_factor = 1.0;
1371  }
1372  objective_definition->offset = objective_proto.offset();
1373  objective_definition->objective_var = objective_var;
1374 
1375  const int size = objective_proto.vars_size();
1376  objective_definition->vars.resize(size);
1377  objective_definition->coeffs.resize(size);
1378  for (int i = 0; i < objective_proto.vars_size(); ++i) {
1379  // Note that if there is no mapping, then the variable will be
1380  // kNoIntegerVariable.
1381  objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1382  objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1383 
1384  // Fill the objective heuristics data.
1385  const int ref = objective_proto.vars(i);
1386  if (mapping->IsInteger(ref)) {
1387  const IntegerVariable var = mapping->Integer(objective_proto.vars(i));
1388  objective_definition->objective_impacting_variables.insert(
1389  objective_proto.coeffs(i) > 0 ? var : NegationOf(var));
1390  }
1391  }
1392 
1393  // Register an objective special propagator.
1394  model->TakeOwnership(
1395  new LevelZeroEquality(objective_var, objective_definition->vars,
1396  objective_definition->coeffs, model));
1397  }
1398 
1399  // Intersect the objective domain with the given one if any.
1400  if (!model_proto.objective().domain().empty()) {
1401  const Domain user_domain = ReadDomainFromProto(model_proto.objective());
1402  const Domain automatic_domain =
1403  model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1404  objective_var);
1405  VLOG(3) << "Objective offset:" << model_proto.objective().offset()
1406  << " scaling_factor:" << model_proto.objective().scaling_factor();
1407  VLOG(3) << "Automatic internal objective domain: " << automatic_domain;
1408  VLOG(3) << "User specified internal objective domain: " << user_domain;
1409  CHECK_NE(objective_var, kNoIntegerVariable);
1410  const bool ok = model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1411  objective_var, user_domain);
1412  if (!ok) {
1413  VLOG(2) << "UNSAT due to the objective domain.";
1414  return unsat();
1415  }
1416 
1417  // Make sure the sum take a value inside the objective domain by adding
1418  // the other side: objective <= sum terms.
1419  //
1420  // TODO(user): Use a better condition to detect when this is not useful.
1421  // Note also that for the core algorithm, we might need the other side too,
1422  // otherwise we could return feasible solution with an objective above the
1423  // user specified upper bound.
1424  if (!automatic_domain.IsIncludedIn(user_domain)) {
1425  std::vector<IntegerVariable> vars;
1426  std::vector<int64> coeffs;
1427  const CpObjectiveProto& obj = model_proto.objective();
1428  for (int i = 0; i < obj.vars_size(); ++i) {
1429  vars.push_back(mapping->Integer(obj.vars(i)));
1430  coeffs.push_back(obj.coeffs(i));
1431  }
1432  vars.push_back(objective_var);
1433  coeffs.push_back(-1);
1434  model->Add(WeightedSumGreaterOrEqual(vars, coeffs, 0));
1435  }
1436  }
1437 
1438  // Note that we do one last propagation at level zero once all the
1439  // constraints were added.
1440  if (!sat_solver->FinishPropagation()) return unsat();
1441 
1442  if (model_proto.has_objective()) {
1443  // Report the initial objective variable bounds.
1444  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
1445  shared_response_manager->UpdateInnerObjectiveBounds(
1446  "init", integer_trail->LowerBound(objective_var),
1447  integer_trail->UpperBound(objective_var));
1448 
1449  // Watch improved objective best bounds.
1450  RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1451  model);
1452 
1453  // Import objective bounds.
1454  // TODO(user): Support objective bounds import in LNS and Core based
1455  // search.
1456  if (model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1457  RegisterObjectiveBoundsImport(shared_response_manager, model);
1458  }
1459  }
1460 
1461  // Cache the links between model vars, IntegerVariables and lp constraints.
1462  // TODO(user): Cache this only if it is actually used.
1463  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
1464  auto* lp_dispatcher = model->GetOrCreate<LinearProgrammingDispatcher>();
1465  auto* lp_vars = model->GetOrCreate<LPVariables>();
1466  IntegerVariable size = integer_trail->NumIntegerVariables();
1467  for (IntegerVariable positive_var(0); positive_var < size;
1468  positive_var += 2) {
1469  LPVariable lp_var;
1470  lp_var.positive_var = positive_var;
1471  lp_var.model_var =
1472  mapping->GetProtoVariableFromIntegerVariable(positive_var);
1473  lp_var.lp = gtl::FindWithDefault(*lp_dispatcher, positive_var, nullptr);
1474 
1475  if (lp_var.model_var >= 0) {
1476  lp_vars->vars.push_back(lp_var);
1477  lp_vars->model_vars_size =
1478  std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1479  }
1480  }
1481 
1482  // Initialize the fixed_search strategy.
1483  auto* search_heuristics = model->GetOrCreate<SearchHeuristics>();
1484  search_heuristics->fixed_search = ConstructSearchStrategy(
1485  model_proto, mapping->GetVariableMapping(), objective_var, model);
1486  if (VLOG_IS_ON(3)) {
1487  search_heuristics->fixed_search =
1488  InstrumentSearchStrategy(model_proto, mapping->GetVariableMapping(),
1489  search_heuristics->fixed_search, model);
1490  }
1491 
1492  // Initialize the "follow hint" strategy.
1493  std::vector<BooleanOrIntegerVariable> vars;
1494  std::vector<IntegerValue> values;
1495  for (int i = 0; i < model_proto.solution_hint().vars_size(); ++i) {
1496  const int ref = model_proto.solution_hint().vars(i);
1497  CHECK(RefIsPositive(ref));
1498  BooleanOrIntegerVariable var;
1499  if (mapping->IsBoolean(ref)) {
1500  var.bool_var = mapping->Literal(ref).Variable();
1501  } else {
1502  var.int_var = mapping->Integer(ref);
1503  }
1504  vars.push_back(var);
1505  values.push_back(IntegerValue(model_proto.solution_hint().values(i)));
1506  }
1507  search_heuristics->hint_search = FollowHint(vars, values, model);
1508 
1509  // Create the CoreBasedOptimizer class if needed.
1510  if (parameters.optimize_with_core()) {
1511  // TODO(user): Remove code duplication with the solution_observer in
1512  // SolveLoadedCpModel().
1513  const std::string solution_info = model->Name();
1514  const auto solution_observer = [&model_proto, model, solution_info,
1515  shared_response_manager]() {
1516  CpSolverResponse response;
1517  FillSolutionInResponse(model_proto, *model, &response);
1518  response.set_solution_info(solution_info);
1519  shared_response_manager->NewSolution(response, model);
1520  };
1521 
1522  const auto& objective = *model->GetOrCreate<ObjectiveDefinition>();
1523  CoreBasedOptimizer* core =
1524  new CoreBasedOptimizer(objective_var, objective.vars, objective.coeffs,
1525  solution_observer, model);
1526  model->Register<CoreBasedOptimizer>(core);
1527  model->TakeOwnership(core);
1528  }
1529 }
1530 
1531 // Solves an already loaded cp_model_proto.
1532 // The final CpSolverResponse must be read from the shared_response_manager.
1533 //
1534 // TODO(user): This should be transformed so that it can be called many times
1535 // and resume from the last search state as if it wasn't interuped. That would
1536 // allow use to easily interleave different heuristics in the same thread.
1537 void SolveLoadedCpModel(const CpModelProto& model_proto,
1538  SharedResponseManager* shared_response_manager,
1539  Model* model) {
1540  if (shared_response_manager->ProblemIsSolved()) return;
1541 
1542  const std::string& solution_info = model->Name();
1543  const auto solution_observer = [&model_proto, &model, &solution_info,
1544  &shared_response_manager]() {
1545  CpSolverResponse response;
1546  FillSolutionInResponse(model_proto, *model, &response);
1547  response.set_solution_info(solution_info);
1548  shared_response_manager->NewSolution(response, model);
1549  };
1550 
1551  // Reconfigure search heuristic if it was changed.
1553 
1554  const auto& mapping = *model->GetOrCreate<CpModelMapping>();
1555  SatSolver::Status status;
1556  const SatParameters& parameters = *model->GetOrCreate<SatParameters>();
1557  if (parameters.use_probing_search()) {
1558  std::vector<BooleanVariable> bool_vars;
1559  std::vector<IntegerVariable> int_vars;
1560  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
1561  absl::flat_hash_set<BooleanVariable> visited;
1562  for (int v = 0; v < model_proto.variables_size(); ++v) {
1563  if (mapping.IsBoolean(v)) {
1564  const BooleanVariable bool_var = mapping.Literal(v).Variable();
1565  if (!visited.contains(bool_var)) {
1566  visited.insert(bool_var);
1567  bool_vars.push_back(bool_var);
1568  }
1569  } else {
1570  IntegerVariable var = mapping.Integer(v);
1571  if (integer_trail->IsFixed(var)) continue;
1572  int_vars.push_back(var);
1573  }
1574  }
1575  status = ContinuousProbing(bool_vars, int_vars, solution_observer, model);
1576  } else if (!model_proto.has_objective()) {
1577  while (true) {
1578  status = ResetAndSolveIntegerProblem(
1579  mapping.Literals(model_proto.assumptions()), model);
1580  if (status != SatSolver::Status::FEASIBLE) break;
1581  solution_observer();
1582  if (!parameters.enumerate_all_solutions()) break;
1584  }
1585  if (status == SatSolver::INFEASIBLE) {
1586  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1587  solution_info);
1588  }
1589  if (status == SatSolver::ASSUMPTIONS_UNSAT) {
1590  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1591  solution_info);
1592 
1593  // Extract a good subset of assumptions and add it to the response.
1594  auto* sat_solver = model->GetOrCreate<SatSolver>();
1595  std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1596  MinimizeCoreWithPropagation(sat_solver, &core);
1597  std::vector<int> core_in_proto_format;
1598  for (const Literal l : core) {
1599  core_in_proto_format.push_back(
1600  mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1601  if (!l.IsPositive()) {
1602  core_in_proto_format.back() = NegatedRef(core_in_proto_format.back());
1603  }
1604  }
1605  shared_response_manager->AddUnsatCore(core_in_proto_format);
1606  }
1607  } else {
1608  // Optimization problem.
1609  const auto& objective = *model->GetOrCreate<ObjectiveDefinition>();
1610  const IntegerVariable objective_var = objective.objective_var;
1611  CHECK_NE(objective_var, kNoIntegerVariable);
1612 
1613  if (parameters.optimize_with_core()) {
1614  // TODO(user): This doesn't work with splitting in chunk for now. It
1615  // shouldn't be too hard to fix.
1616  if (parameters.optimize_with_max_hs()) {
1618  objective, solution_observer, model);
1619  } else {
1620  status = model->Mutable<CoreBasedOptimizer>()->Optimize();
1621  }
1622  } else {
1623  // TODO(user): This parameter break the splitting in chunk of a Solve().
1624  // It should probably be moved into another SubSolver altogether.
1625  if (parameters.binary_search_num_conflicts() >= 0) {
1627  solution_observer, model);
1628  }
1630  objective_var, solution_observer, model);
1631  }
1632 
1633  // The search is done in both case.
1634  //
1635  // TODO(user): Remove the weird translation INFEASIBLE->FEASIBLE in the
1636  // function above?
1637  if (status == SatSolver::INFEASIBLE || status == SatSolver::FEASIBLE) {
1638  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1639  solution_info);
1640  }
1641  }
1642 
1643  // TODO(user): Remove from here when we split in chunk. We just want to
1644  // do that at the end.
1645  shared_response_manager->SetStatsFromModel(model);
1646 }
1647 
1648 // Try to find a solution by following the hint and using a low conflict limit.
1649 // The CpModelProto must already be loaded in the Model.
1650 void QuickSolveWithHint(const CpModelProto& model_proto,
1651  SharedResponseManager* shared_response_manager,
1652  Model* model) {
1653  if (!model_proto.has_solution_hint()) return;
1654  if (shared_response_manager->ProblemIsSolved()) return;
1655 
1656  // Temporarily change the parameters.
1657  auto* parameters = model->GetOrCreate<SatParameters>();
1658  const SatParameters saved_params = *parameters;
1659  parameters->set_max_number_of_conflicts(parameters->hint_conflict_limit());
1660  parameters->set_search_branching(SatParameters::HINT_SEARCH);
1661  parameters->set_optimize_with_core(false);
1662  auto cleanup = ::absl::MakeCleanup(
1663  [parameters, saved_params]() { *parameters = saved_params; });
1664 
1665  // Solve decision problem.
1667  const auto& mapping = *model->GetOrCreate<CpModelMapping>();
1669  mapping.Literals(model_proto.assumptions()), model);
1670 
1671  const std::string& solution_info = model->Name();
1672  if (status == SatSolver::Status::FEASIBLE) {
1673  CpSolverResponse response;
1674  FillSolutionInResponse(model_proto, *model, &response);
1675  response.set_solution_info(absl::StrCat(solution_info, " [hint]"));
1676  shared_response_manager->NewSolution(response, model);
1677 
1678  if (!model_proto.has_objective()) {
1679  if (parameters->enumerate_all_solutions()) {
1681  }
1682  } else {
1683  // Restrict the objective.
1684  const IntegerVariable objective_var =
1685  model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1686  model->GetOrCreate<SatSolver>()->Backtrack(0);
1687  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
1688  if (!integer_trail->Enqueue(
1690  objective_var,
1691  shared_response_manager->GetInnerObjectiveUpperBound()),
1692  {}, {})) {
1693  shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1694  absl::StrCat(solution_info, " [hint]"));
1695  shared_response_manager->SetStatsFromModel(model);
1696  return;
1697  }
1698  }
1699  }
1700 }
1701 
1702 // Solve a model with a different objective consisting of minimizing the L1
1703 // distance with the provided hint. Note that this method creates an in-memory
1704 // copy of the model and loads a local Model object from the copied model.
1705 void MinimizeL1DistanceWithHint(const CpModelProto& model_proto,
1706  SharedResponseManager* shared_response_manager,
1708  SharedTimeLimit* shared_time_limit,
1709  Model* model) {
1710  Model local_model;
1711  if (!model_proto.has_solution_hint()) return;
1712  if (shared_response_manager->ProblemIsSolved()) return;
1713 
1714  auto* parameters = local_model.GetOrCreate<SatParameters>();
1715  // TODO(user): As of now the repair hint doesn't support when
1716  // enumerate_all_solutions is set since the solution is created on a different
1717  // model.
1718  if (parameters->enumerate_all_solutions()) return;
1719 
1720  // Change the parameters.
1721  const SatParameters saved_params = *model->GetOrCreate<SatParameters>();
1722  *parameters = saved_params;
1723  parameters->set_max_number_of_conflicts(parameters->hint_conflict_limit());
1724  parameters->set_optimize_with_core(false);
1725 
1726  // Update the model to introduce penalties to go away from hinted values.
1727  CpModelProto updated_model_proto = model_proto;
1728  updated_model_proto.clear_objective();
1729 
1730  // TODO(user): For boolean variables we can avoid creating new variables.
1731  for (int i = 0; i < model_proto.solution_hint().vars_size(); ++i) {
1732  const int var = model_proto.solution_hint().vars(i);
1733  const int64 value = model_proto.solution_hint().values(i);
1734 
1735  // Add a new var to represent the difference between var and value.
1736  const int new_var_index = updated_model_proto.variables_size();
1737  IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1738  const int64 min_domain = model_proto.variables(var).domain(0) - value;
1739  const int64 max_domain = model_proto.variables(var).domain(
1740  model_proto.variables(var).domain_size() - 1) -
1741  value;
1742  var_proto->add_domain(min_domain);
1743  var_proto->add_domain(max_domain);
1744 
1745  // new_var = var - value.
1746  ConstraintProto* const linear_constraint_proto =
1747  updated_model_proto.add_constraints();
1748  LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1749  linear->add_vars(new_var_index);
1750  linear->add_coeffs(1);
1751  linear->add_vars(var);
1752  linear->add_coeffs(-1);
1753  linear->add_domain(-value);
1754  linear->add_domain(-value);
1755 
1756  // abs_var = abs(new_var).
1757  const int abs_var_index = updated_model_proto.variables_size();
1758  IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1759  const int64 abs_min_domain = 0;
1760  const int64 abs_max_domain =
1761  std::max(std::abs(min_domain), std::abs(max_domain));
1762  abs_var_proto->add_domain(abs_min_domain);
1763  abs_var_proto->add_domain(abs_max_domain);
1764  ConstraintProto* const abs_constraint_proto =
1765  updated_model_proto.add_constraints();
1766  abs_constraint_proto->mutable_int_max()->set_target(abs_var_index);
1767  abs_constraint_proto->mutable_int_max()->add_vars(new_var_index);
1768  abs_constraint_proto->mutable_int_max()->add_vars(
1769  NegatedRef(new_var_index));
1770 
1771  updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1772  updated_model_proto.mutable_objective()->add_coeffs(1);
1773  }
1774 
1775  SharedResponseManager local_response_manager(
1776  /*log_updates=*/false, parameters->enumerate_all_solutions(),
1777  &updated_model_proto, wall_timer, shared_time_limit);
1778 
1779  local_model.Register<SharedResponseManager>(&local_response_manager);
1780 
1781  // Solve optimization problem.
1782  LoadCpModel(updated_model_proto, &local_response_manager, &local_model);
1783 
1784  ConfigureSearchHeuristics(&local_model);
1785  const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1787  mapping.Literals(updated_model_proto.assumptions()), &local_model);
1788 
1789  const std::string& solution_info = model->Name();
1790  if (status == SatSolver::Status::FEASIBLE) {
1791  CpSolverResponse response;
1792  FillSolutionInResponse(model_proto, local_model, &response);
1793  if (DEBUG_MODE) {
1794  CpSolverResponse updated_response;
1795  FillSolutionInResponse(updated_model_proto, local_model,
1796  &updated_response);
1797  LOG(INFO) << "Found solution with repaired hint penalty = "
1798  << ComputeInnerObjective(updated_model_proto.objective(),
1799  updated_response);
1800  }
1801  response.set_solution_info(absl::StrCat(solution_info, " [repaired]"));
1802  shared_response_manager->NewSolution(response, &local_model);
1803  }
1804 }
1805 
1806 // TODO(user): If this ever shows up in the profile, we could avoid copying
1807 // the mapping_proto if we are careful about how we modify the variable domain
1808 // before postsolving it. Note that 'num_variables_in_original_model' refers to
1809 // the model before presolve.
1810 void PostsolveResponseWithFullSolver(
1811  const int64 num_variables_in_original_model, CpModelProto mapping_proto,
1812  const std::vector<int>& postsolve_mapping, WallTimer* wall_timer,
1813  CpSolverResponse* response) {
1814  if (response->status() != CpSolverStatus::FEASIBLE &&
1815  response->status() != CpSolverStatus::OPTIMAL) {
1816  return;
1817  }
1818 
1819  // If presolve was not called, the mapping model is empty.
1820  if (mapping_proto.variables_size() == 0) {
1821  return;
1822  }
1823 
1824  // Postsolve.
1825  for (int i = 0; i < response->solution_size(); ++i) {
1826  auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1827  var_proto->clear_domain();
1828  var_proto->add_domain(response->solution(i));
1829  var_proto->add_domain(response->solution(i));
1830  }
1831  for (int i = 0; i < response->solution_lower_bounds_size(); ++i) {
1832  auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1834  ReadDomainFromProto(*var_proto)
1835  .IntersectionWith({response->solution_lower_bounds(i),
1836  response->solution_upper_bounds(i)}),
1837  var_proto);
1838  }
1839 
1840  // Postosolve parameters.
1841  // TODO(user): this problem is usually trivial, but we may still want to
1842  // impose a time limit or copy some of the parameters passed by the user.
1843  Model postsolve_model;
1844  {
1845  SatParameters& params = *postsolve_model.GetOrCreate<SatParameters>();
1846  params.set_linearization_level(0);
1847  params.set_cp_model_probing_level(0);
1848  }
1849 
1850  std::unique_ptr<TimeLimit> time_limit(TimeLimit::Infinite());
1851  SharedTimeLimit shared_time_limit(time_limit.get());
1852  SharedResponseManager local_response_manager(
1853  /*log_updates=*/false, /*enumerate_all_solutions=*/false, &mapping_proto,
1854  wall_timer, &shared_time_limit);
1855  LoadCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1856  SolveLoadedCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1857  const CpSolverResponse postsolve_response =
1858  local_response_manager.GetResponse();
1859  CHECK(postsolve_response.status() == CpSolverStatus::FEASIBLE ||
1860  postsolve_response.status() == CpSolverStatus::OPTIMAL);
1861 
1862  // We only copy the solution from the postsolve_response to the response.
1863  response->clear_solution();
1864  response->clear_solution_lower_bounds();
1865  response->clear_solution_upper_bounds();
1866  if (!postsolve_response.solution().empty()) {
1867  for (int i = 0; i < num_variables_in_original_model; ++i) {
1868  response->add_solution(postsolve_response.solution(i));
1869  }
1870  } else {
1871  for (int i = 0; i < num_variables_in_original_model; ++i) {
1872  response->add_solution_lower_bounds(
1873  postsolve_response.solution_lower_bounds(i));
1874  response->add_solution_upper_bounds(
1875  postsolve_response.solution_upper_bounds(i));
1876  }
1877  }
1878 }
1879 
1880 void PostsolveResponseWrapper(const SatParameters& params,
1881  const int64 num_variables_in_original_model,
1882  const CpModelProto& mapping_proto,
1883  const std::vector<int>& postsolve_mapping,
1885  CpSolverResponse* response) {
1886  if (params.cp_model_postsolve_with_full_solver()) {
1887  PostsolveResponseWithFullSolver(num_variables_in_original_model,
1888  mapping_proto, postsolve_mapping,
1889  wall_timer, response);
1890  } else {
1891  PostsolveResponse(num_variables_in_original_model, mapping_proto,
1892  postsolve_mapping, response);
1893  }
1894 }
1895 
1896 // TODO(user): Uniformize this function with the other one.
1897 CpSolverResponse SolvePureSatModel(const CpModelProto& model_proto,
1898  WallTimer* wall_timer, Model* model) {
1899  std::unique_ptr<SatSolver> solver(new SatSolver());
1900  SatParameters parameters = *model->GetOrCreate<SatParameters>();
1901  solver->SetParameters(parameters);
1902  model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(parameters);
1903 
1904  // Create a DratProofHandler?
1905  std::unique_ptr<DratProofHandler> drat_proof_handler;
1906 #if !defined(__PORTABLE_PLATFORM__)
1907  if (!absl::GetFlag(FLAGS_drat_output).empty() ||
1908  absl::GetFlag(FLAGS_drat_check)) {
1909  if (!absl::GetFlag(FLAGS_drat_output).empty()) {
1910  File* output;
1911  CHECK_OK(file::Open(absl::GetFlag(FLAGS_drat_output), "w", &output,
1912  file::Defaults()));
1913  drat_proof_handler = absl::make_unique<DratProofHandler>(
1914  /*in_binary_format=*/false, output, absl::GetFlag(FLAGS_drat_check));
1915  } else {
1916  drat_proof_handler = absl::make_unique<DratProofHandler>();
1917  }
1918  solver->SetDratProofHandler(drat_proof_handler.get());
1919  }
1920 #endif // __PORTABLE_PLATFORM__
1921 
1922  auto get_literal = [](int ref) {
1923  if (ref >= 0) return Literal(BooleanVariable(ref), true);
1924  return Literal(BooleanVariable(NegatedRef(ref)), false);
1925  };
1926 
1927  std::vector<Literal> temp;
1928  const int num_variables = model_proto.variables_size();
1929  solver->SetNumVariables(num_variables);
1930  if (drat_proof_handler != nullptr) {
1931  drat_proof_handler->SetNumVariables(num_variables);
1932 
1933  // We load the model in the drat_proof_handler for the case where we want
1934  // to do in-memory checking.
1935  for (int ref = 0; ref < num_variables; ++ref) {
1936  const Domain domain = ReadDomainFromProto(model_proto.variables(ref));
1937  if (domain.IsFixed()) {
1938  const Literal ref_literal =
1939  domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1940  drat_proof_handler->AddProblemClause({ref_literal});
1941  }
1942  }
1943  for (const ConstraintProto& ct : model_proto.constraints()) {
1944  switch (ct.constraint_case()) {
1945  case ConstraintProto::ConstraintCase::kBoolAnd: {
1946  if (ct.enforcement_literal_size() == 0) {
1947  for (const int ref : ct.bool_and().literals()) {
1948  drat_proof_handler->AddProblemClause({get_literal(ref)});
1949  }
1950  } else {
1951  // a => b
1952  const Literal not_a =
1953  get_literal(ct.enforcement_literal(0)).Negated();
1954  for (const int ref : ct.bool_and().literals()) {
1955  drat_proof_handler->AddProblemClause({not_a, get_literal(ref)});
1956  }
1957  }
1958  break;
1959  }
1960  case ConstraintProto::ConstraintCase::kBoolOr:
1961  temp.clear();
1962  for (const int ref : ct.bool_or().literals()) {
1963  temp.push_back(get_literal(ref));
1964  }
1965  drat_proof_handler->AddProblemClause(temp);
1966  break;
1967  default:
1968  LOG(FATAL) << "Not supported";
1969  }
1970  }
1971  }
1972 
1973  for (const ConstraintProto& ct : model_proto.constraints()) {
1974  switch (ct.constraint_case()) {
1975  case ConstraintProto::ConstraintCase::kBoolAnd: {
1976  if (ct.enforcement_literal_size() == 0) {
1977  for (const int ref : ct.bool_and().literals()) {
1978  const Literal b = get_literal(ref);
1979  solver->AddUnitClause(b);
1980  }
1981  } else {
1982  // a => b
1983  const Literal not_a =
1984  get_literal(ct.enforcement_literal(0)).Negated();
1985  for (const int ref : ct.bool_and().literals()) {
1986  const Literal b = get_literal(ref);
1987  solver->AddProblemClause({not_a, b});
1988  }
1989  }
1990  break;
1991  }
1992  case ConstraintProto::ConstraintCase::kBoolOr:
1993  temp.clear();
1994  for (const int ref : ct.bool_or().literals()) {
1995  temp.push_back(get_literal(ref));
1996  }
1997  solver->AddProblemClause(temp);
1998  break;
1999  default:
2000  LOG(FATAL) << "Not supported";
2001  }
2002  }
2003 
2004  // Deal with fixed variables.
2005  for (int ref = 0; ref < num_variables; ++ref) {
2006  const Domain domain = ReadDomainFromProto(model_proto.variables(ref));
2007  if (domain.Min() == domain.Max()) {
2008  const Literal ref_literal =
2009  domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
2010  solver->AddUnitClause(ref_literal);
2011  }
2012  }
2013 
2014  SatSolver::Status status;
2015  CpSolverResponse response;
2016  if (parameters.cp_model_presolve()) {
2017  std::vector<bool> solution;
2018  status = SolveWithPresolve(&solver, model->GetOrCreate<TimeLimit>(),
2019  &solution, drat_proof_handler.get());
2020  if (status == SatSolver::FEASIBLE) {
2021  response.clear_solution();
2022  for (int ref = 0; ref < num_variables; ++ref) {
2023  response.add_solution(solution[ref]);
2024  }
2025  }
2026  } else {
2027  status = solver->SolveWithTimeLimit(model->GetOrCreate<TimeLimit>());
2028  if (status == SatSolver::FEASIBLE) {
2029  response.clear_solution();
2030  for (int ref = 0; ref < num_variables; ++ref) {
2031  response.add_solution(
2032  solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
2033  }
2034  }
2035  }
2036 
2037  // Tricky: the model local time limit is updated by the new functions, but
2038  // the old ones update time_limit directly.
2039  model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
2040  solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
2041 
2042  switch (status) {
2043  case SatSolver::LIMIT_REACHED: {
2044  response.set_status(CpSolverStatus::UNKNOWN);
2045  break;
2046  }
2047  case SatSolver::FEASIBLE: {
2049  std::vector<int64>(response.solution().begin(),
2050  response.solution().end())));
2051  response.set_status(CpSolverStatus::OPTIMAL);
2052  break;
2053  }
2054  case SatSolver::INFEASIBLE: {
2056  break;
2057  }
2058  default:
2059  LOG(FATAL) << "Unexpected SatSolver::Status " << status;
2060  }
2061  response.set_num_booleans(solver->NumVariables());
2062  response.set_num_branches(solver->num_branches());
2063  response.set_num_conflicts(solver->num_failures());
2064  response.set_num_binary_propagations(solver->num_propagations());
2065  response.set_num_integer_propagations(0);
2066  response.set_wall_time(wall_timer->Get());
2067  response.set_deterministic_time(
2068  model->Get<TimeLimit>()->GetElapsedDeterministicTime());
2069 
2070  if (status == SatSolver::INFEASIBLE && drat_proof_handler != nullptr) {
2071  WallTimer drat_timer;
2072  drat_timer.Start();
2073  DratChecker::Status drat_status = drat_proof_handler->Check(
2074  absl::GetFlag(FLAGS_max_drat_time_in_seconds));
2075  switch (drat_status) {
2076  case DratChecker::UNKNOWN:
2077  LOG(INFO) << "DRAT status: UNKNOWN";
2078  break;
2079  case DratChecker::VALID:
2080  LOG(INFO) << "DRAT status: VALID";
2081  break;
2082  case DratChecker::INVALID:
2083  LOG(ERROR) << "DRAT status: INVALID";
2084  break;
2085  default:
2086  // Should not happen.
2087  break;
2088  }
2089  LOG(INFO) << "DRAT wall time: " << drat_timer.Get();
2090  } else if (drat_proof_handler != nullptr) {
2091  // Always log a DRAT status to make it easier to extract it from a multirun
2092  // result with awk.
2093  LOG(INFO) << "DRAT status: NA";
2094  LOG(INFO) << "DRAT wall time: NA";
2095  LOG(INFO) << "DRAT user time: NA";
2096  }
2097  return response;
2098 }
2099 
2100 #if !defined(__PORTABLE_PLATFORM__)
2101 
2102 // Small wrapper to simplify the constructions of the two SubSolver below.
2103 struct SharedClasses {
2104  CpModelProto const* model_proto;
2106  SharedTimeLimit* time_limit;
2107  SharedBoundsManager* bounds;
2108  SharedResponseManager* response;
2109  SharedRelaxationSolutionRepository* relaxation_solutions;
2110  SharedLPSolutionRepository* lp_solutions;
2111  SharedIncompleteSolutionManager* incomplete_solutions;
2112 
2113  bool SearchIsDone() {
2114  if (response->ProblemIsSolved()) return true;
2115  if (time_limit->LimitReached()) return true;
2116  return false;
2117  }
2118 };
2119 
2120 // Encapsulate a full CP-SAT solve without presolve in the SubSolver API.
2121 class FullProblemSolver : public SubSolver {
2122  public:
2123  FullProblemSolver(const std::string& name,
2124  const SatParameters& local_parameters, bool split_in_chunks,
2125  SharedClasses* shared)
2126  : SubSolver(name),
2127  shared_(shared),
2128  split_in_chunks_(split_in_chunks),
2129  local_model_(absl::make_unique<Model>(name)) {
2130  // Setup the local model parameters and time limit.
2131  *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2132  shared_->time_limit->UpdateLocalLimit(
2133  local_model_->GetOrCreate<TimeLimit>());
2134 
2135  if (shared->response != nullptr) {
2136  local_model_->Register<SharedResponseManager>(shared->response);
2137  }
2138 
2139  if (shared->relaxation_solutions != nullptr) {
2140  local_model_->Register<SharedRelaxationSolutionRepository>(
2141  shared->relaxation_solutions);
2142  }
2143 
2144  if (shared->lp_solutions != nullptr) {
2145  local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2146  }
2147 
2148  if (shared->incomplete_solutions != nullptr) {
2149  local_model_->Register<SharedIncompleteSolutionManager>(
2150  shared->incomplete_solutions);
2151  }
2152 
2153  // Level zero variable bounds sharing.
2154  if (shared_->bounds != nullptr) {
2155  RegisterVariableBoundsLevelZeroExport(
2156  *shared_->model_proto, shared_->bounds, local_model_.get());
2157  RegisterVariableBoundsLevelZeroImport(
2158  *shared_->model_proto, shared_->bounds, local_model_.get());
2159  }
2160  }
2161 
2162  bool TaskIsAvailable() override {
2163  if (shared_->SearchIsDone()) return false;
2164 
2165  absl::MutexLock mutex_lock(&mutex_);
2166  return previous_task_is_completed_;
2167  }
2168 
2169  std::function<void()> GenerateTask(int64 task_id) override {
2170  {
2171  absl::MutexLock mutex_lock(&mutex_);
2172  previous_task_is_completed_ = false;
2173  }
2174  return [this]() {
2175  if (solving_first_chunk_) {
2176  LoadCpModel(*shared_->model_proto, shared_->response,
2177  local_model_.get());
2178  if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2179  MinimizeL1DistanceWithHint(*shared_->model_proto, shared_->response,
2180  shared_->wall_timer, shared_->time_limit,
2181  local_model_.get());
2182  } else {
2183  QuickSolveWithHint(*shared_->model_proto, shared_->response,
2184  local_model_.get());
2185  }
2186 
2187  // No need for mutex since we only run one task at the time.
2188  solving_first_chunk_ = false;
2189 
2190  if (split_in_chunks_) {
2191  // Abort first chunk and allow to schedule the next.
2192  absl::MutexLock mutex_lock(&mutex_);
2193  previous_task_is_completed_ = true;
2194  return;
2195  }
2196  }
2197 
2198  auto* time_limit = local_model_->GetOrCreate<TimeLimit>();
2199  if (split_in_chunks_) {
2200  // Configure time limit for chunk solving. Note that we do not want
2201  // to do that for the hint search for now.
2202  auto* params = local_model_->GetOrCreate<SatParameters>();
2203  params->set_max_deterministic_time(1);
2204  time_limit->ResetLimitFromParameters(*params);
2205  shared_->time_limit->UpdateLocalLimit(time_limit);
2206  }
2207 
2208  const double saved_dtime = time_limit->GetElapsedDeterministicTime();
2209  SolveLoadedCpModel(*shared_->model_proto, shared_->response,
2210  local_model_.get());
2211  {
2212  absl::MutexLock mutex_lock(&mutex_);
2213  deterministic_time_since_last_synchronize_ +=
2214  time_limit->GetElapsedDeterministicTime() - saved_dtime;
2215  }
2216 
2217  // Abort if the problem is solved.
2218  if (shared_->SearchIsDone()) {
2219  shared_->time_limit->Stop();
2220  return;
2221  }
2222 
2223  // In this mode, we allow to generate more task.
2224  if (split_in_chunks_) {
2225  absl::MutexLock mutex_lock(&mutex_);
2226  previous_task_is_completed_ = true;
2227  return;
2228  }
2229 
2230  // Once a solver is done clear its memory and do not wait for the
2231  // destruction of the SubSolver. This is important because the full solve
2232  // might not be done at all, for instance this might have been configured
2233  // with stop_after_first_solution.
2234  local_model_.reset();
2235  };
2236  }
2237 
2238  // TODO(user): A few of the information sharing we do between threads does not
2239  // happen here (bound sharing, RINS neighborhood, objective). Fix that so we
2240  // can have a deterministic parallel mode.
2241  void Synchronize() override {
2242  absl::MutexLock mutex_lock(&mutex_);
2243  deterministic_time_ += deterministic_time_since_last_synchronize_;
2244  shared_->time_limit->AdvanceDeterministicTime(
2245  deterministic_time_since_last_synchronize_);
2246  deterministic_time_since_last_synchronize_ = 0.0;
2247  }
2248 
2249  private:
2250  SharedClasses* shared_;
2251  const bool split_in_chunks_;
2252  std::unique_ptr<Model> local_model_;
2253 
2254  // The first chunk is special. It is the one in which we load the model and
2255  // try to follow the hint.
2256  bool solving_first_chunk_ = true;
2257 
2258  absl::Mutex mutex_;
2259  double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2260  0.0;
2261  bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) = true;
2262 };
2263 
2264 class FeasibilityPumpSolver : public SubSolver {
2265  public:
2266  FeasibilityPumpSolver(const SatParameters& local_parameters,
2267  SharedClasses* shared)
2268  : SubSolver("feasibility_pump"),
2269  shared_(shared),
2270  local_model_(absl::make_unique<Model>(name_)) {
2271  // Setup the local model parameters and time limit.
2272  *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2273  shared_->time_limit->UpdateLocalLimit(
2274  local_model_->GetOrCreate<TimeLimit>());
2275 
2276  if (shared->response != nullptr) {
2277  local_model_->Register<SharedResponseManager>(shared->response);
2278  }
2279 
2280  if (shared->relaxation_solutions != nullptr) {
2281  local_model_->Register<SharedRelaxationSolutionRepository>(
2282  shared->relaxation_solutions);
2283  }
2284 
2285  if (shared->lp_solutions != nullptr) {
2286  local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2287  }
2288 
2289  if (shared->incomplete_solutions != nullptr) {
2290  local_model_->Register<SharedIncompleteSolutionManager>(
2291  shared->incomplete_solutions);
2292  }
2293 
2294  // Level zero variable bounds sharing.
2295  if (shared_->bounds != nullptr) {
2296  RegisterVariableBoundsLevelZeroImport(
2297  *shared_->model_proto, shared_->bounds, local_model_.get());
2298  }
2299  }
2300 
2301  bool TaskIsAvailable() override {
2302  if (shared_->SearchIsDone()) return false;
2303  absl::MutexLock mutex_lock(&mutex_);
2304  return previous_task_is_completed_;
2305  }
2306 
2307  std::function<void()> GenerateTask(int64 task_id) override {
2308  return [this]() {
2309  {
2310  absl::MutexLock mutex_lock(&mutex_);
2311  if (!previous_task_is_completed_) return;
2312  previous_task_is_completed_ = false;
2313  }
2314  {
2315  absl::MutexLock mutex_lock(&mutex_);
2316  if (solving_first_chunk_) {
2317  LoadFeasibilityPump(*shared_->model_proto, shared_->response,
2318  local_model_.get());
2319  // No new task will be scheduled for this worker if there is no
2320  // linear relaxation.
2321  if (local_model_->Get<FeasibilityPump>() == nullptr) return;
2322  solving_first_chunk_ = false;
2323  // Abort first chunk and allow to schedule the next.
2324  previous_task_is_completed_ = true;
2325  return;
2326  }
2327  }
2328 
2329  auto* time_limit = local_model_->GetOrCreate<TimeLimit>();
2330  const double saved_dtime = time_limit->GetElapsedDeterministicTime();
2331  auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2332  if (!feasibility_pump->Solve()) {
2333  shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2334  }
2335 
2336  {
2337  absl::MutexLock mutex_lock(&mutex_);
2338  deterministic_time_since_last_synchronize_ +=
2339  time_limit->GetElapsedDeterministicTime() - saved_dtime;
2340  }
2341 
2342  // Abort if the problem is solved.
2343  if (shared_->SearchIsDone()) {
2344  shared_->time_limit->Stop();
2345  return;
2346  }
2347 
2348  absl::MutexLock mutex_lock(&mutex_);
2349  previous_task_is_completed_ = true;
2350  };
2351  }
2352 
2353  void Synchronize() override {
2354  absl::MutexLock mutex_lock(&mutex_);
2355  deterministic_time_ += deterministic_time_since_last_synchronize_;
2356  shared_->time_limit->AdvanceDeterministicTime(
2357  deterministic_time_since_last_synchronize_);
2358  deterministic_time_since_last_synchronize_ = 0.0;
2359  }
2360 
2361  private:
2362  SharedClasses* shared_;
2363  std::unique_ptr<Model> local_model_;
2364 
2365  absl::Mutex mutex_;
2366 
2367  // The first chunk is special. It is the one in which we load the linear
2368  // constraints.
2369  bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) = true;
2370 
2371  double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2372  0.0;
2373  bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) = true;
2374 };
2375 
2376 // A Subsolver that generate LNS solve from a given neighborhood.
2377 class LnsSolver : public SubSolver {
2378  public:
2379  LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2380  const SatParameters& parameters,
2381  NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2382  : SubSolver(generator->name()),
2383  generator_(std::move(generator)),
2384  helper_(helper),
2385  parameters_(parameters),
2386  shared_(shared) {}
2387 
2388  bool TaskIsAvailable() override {
2389  if (shared_->SearchIsDone()) return false;
2390  return generator_->ReadyToGenerate();
2391  }
2392 
2393  std::function<void()> GenerateTask(int64 task_id) override {
2394  return [task_id, this]() {
2395  if (shared_->SearchIsDone()) return;
2396 
2397  // Create a random number generator whose seed depends both on the task_id
2398  // and on the parameters_.random_seed() so that changing the later will
2399  // change the LNS behavior.
2400  const int32 low = static_cast<int32>(task_id);
2401  const int32 high = task_id >> 32;
2402  std::seed_seq seed{low, high, parameters_.random_seed()};
2403  random_engine_t random(seed);
2404 
2405  NeighborhoodGenerator::SolveData data;
2406  data.difficulty = generator_->difficulty();
2407  data.deterministic_limit = generator_->deterministic_limit();
2408 
2409  // Choose a base solution for this neighborhood.
2410  CpSolverResponse base_response;
2411  {
2412  const SharedSolutionRepository<int64>& repo =
2413  shared_->response->SolutionsRepository();
2414  if (repo.NumSolutions() > 0) {
2415  base_response.set_status(CpSolverStatus::FEASIBLE);
2416  const SharedSolutionRepository<int64>::Solution solution =
2417  repo.GetRandomBiasedSolution(random);
2418  for (const int64 value : solution.variable_values) {
2419  base_response.add_solution(value);
2420  }
2421  // Note: We assume that the solution rank is the solution internal
2422  // objective.
2423  data.initial_best_objective = repo.GetSolution(0).rank;
2424  data.base_objective = solution.rank;
2425  } else {
2426  base_response.set_status(CpSolverStatus::UNKNOWN);
2427 
2428  // If we do not have a solution, we use the current objective upper
2429  // bound so that our code that compute an "objective" improvement
2430  // works.
2431  //
2432  // TODO(user): this is non-deterministic. Fix.
2433  data.initial_best_objective =
2434  shared_->response->GetInnerObjectiveUpperBound();
2435  data.base_objective = data.initial_best_objective;
2436  }
2437  }
2438 
2439  Neighborhood neighborhood;
2440  {
2441  absl::MutexLock mutex_lock(helper_->MutableMutex());
2442  neighborhood =
2443  generator_->Generate(base_response, data.difficulty, random);
2444  }
2445  neighborhood.cp_model.set_name(absl::StrCat("lns_", task_id));
2446  if (!neighborhood.is_generated) return;
2447  data.neighborhood_id = neighborhood.id;
2448 
2449  const double fully_solved_proportion =
2450  static_cast<double>(generator_->num_fully_solved_calls()) /
2451  std::max(int64{1}, generator_->num_calls());
2452  std::string source_info = name();
2453  if (!neighborhood.source_info.empty()) {
2454  absl::StrAppend(&source_info, "_", neighborhood.source_info);
2455  }
2456  const std::string solution_info = absl::StrFormat(
2457  "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2458  task_id, data.deterministic_limit, fully_solved_proportion);
2459 
2460  SatParameters local_params(parameters_);
2461  local_params.set_max_deterministic_time(data.deterministic_limit);
2462  local_params.set_stop_after_first_solution(false);
2463  local_params.set_log_search_progress(false);
2464  local_params.set_cp_model_probing_level(0);
2465 
2466  if (absl::GetFlag(FLAGS_cp_model_dump_lns)) {
2467  const std::string name =
2468  absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2469  neighborhood.cp_model.name(), ".pbtxt");
2470  LOG(INFO) << "Dumping LNS model to '" << name << "'.";
2471  CHECK_OK(
2472  file::SetTextProto(name, neighborhood.cp_model, file::Defaults()));
2473  }
2474 
2475  Model local_model;
2476  *(local_model.GetOrCreate<SatParameters>()) = local_params;
2477  TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2478  local_time_limit->ResetLimitFromParameters(local_params);
2479  shared_->time_limit->UpdateLocalLimit(local_time_limit);
2480 
2481  const int64 num_neighborhood_model_vars =
2482  neighborhood.cp_model.variables_size();
2483  // Presolve and solve the LNS fragment.
2484  CpModelProto mapping_proto;
2485  std::vector<int> postsolve_mapping;
2486  auto context = absl::make_unique<PresolveContext>(
2487  VLOG_IS_ON(3), &local_model, &neighborhood.cp_model, &mapping_proto);
2488  PresolveCpModel(context.get(), &postsolve_mapping);
2489 
2490  // Release the context
2491  context.reset(nullptr);
2492 
2493  // TODO(user): Depending on the problem, we should probably use the
2494  // parameters that work bests (core, linearization_level, etc...) or
2495  // maybe we can just randomize them like for the base solution used.
2496  SharedResponseManager local_response_manager(
2497  /*log_updates=*/false, /*enumerate_all_solutions=*/false,
2498  &neighborhood.cp_model, shared_->wall_timer, shared_->time_limit);
2499  LoadCpModel(neighborhood.cp_model, &local_response_manager, &local_model);
2500  QuickSolveWithHint(neighborhood.cp_model, &local_response_manager,
2501  &local_model);
2502  SolveLoadedCpModel(neighborhood.cp_model, &local_response_manager,
2503  &local_model);
2504  CpSolverResponse local_response = local_response_manager.GetResponse();
2505  if (local_response.solution_info().empty()) {
2506  local_response.set_solution_info(solution_info);
2507  } else {
2508  local_response.set_solution_info(
2509  absl::StrCat(local_response.solution_info(), " ", solution_info));
2510  }
2511 
2512  // TODO(user): we actually do not need to postsolve if the solution is
2513  // not going to be used...
2514  PostsolveResponseWrapper(local_params, num_neighborhood_model_vars,
2515  mapping_proto, postsolve_mapping,
2516  shared_->wall_timer, &local_response);
2517  data.status = local_response.status();
2518  data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2519 
2520  if (generator_->IsRelaxationGenerator()) {
2521  bool has_feasible_solution = false;
2522  if (local_response.status() == CpSolverStatus::OPTIMAL ||
2523  local_response.status() == CpSolverStatus::FEASIBLE) {
2524  has_feasible_solution = true;
2525  }
2526 
2527  if (local_response.status() == CpSolverStatus::INFEASIBLE) {
2528  shared_->response->NotifyThatImprovingProblemIsInfeasible(
2529  local_response.solution_info());
2530  }
2531 
2532  if (shared_->model_proto->has_objective()) {
2533  // TODO(user): This is not deterministic since it is updated without
2534  // synchronization! So we shouldn't base the LNS score out of that.
2535  const IntegerValue current_obj_lb =
2536  shared_->response->GetInnerObjectiveLowerBound();
2537 
2538  const IntegerValue local_obj_lb =
2539  local_response_manager.GetInnerObjectiveLowerBound();
2540 
2541  const double scaled_local_obj_bound = ScaleObjectiveValue(
2542  neighborhood.cp_model.objective(), local_obj_lb.value());
2543 
2544  // Update the bound.
2545  const IntegerValue new_inner_obj_lb = IntegerValue(
2546  std::ceil(UnscaleObjectiveValue(shared_->model_proto->objective(),
2547  scaled_local_obj_bound) -
2548  1e-6));
2549  data.new_objective_bound = new_inner_obj_lb;
2550  data.initial_best_objective_bound = current_obj_lb;
2551  if (new_inner_obj_lb > current_obj_lb) {
2552  shared_->response->UpdateInnerObjectiveBounds(
2553  solution_info, new_inner_obj_lb, kMaxIntegerValue);
2554  }
2555  }
2556 
2557  // If we have a solution of the relaxed problem, we check if it is also
2558  // a valid solution of the non-relaxed one.
2559  if (has_feasible_solution) {
2560  if (SolutionIsFeasible(
2561  *shared_->model_proto,
2562  std::vector<int64>(local_response.solution().begin(),
2563  local_response.solution().end()))) {
2564  shared_->response->NewSolution(local_response,
2565  /*model=*/nullptr);
2566 
2567  // Mark the solution optimal if the relaxation status is optimal.
2568  if (local_response.status() == CpSolverStatus::OPTIMAL) {
2569  shared_->response->NotifyThatImprovingProblemIsInfeasible(
2570  local_response.solution_info());
2571  shared_->time_limit->Stop();
2572  }
2573  }
2574  shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2575  }
2576  } else {
2577  if (!local_response.solution().empty()) {
2579  *shared_->model_proto,
2580  std::vector<int64>(local_response.solution().begin(),
2581  local_response.solution().end())))
2582  << solution_info;
2583  }
2584 
2585  // Finish to fill the SolveData now that the local solve is done.
2586  data.new_objective = data.base_objective;
2587  if (local_response.status() == CpSolverStatus::OPTIMAL ||
2588  local_response.status() == CpSolverStatus::FEASIBLE) {
2589  data.new_objective = IntegerValue(ComputeInnerObjective(
2590  shared_->model_proto->objective(), local_response));
2591  }
2592 
2593  // Report any feasible solution we have.
2594  if (local_response.status() == CpSolverStatus::OPTIMAL ||
2595  local_response.status() == CpSolverStatus::FEASIBLE) {
2596  shared_->response->NewSolution(local_response,
2597  /*model=*/nullptr);
2598  }
2599  if (!neighborhood.is_reduced &&
2600  (local_response.status() == CpSolverStatus::OPTIMAL ||
2601  local_response.status() == CpSolverStatus::INFEASIBLE)) {
2602  shared_->response->NotifyThatImprovingProblemIsInfeasible(
2603  local_response.solution_info());
2604  shared_->time_limit->Stop();
2605  }
2606  }
2607 
2608  generator_->AddSolveData(data);
2609 
2610  // The total number of call when this was called is the same as task_id.
2611  const int total_num_calls = task_id;
2612  VLOG(2) << name() << ": [difficulty: " << data.difficulty
2613  << ", id: " << task_id
2614  << ", deterministic_time: " << data.deterministic_time << " / "
2615  << data.deterministic_limit
2616  << ", status: " << ProtoEnumToString<CpSolverStatus>(data.status)
2617  << ", num calls: " << generator_->num_calls()
2618  << ", UCB1 Score: " << generator_->GetUCBScore(total_num_calls)
2619  << ", p: " << fully_solved_proportion << "]";
2620  };
2621  }
2622 
2623  void Synchronize() override {
2624  generator_->Synchronize();
2625  const double old = deterministic_time_;
2626  deterministic_time_ = generator_->deterministic_time();
2627  shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2628  }
2629 
2630  private:
2631  std::unique_ptr<NeighborhoodGenerator> generator_;
2632  NeighborhoodGeneratorHelper* helper_;
2633  const SatParameters parameters_;
2634  SharedClasses* shared_;
2635 };
2636 
2637 void SolveCpModelParallel(const CpModelProto& model_proto,
2638  SharedResponseManager* shared_response_manager,
2639  SharedTimeLimit* shared_time_limit,
2640  WallTimer* wall_timer, Model* global_model) {
2641  CHECK(shared_response_manager != nullptr);
2642  const SatParameters& parameters = *global_model->GetOrCreate<SatParameters>();
2643  const int num_search_workers = parameters.num_search_workers();
2644  const bool log_search = parameters.log_search_progress() || VLOG_IS_ON(1);
2645  CHECK(!parameters.enumerate_all_solutions())
2646  << "Enumerating all solutions in parallel is not supported.";
2647 
2648  std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2649  if (parameters.share_level_zero_bounds()) {
2650  shared_bounds_manager = absl::make_unique<SharedBoundsManager>(model_proto);
2651  }
2652 
2653  std::unique_ptr<SharedRelaxationSolutionRepository>
2654  shared_relaxation_solutions;
2655  if (parameters.use_relaxation_lns()) {
2656  shared_relaxation_solutions =
2657  absl::make_unique<SharedRelaxationSolutionRepository>(
2658  /*num_solutions_to_keep=*/10);
2659  global_model->Register<SharedRelaxationSolutionRepository>(
2660  shared_relaxation_solutions.get());
2661  }
2662 
2663  auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2664  /*num_solutions_to_keep=*/10);
2665  global_model->Register<SharedLPSolutionRepository>(shared_lp_solutions.get());
2666 
2667  // We currently only use the feasiblity pump if it is enabled and some other
2668  // parameters are not on.
2669  std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2670  const bool use_feasibility_pump = parameters.use_feasibility_pump() &&
2671  parameters.linearization_level() > 0 &&
2672  !parameters.use_lns_only() &&
2673  !parameters.interleave_search();
2674  if (use_feasibility_pump) {
2675  shared_incomplete_solutions =
2676  absl::make_unique<SharedIncompleteSolutionManager>();
2677  global_model->Register<SharedIncompleteSolutionManager>(
2678  shared_incomplete_solutions.get());
2679  }
2680 
2681  SharedClasses shared;
2682  shared.model_proto = &model_proto;
2683  shared.wall_timer = wall_timer;
2684  shared.time_limit = shared_time_limit;
2685  shared.bounds = shared_bounds_manager.get();
2686  shared.response = shared_response_manager;
2687  shared.relaxation_solutions = shared_relaxation_solutions.get();
2688  shared.lp_solutions = shared_lp_solutions.get();
2689  shared.incomplete_solutions = shared_incomplete_solutions.get();
2690 
2691  // The list of all the SubSolver that will be used in this parallel search.
2692  std::vector<std::unique_ptr<SubSolver>> subsolvers;
2693 
2694  // Add a synchronization point for the shared classes.
2695  subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2696  [shared_response_manager, &shared_bounds_manager,
2697  &shared_relaxation_solutions, &shared_lp_solutions]() {
2698  shared_response_manager->Synchronize();
2699  shared_response_manager->MutableSolutionsRepository()->Synchronize();
2700  if (shared_bounds_manager != nullptr) {
2701  shared_bounds_manager->Synchronize();
2702  }
2703  if (shared_relaxation_solutions != nullptr) {
2704  shared_relaxation_solutions->Synchronize();
2705  }
2706  if (shared_lp_solutions != nullptr) {
2707  shared_lp_solutions->Synchronize();
2708  }
2709  }));
2710 
2711  if (parameters.use_lns_only()) {
2712  // Register something to find a first solution. Note that this is mainly
2713  // used for experimentation, and using no LP ususally result in a faster
2714  // first solution.
2715  SatParameters local_params = parameters;
2716  local_params.set_stop_after_first_solution(true);
2717  local_params.set_linearization_level(0);
2718  subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2719  "first_solution", local_params,
2720  /*split_in_chunks=*/false, &shared));
2721  } else {
2722  for (const SatParameters& local_params : GetDiverseSetOfParameters(
2723  parameters, model_proto, num_search_workers)) {
2724  // TODO(user): This is currently not supported here.
2725  if (parameters.optimize_with_max_hs()) continue;
2726 
2727  subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2728  local_params.name(), local_params,
2729  /*split_in_chunks=*/parameters.interleave_search(), &shared));
2730  }
2731  }
2732 
2733  // Add FeasibilityPumpSolver if enabled.
2734  if (use_feasibility_pump) {
2735  subsolvers.push_back(
2736  absl::make_unique<FeasibilityPumpSolver>(parameters, &shared));
2737  }
2738 
2739  // Add LNS SubSolver(s).
2740 
2741  // Add the NeighborhoodGeneratorHelper as a special subsolver so that its
2742  // Synchronize() is called before any LNS neighborhood solvers.
2743  auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2744  &model_proto, &parameters, shared_response_manager, shared_time_limit,
2745  shared_bounds_manager.get());
2746  NeighborhoodGeneratorHelper* helper = unique_helper.get();
2747  subsolvers.push_back(std::move(unique_helper));
2748 
2749  // By default we use the user provided parameters.
2750  std::vector<SatParameters> lns_params = {parameters};
2751  lns_params.back().set_name("default");
2752  if (parameters.diversify_lns_params()) {
2753  std::vector<SatParameters> lns_params =
2755  }
2756  for (const SatParameters& local_params : lns_params) {
2757  // Only register following LNS SubSolver if there is an objective.
2758  if (model_proto.has_objective()) {
2759  // Enqueue all the possible LNS neighborhood subsolvers.
2760  // Each will have their own metrics.
2761  subsolvers.push_back(absl::make_unique<LnsSolver>(
2762  absl::make_unique<SimpleNeighborhoodGenerator>(
2763  helper, absl::StrCat("rnd_lns_", local_params.name())),
2764  local_params, helper, &shared));
2765  subsolvers.push_back(absl::make_unique<LnsSolver>(
2766  absl::make_unique<VariableGraphNeighborhoodGenerator>(
2767  helper, absl::StrCat("var_lns_", local_params.name())),
2768  local_params, helper, &shared));
2769  subsolvers.push_back(absl::make_unique<LnsSolver>(
2770  absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2771  helper, absl::StrCat("cst_lns_", local_params.name())),
2772  local_params, helper, &shared));
2773 
2774  if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty()) {
2775  subsolvers.push_back(absl::make_unique<LnsSolver>(
2776  absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2777  helper, absl::StrCat("scheduling_time_window_lns_",
2778  local_params.name())),
2779  local_params, helper, &shared));
2780  subsolvers.push_back(absl::make_unique<LnsSolver>(
2781  absl::make_unique<SchedulingNeighborhoodGenerator>(
2782  helper,
2783  absl::StrCat("scheduling_random_lns_", local_params.name())),
2784  local_params, helper, &shared));
2785  }
2786  }
2787 
2788  // TODO(user): for now this is not deterministic so we disable it on
2789  // interleave search. Fix.
2790  if (parameters.use_rins_lns() && !parameters.interleave_search()) {
2791  // Note that we always create the SharedLPSolutionRepository. This meets
2792  // the requirement of having at least one of
2793  // SharedRelaxationSolutionRepository or SharedLPSolutionRepository to
2794  // create RINS/RENS lns generators.
2795 
2796  // RINS.
2797  subsolvers.push_back(absl::make_unique<LnsSolver>(
2798  absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2799  helper, shared.response, shared.relaxation_solutions,
2800  shared.lp_solutions, /*incomplete_solutions=*/nullptr,
2801  absl::StrCat("rins_lns_", local_params.name())),
2802  local_params, helper, &shared));
2803 
2804  // RENS.
2805  subsolvers.push_back(absl::make_unique<LnsSolver>(
2806  absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2807  helper, /*respons_manager=*/nullptr, shared.relaxation_solutions,
2808  shared.lp_solutions, shared.incomplete_solutions,
2809  absl::StrCat("rens_lns_", local_params.name())),
2810  local_params, helper, &shared));
2811  }
2812 
2813  if (parameters.use_relaxation_lns()) {
2814  subsolvers.push_back(absl::make_unique<LnsSolver>(
2815  absl::make_unique<
2816  ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2817  helper, absl::StrCat("rnd_rel_lns_", local_params.name())),
2818  local_params, helper, &shared));
2819 
2820  subsolvers.push_back(absl::make_unique<LnsSolver>(
2821  absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2822  helper, absl::StrCat("wgt_rel_lns_", local_params.name())),
2823  local_params, helper, &shared));
2824  }
2825  }
2826 
2827  // Add a synchronization point for the primal integral that is executed last.
2828  // This way, after each batch, the proper deterministic time is updated and
2829  // then the function to integrate take the value of the new gap.
2830  subsolvers.push_back(
2831  absl::make_unique<SynchronizationPoint>([shared_response_manager]() {
2832  shared_response_manager->UpdatePrimalIntegral();
2833  }));
2834 
2835  // Log the name of all our SubSolvers.
2836  if (log_search) {
2837  std::vector<std::string> names;
2838  for (const auto& subsolver : subsolvers) {
2839  if (!subsolver->name().empty()) names.push_back(subsolver->name());
2840  }
2841  LOG(INFO) << absl::StrFormat(
2842  "*** starting Search at %.2fs with %i workers and subsolvers: [ %s ]",
2843  wall_timer->Get(), num_search_workers, absl::StrJoin(names, ", "));
2844  }
2845 
2846  // Launch the main search loop.
2847  if (parameters.interleave_search()) {
2848  DeterministicLoop(subsolvers, num_search_workers,
2849  parameters.interleave_batch_size());
2850  } else {
2851  NonDeterministicLoop(subsolvers, num_search_workers);
2852  }
2853 }
2854 
2855 #endif // __PORTABLE_PLATFORM__
2856 
2857 // If the option use_sat_inprocessing is true, then before postsolving a
2858 // solution, we need to make sure we add any new clause required for postsolving
2859 // to the mapping_model.
2860 void AddPostsolveClauses(const std::vector<int>& postsolve_mapping,
2861  Model* model, CpModelProto* mapping_proto) {
2862  auto* mapping = model->GetOrCreate<CpModelMapping>();
2863  auto* postsolve = model->GetOrCreate<PostsolveClauses>();
2864  for (const auto& clause : postsolve->clauses) {
2865  auto* ct = mapping_proto->add_constraints()->mutable_bool_or();
2866  for (const Literal l : clause) {
2867  int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2868  CHECK_NE(var, -1);
2869  var = postsolve_mapping[var];
2870  ct->add_literals(l.IsPositive() ? var : NegatedRef(var));
2871  }
2872  }
2873  postsolve->clauses.clear();
2874 }
2875 
2876 } // namespace
2877 
2878 CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
2880  UserTimer user_timer;
2881  wall_timer.Start();
2882  user_timer.Start();
2883 
2884 #if defined(_MSC_VER)
2885  // On windows, The final_response is optimized out in the return part, and is
2886  // swapped out before the cleanup callback is called. A workaround is to
2887  // create a unique ptr that will forbid this optimization.
2888  std::unique_ptr<CpSolverResponse> final_response_ptr(new CpSolverResponse());
2889  CpSolverResponse& final_response = *final_response_ptr.get();
2890 #else
2891  CpSolverResponse final_response;
2892 #endif
2893 
2894 #if !defined(__PORTABLE_PLATFORM__)
2895  // Dump?
2896  if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2897  const std::string file =
2898  absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix), "model.pbtxt");
2899  LOG(INFO) << "Dumping cp model proto to '" << file << "'.";
2901  }
2902 
2903  auto dump_response_cleanup = absl::MakeCleanup([&final_response] {
2904  if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
2905  const std::string file = absl::StrCat(
2906  absl::GetFlag(FLAGS_cp_model_dump_prefix), "response.pbtxt");
2907  LOG(INFO) << "Dumping response proto to '" << file << "'.";
2908  CHECK_OK(file::SetTextProto(file, final_response, file::Defaults()));
2909  }
2910  });
2911 
2912  // Override parameters?
2913  if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
2914  SatParameters params = *model->GetOrCreate<SatParameters>();
2915  SatParameters flag_params;
2916  CHECK(google::protobuf::TextFormat::ParseFromString(
2917  absl::GetFlag(FLAGS_cp_model_params), &flag_params));
2918  params.MergeFrom(flag_params);
2919  *(model->GetOrCreate<SatParameters>()) = params;
2920  }
2921 #endif // __PORTABLE_PLATFORM__
2922 
2923  // Initialize the time limit from the parameters.
2924  model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
2925  *model->GetOrCreate<SatParameters>());
2926  SharedTimeLimit shared_time_limit(model->GetOrCreate<TimeLimit>());
2927 
2928 #if !defined(__PORTABLE_PLATFORM__)
2929  // Register SIGINT handler if requested by the parameters.
2930  SigintHandler handler;
2931  if (model->GetOrCreate<SatParameters>()->catch_sigint_signal()) {
2932  handler.Register([&shared_time_limit]() { shared_time_limit.Stop(); });
2933  }
2934 #endif // __PORTABLE_PLATFORM__
2935 
2936  const SatParameters& params = *model->GetOrCreate<SatParameters>();
2937  const bool log_search = params.log_search_progress() || VLOG_IS_ON(1);
2938  LOG_IF(INFO, log_search) << "Parameters: " << params.ShortDebugString();
2939  if (log_search && params.use_absl_random()) {
2940  model->GetOrCreate<ModelRandomGenerator>()->LogSalt();
2941  }
2942 
2943  // Always display the final response stats if requested.
2944  auto display_response_cleanup =
2945  absl::MakeCleanup([&final_response, &model_proto, log_search] {
2946  if (log_search) {
2947  LOG(INFO) << CpSolverResponseStats(final_response,
2948  model_proto.has_objective());
2949  }
2950  });
2951 
2952  // Validate model_proto.
2953  // TODO(user): provide an option to skip this step for speed?
2954  {
2955  const std::string error = ValidateCpModel(model_proto);
2956  if (!error.empty()) {
2957  LOG_IF(INFO, log_search) << error;
2958  final_response.set_status(CpSolverStatus::MODEL_INVALID);
2959  return final_response;
2960  }
2961  }
2962  LOG_IF(INFO, log_search) << CpModelStats(model_proto);
2963 
2964  // Special case for pure-sat problem.
2965  // TODO(user): improve the normal presolver to do the same thing.
2966  // TODO(user): Support solution hint, but then the first TODO will make it
2967  // automatic.
2968  if (!params.use_sat_inprocessing() && !model_proto.has_objective() &&
2969  !model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
2970  !params.use_lns_only() && params.num_search_workers() <= 1 &&
2971  model_proto.assumptions().empty()) {
2972  bool is_pure_sat = true;
2973  for (const IntegerVariableProto& var : model_proto.variables()) {
2974  if (var.domain_size() != 2 || var.domain(0) < 0 || var.domain(1) > 1) {
2975  is_pure_sat = false;
2976  break;
2977  }
2978  }
2979  if (is_pure_sat) {
2980  for (const ConstraintProto& ct : model_proto.constraints()) {
2981  if (ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
2982  ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
2983  is_pure_sat = false;
2984  break;
2985  }
2986  }
2987  }
2988  if (is_pure_sat) {
2989  // TODO(user): All this duplication will go away when we are fast enough
2990  // on pure-sat model with the CpModel presolve...
2991  final_response = SolvePureSatModel(model_proto, &wall_timer, model);
2992  final_response.set_wall_time(wall_timer.Get());
2993  final_response.set_user_time(user_timer.Get());
2994  final_response.set_deterministic_time(
2995  shared_time_limit.GetElapsedDeterministicTime());
2996  const SatParameters& params = *model->GetOrCreate<SatParameters>();
2997  if (params.fill_tightened_domains_in_response()) {
2998  *final_response.mutable_tightened_variables() = model_proto.variables();
2999  }
3000  return final_response;
3001  }
3002  }
3003 
3004  // Presolve and expansions.
3005  LOG_IF(INFO, log_search) << absl::StrFormat(
3006  "*** starting model presolve at %.2fs", wall_timer.Get());
3007  CpModelProto new_cp_model_proto = model_proto; // Copy.
3008 
3009  CpModelProto mapping_proto;
3010  auto context = absl::make_unique<PresolveContext>(
3011  log_search, model, &new_cp_model_proto, &mapping_proto);
3012 
3013  bool degraded_assumptions_support = false;
3014  if (params.num_search_workers() > 1 || model_proto.has_objective()) {
3015  // For the case where the assumptions are currently not supported, we just
3016  // assume they are fixed, and will always report all of them in the UNSAT
3017  // core if the problem turn out to be UNSAT.
3018  //
3019  // If the mode is not degraded, we will hopefully report a small subset
3020  // in case there is no feasible solution under these assumptions.
3021  degraded_assumptions_support = true;
3022  context->InitializeNewDomains();
3023  for (const int ref : model_proto.assumptions()) {
3024  if (!context->SetLiteralToTrue(ref)) {
3025  final_response.set_status(CpSolverStatus::INFEASIBLE);
3026  final_response.add_sufficient_assumptions_for_infeasibility(ref);
3027  return final_response;
3028  }
3029  }
3030  }
3031 
3032  // This function will be called before any CpSolverResponse is returned
3033  // to the user (at the end and in callbacks).
3034  std::function<void(CpSolverResponse * response)> postprocess_solution;
3035 
3036  // Do the actual presolve.
3037  std::vector<int> postsolve_mapping;
3038  const bool ok = PresolveCpModel(context.get(), &postsolve_mapping);
3039  if (!ok) {
3040  LOG(ERROR) << "Error while presolving, likely due to integer overflow.";
3041  final_response.set_status(CpSolverStatus::MODEL_INVALID);
3042  return final_response;
3043  }
3044  LOG_IF(INFO, log_search) << CpModelStats(new_cp_model_proto);
3045  if (params.cp_model_presolve()) {
3046  postprocess_solution = [&model_proto, &params, &mapping_proto,
3047  &shared_time_limit, &postsolve_mapping, &wall_timer,
3048  &user_timer, model](CpSolverResponse* response) {
3049  AddPostsolveClauses(postsolve_mapping, model, &mapping_proto);
3050  PostsolveResponseWrapper(params, model_proto.variables_size(),
3051  mapping_proto, postsolve_mapping, &wall_timer,
3052  response);
3053  if (!response->solution().empty()) {
3054  CHECK(
3056  std::vector<int64>(response->solution().begin(),
3057  response->solution().end()),
3058  &mapping_proto, &postsolve_mapping))
3059  << "final postsolved solution";
3060  }
3061  if (params.fill_tightened_domains_in_response()) {
3062  // TODO(user): for now, we just use the domain infered during presolve.
3063  if (mapping_proto.variables().size() >=
3064  model_proto.variables().size()) {
3065  for (int i = 0; i < model_proto.variables().size(); ++i) {
3066  *response->add_tightened_variables() = mapping_proto.variables(i);
3067  }
3068  }
3069  }
3070  response->set_wall_time(wall_timer.Get());
3071  response->set_user_time(user_timer.Get());
3072  response->set_deterministic_time(
3073  shared_time_limit.GetElapsedDeterministicTime());
3074  };
3075  } else {
3076  postprocess_solution = [&model_proto, &params, &wall_timer,
3077  &shared_time_limit,
3078  &user_timer](CpSolverResponse* response) {
3079  // Truncate the solution in case model expansion added more variables.
3080  const int initial_size = model_proto.variables_size();
3081  if (response->solution_size() > 0) {
3082  response->mutable_solution()->Truncate(initial_size);
3083  } else if (response->solution_lower_bounds_size() > 0) {
3084  response->mutable_solution_lower_bounds()->Truncate(initial_size);
3085  response->mutable_solution_upper_bounds()->Truncate(initial_size);
3086  }
3087  if (params.fill_tightened_domains_in_response()) {
3088  *response->mutable_tightened_variables() = model_proto.variables();
3089  }
3090  response->set_wall_time(wall_timer.Get());
3091  response->set_user_time(user_timer.Get());
3092  response->set_deterministic_time(
3093  shared_time_limit.GetElapsedDeterministicTime());
3094  };
3095  }
3096 
3097  // Delete the context.
3098  context.reset(nullptr);
3099 
3100  SharedResponseManager shared_response_manager(
3101  log_search, params.enumerate_all_solutions(), &new_cp_model_proto,
3102  &wall_timer, &shared_time_limit);
3103  shared_response_manager.set_dump_prefix(
3104  absl::GetFlag(FLAGS_cp_model_dump_prefix));
3105  shared_response_manager.SetGapLimitsFromParameters(params);
3106  model->Register<SharedResponseManager>(&shared_response_manager);
3107  const auto& observers = model->GetOrCreate<SolutionObservers>()->observers;
3108  if (!observers.empty()) {
3109  shared_response_manager.AddSolutionCallback(
3110  [&model_proto, &observers, &postprocess_solution](
3111  const CpSolverResponse& response_of_presolved_problem) {
3112  CpSolverResponse response = response_of_presolved_problem;
3113  postprocess_solution(&response);
3114  if (!response.solution().empty()) {
3115  if (DEBUG_MODE ||
3116  absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
3118  model_proto, std::vector<int64>(response.solution().begin(),
3119  response.solution().end())));
3120  }
3121  }
3122 
3123  for (const auto& observer : observers) {
3124  observer(response);
3125  }
3126  });
3127  }
3128 
3129  // If specified, we load the initial objective domain right away in the
3130  // response manager. Note that the presolve will always fill it with the
3131  // trivial min/max value if the user left it empty. This avoids to display
3132  // [-infinity, infinity] for the initial objective search space.
3133  if (new_cp_model_proto.has_objective()) {
3134  const Domain domain = ReadDomainFromProto(new_cp_model_proto.objective());
3135  if (!domain.IsEmpty()) {
3136  shared_response_manager.UpdateInnerObjectiveBounds(
3137  "initial domain", IntegerValue(domain.Min()),
3138  IntegerValue(domain.Max()));
3139  }
3140  }
3141 
3142  // Start counting the primal integral from the current determistic time and
3143  // initial objective domain gap that we just filled.
3144  shared_response_manager.UpdatePrimalIntegral();
3145 
3146 #if !defined(__PORTABLE_PLATFORM__)
3147  if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3148  const std::string presolved_file = absl::StrCat(
3149  absl::GetFlag(FLAGS_cp_model_dump_prefix), "presolved_model.pbtxt");
3150  LOG(INFO) << "Dumping presolved cp model proto to '" << presolved_file
3151  << "'.";
3152  CHECK_OK(file::SetTextProto(presolved_file, new_cp_model_proto,
3153  file::Defaults()));
3154 
3155  const std::string mapping_file = absl::StrCat(
3156  absl::GetFlag(FLAGS_cp_model_dump_prefix), "mapping_model.pbtxt");
3157  LOG(INFO) << "Dumping mapping cp model proto to '" << mapping_file << "'.";
3158  CHECK_OK(file::SetTextProto(mapping_file, mapping_proto, file::Defaults()));
3159  }
3160 #endif // __PORTABLE_PLATFORM__
3161 
3162  if (params.stop_after_presolve() || shared_time_limit.LimitReached()) {
3163  int64 num_terms = 0;
3164  for (const ConstraintProto& ct : new_cp_model_proto.constraints()) {
3165  num_terms += UsedVariables(ct).size();
3166  }
3167  LOG_IF(INFO, log_search)
3168  << "Stopped after presolve."
3169  << "\nPresolvedNumVariables: " << new_cp_model_proto.variables().size()
3170  << "\nPresolvedNumConstraints: "
3171  << new_cp_model_proto.constraints().size()
3172  << "\nPresolvedNumTerms: " << num_terms;
3173 
3174  shared_response_manager.SetStatsFromModel(model);
3175 
3176  final_response = shared_response_manager.GetResponse();
3177  postprocess_solution(&final_response);
3178  return final_response;
3179  }
3180 
3181  // Make sure everything stops when we have a first solution if requested.
3182  if (params.stop_after_first_solution()) {
3183  shared_response_manager.AddSolutionCallback(
3184  [&shared_time_limit](
3185  const CpSolverResponse& response_of_presolved_problem) {
3186  shared_time_limit.Stop();
3187  });
3188  }
3189 
3190 #if defined(__PORTABLE_PLATFORM__)
3191  if (/* DISABLES CODE */ (false)) {
3192  // We ignore the multithreading parameter in this case.
3193 #else // __PORTABLE_PLATFORM__
3194  if (params.num_search_workers() > 1 || params.interleave_search()) {
3195  SolveCpModelParallel(new_cp_model_proto, &shared_response_manager,
3196  &shared_time_limit, &wall_timer, model);
3197 #endif // __PORTABLE_PLATFORM__
3198  } else {
3199  if (log_search) {
3200  LOG(INFO) << absl::StrFormat("*** starting to load the model at %.2fs",
3201  wall_timer.Get());
3202  }
3203  shared_response_manager.SetUpdatePrimalIntegralOnEachChange(true);
3204  LoadCpModel(new_cp_model_proto, &shared_response_manager, model);
3205  shared_response_manager.LoadDebugSolution(model);
3206  if (log_search) {
3207  LOG(INFO) << absl::StrFormat("*** starting sequential search at %.2fs",
3208  wall_timer.Get());
3209  LOG(INFO) << "Initial num_bool: "
3210  << model->Get<SatSolver>()->NumVariables();
3211  }
3212  if (params.repair_hint()) {
3213  MinimizeL1DistanceWithHint(new_cp_model_proto, &shared_response_manager,
3214  &wall_timer, &shared_time_limit, model);
3215  } else {
3216  QuickSolveWithHint(new_cp_model_proto, &shared_response_manager, model);
3217  }
3218  SolveLoadedCpModel(new_cp_model_proto, &shared_response_manager, model);
3219  }
3220 
3221  final_response = shared_response_manager.GetResponse();
3222  postprocess_solution(&final_response);
3223  if (!final_response.solution().empty()) {
3225  model_proto, std::vector<int64>(final_response.solution().begin(),
3226  final_response.solution().end())));
3227  }
3228  if (degraded_assumptions_support &&
3229  final_response.status() == CpSolverStatus::INFEASIBLE) {
3230  // For now, just pass in all assumptions.
3231  *final_response.mutable_sufficient_assumptions_for_infeasibility() =
3232  model_proto.assumptions();
3233  }
3234  return final_response;
3235 }
3236 
3237 CpSolverResponse Solve(const CpModelProto& model_proto) {
3238  Model model;
3239  return SolveCpModel(model_proto, &model);
3240 }
3241 
3242 CpSolverResponse SolveWithParameters(const CpModelProto& model_proto,
3243  const SatParameters& params) {
3244  Model model;
3245  model.Add(NewSatParameters(params));
3246  return SolveCpModel(model_proto, &model);
3247 }
3248 
3249 #if !defined(__PORTABLE_PLATFORM__)
3250 CpSolverResponse SolveWithParameters(const CpModelProto& model_proto,
3251  const std::string& params) {
3252  Model model;
3253  model.Add(NewSatParameters(params));
3254  return SolveCpModel(model_proto, &model);
3255 }
3256 #endif // !__PORTABLE_PLATFORM__
3257 
3258 } // namespace sat
3259 } // namespace operations_research
int64 min
Definition: alldiff_cst.cc:138
int64 max
Definition: alldiff_cst.cc:139
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_OK(x)
Definition: base/logging.h:40
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:884
#define VLOG(verboselevel)
Definition: base/logging.h:978
void AddEdge(int node1, int node2)
Definition: base/file.h:32
void Start()
Definition: timer.h:31
double Get() const
Definition: timer.h:45
We call domain any subset of Int64 = [kint64min, kint64max].
int64 Min() const
Returns the min value of the domain.
int64 Max() const
Returns the max value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
void Register(const std::function< void()> &f)
Definition: sigint.cc:22
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
static std::unique_ptr< TimeLimit > Infinite()
Creates a time limit object that uses infinite time for wall time, deterministic time and instruction...
Definition: time_limit.h:134
Literal(int signed_value)
Definition: sat_base.h:68
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
void set_dump_prefix(const std::string &dump_prefix)
void UpdateInnerObjectiveBounds(const std::string &worker_info, IntegerValue lb, IntegerValue ub)
void NewSolution(const CpSolverResponse &response, Model *model)
void SetGapLimitsFromParameters(const SatParameters &parameters)
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
SatParameters parameters
SharedBoundsManager * bounds
SharedRelaxationSolutionRepository * relaxation_solutions
SharedLPSolutionRepository * lp_solutions
CpModelProto const * model_proto
SharedIncompleteSolutionManager * incomplete_solutions
ABSL_FLAG(std::string, cp_model_dump_prefix, "/tmp/", "Prefix filename for all dumped files")
SharedResponseManager * response
SharedTimeLimit * time_limit
WallTimer * wall_timer
const std::string name
const Constraint * ct
int64 value
IntVar * var
Definition: expr_array.cc:1858
GRBmodel * model
GurobiMPCallbackContext * context
int int32
static const int64 kint64max
int64_t int64
static const int64 kint64min
const int INFO
Definition: log_severity.h:31
const int ERROR
Definition: log_severity.h:32
const int FATAL
Definition: log_severity.h:32
const bool DEBUG_MODE
Definition: macros.h:24
Definition: cleanup.h:22
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Definition: cleanup.h:120
Definition: file.cc:141
int Defaults()
Definition: base/file.h:119
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: file.cc:285
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
Definition: file.cc:142
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:26
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
std::function< int64(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1467
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::vector< IntegerVariable > AppendLinMaxRelaxation(IntegerVariable target, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
std::function< SatParameters(Model *)> NewSatParameters(const std::string &params)
Creates parameters for the solver, which you can add to the model with.
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64 lower_bound)
Definition: integer_expr.h:404
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
Returns a string with some statistics on the solver response.
bool LoadConstraint(const ConstraintProto &ct, Model *m)
void MinimizeCoreWithPropagation(SatSolver *solver, std::vector< Literal > *core)
std::vector< int > UsedVariables(const ConstraintProto &ct)
double UnscaleObjectiveValue(const CpObjectiveProto &proto, double value)
bool RefIsPositive(int ref)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
Definition: cuts.cc:2348
void MaybeFullyEncodeMoreVariables(const CpModelProto &model_proto, Model *m)
std::function< IntegerVariable(Model *)> ConstantIntegerVariable(int64 value)
Definition: integer.h:1418
bool SolutionIsFeasible(const CpModelProto &model, const std::vector< int64 > &variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
CutGenerator CreateCumulativeCutGenerator(const std::vector< IntervalVariable > &intervals, const IntegerVariable capacity, const std::vector< IntegerVariable > &demands, Model *model)
Definition: cuts.cc:2198
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
SatSolver::Status SolveWithPresolve(std::unique_ptr< SatSolver > *solver, TimeLimit *time_limit, std::vector< bool > *solution, DratProofHandler *drat_proof_handler)
CutGenerator CreateNoOverlapCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
Definition: cuts.cc:2331
std::function< int64(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1487
bool HasEnforcementLiteral(const ConstraintProto &ct)
int64 ComputeInnerObjective(const CpObjectiveProto &objective, const CpSolverResponse &response)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
CutGenerator CreateOverlappingCumulativeCutGenerator(const std::vector< IntervalVariable > &intervals, const IntegerVariable capacity, const std::vector< IntegerVariable > &demands, Model *model)
Definition: cuts.cc:2217
void DeterministicLoop(const std::vector< std::unique_ptr< SubSolver >> &subsolvers, int num_threads, int batch_size)
Definition: subsolver.cc:84
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, Model *model)
Definition: cuts.cc:1424
bool PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
void PostsolveResponse(const int64 num_variables_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, CpSolverResponse *response)
LinearExpression GetExprFromProto(const LinearExpressionProto &expr_proto, const CpModelMapping &mapping)
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64 value)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
Definition: integer.h:1444
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64 lb, int64 ub)
Definition: integer.h:1426
void ConfigureSearchHeuristics(Model *model)
SatSolver::Status MinimizeWithHittingSetAndLazyEncoding(const ObjectiveDefinition &objective_definition, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:134
void NonDeterministicLoop(const std::vector< std::unique_ptr< SubSolver >> &subsolvers, int num_threads)
Definition: subsolver.cc:116
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
Definition: cuts.cc:1915
CutGenerator CreateAllDifferentCutGenerator(const std::vector< IntegerVariable > &vars, Model *model)
Definition: cuts.cc:1818
CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const std::vector< int64 > &demands, int64 capacity, Model *model)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
Definition: circuit.h:168
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, int linearization_level, LinearRelaxation *relaxation)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64 upper_bound)
Definition: integer_expr.h:299
std::function< int64(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1473
std::string CpModelStats(const CpModelProto &model_proto)
Returns a string with some statistics on the given CpModelProto.
void AppendPartialEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:27
std::function< void(Model *)> ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack()
Definition: integer.cc:1989
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
SatSolver::Status ContinuousProbing(const std::vector< BooleanVariable > &bool_vars, const std::vector< IntegerVariable > &int_vars, const std::function< void()> &feasible_solution_observer, Model *model)
CutGenerator CreateKnapsackCoverCutGenerator(const std::vector< LinearConstraint > &base_constraints, const std::vector< IntegerVariable > &vars, Model *model)
Definition: cuts.cc:437
std::vector< SatParameters > GetDiverseSetOfParameters(const SatParameters &base_params, const CpModelProto &cp_model, const int num_workers)
CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, IntegerVariable x, IntegerVariable y, Model *model)
Definition: cuts.cc:1328
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1479
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
Definition: cuts.cc:2411
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
std::string ValidateCpModel(const CpModelProto &model)
std::function< BooleanOrIntegerLiteral()> ConstructSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const SatParameters &params)
Solves the given CpModelProto with the given parameters.
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
std::function< BooleanOrIntegerLiteral()> InstrumentSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, const std::function< BooleanOrIntegerLiteral()> &instrumented_strategy, Model *model)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
std::mt19937 random_engine_t
Definition: random_engine.h:23
std::string ProtobufDebugString(const P &message)
Literal literal
Definition: optimization.cc:84
static int input(yyscan_t yyscanner)
int64 capacity
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1270
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1264
std::vector< std::function< void(const CpSolverResponse &response)> > observers
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41