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