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