Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
bop_interface.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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
14#include <atomic>
15#include <cstdint>
16#include <memory>
17#include <string>
18#include <utility>
19#include <vector>
20
21#include "absl/base/attributes.h"
26
27namespace operations_research {
28namespace {
29
30MPSolver::ResultStatus TranslateProblemStatus(bop::BopSolveStatus status) {
31 switch (status) {
33 return MPSolver::OPTIMAL;
35 return MPSolver::FEASIBLE;
41 return MPSolver::ABNORMAL;
42 }
43 LOG(DFATAL) << "Invalid bop::BopSolveStatus";
44 return MPSolver::ABNORMAL;
45}
46
47} // Anonymous namespace
48
50 public:
51 explicit BopInterface(MPSolver* solver);
52 ~BopInterface() override;
53
54 // ----- Solve -----
55 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
56
57 // ----- Model modifications and extraction -----
58 void Reset() override;
59 void SetOptimizationDirection(bool maximize) override;
60 void SetVariableBounds(int index, double lb, double ub) override;
61 void SetVariableInteger(int index, bool integer) override;
62 void SetConstraintBounds(int index, double lb, double ub) override;
63 void AddRowConstraint(MPConstraint* ct) override;
64 void AddVariable(MPVariable* var) override;
65 void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
66 double new_value, double old_value) override;
67 void ClearConstraint(MPConstraint* constraint) override;
68 void SetObjectiveCoefficient(const MPVariable* variable,
69 double coefficient) override;
70 void SetObjectiveOffset(double value) override;
71 void ClearObjective() override;
72
73 // ------ Query statistics on the solution and the solve ------
74 int64_t iterations() const override;
75 int64_t nodes() const override;
76 MPSolver::BasisStatus row_status(int constraint_index) const override;
77 MPSolver::BasisStatus column_status(int variable_index) const override;
78
79 // ----- Misc -----
80 bool IsContinuous() const override;
81 bool IsLP() const override;
82 bool IsMIP() const override;
83
84 std::string SolverVersion() const override;
85 bool InterruptSolve() override;
86 void* underlying_solver() override;
87
88 void ExtractNewVariables() override;
89 void ExtractNewConstraints() override;
90 void ExtractObjective() override;
91
92 void SetParameters(const MPSolverParameters& param) override;
93 void SetRelativeMipGap(double value) override;
94 void SetPrimalTolerance(double value) override;
95 void SetDualTolerance(double value) override;
96 void SetPresolveMode(int value) override;
97 void SetScalingMode(int value) override;
98 void SetLpAlgorithm(int value) override;
100 const std::string& parameters) override;
101
102 private:
103 void NonIncrementalChange();
104
105 glop::LinearProgram linear_program_;
106 bop::IntegralSolver bop_solver_;
107 std::vector<MPSolver::BasisStatus> column_status_;
108 std::vector<MPSolver::BasisStatus> row_status_;
109 bop::BopParameters parameters_;
110 std::atomic<bool> interrupt_solver_;
111};
112
114 : MPSolverInterface(solver),
115 linear_program_(),
116 bop_solver_(),
117 column_status_(),
118 row_status_(),
119 parameters_(),
120 interrupt_solver_(false) {}
121
123
125 // Check whenever the solve has already been stopped by the user.
126 if (interrupt_solver_) {
127 Reset();
128 // linear_solver.cc as DCHECK_EQ that interface_->result_status_ is the same
129 // as the status returned by interface_->Solve().
131 return result_status_;
132 }
133
134 // Reset extraction as this interface is not incremental yet.
135 Reset();
136 ExtractModel();
137 SetParameters(param);
138
139 linear_program_.SetMaximizationProblem(maximize_);
140 linear_program_.CleanUp();
141
142 // Time limit.
143 if (solver_->time_limit()) {
144 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
145 parameters_.set_max_time_in_seconds(
146 static_cast<double>(solver_->time_limit()) / 1000.0);
147 }
148 parameters_.set_log_search_progress(!quiet());
149
150 glop::DenseRow initial_solution;
151 if (!solver_->solution_hint_.empty()) {
152 const int num_vars = solver_->variables_.size();
153 if (solver_->solution_hint_.size() != num_vars) {
154 LOG(WARNING) << "Bop currently doesn't handle partial solution hints. "
155 << "Filling the missing positions with zeros...";
156 }
157 initial_solution.assign(glop::ColIndex(num_vars), glop::Fractional(0.0));
158 for (const std::pair<const MPVariable*, double>& p :
159 solver_->solution_hint_) {
160 initial_solution[glop::ColIndex(p.first->index())] =
161 glop::Fractional(p.second);
162 }
163 }
164
165 solver_->SetSolverSpecificParametersAsString(
166 solver_->solver_specific_parameter_string_);
167 bop_solver_.SetParameters(parameters_);
168 std::unique_ptr<TimeLimit> time_limit =
169 TimeLimit::FromParameters(parameters_);
170 time_limit->RegisterExternalBooleanAsLimit(&interrupt_solver_);
171 const bop::BopSolveStatus status =
172 initial_solution.empty()
173 ? bop_solver_.SolveWithTimeLimit(linear_program_, time_limit.get())
174 : bop_solver_.SolveWithTimeLimit(linear_program_, initial_solution,
175 time_limit.get());
176
177 // The solution must be marked as synchronized even when no solution exists.
179 result_status_ = TranslateProblemStatus(status);
182 // Get the results.
183 objective_value_ = bop_solver_.objective_value();
184 best_objective_bound_ = bop_solver_.best_bound();
185
186 // TODO(user): Implement the column status.
187 const size_t num_vars = solver_->variables_.size();
188 column_status_.resize(num_vars, MPSolver::FREE);
189 for (int var_id = 0; var_id < num_vars; ++var_id) {
190 MPVariable* const var = solver_->variables_[var_id];
191 const glop::ColIndex lp_solver_var_id(var->index());
192 const glop::Fractional solution_value =
193 bop_solver_.variable_values()[lp_solver_var_id];
194 var->set_solution_value(static_cast<double>(solution_value));
195 }
196
197 // TODO(user): Implement the row status.
198 const size_t num_constraints = solver_->constraints_.size();
199 row_status_.resize(num_constraints, MPSolver::FREE);
200 }
201
202 return result_status_;
203}
204
207 linear_program_.Clear();
208 interrupt_solver_ = false;
209}
210
212 NonIncrementalChange();
213}
214
215void BopInterface::SetVariableBounds(int index, double lb, double ub) {
216 NonIncrementalChange();
217}
218
219void BopInterface::SetVariableInteger(int index, bool integer) {
220 NonIncrementalChange();
221}
222
223void BopInterface::SetConstraintBounds(int index, double lb, double ub) {
224 NonIncrementalChange();
225}
226
228 NonIncrementalChange();
229}
230
232 NonIncrementalChange();
233}
234
236 const MPVariable* const variable,
237 double new_value, double old_value) {
238 NonIncrementalChange();
239}
240
242 NonIncrementalChange();
243}
244
246 double coefficient) {
247 NonIncrementalChange();
248}
249
250void BopInterface::SetObjectiveOffset(double value) { NonIncrementalChange(); }
251
252void BopInterface::ClearObjective() { NonIncrementalChange(); }
253
255 LOG(DFATAL) << "Number of iterations not available";
257}
258
259int64_t BopInterface::nodes() const {
260 LOG(DFATAL) << "Number of nodes not available";
262}
263
265 return row_status_[constraint_index];
266}
267
269 return column_status_[variable_index];
270}
271
272bool BopInterface::IsContinuous() const { return false; }
273bool BopInterface::IsLP() const { return false; }
274bool BopInterface::IsMIP() const { return true; }
275
276std::string BopInterface::SolverVersion() const {
277 // TODO(user): Decide how to version bop.
278 return "Bop-0.0";
279}
280
282 interrupt_solver_ = true;
283 return true;
284}
285
286void* BopInterface::underlying_solver() { return &bop_solver_; }
287
288// TODO(user): remove duplication with GlopInterface.
290 DCHECK_EQ(0, last_variable_index_);
291 DCHECK_EQ(0, last_constraint_index_);
292
293 const glop::ColIndex num_cols(solver_->variables_.size());
294 for (glop::ColIndex col(last_variable_index_); col < num_cols; ++col) {
295 MPVariable* const var = solver_->variables_[col.value()];
296 const glop::ColIndex new_col = linear_program_.CreateNewVariable();
297 DCHECK_EQ(new_col, col);
298 set_variable_as_extracted(col.value(), true);
299 linear_program_.SetVariableBounds(col, var->lb(), var->ub());
300 if (var->integer()) {
301 linear_program_.SetVariableType(
303 }
304 }
305}
306
307// TODO(user): remove duplication with GlopInterface.
309 DCHECK_EQ(0, last_constraint_index_);
310
311 const glop::RowIndex num_rows(solver_->constraints_.size());
312 for (glop::RowIndex row(0); row < num_rows; ++row) {
313 MPConstraint* const ct = solver_->constraints_[row.value()];
314 set_constraint_as_extracted(row.value(), true);
315
316 const double lb = ct->lb();
317 const double ub = ct->ub();
318 const glop::RowIndex new_row = linear_program_.CreateNewConstraint();
319 DCHECK_EQ(new_row, row);
320 linear_program_.SetConstraintBounds(row, lb, ub);
321
322 for (const auto& entry : ct->coefficients_) {
323 const int var_index = entry.first->index();
324 DCHECK(variable_is_extracted(var_index));
325 const glop::ColIndex col(var_index);
326 const double coeff = entry.second;
327 linear_program_.SetCoefficient(row, col, coeff);
328 }
329 }
330}
331
332// TODO(user): remove duplication with GlopInterface.
334 linear_program_.SetObjectiveOffset(solver_->Objective().offset());
335 for (const auto& entry : solver_->objective_->coefficients_) {
336 const int var_index = entry.first->index();
337 const glop::ColIndex col(var_index);
338 const double coeff = entry.second;
339 linear_program_.SetObjectiveCoefficient(col, coeff);
340 }
341}
342
344 parameters_.Clear();
345 SetCommonParameters(param);
346}
347
348// All these have no effect.
354
356 switch (value) {
358 // TODO(user): add this to BopParameters.
359 break;
361 // TODO(user): add this to BopParameters.
362 break;
363 default:
366 }
367 }
368}
369
371 const std::string& parameters) {
372 const bool ok =
373 google::protobuf::TextFormat::MergeFromString(parameters, &parameters_);
374 bop_solver_.SetParameters(parameters_);
375 return ok;
376}
377
378void BopInterface::NonIncrementalChange() {
379 // The current implementation is not incremental.
381}
382
383namespace {
384
385// See MpSolverInterfaceFactoryRepository for details.
386const void* const kRegisterBop ABSL_ATTRIBUTE_UNUSED = [] {
388 [](MPSolver* const solver) { return new BopInterface(solver); },
390 return nullptr;
391}();
392
393} // namespace
394
395} // namespace operations_research
void SetParameters(const MPSolverParameters &param) override
void SetRelativeMipGap(double value) override
void SetScalingMode(int value) override
MPSolver::BasisStatus row_status(int constraint_index) const override
void AddVariable(MPVariable *var) override
int64_t nodes() const override
void SetVariableInteger(int index, bool integer) override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
std::string SolverVersion() const override
int64_t iterations() const override
void SetLpAlgorithm(int value) override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
void SetVariableBounds(int index, double lb, double ub) override
bool SetSolverSpecificParametersAsString(const std::string &parameters) override
void SetOptimizationDirection(bool maximize) override
void SetConstraintBounds(int index, double lb, double ub) override
void SetDualTolerance(double value) override
bool IsContinuous() const override
void SetPresolveMode(int value) override
void ClearConstraint(MPConstraint *constraint) override
MPSolver::BasisStatus column_status(int variable_index) const override
void SetObjectiveOffset(double value) override
void AddRowConstraint(MPConstraint *ct) override
void SetPrimalTolerance(double value) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
static MPSolverInterfaceFactoryRepository * GetInstance()
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
void set_variable_as_extracted(int var_index, bool extracted)
static constexpr int64_t kUnknownNumberOfIterations
void set_constraint_as_extracted(int ct_index, bool extracted)
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
bool variable_is_extracted(int var_index) const
static constexpr int64_t kUnknownNumberOfNodes
void SetCommonParameters(const MPSolverParameters &param)
@ PRESOLVE
Advanced usage: presolve mode.
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ ABNORMAL
abnormal, i.e., error of some kind.
The class for variables of a Mathematical Programming (MP) model.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
void set_solution_value(double value)
int index() const
Returns the index of the variable in the MPSolver::variables_.
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Definition time_limit.h:143
void assign(IntType size, const T &v)
Definition lp_types.h:317
StrictITIVector< ColIndex, Fractional > DenseRow
Definition lp_types.h:351
OR-Tools root namespace.