Sleipnir C++ API
Loading...
Searching...
No Matches
OptimizationProblem.hpp
Go to the documentation of this file.
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <array>
7#include <concepts>
8#include <functional>
9#include <iterator>
10#include <optional>
11#include <utility>
12
13#include <Eigen/Core>
14
27
28namespace sleipnir {
29
52 public:
56 OptimizationProblem() noexcept = default;
57
61 [[nodiscard]]
62 Variable DecisionVariable() {
63 m_decisionVariables.emplace_back();
64 return m_decisionVariables.back();
65 }
66
73 [[nodiscard]]
74 VariableMatrix DecisionVariable(int rows, int cols = 1) {
75 m_decisionVariables.reserve(m_decisionVariables.size() + rows * cols);
76
77 VariableMatrix vars{rows, cols};
78
79 for (int row = 0; row < rows; ++row) {
80 for (int col = 0; col < cols; ++col) {
81 m_decisionVariables.emplace_back();
82 vars(row, col) = m_decisionVariables.back();
83 }
84 }
85
86 return vars;
87 }
88
98 [[nodiscard]]
100 // We only need to store the lower triangle of an n x n symmetric matrix;
101 // the other elements are duplicates. The lower triangle has (n² + n)/2
102 // elements.
103 //
104 // n
105 // Σ k = (n² + n)/2
106 // k=1
107 m_decisionVariables.reserve(m_decisionVariables.size() +
108 (rows * rows + rows) / 2);
109
110 VariableMatrix vars{rows, rows};
111
112 for (int row = 0; row < rows; ++row) {
113 for (int col = 0; col <= row; ++col) {
114 m_decisionVariables.emplace_back();
115 vars(row, col) = m_decisionVariables.back();
116 vars(col, row) = m_decisionVariables.back();
117 }
118 }
119
120 return vars;
121 }
122
132 void Minimize(const Variable& cost) {
133 m_f = cost;
134 status.costFunctionType = m_f.value().Type();
135 }
136
146 void Minimize(Variable&& cost) {
147 m_f = std::move(cost);
148 status.costFunctionType = m_f.value().Type();
149 }
150
160 void Maximize(const Variable& objective) {
161 // Maximizing a cost function is the same as minimizing its negative
162 m_f = -objective;
163 status.costFunctionType = m_f.value().Type();
164 }
165
175 void Maximize(Variable&& objective) {
176 // Maximizing a cost function is the same as minimizing its negative
177 m_f = -std::move(objective);
178 status.costFunctionType = m_f.value().Type();
179 }
180
187 void SubjectTo(const EqualityConstraints& constraint) {
188 // Get the highest order equality constraint expression type
189 for (const auto& c : constraint.constraints) {
190 status.equalityConstraintType =
191 std::max(status.equalityConstraintType, c.Type());
192 }
193
194 m_equalityConstraints.reserve(m_equalityConstraints.size() +
195 constraint.constraints.size());
196 std::copy(constraint.constraints.begin(), constraint.constraints.end(),
197 std::back_inserter(m_equalityConstraints));
198 }
199
206 void SubjectTo(EqualityConstraints&& constraint) {
207 // Get the highest order equality constraint expression type
208 for (const auto& c : constraint.constraints) {
209 status.equalityConstraintType =
210 std::max(status.equalityConstraintType, c.Type());
211 }
212
213 m_equalityConstraints.reserve(m_equalityConstraints.size() +
214 constraint.constraints.size());
215 std::copy(constraint.constraints.begin(), constraint.constraints.end(),
216 std::back_inserter(m_equalityConstraints));
217 }
218
225 void SubjectTo(const InequalityConstraints& constraint) {
226 // Get the highest order inequality constraint expression type
227 for (const auto& c : constraint.constraints) {
228 status.inequalityConstraintType =
229 std::max(status.inequalityConstraintType, c.Type());
230 }
231
232 m_inequalityConstraints.reserve(m_inequalityConstraints.size() +
233 constraint.constraints.size());
234 std::copy(constraint.constraints.begin(), constraint.constraints.end(),
235 std::back_inserter(m_inequalityConstraints));
236 }
237
244 void SubjectTo(InequalityConstraints&& constraint) {
245 // Get the highest order inequality constraint expression type
246 for (const auto& c : constraint.constraints) {
247 status.inequalityConstraintType =
248 std::max(status.inequalityConstraintType, c.Type());
249 }
250
251 m_inequalityConstraints.reserve(m_inequalityConstraints.size() +
252 constraint.constraints.size());
253 std::copy(constraint.constraints.begin(), constraint.constraints.end(),
254 std::back_inserter(m_inequalityConstraints));
255 }
256
264 // Create the initial value column vector
265 Eigen::VectorXd x{m_decisionVariables.size()};
266 for (size_t i = 0; i < m_decisionVariables.size(); ++i) {
267 x(i) = m_decisionVariables[i].Value();
268 }
269
270 status.exitCondition = SolverExitCondition::kSuccess;
271
272 // If there's no cost function, make it zero and continue
273 if (!m_f.has_value()) {
274 m_f = Variable();
275 }
276
277 if (config.diagnostics) {
278 constexpr std::array kExprTypeToName{"empty", "constant", "linear",
279 "quadratic", "nonlinear"};
280
281 // Print cost function and constraint expression types
283 "The cost function is {}.",
284 kExprTypeToName[static_cast<int>(status.costFunctionType)]);
286 "The equality constraints are {}.",
287 kExprTypeToName[static_cast<int>(status.equalityConstraintType)]);
289 "The inequality constraints are {}.",
290 kExprTypeToName[static_cast<int>(status.inequalityConstraintType)]);
292
293 // Print problem dimensionality
294 sleipnir::println("Number of decision variables: {}",
295 m_decisionVariables.size());
296 sleipnir::println("Number of equality constraints: {}",
297 m_equalityConstraints.size());
298 sleipnir::println("Number of inequality constraints: {}\n",
299 m_inequalityConstraints.size());
300 }
301
302 // If the problem is empty or constant, there's nothing to do
303 if (status.costFunctionType <= ExpressionType::kConstant &&
304 status.equalityConstraintType <= ExpressionType::kConstant &&
305 status.inequalityConstraintType <= ExpressionType::kConstant) {
306 return status;
307 }
308
309 // Solve the optimization problem
310 if (m_equalityConstraints.empty() && m_inequalityConstraints.empty()) {
311 Newton(m_decisionVariables, m_f.value(), m_callback, config, x, &status);
312 } else if (m_inequalityConstraints.empty()) {
313 SQP(m_decisionVariables, m_equalityConstraints, m_f.value(), m_callback,
314 config, x, &status);
315 } else {
316 Eigen::VectorXd s = Eigen::VectorXd::Ones(m_inequalityConstraints.size());
317 InteriorPoint(m_decisionVariables, m_equalityConstraints,
318 m_inequalityConstraints, m_f.value(), m_callback, config,
319 false, x, s, &status);
320 }
321
322 if (config.diagnostics) {
323 sleipnir::println("Exit condition: {}", ToMessage(status.exitCondition));
324 }
325
326 // Assign the solution to the original Variable instances
327 VariableMatrix{m_decisionVariables}.SetValue(x);
328
329 return status;
330 }
331
339 template <typename F>
340 requires requires(F callback, const SolverIterationInfo& info) {
341 { callback(info) } -> std::same_as<void>;
342 }
343 void Callback(F&& callback) {
344 m_callback = [=, callback = std::forward<F>(callback)](
345 const SolverIterationInfo& info) {
346 callback(info);
347 return false;
348 };
349 }
350
359 template <typename F>
360 requires requires(F callback, const SolverIterationInfo& info) {
361 { callback(info) } -> std::same_as<bool>;
362 }
363 void Callback(F&& callback) {
364 m_callback = std::forward<F>(callback);
365 }
366
367 private:
368 // The list of decision variables, which are the root of the problem's
369 // expression tree
370 small_vector<Variable> m_decisionVariables;
371
372 // The cost function: f(x)
373 std::optional<Variable> m_f;
374
375 // The list of equality constraints: cₑ(x) = 0
376 small_vector<Variable> m_equalityConstraints;
377
378 // The list of inequality constraints: cᵢ(x) ≥ 0
379 small_vector<Variable> m_inequalityConstraints;
380
381 // The user callback
382 std::function<bool(const SolverIterationInfo& info)> m_callback =
383 [](const SolverIterationInfo&) { return false; };
384
385 // The solver status
386 SolverStatus status;
387};
388
389} // namespace sleipnir
#define SLEIPNIR_DLLEXPORT
Definition SymbolExports.hpp:34
Definition OptimizationProblem.hpp:51
void SubjectTo(InequalityConstraints &&constraint)
Definition OptimizationProblem.hpp:244
void Callback(F &&callback)
Definition OptimizationProblem.hpp:363
OptimizationProblem() noexcept=default
void SubjectTo(EqualityConstraints &&constraint)
Definition OptimizationProblem.hpp:206
void Callback(F &&callback)
Definition OptimizationProblem.hpp:343
VariableMatrix DecisionVariable(int rows, int cols=1)
Definition OptimizationProblem.hpp:74
SolverStatus Solve(const SolverConfig &config=SolverConfig{})
Definition OptimizationProblem.hpp:263
void SubjectTo(const EqualityConstraints &constraint)
Definition OptimizationProblem.hpp:187
void Minimize(const Variable &cost)
Definition OptimizationProblem.hpp:132
void Minimize(Variable &&cost)
Definition OptimizationProblem.hpp:146
void SubjectTo(const InequalityConstraints &constraint)
Definition OptimizationProblem.hpp:225
void Maximize(Variable &&objective)
Definition OptimizationProblem.hpp:175
void Maximize(const Variable &objective)
Definition OptimizationProblem.hpp:160
VariableMatrix SymmetricDecisionVariable(int rows)
Definition OptimizationProblem.hpp:99
Definition VariableMatrix.hpp:28
Definition Variable.hpp:33
ExpressionType Type() const
Definition Variable.hpp:219
Definition small_vector.hpp:3616
Definition Expression.hpp:18
SLEIPNIR_DLLEXPORT void SQP(std::span< Variable > decisionVariables, std::span< Variable > equalityConstraints, Variable &f, function_ref< bool(const SolverIterationInfo &info)> callback, const SolverConfig &config, Eigen::VectorXd &x, SolverStatus *status)
SLEIPNIR_DLLEXPORT void InteriorPoint(std::span< Variable > decisionVariables, std::span< Variable > equalityConstraints, std::span< Variable > inequalityConstraints, Variable &f, function_ref< bool(const SolverIterationInfo &info)> callback, const SolverConfig &config, bool feasibilityRestoration, Eigen::VectorXd &x, Eigen::VectorXd &s, SolverStatus *status)
SLEIPNIR_DLLEXPORT constexpr std::string_view ToMessage(const SolverExitCondition &exitCondition)
Definition SolverExitCondition.hpp:47
void println(std::format_string< T... > fmt, T &&... args)
Definition Print.hpp:38
SLEIPNIR_DLLEXPORT void Newton(std::span< Variable > decisionVariables, Variable &f, function_ref< bool(const SolverIterationInfo &info)> callback, const SolverConfig &config, Eigen::VectorXd &x, SolverStatus *status)
Definition Variable.hpp:547
small_vector< Variable > constraints
A vector of scalar equality constraints.
Definition Variable.hpp:549
Definition Variable.hpp:609
small_vector< Variable > constraints
A vector of scalar inequality constraints.
Definition Variable.hpp:611
Definition SolverConfig.hpp:15
Definition SolverIterationInfo.hpp:13
Definition SolverStatus.hpp:15
SolverExitCondition exitCondition
The solver's exit condition.
Definition SolverStatus.hpp:26