OR-Tools  9.6
model_builder_helper.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 <cmath>
17 #include <functional>
18 #include <limits>
19 #include <optional>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
25 #include "ortools/linear_solver/linear_solver.pb.h"
28 #if defined(USE_SCIP)
30 #endif // defined(USE_SCIP)
31 #if defined(USE_LP_PARSER)
33 #endif // defined(USE_LP_PARSER)
35 
36 namespace operations_research {
37 
39  const MPModelExportOptions& options) {
40  return operations_research::ExportModelAsMpsFormat(model_, options)
41  .value_or("");
42 }
43 
45  const MPModelExportOptions& options) {
46  return operations_research::ExportModelAsLpFormat(model_, options)
47  .value_or("");
48 }
49 
50 bool ModelBuilderHelper::WriteModelToFile(const std::string& filename) {
51  if (absl::EndsWith(filename, "txt")) {
52  return file::SetTextProto(filename, model_, file::Defaults()).ok();
53  } else {
54  return file::SetBinaryProto(filename, model_, file::Defaults()).ok();
55  }
56 }
57 
58 // See comment in the header file why we need to wrap absl::Status code with
59 // code having simpler APIs.
60 bool ModelBuilderHelper::ImportFromMpsString(const std::string& mps_string) {
61  absl::StatusOr<MPModelProto> model_or =
63  if (!model_or.ok()) return false;
64  model_ = model_or.value();
65  return true;
66 }
67 
68 bool ModelBuilderHelper::ImportFromMpsFile(const std::string& mps_file) {
69  absl::StatusOr<MPModelProto> model_or =
71  if (!model_or.ok()) return false;
72  model_ = model_or.value();
73  return true;
74 }
75 
76 #if defined(USE_LP_PARSER)
77 bool ModelBuilderHelper::ImportFromLpString(const std::string& lp_string) {
78  absl::StatusOr<MPModelProto> model_or = ModelProtoFromLpFormat(lp_string);
79  if (!model_or.ok()) return false;
80  model_ = model_or.value();
81  return true;
82 }
83 
84 bool ModelBuilderHelper::ImportFromLpFile(const std::string& lp_file) {
85  std::string lp_data;
86  if (!file::GetContents(lp_file, &lp_data, file::Defaults()).ok()) {
87  return false;
88  }
89  absl::StatusOr<MPModelProto> model_or = ModelProtoFromLpFormat(lp_data);
90  if (!model_or.ok()) return false;
91  model_ = model_or.value();
92  return true;
93 }
94 #endif // #if defined(USE_LP_PARSER)
95 
96 const MPModelProto& ModelBuilderHelper::model() const { return model_; }
97 
98 MPModelProto* ModelBuilderHelper::mutable_model() { return &model_; }
99 
101  const int index = model_.variable_size();
102  model_.add_variable();
103  return index;
104 }
105 
106 void ModelBuilderHelper::SetVarLowerBound(int var_index, double lb) {
107  model_.mutable_variable(var_index)->set_lower_bound(lb);
108 }
109 
110 void ModelBuilderHelper::SetVarUpperBound(int var_index, double ub) {
111  model_.mutable_variable(var_index)->set_upper_bound(ub);
112 }
113 
114 void ModelBuilderHelper::SetVarIntegrality(int var_index, bool is_integer) {
115  model_.mutable_variable(var_index)->set_is_integer(is_integer);
116 }
117 
119  double coeff) {
120  model_.mutable_variable(var_index)->set_objective_coefficient(coeff);
121 }
122 
123 void ModelBuilderHelper::SetVarName(int var_index, const std::string& name) {
124  model_.mutable_variable(var_index)->set_name(name);
125 }
126 
128  const int index = model_.constraint_size();
129  model_.add_constraint();
130  return index;
131 }
132 
133 void ModelBuilderHelper::SetConstraintLowerBound(int ct_index, double lb) {
134  model_.mutable_constraint(ct_index)->set_lower_bound(lb);
135 }
136 
137 void ModelBuilderHelper::SetConstraintUpperBound(int ct_index, double ub) {
138  model_.mutable_constraint(ct_index)->set_upper_bound(ub);
139 }
140 
141 void ModelBuilderHelper::AddConstraintTerm(int ct_index, int var_index,
142  double coeff) {
143  MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
144  ct_proto->add_var_index(var_index);
145  ct_proto->add_coefficient(coeff);
146 }
147 
149  const std::string& name) {
150  model_.mutable_constraint(ct_index)->set_name(name);
151 }
152 
153 int ModelBuilderHelper::num_variables() const { return model_.variable_size(); }
154 
155 double ModelBuilderHelper::VarLowerBound(int var_index) const {
156  return model_.variable(var_index).lower_bound();
157 }
158 
159 double ModelBuilderHelper::VarUpperBound(int var_index) const {
160  return model_.variable(var_index).upper_bound();
161 }
162 
163 bool ModelBuilderHelper::VarIsIntegral(int var_index) const {
164  return model_.variable(var_index).is_integer();
165 }
166 
167 double ModelBuilderHelper::VarObjectiveCoefficient(int var_index) const {
168  return model_.variable(var_index).objective_coefficient();
169 }
170 
171 std::string ModelBuilderHelper::VarName(int var_index) const {
172  return model_.variable(var_index).name();
173 }
174 
176  return model_.constraint_size();
177 }
178 
179 double ModelBuilderHelper::ConstraintLowerBound(int ct_index) const {
180  return model_.constraint(ct_index).lower_bound();
181 }
182 
183 double ModelBuilderHelper::ConstraintUpperBound(int ct_index) const {
184  return model_.constraint(ct_index).upper_bound();
185 }
186 
187 std::string ModelBuilderHelper::ConstraintName(int ct_index) const {
188  return model_.constraint(ct_index).name();
189 }
190 
191 std::vector<int> ModelBuilderHelper::ConstraintVarIndices(int ct_index) const {
192  const MPConstraintProto& ct_proto = model_.constraint(ct_index);
193  return {ct_proto.var_index().begin(), ct_proto.var_index().end()};
194 }
195 
197  int ct_index) const {
198  const MPConstraintProto& ct_proto = model_.constraint(ct_index);
199  return {ct_proto.coefficient().begin(), ct_proto.coefficient().end()};
200 }
201 
202 std::string ModelBuilderHelper::name() const { return model_.name(); }
203 
204 void ModelBuilderHelper::SetName(const std::string& name) {
205  model_.set_name(name);
206 }
208  for (MPVariableProto& var : *model_.mutable_variable()) {
209  var.clear_objective_coefficient();
210  }
211 }
212 
213 bool ModelBuilderHelper::maximize() const { return model_.maximize(); }
214 
215 void ModelBuilderHelper::SetMaximize(bool maximize) {
216  model_.set_maximize(maximize);
217 }
218 
220  return model_.objective_offset();
221 }
222 
224  model_.set_objective_offset(offset);
225 }
226 
227 std::optional<MPSolutionResponse> ModelSolverHelper::SolveRequest(
228  const MPModelRequest& request) {
229  MPSolutionResponse temp;
230  MPSolver::SolveWithProto(request, &temp, &interrupt_solve_);
231  return temp;
232 }
233 
234 namespace {
235 SolveStatus MPSolverResponseStatusToSolveStatus(MPSolverResponseStatus s) {
236  switch (s) {
237  case MPSOLVER_OPTIMAL:
238  return SolveStatus::OPTIMAL;
239  case MPSOLVER_FEASIBLE:
240  return SolveStatus::FEASIBLE;
241  case MPSOLVER_INFEASIBLE:
243  case MPSOLVER_UNBOUNDED:
244  return SolveStatus::UNBOUNDED;
245  case MPSOLVER_ABNORMAL:
246  return SolveStatus::ABNORMAL;
247  case MPSOLVER_NOT_SOLVED:
249  case MPSOLVER_MODEL_IS_VALID:
251  case MPSOLVER_CANCELLED_BY_USER:
253  case MPSOLVER_UNKNOWN_STATUS:
255  case MPSOLVER_MODEL_INVALID:
257  case MPSOLVER_MODEL_INVALID_SOLUTION_HINT:
259  case MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS:
261  case MPSOLVER_SOLVER_TYPE_UNAVAILABLE:
263  case MPSOLVER_INCOMPATIBLE_OPTIONS:
265  default:
267  }
268 }
269 } // namespace
270 
271 ModelSolverHelper::ModelSolverHelper(const std::string& solver_name) {
272  if (solver_name.empty()) return;
274  if (!MPSolver::ParseSolverType(solver_name, &parsed_type)) {
275  VLOG(1) << "Unsupported type " << solver_name;
276  } else {
277  solver_type_ = static_cast<MPModelRequest::SolverType>(parsed_type);
278  }
279 }
280 
282  return solver_type_.has_value();
283 }
284 
286  response_.reset();
287  if (!solver_type_.has_value()) {
288  response_->set_status(
289  MPSolverResponseStatus::MPSOLVER_SOLVER_TYPE_UNAVAILABLE);
290  return;
291  }
292 
293  MPModelRequest request;
294  *request.mutable_model() = model.model();
295  request.set_solver_type(solver_type_.value());
296  request.set_enable_internal_solver_output(solver_output_);
297  if (time_limit_in_second_.has_value()) {
298  request.set_solver_time_limit_seconds(time_limit_in_second_.value());
299  }
300  if (!solver_specific_parameters_.empty()) {
301  request.set_solver_specific_parameters(solver_specific_parameters_);
302  }
303  switch (solver_type_.value()) {
304  case MPModelRequest::GLOP_LINEAR_PROGRAMMING: {
305  // TODO(user): Enable log_callback support.
306  MPSolutionResponse temp;
307  MPSolver::SolveWithProto(request, &temp, &interrupt_solve_);
308  response_ = std::move(temp);
309  break;
310  }
311  case MPModelRequest::SAT_INTEGER_PROGRAMMING: {
312  const auto temp =
313  SatSolveProto(request, &interrupt_solve_, log_callback_, nullptr);
314  if (temp.ok()) {
315  response_ = std::move(temp.value());
316  }
317  break;
318  }
319 #if defined(USE_SCIP)
320  case MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING: {
321  // TODO(user): Enable log_callback support.
322  // TODO(user): Enable interrupt_solve.
323  const auto temp = ScipSolveProto(request);
324  if (temp.ok()) {
325  response_ = std::move(temp.value());
326  }
327  break;
328  }
329 #endif // defined(USE_SCIP)
330 #if defined(USE_HIGHS)
331  case MPModelRequest::HIGHS_MIXED_INTEGER_PROGRAMMING:
332  case MPModelRequest::HIGHS_LINEAR_PROGRAMMING: {
333  const auto temp = HighsSolveProto(request);
334  if (temp.ok()) {
335  response_ = std::move(temp.value());
336  }
337  break;
338  }
339 #endif // defined(USE_HIGHS)
340  default: {
341  response_->set_status(
342  MPSolverResponseStatus::MPSOLVER_SOLVER_TYPE_UNAVAILABLE);
343  }
344  }
345  if (response_->status() == MPSOLVER_OPTIMAL ||
346  response_->status() == MPSOLVER_FEASIBLE) {
347  model_of_last_solve_ = &model.model();
348  activities_.assign(model.num_constraints(),
349  std::numeric_limits<double>::quiet_NaN());
350  } else {
351  activities_.clear();
352  }
353 }
354 
356  std::function<void(const std::string&)> log_callback) {
357  log_callback_ = std::move(log_callback);
358 }
359 
361  LogCallback* log_callback) {
362  log_callback_ = [log_callback](const std::string& message) {
363  log_callback->NewMessage(message);
364  };
365 }
366 
367 void ModelSolverHelper::ClearLogCallback() { log_callback_ = nullptr; }
368 
370  interrupt_solve_ = true;
371  return true;
372 }
373 
374 bool ModelSolverHelper::has_response() const { return response_.has_value(); }
375 
377  return response_.has_value() &&
378  (response_.value().status() ==
379  MPSolverResponseStatus::MPSOLVER_OPTIMAL ||
380  response_.value().status() ==
381  MPSolverResponseStatus::MPSOLVER_FEASIBLE);
382 }
383 
384 const MPSolutionResponse& ModelSolverHelper::response() const {
385  return response_.value();
386 }
387 
389  if (!response_.has_value()) {
391  }
392  return MPSolverResponseStatusToSolveStatus(response_.value().status());
393 }
394 
396  if (!has_response()) return 0.0;
397  return response_.value().objective_value();
398 }
399 
401  if (!has_response()) return 0.0;
402  return response_.value().best_objective_bound();
403 }
404 
405 double ModelSolverHelper::variable_value(int var_index) const {
406  if (!has_response()) return 0.0;
407  if (var_index >= response_.value().variable_value_size()) return 0.0;
408  return response_.value().variable_value(var_index);
409 }
410 
411 double ModelSolverHelper::reduced_cost(int var_index) const {
412  if (!has_response()) return 0.0;
413  if (var_index >= response_.value().reduced_cost_size()) return 0.0;
414  return response_.value().reduced_cost(var_index);
415 }
416 
417 double ModelSolverHelper::dual_value(int ct_index) const {
418  if (!has_response()) return 0.0;
419  if (ct_index >= response_.value().dual_value_size()) return 0.0;
420  return response_.value().dual_value(ct_index);
421 }
422 
423 double ModelSolverHelper::activity(int ct_index) {
424  if (!has_response() || ct_index >= activities_.size() ||
425  !model_of_last_solve_.has_value()) {
426  return 0.0;
427  }
428 
429  if (std::isnan(activities_[ct_index])) {
430  const MPConstraintProto& ct_proto =
431  model_of_last_solve_.value()->constraint(ct_index);
432  double result = 0.0;
433  for (int i = 0; i < ct_proto.var_index_size(); ++i) {
434  result += response_->variable_value(ct_proto.var_index(i)) *
435  ct_proto.coefficient(i);
436  }
437  activities_[ct_index] = result;
438  }
439  return activities_[ct_index];
440 }
441 
442 std::string ModelSolverHelper::status_string() const {
443  if (!has_response()) return "";
444  return response_.value().status_str();
445 }
446 
448  if (!response_.has_value()) return 0.0;
449  if (!response_.value().has_solve_info()) return 0.0;
450  return response_.value().solve_info().solve_wall_time_seconds();
451 }
452 
454  if (!response_.has_value()) return 0.0;
455  if (!response_.value().has_solve_info()) return 0.0;
456  return response_.value().solve_info().solve_user_time_seconds();
457 }
458 
460  time_limit_in_second_ = limit;
461 }
462 
464  const std::string& solver_specific_parameters) {
465  solver_specific_parameters_ = solver_specific_parameters;
466 }
467 
468 void ModelSolverHelper::EnableOutput(bool enabled) { solver_output_ = enabled; }
469 
470 } // namespace operations_research
virtual void NewMessage(const std::string &message)=0
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
Parses the name of the solver.
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
Solves the model encoded by a MPModelRequest protocol buffer and fills the solution encoded as a MPSo...
bool WriteModelToFile(const std::string &filename)
void SetVarUpperBound(int var_index, double ub)
void SetConstraintUpperBound(int ct_index, double ub)
double VarObjectiveCoefficient(int var_index) const
void SetVarObjectiveCoefficient(int var_index, double coeff)
void AddConstraintTerm(int ct_index, int var_index, double coeff)
double ConstraintUpperBound(int ct_index) const
std::string VarName(int var_index) const
std::string ExportToMpsString(const operations_research::MPModelExportOptions &options=MPModelExportOptions())
std::string ConstraintName(int ct_index) const
void SetVarName(int var_index, const std::string &name)
void SetConstraintName(int ct_index, const std::string &name)
std::vector< double > ConstraintCoefficients(int ct_index) const
bool ImportFromMpsFile(const std::string &mps_file)
std::vector< int > ConstraintVarIndices(int ct_index) const
double ConstraintLowerBound(int ct_index) const
std::string ExportToLpString(const operations_research::MPModelExportOptions &options=MPModelExportOptions())
void SetConstraintLowerBound(int ct_index, double lb)
void SetVarLowerBound(int var_index, double lb)
void SetVarIntegrality(int var_index, bool is_integer)
bool ImportFromMpsString(const std::string &mps_string)
double variable_value(int var_index) const
void SetSolverSpecificParameters(const std::string &solver_specific_parameters)
void SetLogCallbackFromDirectorClass(LogCallback *log_callback)
void SetLogCallback(std::function< void(const std::string &)> log_callback)
void Solve(const ModelBuilderHelper &model)
const MPSolutionResponse & response() const
std::optional< MPSolutionResponse > SolveRequest(const MPModelRequest &request)
ModelSolverHelper(const std::string &solver_name)
const std::string name
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
int index
absl::Status GetContents(const absl::string_view &filename, std::string *output, int flags)
Definition: base/file.cc:164
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:299
Options Defaults()
Definition: base/file.h:123
absl::Status SetBinaryProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:322
absl::StatusOr< MPModelProto > MpsDataToMPModelProto(const std::string &mps_data)
Definition: mps_reader.cc:1193
absl::StatusOr< MPModelProto > MpsFileToMPModelProto(const std::string &mps_file)
Definition: mps_reader.cc:1201
Collection of objects used to extend the Constraint Solver library.
absl::StatusOr< MPSolutionResponse > ScipSolveProto(const MPModelRequest &request)
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in MPS file format,...
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)
absl::StatusOr< MPSolutionResponse > HighsSolveProto(MPModelRequest request)
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in the so-called "C...
std::string message
Definition: trace.cc:399
#define VLOG(verboselevel)
Definition: vlog.h:39