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