1#ifndef STAN_MATH_REV_FUN_SYLVESTER_SOLVER_HPP
2#define STAN_MATH_REV_FUN_SYLVESTER_SOLVER_HPP
9#include <Eigen/Eigenvalues>
36template <
typename MatA,
typename MatB,
typename MatC,
37 require_all_matrix_t<MatA, MatB, MatC>* =
nullptr,
38 require_any_bt_var<MatA, MatB, MatC>* =
nullptr>
40 using cdouble = std::complex<double>;
42 = Eigen::Matrix<std::complex<var>, Eigen::Dynamic, Eigen::Dynamic>;
50 if (
unlikely(A.rows() == 0 || B.rows() == 0)) {
51 return ret_type(A.rows(), B.rows());
54 auto arena_A =
to_arena(std::forward<MatA>(A));
55 auto arena_B =
to_arena(std::forward<MatB>(B));
56 auto arena_C =
to_arena(std::forward<MatC>(C));
58 const Eigen::Index m = B.rows();
63 auto arena_U =
to_arena(schur_A.matrixU());
64 auto arena_S =
to_arena(schur_A.matrixT());
68 auto arena_V =
to_arena(schur_B.matrixU());
69 auto arena_T =
to_arena(schur_B.matrixT());
72 auto Y = (arena_U.adjoint() *
value_of(arena_C) * arena_V).
eval();
74 for (Eigen::Index i = 0; i < m; ++i) {
75 Y.col(i).noalias() -= Y.leftCols(i) * arena_T.col(i).head(i);
76 Sshift.diagonal() = arena_S.diagonal().array() + arena_T(i, i);
77 Sshift.template triangularView<Eigen::Upper>().solveInPlace(Y.col(i));
79 arena_t<Eigen::MatrixXcd> arena_X = arena_U * Y * arena_V.adjoint();
81 auto res =
to_arena(arena_X.cast<std::complex<var>>());
85 arena_T, arena_X, res]()
mutable {
86 const Eigen::Index
rows = arena_S.rows();
87 const Eigen::Index
cols = arena_T.rows();
91 for (Eigen::Index c = 0; c <
cols; ++c) {
92 for (Eigen::Index r = 0; r <
rows; ++r) {
93 G(r, c) = cdouble(res(r, c).
real().adj(), res(r, c).
imag().adj());
99 auto W = (arena_U.adjoint() * G * arena_V).
eval();
100 auto Sshift_r = arena_S.eval();
101 for (Eigen::Index j =
cols - 1; j >= 0; --j) {
102 const Eigen::Index k =
cols - 1 - j;
103 W.col(j).noalias() -= W.rightCols(k) * arena_T.row(j).tail(k).adjoint();
104 Sshift_r.diagonal() = arena_S.diagonal().array() + arena_T(j, j);
105 Sshift_r.template triangularView<Eigen::Upper>().adjoint().solveInPlace(
108 const auto Z = (arena_U * W * arena_V.adjoint()).
eval();
112 auto accumulate = [](
auto& in,
const Eigen::MatrixXcd& g) {
113 using in_t = std::decay_t<
decltype(in)>;
114 if constexpr (is_complex<value_type_t<in_t>>::value) {
115 const Eigen::Index
rows = g.rows();
116 const Eigen::Index
cols = g.cols();
117 for (Eigen::Index c = 0; c <
cols; ++c) {
118 for (Eigen::Index r = 0; r <
rows; ++r) {
119 in(r, c).real().adj() += g(r, c).real();
120 in(r, c).imag().adj() += g(r, c).imag();
124 in.adj() += g.real();
128 if constexpr (is_var<base_type_t<MatA>>::value) {
129 accumulate(arena_A, -(Z * arena_X.adjoint()));
131 if constexpr (is_var<base_type_t<MatB>>::value) {
132 accumulate(arena_B, -(arena_X.adjoint() * Z));
134 if constexpr (is_var<base_type_t<MatC>>::value) {
135 accumulate(arena_C, Z);
138 return ret_type(res);
int64_t cols(const T_x &x)
Returns the number of columns in the specified kernel generator expression.
int64_t rows(const T_x &x)
Returns the number of rows in the specified kernel generator expression.
void check_square(const char *function, const char *name, const T_y &y)
Check if the specified matrix is square.
T real(const std::complex< T > &z)
Return the real component of the complex argument.
auto sylvester_solver(const MatA &A, const MatB &B, const MatC &C)
Solve the Sylvester equation A X + X B = C for X, forward mode.
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
T eval(T &&arg)
Inputs which have a plain_type equal to the own time are forwarded unmodified (for Eigen expressions ...
T value_of(const fvar< T > &v)
Return the value of the specified variable.
T imag(const std::complex< T > &z)
Return the imaginary component of the complex argument.
arena_t< T > to_arena(const T &a)
Converts given argument into a type that either has any dynamic allocation on AD stack or schedules i...
void check_size_match(const char *function, const char *name_i, T_size1 i, const char *name_j, T_size2 j)
Check if the provided sizes match.
typename plain_type< std::decay_t< T > >::type plain_type_t
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...