4#include <Eigen/Eigenvalues>
19 unsigned long product = 1;
20 for(; from < to; ++from){
30 constexpr T
pow(
const T& base,
const int& exp){
31 T result =
static_cast<T
>(1);
33 for(
int i = 0; i < exp; ++i){
37 for(
int i = 0; i < -exp; ++i){
54 template<
class T,
int N>
55 Eigen::Vector<T, N>
unwrap(
const Eigen::Vector<T, N>& y,
const T& modulo){
56 Eigen::Vector<T, N> result;
57 if constexpr (N == Eigen::Dynamic) result.resize(y.size());
60 for(
int i = 1; i < y.size(); ++i){
61 while(((y(i) + offset) - result(i-1)) > (modulo/2)){
64 while(((y(i) + offset) - result(i-1)) < (-modulo/2)){
67 result(i) = y(i) + offset;
79 template<
class T,
int N>
81 const T modulo = std::numbers::pi_v<T> *
static_cast<T
>(2);
92 template<
class T,
int N>
114 template<
class T =
double>
116 const std::int64_t num = (m - k);
117 const std::int64_t den = (k + 1) * (m + n - k);
118 const double f =
static_cast<T
>(num) /
static_cast<T
>(den);
130 template<
class T =
double>
131 constexpr void pade_params(T* params, std::size_t size, std::uint32_t m, std::uint32_t n){
133 for(std::size_t k = 1; k < size; ++k){
164 template<
class T =
double>
167 for(std::int32_t i = 0; i < k; ++i){
199 template<
class T =
double>
204 template<
class T,
int Rows,
int Cols,
int Options,
int MaxRows,
int MaxCols>
205 Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>
identity_like([[maybe_unused]]
const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& unused){
206 if constexpr (Rows != Eigen::Dynamic && Cols != Eigen::Dynamic){
207 return Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>::Identity();
209 Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> result;
210 result.setIdentity();
215 template<
class T,
int LSize,
int RSize>
216 Eigen::Matrix<T, LSize + RSize, LSize + RSize>
join_to_diagonal(
const Eigen::Vector<T, LSize>& l,
const Eigen::Vector<T, RSize>& r){
217 Eigen::Matrix<T, LSize + RSize, LSize + RSize> result;
218 result.diagonal().head(LSize) = l;
219 result.diagonal().tail(RSize) = r;
223 template<
class T,
int LSize,
int RSize>
224 requires(LSize != Eigen::Dynamic && RSize != Eigen::Dynamic)
225 Eigen::Vector<T, LSize + RSize>
join_to_vector(
const Eigen::Vector<T, LSize>& l,
const Eigen::Vector<T, RSize>& r){
226 Eigen::Vector<T, LSize + RSize> result;
227 result.head(LSize) = l;
228 result.tail(RSize) = r;
232 template<
class T,
int LSize,
int RSize>
233 requires(!(LSize != Eigen::Dynamic && RSize != Eigen::Dynamic))
235 Eigen::Vector<T, Eigen::Dynamic> result(l.size() + r.size());
236 result.head(l.size()) = l;
237 result.tail(r.size()) = r;
247 template<
class T,
int Rows,
int Cols,
int Options,
int MaxRows,
int MaxCols>
248 Eigen::Matrix<T, Rows-1, Cols-1>
minor(
const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& A,
size_t ex_row,
size_t ex_col){
249 Eigen::Matrix<T, Rows-1, Cols-1> result;
252 for(
size_t result_row = 0; result_row < (Rows-1); ++result_row, (void)++A_row){
253 for(
size_t result_col = 0; result_col < (Cols-1); ++result_col, (void)++A_col){
254 A_row += (A_row == ex_row) ? 1 : 0;
255 A_col += (A_col == ex_col) ? 1 : 0;
256 result(result_row, result_col) = A(A_row, A_col);
262 template<
class T,
int Rows,
int Cols,
int Options,
int MaxRows,
int MaxCols>
263 Eigen::Matrix<T, Rows, Cols>
adj(
const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& A){
264 Eigen::Matrix<T, Rows, Cols> result;
265 for(
int row = 0; row < Rows; ++row){
266 const bool row_sign = ((row & 1) == 1);
267 for(
int col = 0; col < Cols; ++col){
268 const bool col_sign = ((col & 1) == 1);
269 const bool value_sign = row_sign != col_sign;
270 const auto min =
minor(A, row, col);
271 const T det = min.determinant();
274 result(col, row) = -det;
277 result(col, row) = det;
313 template<
class T,
int N>
314 Eigen::Matrix<T, N-1, N-1>
companion(
const Eigen::Vector<T, N>& v){
315 Eigen::Matrix<T, N-1, N-1> result;
316 result.template block<N-2, N-2>(0, 1) = Eigen::Matrix<T, N-2, N-2>::Identity();
317 result.col(0).setZero();
318 result.row(N-2) = -v.head(N-1) / v(N-1);
341 template<
class T,
int N>
344 const Eigen::Matrix<T, N, N> H
347 Eigen::ComplexEigenSolver<Eigen::Matrix<T, N, N>> ces;
349 const auto& H_eigvals = ces.eigenvalues();
350 const auto& H_eigvecs = ces.eigenvectors();
353 Eigen::Matrix<std::complex<T>, N, N/2> StableEigenVecs;
356 for(; (si < N/2) && (ei < N); ++ei){
357 if(H_eigvals(ei).real() <= 0){
358 StableEigenVecs.col(si) = H_eigvecs.col(ei);
364 const auto TopPartition = StableEigenVecs.template block<N/2, N/2>(0, 0);
365 const auto BottomPartition = StableEigenVecs.template block<N/2, N/2>(N/2, 0);
368 const Eigen::Matrix<std::complex<T>, N/2, N/2> X = TopPartition.transpose().partialPivLu().solve(BottomPartition.transpose()).transpose();
371 const Eigen::Matrix<T, N/2, N/2> realX = X.real();
374 const Eigen::Matrix<T, N/2, N/2> result =
static_cast<T
>(0.5) * (realX + realX.transpose());
397 template<
class T,
int N>
399 const Eigen::Matrix<T, N, N> S
402 Eigen::ComplexEigenSolver<Eigen::Matrix<T, N, N>> ces;
404 const auto& eigvals = ces.eigenvalues();
405 const auto& eigvecs = ces.eigenvectors();
408 Eigen::Matrix<std::complex<T>, N, N/2> StableEigenVecs;
409 StableEigenVecs.setZero();
412 for(; (si < N/2) && (ei < N); ++ei){
413 if(std::abs(eigvals(ei)) <= 1){
414 StableEigenVecs.col(si) = eigvecs.col(ei);
420 const auto TopPartition = StableEigenVecs.template block<N/2, N/2>(0, 0);
421 const auto BottomPartition = StableEigenVecs.template block<N/2, N/2>(N/2, 0);
424 const Eigen::Matrix<std::complex<T>, N/2, N/2> X = TopPartition.transpose().partialPivLu().solve(BottomPartition.transpose()).transpose();
427 const Eigen::Matrix<T, N/2, N/2> realX = X.real();
430 const Eigen::Matrix<T, N/2, N/2> result =
static_cast<T
>(0.5) * (realX + realX.transpose());
465 template<
class T,
int NStates,
466 int AOpt,
int AMaxR,
int AMaxC,
467 int QOpt,
int QMaxR,
int QMaxC
470 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
471 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q
474 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
475 H.topLeftCorner(NStates, NStates) = A;
476 H.topRightCorner(NStates, NStates).setZero();
477 H.bottomLeftCorner(NStates, NStates) = - Q;
478 H.bottomRightCorner(NStates, NStates) = -A.transpose();
506 template<
class ValueType,
int NStates,
int NInputs,
int NOutputs>
507 std::tuple<Eigen::Matrix<ValueType, NStates, NStates>, Eigen::Matrix<ValueType, NStates, NStates>>
energy_scaling_matrix(
508 const Eigen::Matrix<ValueType, NStates, NStates> A,
509 const Eigen::Matrix<ValueType, NStates, NInputs> B,
510 const Eigen::Matrix<ValueType, NOutputs, NStates> C
513 const Eigen::Matrix<ValueType, NStates, NStates> Bsqr = B * B.transpose();
514 const Eigen::Matrix<ValueType, NStates, NStates> Wc =
lyapunov_solver(A.transpose(), Bsqr);
516 const Eigen::Matrix<ValueType, NStates, NStates> Csqr = C.transpose() * C;
517 const Eigen::Matrix<ValueType, NStates, NStates> Wo =
lyapunov_solver(A, Csqr);
520 const ValueType eps = std::numeric_limits<ValueType>::epsilon();
522 Eigen::Matrix<ValueType, NStates, NStates> Wc_sym = (Wc + Wc.transpose()) *
static_cast<ValueType
>(0.5);
524 ValueType jitter = std::max(ValueType(1), Wc_sym.diagonal().cwiseAbs().maxCoeff());
525 Wc_sym.diagonal().array() += jitter * eps;
527 Eigen::Matrix<ValueType, NStates, NStates> Wo_sym = (Wo + Wo.transpose()) *
static_cast<ValueType
>(0.5);
529 ValueType jitter = std::max(ValueType(1), Wo_sym.diagonal().cwiseAbs().maxCoeff());
530 Wo_sym.diagonal().array() += jitter * eps;
534 const Eigen::LLT<Eigen::Matrix<ValueType, NStates, NStates>> Wc_colesky(Wc_sym);
535 const Eigen::LLT<Eigen::Matrix<ValueType, NStates, NStates>> Wo_colesky(Wo_sym);
538 const Eigen::Matrix<ValueType, NStates, NStates> Rc = Wc_colesky.matrixU();
539 const Eigen::Matrix<ValueType, NStates, NStates> Ro = Wo_colesky.matrixL();
542 const Eigen::Matrix<ValueType, NStates, NStates> M = Ro * Rc;
543 const Eigen::JacobiSVD<Eigen::Matrix<ValueType, NStates, NStates>> svd(M, Eigen::ComputeThinU | Eigen::ComputeThinV);
544 const Eigen::Matrix<ValueType, NStates, NStates> U = svd.matrixU();
545 const Eigen::Matrix<ValueType, NStates, 1> s = svd.singularValues();
546 const Eigen::Matrix<ValueType, NStates, NStates> V = svd.matrixV();
549 const Eigen::Matrix<ValueType, NStates, 1> s_sqrt = (s.array().max(eps)).sqrt().matrix();
550 const Eigen::Matrix<ValueType, NStates, NStates> S_sqrt = s_sqrt.asDiagonal();
553 const Eigen::Matrix<ValueType, NStates, NStates> T = Rc.transpose().template triangularView<Eigen::Lower>().solve((S_sqrt * V.transpose()).transpose()).transpose();
554 const Eigen::Matrix<ValueType, NStates, NStates> T_inverse = Ro.template triangularView<Eigen::Lower>().solve(U * S_sqrt);
556 return {T, T_inverse};
602 template<
class T,
int NStates,
int NInputs,
603 int AOpt,
int AMaxR,
int AMaxC,
604 int BOpt,
int BMaxR,
int BMaxC,
605 int ROpt,
int RMaxR,
int RMaxC,
606 int QOpt,
int QMaxR,
int QMaxC
609 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
610 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
611 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
612 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R
614 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
615 H.topLeftCorner(NStates, NStates) = A;
616 H.topRightCorner(NStates, NStates) = -B * R.ldlt().solve(B.transpose());
617 H.bottomLeftCorner(NStates, NStates) = -Q;
618 H.bottomRightCorner(NStates, NStates) = -A;
662 template<
class T,
int NStates,
int NInputs,
663 int AOpt,
int AMaxR,
int AMaxC,
664 int BOpt,
int BMaxR,
int BMaxC,
665 int ROpt,
int RMaxR,
int RMaxC,
666 int QOpt,
int QMaxR,
int QMaxC
669 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
670 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
671 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
672 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R
674 const Eigen::Matrix<T, NStates, NStates> brb = B * R.llt().solve(B.transpose());
675 Eigen::ColPivHouseholderQR<Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>> At_qr(A.transpose());
676 const Eigen::Matrix<T, NStates, NStates> aq = At_qr.solve(Q);
677 Eigen::Matrix<T, 2*NStates, 2*NStates> S;
678 S.topLeftCorner(NStates, NStates) = A + brb * aq;
679 S.topRightCorner(NStates, NStates) = -A.colPivHouseholderQr().solve(brb.transpose()).transpose();
680 S.bottomLeftCorner(NStates, NStates) = - aq;
681 S.bottomRightCorner(NStates, NStates) = At_qr.solve(
identity_like(A));
727 template<
class T,
int NStates,
int NInputs,
728 int AOpt,
int AMaxR,
int AMaxC,
729 int BOpt,
int BMaxR,
int BMaxC,
730 int ROpt,
int RMaxR,
int RMaxC,
731 int QOpt,
int QMaxR,
int QMaxC,
732 int NOpt,
int NMaxR,
int NMaxC
735 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
736 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
737 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
738 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R,
739 const Eigen::Matrix<T, NStates, NInputs, NOpt, NMaxR, NMaxC>& N
742 const Eigen::Matrix<T, NInputs, NStates> R_inv_DT_C = R.ldlt().solve((N.transpose()));
743 const Eigen::Matrix<T, NStates, NStates> A_ = A - B * R_inv_DT_C;
745 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
746 H.topLeftCorner(NStates, NStates) = A_;
747 H.topRightCorner(NStates, NStates) = - B * R.ldlt().solve(B.transpose());
748 H.bottomLeftCorner(NStates, NStates) = - (Q - N * R_inv_DT_C);
749 H.bottomRightCorner(NStates, NStates) = -A_.transpose();
Eigen::Vector< T, Eigen::Dynamic > phases(const Bode< T > &bode)
Creates a vector of phases in rad.
Definition Bode.hpp:572
Definition Polynom.hpp:1012
The main namespace for the Control++ library.
Definition Bode.cpp:3
constexpr T product_over(T from, T to)
Calculates the product of all numbers in the closed open range [from, to)
Definition math.hpp:18
std::tuple< Eigen::Matrix< ValueType, NStates, NStates >, Eigen::Matrix< ValueType, NStates, NStates > > energy_scaling_matrix(const Eigen::Matrix< ValueType, NStates, NStates > A, const Eigen::Matrix< ValueType, NStates, NInputs > B, const Eigen::Matrix< ValueType, NOutputs, NStates > C)
Calculates the energy (Gramian) scaling via Lyapunov equations.
Definition math.hpp:507
Eigen::Matrix< T, N/2, N/2 > hamilton_solver(const Eigen::Matrix< T, N, N > H)
Solves the hamilton matrix.
Definition math.hpp:343
constexpr T pade_den_param(unsigned long m, unsigned long n, unsigned long k)
Calculates the numerator parameters of the pade approximation.
Definition math.hpp:200
Eigen::Matrix< T, LSize+RSize, LSize+RSize > join_to_diagonal(const Eigen::Vector< T, LSize > &l, const Eigen::Vector< T, RSize > &r)
Definition math.hpp:216
constexpr T pade_param_reccursice(T P_k, std::int32_t m, std::int32_t n, std::int32_t k)
Calculates the next pade parameter recursively.
Definition math.hpp:115
Eigen::Matrix< T, Rows, Cols > adj(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &A)
Definition math.hpp:263
Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > identity_like(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &unused)
Definition math.hpp:205
constexpr T pade_num_param(std::int32_t m, std::int32_t n, std::int32_t k)
Calculates the numerator parameters of the pade approximation.
Definition math.hpp:165
Eigen::Vector< T, N > unwrap(const Eigen::Vector< T, N > &y, const T &modulo)
Unwinds modulo jumps.
Definition math.hpp:55
Eigen::Matrix< T, N/2, N/2 > symplectic_solver(const Eigen::Matrix< T, N, N > S)
Solves the hamilton matrix.
Definition math.hpp:398
Eigen::Vector< T, N > unwrap_deg(const Eigen::Vector< T, N > &phases)
Unwinds phase jumps of in degrees.
Definition math.hpp:93
Eigen::Vector< T, LSize+RSize > join_to_vector(const Eigen::Vector< T, LSize > &l, const Eigen::Vector< T, RSize > &r)
Definition math.hpp:225
Eigen::Matrix< T, NStates, NStates > dare_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NInputs, BOpt, BMaxR, BMaxC > &B, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q, const Eigen::Matrix< T, NInputs, NInputs, ROpt, RMaxR, RMaxC > &R)
Solves the discrete time riccati equation (DARE)
Definition math.hpp:668
constexpr T pow(const T &base, const int &exp)
Power function for integral exponents.
Definition math.hpp:30
constexpr void pade_params(T *params, std::size_t size, std::uint32_t m, std::uint32_t n)
Calculates all pade parameters up to the given size.
Definition math.hpp:131
Eigen::Matrix< T, Rows-1, Cols-1 > minor(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &A, size_t ex_row, size_t ex_col)
returns the minor matrix excluding the provided column and row
Definition math.hpp:248
Eigen::Matrix< T, NStates, NStates > lyapunov_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q)
Solves the continuous time Lyapunov equation.
Definition math.hpp:469
Eigen::Matrix< T, NStates, NStates > care_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NInputs, BOpt, BMaxR, BMaxC > &B, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q, const Eigen::Matrix< T, NInputs, NInputs, ROpt, RMaxR, RMaxC > &R)
Solves the continuous time riccati equation (CARE)
Definition math.hpp:608
Eigen::Matrix< T, N-1, N-1 > companion(const Eigen::Vector< T, N > &v)
Creates a companion matrix from a vector.
Definition math.hpp:314
Eigen::Vector< T, N > unwrap_rad(const Eigen::Vector< T, N > &phases)
Unwinds phase jumps of in radiants.
Definition math.hpp:80