WPILibC++ 2027.0.0-alpha-2
Loading...
Searching...
No Matches
problem.hpp
Go to the documentation of this file.
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <concepts>
7#include <functional>
8#include <iterator>
9#include <optional>
10#include <ranges>
11#include <utility>
12
13#include <Eigen/Core>
14#include <gch/small_vector.hpp>
15
23
24namespace slp {
25
26/**
27 * This class allows the user to pose a constrained nonlinear optimization
28 * problem in natural mathematical notation and solve it.
29 *
30 * This class supports problems of the form:
31@verbatim
32 minₓ f(x)
33subject to cₑ(x) = 0
34 cᵢ(x) ≥ 0
35@endverbatim
36 *
37 * where f(x) is the scalar cost function, x is the vector of decision variables
38 * (variables the solver can tweak to minimize the cost function), cᵢ(x) are the
39 * inequality constraints, and cₑ(x) are the equality constraints. Constraints
40 * are equations or inequalities of the decision variables that constrain what
41 * values the solver is allowed to use when searching for an optimal solution.
42 *
43 * The nice thing about this class is users don't have to put their system in
44 * the form shown above manually; they can write it in natural mathematical form
45 * and it'll be converted for them.
46 */
48 public:
49 /**
50 * Construct the optimization problem.
51 */
52 Problem() noexcept = default;
53
54 /**
55 * Create a decision variable in the optimization problem.
56 *
57 * @return A decision variable in the optimization problem.
58 */
59 [[nodiscard]]
60 Variable decision_variable() {
61 m_decision_variables.emplace_back();
62 return m_decision_variables.back();
63 }
64
65 /**
66 * Create a matrix of decision variables in the optimization problem.
67 *
68 * @param rows Number of matrix rows.
69 * @param cols Number of matrix columns.
70 * @return A matrix of decision variables in the optimization problem.
71 */
72 [[nodiscard]]
73 VariableMatrix decision_variable(int rows, int cols = 1) {
74 m_decision_variables.reserve(m_decision_variables.size() + rows * cols);
75
76 VariableMatrix vars{rows, cols};
77
78 for (int row = 0; row < rows; ++row) {
79 for (int col = 0; col < cols; ++col) {
80 m_decision_variables.emplace_back();
81 vars(row, col) = m_decision_variables.back();
82 }
83 }
84
85 return vars;
86 }
87
88 /**
89 * Create a symmetric matrix of decision variables in the optimization
90 * problem.
91 *
92 * Variable instances are reused across the diagonal, which helps reduce
93 * problem dimensionality.
94 *
95 * @param rows Number of matrix rows.
96 * @return A symmetric matrix of decision varaibles in the optimization
97 * problem.
98 */
99 [[nodiscard]]
101 // We only need to store the lower triangle of an n x n symmetric matrix;
102 // the other elements are duplicates. The lower triangle has (n² + n)/2
103 // elements.
104 //
105 // n
106 // Σ k = (n² + n)/2
107 // k=1
108 m_decision_variables.reserve(m_decision_variables.size() +
109 (rows * rows + rows) / 2);
110
111 VariableMatrix vars{rows, rows};
112
113 for (int row = 0; row < rows; ++row) {
114 for (int col = 0; col <= row; ++col) {
115 m_decision_variables.emplace_back();
116 vars(row, col) = m_decision_variables.back();
117 vars(col, row) = m_decision_variables.back();
118 }
119 }
120
121 return vars;
122 }
123
124 /**
125 * Tells the solver to minimize the output of the given cost function.
126 *
127 * Note that this is optional. If only constraints are specified, the solver
128 * will find the closest solution to the initial conditions that's in the
129 * feasible set.
130 *
131 * @param cost The cost function to minimize.
132 */
133 void minimize(const Variable& cost) { m_f = cost; }
134
135 /**
136 * Tells the solver to minimize the output of the given cost function.
137 *
138 * Note that this is optional. If only constraints are specified, the solver
139 * will find the closest solution to the initial conditions that's in the
140 * feasible set.
141 *
142 * @param cost The cost function to minimize.
143 */
144 void minimize(Variable&& cost) { m_f = std::move(cost); }
145
146 /**
147 * Tells the solver to maximize the output of the given objective function.
148 *
149 * Note that this is optional. If only constraints are specified, the solver
150 * will find the closest solution to the initial conditions that's in the
151 * feasible set.
152 *
153 * @param objective The objective function to maximize.
154 */
155 void maximize(const Variable& objective) {
156 // Maximizing a cost function is the same as minimizing its negative
157 m_f = -objective;
158 }
159
160 /**
161 * Tells the solver to maximize the output of the given objective function.
162 *
163 * Note that this is optional. If only constraints are specified, the solver
164 * will find the closest solution to the initial conditions that's in the
165 * feasible set.
166 *
167 * @param objective The objective function to maximize.
168 */
169 void maximize(Variable&& objective) {
170 // Maximizing a cost function is the same as minimizing its negative
171 m_f = -std::move(objective);
172 }
173
174 /**
175 * Tells the solver to solve the problem while satisfying the given equality
176 * constraint.
177 *
178 * @param constraint The constraint to satisfy.
179 */
180 void subject_to(const EqualityConstraints& constraint) {
181 m_equality_constraints.reserve(m_equality_constraints.size() +
182 constraint.constraints.size());
183 std::ranges::copy(constraint.constraints,
184 std::back_inserter(m_equality_constraints));
185 }
186
187 /**
188 * Tells the solver to solve the problem while satisfying the given equality
189 * constraint.
190 *
191 * @param constraint The constraint to satisfy.
192 */
193 void subject_to(EqualityConstraints&& constraint) {
194 m_equality_constraints.reserve(m_equality_constraints.size() +
195 constraint.constraints.size());
196 std::ranges::copy(constraint.constraints,
197 std::back_inserter(m_equality_constraints));
198 }
199
200 /**
201 * Tells the solver to solve the problem while satisfying the given inequality
202 * constraint.
203 *
204 * @param constraint The constraint to satisfy.
205 */
206 void subject_to(const InequalityConstraints& constraint) {
207 m_inequality_constraints.reserve(m_inequality_constraints.size() +
208 constraint.constraints.size());
209 std::ranges::copy(constraint.constraints,
210 std::back_inserter(m_inequality_constraints));
211 }
212
213 /**
214 * Tells the solver to solve the problem while satisfying the given inequality
215 * constraint.
216 *
217 * @param constraint The constraint to satisfy.
218 */
220 m_inequality_constraints.reserve(m_inequality_constraints.size() +
221 constraint.constraints.size());
222 std::ranges::copy(constraint.constraints,
223 std::back_inserter(m_inequality_constraints));
224 }
225
226 /**
227 * Returns the cost function's type.
228 *
229 * @return The cost function's type.
230 */
232 if (m_f) {
233 return m_f.value().type();
234 } else {
235 return ExpressionType::NONE;
236 }
237 }
238
239 /**
240 * Returns the type of the highest order equality constraint.
241 *
242 * @return The type of the highest order equality constraint.
243 */
245 if (!m_equality_constraints.empty()) {
246 return std::ranges::max(m_equality_constraints, {}, &Variable::type)
247 .type();
248 } else {
249 return ExpressionType::NONE;
250 }
251 }
252
253 /**
254 * Returns the type of the highest order inequality constraint.
255 *
256 * @return The type of the highest order inequality constraint.
257 */
259 if (!m_inequality_constraints.empty()) {
260 return std::ranges::max(m_inequality_constraints, {}, &Variable::type)
261 .type();
262 } else {
263 return ExpressionType::NONE;
264 }
265 }
266
267 /**
268 * Solve the optimization problem. The solution will be stored in the original
269 * variables used to construct the problem.
270 *
271 * @param options Solver options.
272 * @param spy Enables writing sparsity patterns of H, Aₑ, and Aᵢ to files
273 * named H.spy, A_e.spy, and A_i.spy respectively during solve. Use
274 * tools/spy.py to plot them.
275 * @return The solver status.
276 */
277 ExitStatus solve(const Options& options = Options{},
278 [[maybe_unused]] bool spy = false);
279
280 /**
281 * Adds a callback to be called at the beginning of each solver iteration.
282 *
283 * The callback for this overload should return void.
284 *
285 * @param callback The callback.
286 */
287 template <typename F>
288 requires requires(F callback, const IterationInfo& info) {
289 { callback(info) } -> std::same_as<void>;
290 }
291 void add_callback(F&& callback) {
292 m_iteration_callbacks.emplace_back(
293 [=, callback = std::forward<F>(callback)](const IterationInfo& info) {
294 callback(info);
295 return false;
296 });
297 }
298
299 /**
300 * Adds a callback to be called at the beginning of each solver iteration.
301 *
302 * The callback for this overload should return bool.
303 *
304 * @param callback The callback. Returning true from the callback causes the
305 * solver to exit early with the solution it has so far.
306 */
307 template <typename F>
308 requires requires(F callback, const IterationInfo& info) {
309 { callback(info) } -> std::same_as<bool>;
310 }
311 void add_callback(F&& callback) {
312 m_iteration_callbacks.emplace_back(std::forward<F>(callback));
313 }
314
315 /**
316 * Clears the registered callbacks.
317 */
318 void clear_callbacks() { m_iteration_callbacks.clear(); }
319
320 private:
321 // The list of decision variables, which are the root of the problem's
322 // expression tree
323 gch::small_vector<Variable> m_decision_variables;
324
325 // The cost function: f(x)
326 std::optional<Variable> m_f;
327
328 // The list of equality constraints: cₑ(x) = 0
329 gch::small_vector<Variable> m_equality_constraints;
330
331 // The list of inequality constraints: cᵢ(x) ≥ 0
332 gch::small_vector<Variable> m_inequality_constraints;
333
334 // The iteration callbacks
335 gch::small_vector<std::function<bool(const IterationInfo& info)>>
336 m_iteration_callbacks;
337
338 void print_exit_conditions([[maybe_unused]] const Options& options);
339 void print_problem_analysis();
340};
341
342} // namespace slp
This class allows the user to pose a constrained nonlinear optimization problem in natural mathematic...
Definition problem.hpp:47
void clear_callbacks()
Clears the registered callbacks.
Definition problem.hpp:318
void subject_to(const EqualityConstraints &constraint)
Tells the solver to solve the problem while satisfying the given equality constraint.
Definition problem.hpp:180
VariableMatrix decision_variable(int rows, int cols=1)
Create a matrix of decision variables in the optimization problem.
Definition problem.hpp:73
Problem() noexcept=default
Construct the optimization problem.
void maximize(Variable &&objective)
Tells the solver to maximize the output of the given objective function.
Definition problem.hpp:169
ExitStatus solve(const Options &options=Options{}, bool spy=false)
Solve the optimization problem.
ExpressionType equality_constraint_type() const
Returns the type of the highest order equality constraint.
Definition problem.hpp:244
void subject_to(InequalityConstraints &&constraint)
Tells the solver to solve the problem while satisfying the given inequality constraint.
Definition problem.hpp:219
void minimize(const Variable &cost)
Tells the solver to minimize the output of the given cost function.
Definition problem.hpp:133
VariableMatrix symmetric_decision_variable(int rows)
Create a symmetric matrix of decision variables in the optimization problem.
Definition problem.hpp:100
void subject_to(EqualityConstraints &&constraint)
Tells the solver to solve the problem while satisfying the given equality constraint.
Definition problem.hpp:193
void maximize(const Variable &objective)
Tells the solver to maximize the output of the given objective function.
Definition problem.hpp:155
void add_callback(F &&callback)
Adds a callback to be called at the beginning of each solver iteration.
Definition problem.hpp:291
ExpressionType cost_function_type() const
Returns the cost function's type.
Definition problem.hpp:231
void add_callback(F &&callback)
Adds a callback to be called at the beginning of each solver iteration.
Definition problem.hpp:311
void minimize(Variable &&cost)
Tells the solver to minimize the output of the given cost function.
Definition problem.hpp:144
void subject_to(const InequalityConstraints &constraint)
Tells the solver to solve the problem while satisfying the given inequality constraint.
Definition problem.hpp:206
ExpressionType inequality_constraint_type() const
Returns the type of the highest order inequality constraint.
Definition problem.hpp:258
An autodiff variable pointing to an expression node.
Definition variable.hpp:40
A matrix of autodiff variables.
Definition variable_matrix.hpp:29
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition SmallVector.h:1198
Definition expression_graph.hpp:11
ExpressionType
Expression type.
Definition expression_type.hpp:18
ExitStatus
Solver exit status.
Definition exit_status.hpp:16
A vector of equality constraints of the form cₑ(x) = 0.
Definition variable.hpp:538
gch::small_vector< Variable > constraints
A vector of scalar equality constraints.
Definition variable.hpp:540
A vector of inequality constraints of the form cᵢ(x) ≥ 0.
Definition variable.hpp:599
gch::small_vector< Variable > constraints
A vector of scalar inequality constraints.
Definition variable.hpp:601
Solver iteration information exposed to an iteration callback.
Definition iteration_info.hpp:13
Solver options.
Definition options.hpp:15
#define SLEIPNIR_DLLEXPORT
Definition symbol_exports.hpp:34