OR-Tools  9.6
cp_sat_solver.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <atomic>
18 #include <cmath>
19 #include <cstdint>
20 #include <functional>
21 #include <limits>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "absl/container/flat_hash_set.h"
28 #include "absl/memory/memory.h"
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_join.h"
34 #include "absl/strings/str_split.h"
35 #include "absl/time/clock.h"
36 #include "absl/time/time.h"
37 #include "absl/types/span.h"
38 #include "absl/log/check.h"
39 #include "ortools/base/logging.h"
40 #include "ortools/base/protoutil.h"
42 #include "ortools/linear_solver/linear_solver.pb.h"
44 #include "ortools/math_opt/callback.pb.h"
51 #include "ortools/math_opt/model.pb.h"
52 #include "ortools/math_opt/model_parameters.pb.h"
53 #include "ortools/math_opt/model_update.pb.h"
54 #include "ortools/math_opt/parameters.pb.h"
55 #include "ortools/math_opt/result.pb.h"
56 #include "ortools/math_opt/solution.pb.h"
57 #include "ortools/math_opt/sparse_containers.pb.h"
60 #include "ortools/sat/sat_parameters.pb.h"
61 
62 namespace operations_research {
63 namespace math_opt {
64 
65 namespace {
66 
67 constexpr double kInf = std::numeric_limits<double>::infinity();
68 
69 constexpr SupportedProblemStructures kCpSatSupportedStructures = {
70  .integer_variables = SupportType::kSupported,
71  .quadratic_objectives = SupportType::kNotImplemented,
72  .quadratic_constraints = SupportType::kNotImplemented,
73  .sos1_constraints = SupportType::kNotImplemented,
74  .sos2_constraints = SupportType::kNotImplemented,
75  .indicator_constraints = SupportType::kNotImplemented};
76 
77 // Returns true on success.
78 bool ApplyCutoff(const double cutoff, MPModelProto* model) {
79  // TODO(b/204083726): we need to be careful here if we support quadratic
80  // objectives
81  if (model->has_quadratic_objective()) {
82  return false;
83  }
84  // CP-SAT detects a constraint parallel to the objective and uses it as
85  // an objective bound, which is the closest we can get to cutoff.
86  // See FindDuplicateConstraints() in CP-SAT codebase.
87  MPConstraintProto* const cutoff_constraint = model->add_constraint();
88  for (int i = 0; i < model->variable_size(); ++i) {
89  const double obj_coef = model->variable(i).objective_coefficient();
90  if (obj_coef != 0) {
91  cutoff_constraint->add_var_index(i);
92  cutoff_constraint->add_coefficient(obj_coef);
93  }
94  }
95  const double cutoff_minus_offset = cutoff - model->objective_offset();
96  if (model->maximize()) {
97  // Add the constraint obj >= cutoff
98  cutoff_constraint->set_lower_bound(cutoff_minus_offset);
99  } else {
100  // Add the constraint obj <= cutoff
101  cutoff_constraint->set_upper_bound(cutoff_minus_offset);
102  }
103  return true;
104 }
105 
106 // Returns a list of warnings from parameter settings that were
107 // invalid/unsupported (specific to CP-SAT), one element per bad parameter.
108 std::vector<std::string> SetSolveParameters(
109  const SolveParametersProto& parameters, const bool has_message_callback,
110  MPModelRequest& request) {
111  std::vector<std::string> warnings;
112  if (parameters.has_time_limit()) {
113  request.set_solver_time_limit_seconds(absl::ToDoubleSeconds(
114  util_time::DecodeGoogleApiProto(parameters.time_limit()).value()));
115  }
116  if (parameters.has_node_limit()) {
117  warnings.push_back("The node_limit parameter is not supported for CP-SAT.");
118  }
119 
120  // Build CP SAT parameters by first initializing them from the common
121  // parameters, and then using the values in `solver_specific_parameters` to
122  // overwrite them if necessary.
123  //
124  // We don't need to set max_time_in_seconds since we already pass it in the
125  // `request.solver_time_limit_seconds`. The logic of `SatSolveProto()` will
126  // apply the logic we want here.
127  sat::SatParameters sat_parameters;
128 
129  // By default CP-SAT catches SIGINT (Ctrl-C) to interrupt the solve but we
130  // don't want this behavior when the users uses CP-SAT through MathOpt.
131  sat_parameters.set_catch_sigint_signal(false);
132 
133  if (parameters.has_random_seed()) {
134  sat_parameters.set_random_seed(parameters.random_seed());
135  }
136  if (parameters.has_threads()) {
137  sat_parameters.set_num_search_workers(parameters.threads());
138  }
139  if (parameters.has_relative_gap_tolerance()) {
140  sat_parameters.set_relative_gap_limit(parameters.relative_gap_tolerance());
141  }
142 
143  if (parameters.has_absolute_gap_tolerance()) {
144  sat_parameters.set_absolute_gap_limit(parameters.absolute_gap_tolerance());
145  }
146  // cutoff_limit is handled outside this function as it modifies the model.
147  if (parameters.has_best_bound_limit()) {
148  warnings.push_back(
149  "The best_bound_limit parameter is not supported for CP-SAT.");
150  }
151  if (parameters.has_objective_limit()) {
152  warnings.push_back(
153  "The objective_limit parameter is not supported for CP-SAT.");
154  }
155  if (parameters.has_solution_limit()) {
156  if (parameters.solution_limit() == 1) {
157  sat_parameters.set_stop_after_first_solution(true);
158  } else {
159  warnings.push_back(absl::StrCat(
160  "The CP-SAT solver only supports value 1 for solution_limit, found: ",
161  parameters.solution_limit()));
162  }
163  }
164  if (parameters.has_solution_pool_size()) {
165  sat_parameters.set_solution_pool_size(parameters.solution_pool_size());
166  sat_parameters.set_fill_additional_solutions_in_response(true);
167  }
168  if (parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
169  warnings.push_back(
170  absl::StrCat("Setting the LP Algorithm (was set to ",
171  ProtoEnumToString(parameters.lp_algorithm()),
172  ") is not supported for CP_SAT solver"));
173  }
174  if (parameters.presolve() != EMPHASIS_UNSPECIFIED) {
175  switch (parameters.presolve()) {
176  case EMPHASIS_OFF:
177  sat_parameters.set_cp_model_presolve(false);
178  break;
179  case EMPHASIS_LOW:
180  case EMPHASIS_MEDIUM:
181  case EMPHASIS_HIGH:
182  case EMPHASIS_VERY_HIGH:
183  sat_parameters.set_cp_model_presolve(true);
184  break;
185  default:
186  LOG(FATAL) << "Presolve emphasis: "
187  << ProtoEnumToString(parameters.presolve())
188  << " unknown, error setting CP-SAT parameters";
189  }
190  }
191  if (parameters.scaling() != EMPHASIS_UNSPECIFIED) {
192  warnings.push_back(absl::StrCat("Setting the scaling (was set to ",
193  ProtoEnumToString(parameters.scaling()),
194  ") is not supported for CP_SAT solver"));
195  }
196  if (parameters.cuts() != EMPHASIS_UNSPECIFIED) {
197  switch (parameters.cuts()) {
198  case EMPHASIS_OFF:
199  // This is not very maintainable, but CP-SAT doesn't expose the
200  // parameters we need.
201  sat_parameters.set_add_cg_cuts(false);
202  sat_parameters.set_add_mir_cuts(false);
203  sat_parameters.set_add_zero_half_cuts(false);
204  sat_parameters.set_add_clique_cuts(false);
205  sat_parameters.set_max_all_diff_cut_size(0);
206  sat_parameters.set_add_lin_max_cuts(false);
207  break;
208  case EMPHASIS_LOW:
209  case EMPHASIS_MEDIUM:
210  case EMPHASIS_HIGH:
211  case EMPHASIS_VERY_HIGH:
212  break;
213  default:
214  LOG(FATAL) << "Cut emphasis: " << ProtoEnumToString(parameters.cuts())
215  << " unknown, error setting CP-SAT parameters";
216  }
217  }
218  if (parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
219  warnings.push_back(absl::StrCat("Setting the heuristics (was set to ",
220  ProtoEnumToString(parameters.heuristics()),
221  ") is not supported for CP_SAT solver"));
222  }
223  sat_parameters.MergeFrom(parameters.cp_sat());
224 
225  // We want to override specifically SAT parameters independently from the user
226  // input when a message callback is used to prevent wrongful writes to stdout
227  // or disabling of messages via these parameters.
228  if (has_message_callback) {
229  // When enable_internal_solver_output is used, CP-SAT solver actually has
230  // the same effect as setting log_search_progress to true.
231  sat_parameters.set_log_search_progress(true);
232 
233  // Default value of log_to_stdout is true; but even if it was not the case,
234  // we don't want to write to stdout when a message callback is used.
235  sat_parameters.set_log_to_stdout(false);
236  } else {
237  // We only set enable_internal_solver_output when we have no message
238  // callback.
239  request.set_enable_internal_solver_output(parameters.enable_output());
240  }
241 
242  request.set_solver_specific_parameters(
243  EncodeSatParametersAsString(sat_parameters));
244  return warnings;
245 }
246 
247 absl::StatusOr<std::pair<SolveStatsProto, TerminationProto>>
248 GetTerminationAndStats(const bool is_interrupted, const bool maximize,
249  const bool used_cutoff,
250  const MPSolutionResponse& response) {
251  SolveStatsProto solve_stats;
252  TerminationProto termination;
253 
254  // Set default status and bounds.
255  solve_stats.mutable_problem_status()->set_primal_status(
256  FEASIBILITY_STATUS_UNDETERMINED);
257  solve_stats.set_best_primal_bound(maximize ? -kInf : kInf);
258  solve_stats.mutable_problem_status()->set_dual_status(
259  FEASIBILITY_STATUS_UNDETERMINED);
260  solve_stats.set_best_dual_bound(maximize ? kInf : -kInf);
261 
262  // Set terminations and update status and bounds as appropriate.
263  switch (response.status()) {
264  case MPSOLVER_OPTIMAL:
265  termination =
266  TerminateForReason(TERMINATION_REASON_OPTIMAL, response.status_str());
267  solve_stats.mutable_problem_status()->set_primal_status(
268  FEASIBILITY_STATUS_FEASIBLE);
269  solve_stats.set_best_primal_bound(response.objective_value());
270  solve_stats.mutable_problem_status()->set_dual_status(
271  FEASIBILITY_STATUS_FEASIBLE);
272  solve_stats.set_best_dual_bound(response.best_objective_bound());
273  break;
274  case MPSOLVER_INFEASIBLE:
275  if (used_cutoff) {
276  termination =
277  NoSolutionFoundTermination(LIMIT_CUTOFF, response.status_str());
278  } else {
279  termination = TerminateForReason(TERMINATION_REASON_INFEASIBLE,
280  response.status_str());
281  solve_stats.mutable_problem_status()->set_primal_status(
282  FEASIBILITY_STATUS_INFEASIBLE);
283  }
284  break;
285  case MPSOLVER_UNKNOWN_STATUS:
286  // For a basic unbounded problem, CP-SAT internally returns
287  // INFEASIBLE_OR_UNBOUNDED after presolve but MPSolver statuses don't
288  // support that thus it get transformed in MPSOLVER_UNKNOWN_STATUS with
289  // a status_str of
290  //
291  // "Problem proven infeasible or unbounded during MIP presolve"
292  //
293  // There may be some other cases where CP-SAT returns UNKNOWN here so we
294  // only return TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED when the
295  // status_str is detected. Otherwise we return OTHER_ERROR.
296  //
297  // TODO(b/202159173): A better solution would be to use CP-SAT API
298  // directly which may help further improve the statuses.
299  if (absl::StrContains(response.status_str(), "infeasible or unbounded")) {
300  termination = TerminateForReason(
301  TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED, response.status_str());
302  solve_stats.mutable_problem_status()->set_primal_or_dual_infeasible(
303  true);
304  } else {
305  termination = TerminateForReason(TERMINATION_REASON_OTHER_ERROR,
306  response.status_str());
307  }
308  break;
309  case MPSOLVER_FEASIBLE:
310  termination = FeasibleTermination(
311  is_interrupted ? LIMIT_INTERRUPTED : LIMIT_UNDETERMINED,
312  response.status_str());
313  solve_stats.mutable_problem_status()->set_primal_status(
314  FEASIBILITY_STATUS_FEASIBLE);
315  solve_stats.set_best_primal_bound(response.objective_value());
316  solve_stats.set_best_dual_bound(response.best_objective_bound());
317  if (std::isfinite(response.best_objective_bound())) {
318  solve_stats.mutable_problem_status()->set_dual_status(
319  FEASIBILITY_STATUS_FEASIBLE);
320  }
321  break;
322  case MPSOLVER_NOT_SOLVED:
323  termination = NoSolutionFoundTermination(
324  is_interrupted ? LIMIT_INTERRUPTED : LIMIT_UNDETERMINED,
325  response.status_str());
326  break;
327  case MPSOLVER_MODEL_INVALID:
328  return absl::InternalError(
329  absl::StrCat("cp-sat solver returned MODEL_INVALID, details: ",
330  response.status_str()));
331  default:
332  return absl::InternalError(
333  absl::StrCat("unexpected solve status: ", response.status()));
334  }
335  return std::make_pair(std::move(solve_stats), std::move(termination));
336 }
337 
338 } // namespace
339 
340 absl::StatusOr<std::unique_ptr<SolverInterface>> CpSatSolver::New(
341  const ModelProto& model, const InitArgs& init_args) {
342  RETURN_IF_ERROR(ModelIsSupported(model, kCpSatSupportedStructures, "CP-SAT"));
343  ASSIGN_OR_RETURN(MPModelProto cp_sat_model,
345  std::vector variable_ids(model.variables().ids().begin(),
346  model.variables().ids().end());
347  std::vector linear_constraint_ids(model.linear_constraints().ids().begin(),
348  model.linear_constraints().ids().end());
349  return absl::WrapUnique(new CpSatSolver(
350  std::move(cp_sat_model),
351  /*variable_ids=*/std::move(variable_ids),
352  /*linear_constraint_ids=*/std::move(linear_constraint_ids)));
353 }
354 
355 absl::StatusOr<SolveResultProto> CpSatSolver::Solve(
356  const SolveParametersProto& parameters,
357  const ModelSolveParametersProto& model_parameters,
358  const MessageCallback message_cb,
359  const CallbackRegistrationProto& callback_registration, const Callback cb,
360  SolveInterrupter* const interrupter) {
361  const absl::Time start = absl::Now();
362 
364  callback_registration,
365  /*supported_events=*/{CALLBACK_EVENT_MIP_SOLUTION}));
366  if (callback_registration.add_lazy_constraints()) {
367  return absl::InvalidArgumentError(
368  "CallbackRegistrationProto.add_lazy_constraints=true is not supported "
369  "for CP-SAT.");
370  }
371  // We need not check callback_registration.add_cuts, as cuts can only be added
372  // at event MIP_NODE which we have already validated is not supported.
373 
374  SolveResultProto result;
375  MPModelRequest req;
376  // Here we must make a copy since Solve() can be called multiple times with
377  // different parameters. Hence we can't move `cp_sat_model`.
378  *req.mutable_model() = cp_sat_model_;
379 
380  req.set_solver_type(MPModelRequest::SAT_INTEGER_PROGRAMMING);
381  bool used_cutoff = false;
382  {
383  std::vector<std::string> param_warnings =
384  SetSolveParameters(parameters,
385  /*has_message_callback=*/message_cb != nullptr, req);
386  if (parameters.has_cutoff_limit()) {
387  used_cutoff = ApplyCutoff(parameters.cutoff_limit(), req.mutable_model());
388  if (!used_cutoff) {
389  param_warnings.push_back(
390  "The cutoff_limit parameter not supported for quadratic objectives "
391  "with CP-SAT.");
392  }
393  }
394  if (!param_warnings.empty()) {
395  return absl::InvalidArgumentError(absl::StrJoin(param_warnings, "; "));
396  }
397  }
398 
399  if (!model_parameters.solution_hints().empty()) {
400  int i = 0;
401  for (const auto [id, val] :
402  MakeView(model_parameters.solution_hints(0).variable_values())) {
403  while (variable_ids_[i] < id) {
404  ++i;
405  }
406  req.mutable_model()->mutable_solution_hint()->add_var_index(i);
407  req.mutable_model()->mutable_solution_hint()->add_var_value(val);
408  }
409  }
410 
411  // We need to chain the user interrupter through a local interrupter, because
412  // if we termiante early from a callback request, we don't want to incorrectly
413  // modify the input state.
414  SolveInterrupter local_interrupter;
415  std::atomic<bool> interrupt_solve = false;
416  local_interrupter.AddInterruptionCallback([&]() { interrupt_solve = true; });
417 
418  // Setup a callback on the user provided so that we interrupt the solver.
419  const ScopedSolveInterrupterCallback scoped_interrupt_cb(
420  interrupter, [&]() { local_interrupter.Interrupt(); });
421 
422  std::function<void(const std::string&)> logging_callback;
423  if (message_cb != nullptr) {
424  logging_callback = [&](const std::string& message) {
425  message_cb(absl::StrSplit(message, '\n'));
426  };
427  }
428 
429  const absl::flat_hash_set<CallbackEventProto> events =
430  EventSet(callback_registration);
431  std::function<void(const MPSolution&)> solution_callback;
432  absl::Status callback_error = absl::OkStatus();
433  if (events.contains(CALLBACK_EVENT_MIP_SOLUTION)) {
434  solution_callback =
435  [this, &cb, &callback_error, &local_interrupter,
436  &callback_registration](const MPSolution& mp_solution) {
437  if (!callback_error.ok()) {
438  // A previous callback failed.
439  return;
440  }
441  CallbackDataProto cb_data;
442  cb_data.set_event(CALLBACK_EVENT_MIP_SOLUTION);
443  *cb_data.mutable_primal_solution_vector() =
444  ExtractSolution(mp_solution.variable_value(),
445  callback_registration.mip_solution_filter());
446  const absl::StatusOr<CallbackResultProto> cb_result = cb(cb_data);
447  if (!cb_result.ok()) {
448  callback_error = cb_result.status();
449  // Note: we will be returning a status error, we do not need to
450  // worry about interpreting this as TERMINATION_REASON_INTERRUPTED.
451  local_interrupter.Interrupt();
452  } else if (cb_result->terminate()) {
453  local_interrupter.Interrupt();
454  }
455  // Note cb_result.cuts and cb_result.suggested solutions are not
456  // supported by CP-SAT and we have validated they are empty.
457  };
458  }
459 
460  // CP-SAT returns "infeasible" for inverted bounds.
461  RETURN_IF_ERROR(ListInvertedBounds().ToStatus());
462 
463  ASSIGN_OR_RETURN(const MPSolutionResponse response,
464  SatSolveProto(std::move(req), &interrupt_solve,
465  logging_callback, solution_callback));
466  RETURN_IF_ERROR(callback_error) << "error in callback";
468  (auto [solve_stats, termination]),
469  GetTerminationAndStats(local_interrupter.IsInterrupted(),
470  /*maximize=*/cp_sat_model_.maximize(),
471  /*used_cutoff=*/used_cutoff, response));
472  *result.mutable_solve_stats() = std::move(solve_stats);
473  *result.mutable_termination() = std::move(termination);
474  const SparseVectorFilterProto& var_values_filter =
475  model_parameters.variable_values_filter();
476  auto add_solution =
477  [this, &result, &var_values_filter](
478  const google::protobuf::RepeatedField<double>& variable_values,
479  double objective) {
480  PrimalSolutionProto& solution =
481  *result.add_solutions()->mutable_primal_solution();
482  *solution.mutable_variable_values() =
483  ExtractSolution(variable_values, var_values_filter);
484  solution.set_objective_value(objective);
485  solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
486  };
487  if (response.status() == MPSOLVER_OPTIMAL ||
488  response.status() == MPSOLVER_FEASIBLE) {
489  add_solution(response.variable_value(), response.objective_value());
490  for (const MPSolution& extra_solution : response.additional_solutions()) {
491  add_solution(extra_solution.variable_value(),
492  extra_solution.objective_value());
493  }
494  }
495 
497  absl::Now() - start, result.mutable_solve_stats()->mutable_solve_time()));
498 
499  return result;
500 }
501 
502 absl::StatusOr<bool> CpSatSolver::Update(const ModelUpdateProto& model_update) {
503  return false;
504 }
505 
506 CpSatSolver::CpSatSolver(MPModelProto cp_sat_model,
507  std::vector<int64_t> variable_ids,
508  std::vector<int64_t> linear_constraint_ids)
509  : cp_sat_model_(std::move(cp_sat_model)),
510  variable_ids_(std::move(variable_ids)),
511  linear_constraint_ids_(std::move(linear_constraint_ids)) {}
512 
513 SparseDoubleVectorProto CpSatSolver::ExtractSolution(
514  const absl::Span<const double> cp_sat_variable_values,
515  const SparseVectorFilterProto& filter) const {
516  // Pre-condition: we assume one-to-one correspondence of input variables to
517  // solution's variables.
518  CHECK_EQ(cp_sat_variable_values.size(), variable_ids_.size());
519 
520  SparseVectorFilterPredicate predicate(filter);
521  SparseDoubleVectorProto result;
522  for (int i = 0; i < variable_ids_.size(); ++i) {
523  const int64_t id = variable_ids_[i];
524  const double value = cp_sat_variable_values[i];
525  if (predicate.AcceptsAndUpdate(id, value)) {
526  result.add_ids(id);
527  result.add_values(value);
528  }
529  }
530  return result;
531 }
532 
533 InvertedBounds CpSatSolver::ListInvertedBounds() const {
534  InvertedBounds inverted_bounds;
535  for (int v = 0; v < cp_sat_model_.variable_size(); ++v) {
536  const MPVariableProto& var = cp_sat_model_.variable(v);
537  if (var.lower_bound() > var.upper_bound()) {
538  inverted_bounds.variables.push_back(variable_ids_[v]);
539  }
540  }
541  for (int c = 0; c < cp_sat_model_.constraint_size(); ++c) {
542  const MPConstraintProto& cstr = cp_sat_model_.constraint(c);
543  if (cstr.lower_bound() > cstr.upper_bound()) {
544  inverted_bounds.linear_constraints.push_back(linear_constraint_ids_[c]);
545  }
546  }
547 
548  return inverted_bounds;
549 }
550 
552 
553 } // namespace math_opt
554 } // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
CallbackId AddInterruptionCallback(Callback callback)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
SatParameters parameters
SharedResponseManager * response
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Span< const int64_t > variable_ids
GRBmodel * model
TerminationProto FeasibleTermination(const LimitProto limit, const absl::string_view detail)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto &registration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_CP_SAT, CpSatSolver::New)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
absl::StatusOr<::operations_research::MPModelProto > MathOptModelToMPModelProto(const ::operations_research::math_opt::ModelProto &model)
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
absl::StatusOr< MPSolutionResponse > SatSolveProto(MPModelRequest request, std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> logging_callback, std::function< void(const MPSolution &)> solution_callback)
std::string EncodeSatParametersAsString(const sat::SatParameters &parameters)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:42
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
Definition: protoutil.h:27
int64_t start
std::string message
Definition: trace.cc:399