Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
glpk_solver.h
Go to the documentation of this file.
1// Copyright 2010-2024 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#ifndef OR_TOOLS_MATH_OPT_SOLVERS_GLPK_SOLVER_H_
15#define OR_TOOLS_MATH_OPT_SOLVERS_GLPK_SOLVER_H_
16
17#include <cstdint>
18#include <memory>
19#include <optional>
20#include <thread>
21#include <vector>
22
23#include "absl/container/flat_hash_map.h"
24#include "absl/status/status.h"
25#include "absl/status/statusor.h"
27#include "ortools/math_opt/callback.pb.h"
29#include "ortools/math_opt/infeasible_subsystem.pb.h"
30#include "ortools/math_opt/model.pb.h"
31#include "ortools/math_opt/model_parameters.pb.h"
32#include "ortools/math_opt/model_update.pb.h"
33#include "ortools/math_opt/parameters.pb.h"
34#include "ortools/math_opt/result.pb.h"
35#include "ortools/math_opt/solution.pb.h"
36#include "ortools/math_opt/sparse_containers.pb.h"
38
39extern "C" {
40#include <glpk.h>
41}
42
43namespace operations_research {
44namespace math_opt {
45
47 public:
48 static absl::StatusOr<std::unique_ptr<SolverInterface>> New(
49 const ModelProto& model, const InitArgs& init_args);
50
51 ~GlpkSolver() override;
52
53 absl::StatusOr<SolveResultProto> Solve(
54 const SolveParametersProto& parameters,
55 const ModelSolveParametersProto& model_parameters,
56 MessageCallback message_cb,
57 const CallbackRegistrationProto& callback_registration, Callback cb,
58 const SolveInterrupter* interrupter) override;
59 absl::StatusOr<bool> Update(const ModelUpdateProto& model_update) override;
60 absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
61 ComputeInfeasibleSubsystem(const SolveParametersProto& parameters,
62 MessageCallback message_cb,
63 const SolveInterrupter* interrupter) override;
64
65 private:
66 // The columns of the GPLK problem.
67 //
68 // This structures is intentionally similar to LinearConstraints so that some
69 // template function can accept either of those to share code between rows and
70 // columns. For this purpose it also defines some aliases to some GLPK
71 // functions and the IsInteger() (which is trivially implemented for
72 // LinearConstraints).
73 struct Variables {
74 static constexpr auto kSetBounds = glp_set_col_bnds;
75 static constexpr auto kGetLb = glp_get_col_lb;
76 static constexpr auto kGetUb = glp_get_col_ub;
77 static constexpr auto kGetType = glp_get_col_type;
78 static constexpr auto kDelElts = glp_del_cols;
79
80 // Returns true if the given one-based column is an integer variable.
81 static inline bool IsInteger(glp_prob* problem, int j);
82
83 // The MathOpt variable id of each column in GLPK. This is zero-based, the
84 // first column corresponds to the 0 and the ids.size() matches the number
85 // of columns.
86 //
87 // The id_to_index map can be used to get the GLPK column index of a given
88 // MathOpt variable id but the return value will be one-based (the
89 // convention used in GLPK). Thus this invariant holds:
90 //
91 // for all i in [0, num_cols), id_to_index.at(ids[i]) == i + 1
92 std::vector<int64_t> ids;
93
94 // Map each MathOpt variable id to the column one-based index in GLPK (thus
95 // values are in [1, num_cols]). See the ids vector for the counter part.
96 absl::flat_hash_map<int64_t, int> id_to_index;
97
98 // The unrounded lower bound value of each column.
99 //
100 // We keep this value since GLPK's glp_intopt() expects integer bounds for
101 // integer variables. We need the unrounded value when the type of a
102 // variable is changed to continuous though by an update.
103 std::vector<double> unrounded_lower_bounds;
104
105 // The unrounded upper bound value of each column.
106 //
107 // See unrounded_lower_bounds documentation for details..
108 std::vector<double> unrounded_upper_bounds;
109 };
110
111 // The columns of the GPLK problem.
112 //
113 // See the comment on Variables for details.
114 struct LinearConstraints {
115 static constexpr auto kSetBounds = glp_set_row_bnds;
116 static constexpr auto kGetLb = glp_get_row_lb;
117 static constexpr auto kGetUb = glp_get_row_ub;
118 static constexpr auto kGetType = glp_get_row_type;
119 static constexpr auto kDelElts = glp_del_rows;
120
121 // Returns false. This function mirrors Variables::IsInteger() and enables
122 // sharing code between variables and constraints.
123 static bool IsInteger(glp_prob*, int) { return false; }
124
125 // The MathOpt linear constraint id of each row in GLPK. This is zero-based,
126 // the first row corresponds to the 0 and ids.size() matches the number of
127 // rows.
128 //
129 // The id_to_index map can be used to get the GLPK row index of a given
130 // MathOpt variable id but the return value will be one-based (the
131 // convention used in GLPK). Thus this invariant holds:
132 //
133 // for all i in [0, num_rows), id_to_index.at(ids[i]) == i + 1
134 std::vector<int64_t> ids;
135
136 // Map each MathOpt linear constraint id to the row one-based index in GLPK
137 // (thus values are in [1, num_rows]). See the ids vector for the counter
138 // part.
139 absl::flat_hash_map<int64_t, int> id_to_index;
140 };
141
142 explicit GlpkSolver(const ModelProto& model);
143
144 // Appends the variables to GLPK cols.
145 void AddVariables(const VariablesProto& new_variables);
146
147 // Appends the variables to GLPK rows.
148 void AddLinearConstraints(
149 const LinearConstraintsProto& new_linear_constraints);
150
151 // Updates the objective coefficients with the new values in
152 // coefficients_proto.
153 void UpdateObjectiveCoefficients(
154 const SparseDoubleVectorProto& coefficients_proto);
155
156 // Updates the constraints matrix with the new values in matrix_updates.
157 //
158 // The first_new_(var|cstr)_id are the smallest ids of the new
159 // variables/constraints (in MathOpt the same id is never reused thus all
160 // variables with ids greater or equal to these values are new). A nullopt
161 // value means that there are not new variables/constraints.
162 void UpdateLinearConstraintMatrix(
163 const SparseDoubleMatrixProto& matrix_updates,
164 std::optional<int64_t> first_new_var_id,
165 std::optional<int64_t> first_new_cstr_id);
166
167 // Adds the primal solution (if it exists) to the result using the provided
168 // functions to get the status of the solution (GLP_FEAS, ...), its
169 // objective value and the structural variables values.
170 //
171 // Here `col_val` is a functions that takes a column index (i.e. the index of
172 // a structural variable) and returns its primal value in the solution.
173 void AddPrimalSolution(int (*get_prim_stat)(glp_prob*),
174 double (*obj_val)(glp_prob*),
175 double (*col_val)(glp_prob*, int),
176 const ModelSolveParametersProto& model_parameters,
177 SolutionProto& solution_proto);
178
179 // Adds the dual solution (if it exists) to the result. This function must
180 // only be called after having solved an LP, with the provided methods
181 // depending on the type of LP solved.
182 //
183 // Here `col_dual` is a function that takes a column index (i.e. the index of
184 // a structural variable) and returns its dual value in the solution. The
185 // `row_dual` does the same for a row index (i.e. the index of an auxiliary
186 // variable associated to a constraint).
187 void AddDualSolution(int (*get_dual_stat)(glp_prob*),
188 double (*obj_val)(glp_prob*),
189 double (*row_dual)(glp_prob*, int),
190 double (*col_dual)(glp_prob*, int),
191 const ModelSolveParametersProto& model_parameters,
192 SolutionProto& solution_proto);
193
194 // Adds a primal or dual ray to the result depending on the value returned by
195 // glp_get_unbnd_ray().
196 absl::Status AddPrimalOrDualRay(
197 const ModelSolveParametersProto& model_parameters,
198 SolveResultProto& result);
199
200 // Returns an error if the current thread is no thread_id_.
201 absl::Status CheckCurrentThread();
202
203 // Returns an "infeasible" result if the model has integer variables with
204 // empty bounds.
205 //
206 // Integer variables' bounds have to be rounded when passed to GLPK. Thus when
207 // the bounds don't contain an integer point (e.g. lb:3.5 ub:3.6) we end up
208 // with inverted bounds (e.g. lb:4 ub:3).
209 std::optional<SolveResultProto> EmptyIntegerBoundsResult();
210
211 // Id of the thread where GlpkSolver was called.
212 const std::thread::id thread_id_;
213
214 glp_prob* const problem_;
215
216 Variables variables_;
217 LinearConstraints linear_constraints_;
218};
219
221// Inline functions implementation.
223
224bool GlpkSolver::Variables::IsInteger(glp_prob* const problem, const int j) {
225 const int kind = glp_get_col_kind(problem, j);
226 switch (kind) {
227 case GLP_IV:
228 case GLP_BV:
229 // GLP_BV is returned when the GLPK internal kind is GLP_IV and the
230 // bounds are [0,1].
231 return true;
232 case GLP_CV:
233 return false;
234 default:
235 LOG(FATAL) << "Unexpected column kind: " << kind;
236 }
237}
238
239} // namespace math_opt
240} // namespace operations_research
241
242#endif // OR_TOOLS_MATH_OPT_SOLVERS_GLPK_SOLVER_H_
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, const SolveInterrupter *interrupter) override
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto &parameters, MessageCallback message_cb, const SolveInterrupter *interrupter) override
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
SatParameters parameters
GRBmodel * model
In SWIG mode, we don't want anything besides these top-level includes.