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