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