alpaqa develop
Nonconvex constrained optimization
Loading...
Searching...
No Matches
lqr.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <Eigen/Cholesky>
5#include <Eigen/LU>
6#include <cassert>
7
8namespace alpaqa {
9
10template <Config Conf>
11struct Dim {
14 struct Horizon {
16 struct Iter {
19 ++i;
20 return *this;
21 }
22 Iter operator++(int) const {
23 Iter t = *this;
24 ++i;
25 return t;
26 }
27 index_t &operator*() { return i; }
28 const index_t &operator*() const { return i; }
29 friend auto operator<=>(const Iter &, const Iter &) = default;
30 };
31 static Iter begin() { return {0}; }
32 Iter end() const { return {N}; }
33 };
34 Horizon horizon() const { return {N}; }
35};
36
37template <Config Conf>
40
42
58
59 void factor_masked(auto &&AB, ///< System matrix A & input matrix B
60 auto &&Q, ///< State cost matrix Q
61 auto &&R, ///< Input cost matrix R
62 auto &&S, ///< Cross cost matrix S
63 auto &&R_prod, ///< Product with input cost matrix R
64 auto &&S_prod, ///< Product with cross cost matrix S
65 auto &&q, ///< Linear state factor q
66 auto &&r, ///< Linear input factor r
67 auto &&u, ///< Fixed inputs u
68 auto &&J, ///< Index set of inactive constraints
69 auto &&K, ///< Index set of active constraints
70 bool use_cholesky ///< Use Cholesky instead of LU solver
71 ) {
72 using mmat = Eigen::Map<mat>;
73 using Eigen::indexing::all;
74 auto [N, nx, nu] = dim;
75
76 min_rcond = 1;
77 P.setZero();
78 Q(N)(P);
79 s = q(N);
80 for (index_t i = N; i-- > 0;) {
81 auto &&ABi = AB(i);
82 auto &&Ai = ABi.leftCols(nx);
83 auto &&Bi = ABi.rightCols(nu);
84 auto &&ui = u(i);
85 auto &&Ji = J(i);
86 auto &&Ki = K(i);
87 length_t nJ = Ji.size(); // number of inactive constraints
88 mmat {R̅_sto.data(), nJ, nJ};
89 mmat {S̅_sto.data(), nJ, nx};
90 mmat BiJ{BiJ_sto.data(), nx, nJ};
91 mmat PBiJ{PBiJ_sto.data(), nx, nJ};
92 auto &&ti = t.topRows(nJ);
93 mmat gain_Ki{gain_K.col(i).data(), nJ, nx};
94 auto &&ei = e.col(i).topRows(nJ);
95 // R̅ ← R + Bᵀ P B
96 BiJ.noalias() = Bi(all, Ji);
97 PBiJ.noalias() = P * BiJ;
98 .noalias() = BiJ.transpose() * PBiJ;
99 R(i)(Ji, );
100 // S̅ ← S + Bᵀ P A
101 PA.noalias() = P * Ai;
102 .noalias() = BiJ.transpose() * PA;
103 S(i)(Ji, );
104 // c = B(·,K) u(K), y ← P c + s
105 c.noalias() = Bi(all, Ki) * ui(Ki);
106 y.noalias() = P * c;
107 y += s;
108 // t ← Bᵀy + r + R(J,K) u(K)
109 ti.noalias() = BiJ.transpose() * y;
110 ti += r(i)(Ji);
111 R_prod(i)(Ji, Ki, ui, ti);
112 // Factor R̅
113 if (use_cholesky) {
114#ifdef EIGEN_RUNTIME_NO_MALLOC
115 bool prev = Eigen::internal::is_malloc_allowed();
116 Eigen::internal::set_is_malloc_allowed(true); // TODO
117#endif
118 Eigen::LDLT<rmat> R̅LU{};
119 min_rcond = std::min(R̅LU.rcond(), min_rcond);
120#ifdef EIGEN_RUNTIME_NO_MALLOC
121 Eigen::internal::set_is_malloc_allowed(prev);
122#endif
123 // K ← -R̅⁻¹S̅
124 gain_Ki.noalias() = R̅LU.solve();
125 // e ← -R̅⁻¹(Bᵀy + r)
126 ei.noalias() = R̅LU.solve(ti);
127 } else {
128#ifdef EIGEN_RUNTIME_NO_MALLOC
129 bool prev = Eigen::internal::is_malloc_allowed();
130 Eigen::internal::set_is_malloc_allowed(true); // TODO
131#endif
132 Eigen::PartialPivLU<rmat> R̅LU{};
133 min_rcond = std::min(R̅LU.rcond(), min_rcond);
134#ifdef EIGEN_RUNTIME_NO_MALLOC
135 Eigen::internal::set_is_malloc_allowed(prev);
136#endif
137 // K ← -R̅⁻¹S̅
138 gain_Ki.noalias() = R̅LU.solve();
139 // e ← -R̅⁻¹(Bᵀy + r)
140 ei.noalias() = R̅LU.solve(ti);
141 }
142 gain_Ki = -gain_Ki;
143 ei = -ei;
144 if (i > 0) {
145 // P ← Q + Aᵀ P A + S̅ᵀ K
146 P.noalias() = Ai.transpose() * PA;
147 P.noalias() += .transpose() * gain_Ki;
148 // s ← S̅ᵀ e + Aᵀ y + q + Sᵀ(·,K) u(K)
149 s.noalias() = .transpose() * ei;
150 s.noalias() += Ai.transpose() * y;
151 s += q(i);
152 S_prod(i)(Ki, ui, s);
153 Q(i)(P);
154 }
155 }
156 }
157
158 void solve_masked(auto &&AB, auto &&J, rvec Δu_eq, rvec Δx) {
159 auto [N, nx, nu] = dim;
160 assert(Δx.size() == 2 * nx);
161 Δx.topRows(nx).setZero();
162 for (index_t i = 0; i < N; ++i) {
163 auto &&ABi = AB(i);
164 auto &&Ai = ABi.leftCols(nx);
165 auto &&Bi = ABi.rightCols(nu);
166 auto &&Ji = J(i);
167 auto &&Δxi = Δx.segment((i % 2) * nx, nx);
168 auto &&Δx_next = Δx.segment(((i + 1) % 2) * nx, nx);
169 length_t nJ = Ji.size();
170 mmat Ki{gain_K.col(i).data(), nJ, nx};
171 auto &&ei = e.col(i).topRows(nJ);
172 auto &&Δui = Δu_eq.segment(i * nu, nu);
173 ei.noalias() += Ki * Δxi;
174 Δui(Ji).noalias() = ei;
175 Δx_next.noalias() = Ai * Δxi;
176 Δx_next.noalias() += Bi * Δui;
177 }
178 }
179};
180
181} // namespace alpaqa
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
typename Conf::mat mat
Definition config.hpp:93
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::index_t index_t
Definition config.hpp:104
typename Conf::mmat mmat
Definition config.hpp:94
typename Conf::length_t length_t
Definition config.hpp:103
constexpr const auto inf
Definition config.hpp:112
typename Conf::rvec rvec
Definition config.hpp:91
typename Conf::vec vec
Definition config.hpp:88
const index_t & operator*() const
Definition lqr.hpp:28
index_t & operator*()
Definition lqr.hpp:27
friend auto operator<=>(const Iter &, const Iter &)=default
Iter operator++(int) const
Definition lqr.hpp:22
static Iter begin()
Definition lqr.hpp:31
Iter end() const
Definition lqr.hpp:32
length_t nx
Definition lqr.hpp:13
length_t N
Definition lqr.hpp:13
length_t nu
Definition lqr.hpp:13
Horizon horizon() const
Definition lqr.hpp:34
void factor_masked(auto &&AB, auto &&Q, auto &&R, auto &&S, auto &&R_prod, auto &&S_prod, auto &&q, auto &&r, auto &&u, auto &&J, auto &&K, bool use_cholesky)
Definition lqr.hpp:59
void solve_masked(auto &&AB, auto &&J, rvec Δu_eq, rvec Δx)
Definition lqr.hpp:158