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