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