1#ifndef INCLUDE_RICCATI_CHEBYSHEV_HPP
2#define INCLUDE_RICCATI_CHEBYSHEV_HPP
7#include <unsupported/Eigen/FFT>
12template <
bool Fwd,
typename T>
14 using Scalar =
typename std::decay_t<T>::Scalar;
15 Eigen::FFT<Scalar>
fft;
16 using T_t = std::decay_t<T>;
17 typename T_t::PlainObject res(x.rows(), x.cols());
18 for (Eigen::Index j = 0; j < x.cols(); ++j) {
20 res.col(j) =
fft.fwd(Eigen::Matrix<std::complex<Scalar>, -1, 1>(x.col(j)))
23 res.col(j) =
fft.inv(Eigen::Matrix<std::complex<Scalar>, -1, 1>(x.col(j)))
57template <
typename Mat>
59 using Scalar =
typename std::decay_t<Mat>::Scalar;
60 const auto n = coeffs.rows();
65 Mat_t fwd_values(n + n - 2, coeffs.cols());
66 fwd_values.topRows(n) = coeffs;
67 fwd_values.block(1, 0, n - 2, n) /= 2.0;
68 fwd_values.bottomRows(n - 1)
69 = fwd_values.topRows(n - 1).rowwise().reverse();
104template <
typename Mat>
106 using Scalar =
typename std::decay_t<Mat>::Scalar;
108 const auto n = values.rows();
110 return Mat_t(Mat_t::Zero(values.rows(), values.cols()));
112 Mat_t rev_values(n + n - 2, values.cols());
113 rev_values.topRows(n) = values;
114 rev_values.bottomRows(n - 1)
115 = rev_values.topRows(n - 1).rowwise().reverse();
117 rev_ret.block(1, 0, n - 2, n).array() *= 2.0;
131template <
typename Mat>
133 using Scalar =
typename std::decay_t<Mat>::Scalar;
135 const auto n = values.rows();
137 return std::make_pair(Mat_t(values),
138 Mat_t(Mat_t::Zero(values.rows(), values.cols())));
140 Mat_t fwd_values(n + n - 2, values.cols());
141 fwd_values.topRows(n) = values;
142 fwd_values.block(1, 0, n - 2, n) /= 2.0;
143 fwd_values.bottomRows(n - 1)
144 = fwd_values.topRows(n - 1).rowwise().reverse();
146 Mat_t rev_values(n + n - 2, values.cols());
147 rev_values.topRows(n) = values;
148 rev_values.bottomRows(n - 1)
149 = rev_values.topRows(n - 1).rowwise().reverse();
151 rev_ret.block(1, 0, n - 2, n).array() *= 2.0;
152 return std::make_pair(fwd_val, rev_ret);
177template <
typename Scalar,
typename Integral>
181 auto&& T = coeffs_pair.first;
182 auto&& T_inverse = coeffs_pair.second;
185 auto k2 =
eval(2 * (k.array() - 1));
186 k2.coeffRef(0) = 1.0;
189 B.diagonal(-1).array() = 1.0 / (2.0 * k).
array();
190 B.diagonal(1).array() = -1.0 / k2.array();
192 for (Integral i = 1; i < n; i += 2) {
195 auto tmp = (v.asDiagonal() * B.block(1, 0, n, n + 1)).eval();
196 B.row(0) = (tmp).colwise().sum();
199 Q.bottomRows(1).setZero();
224template <
typename Scalar,
typename Integral>
234 w.coeffRef(0) = 1.0 /
static_cast<Scalar
>(n * n - 1);
235 w.coeffRef(n) = w.coeff(0);
236 for (Integral i = 1; i < n / 2; ++i) {
237 v.array() -= 2.0 * (2.0 * i * a.segment(1, n - 1).
array()).cos()
240 v.array() -= (n * a.segment(1, n - 1).
array()).cos() / (n * n - 1);
242 w.coeffRef(0) = 1.0 /
static_cast<Scalar
>(n * n);
243 w.coeffRef(n) = w.coeff(0);
244 for (std::size_t i = 0; i < std::floor((n + 1) / 2.0); ++i) {
245 v.array() -= 2.0 * (2.0 * i * a.segment(1, n - 1).
array()).cos()
248 v.array() -= (n * a.segment(1, n - 1).
array()).cos() / (n * n - 1);
250 w.segment(1, n - 1).array() = (2.0 * v.array()) / n;
277template <
typename Scalar,
typename Integral>
286 b.coeffRef(b.size() - 1) = 2.0;
288 for (Integral i = 1; i < n + 1; i += 2) {
291 auto x = a.array().cos().eval();
295 auto dX = (X - X.transpose()).eval();
296 auto c = (b.array() * d.array()).eval();
298 auto D = ((c.matrix() * (1.0 / c).
matrix().transpose()).
array()
302 D -= D.rowwise().sum().asDiagonal();
331template <
typename Vec1,
typename Vec2,
typename Allocator>
333 const auto r = s.size();
334 const auto q = t.size();
336 =
eval(alloc,
matrix_t<
typename std::decay_t<Vec1>::Scalar>::Ones(r, r));
338 =
eval(alloc,
matrix_t<
typename std::decay_t<Vec1>::Scalar>::Ones(q, r));
339 for (std::size_t i = 1; i < static_cast<std::size_t>(r); ++i) {
340 V.col(i).array() = V.col(i - 1).array() * s.array();
341 R.col(i).array() = R.col(i - 1).array() * t.array();
350 return V.transpose().partialPivLu().solve(R.transpose()).transpose().eval();
391template <
typename SolverInfo,
typename Scalar,
typename YScalar,
394 Scalar h, YScalar y0,
395 YScalar dy0, Integral niter) {
396 using complex_t = std::complex<Scalar>;
398 auto x_scaled =
eval(
399 info.alloc_,
riccati::scale(std::get<2>(info.chebyshev_[niter]), x0, h));
400 auto&& D = info.Dn(niter);
401 auto ws =
omega(info, x_scaled);
402 auto gs =
gamma(info, x_scaled);
403 auto D2 =
eval(info.alloc_,
404 (4.0 / (h * h) * (D * D) + 4.0 / h * (gs.asDiagonal() * D)));
405 D2 += (ws.array().
square()).matrix().asDiagonal();
406 const auto n = std::round(std::get<0>(info.chebyshev_[niter]));
408 D2ic.topRows(n + 1) = D2;
409 D2ic.row(n + 1) = 2.0 / h * D.row(D.rows() - 1);
410 auto ic =
eval(info.alloc_, vectorc_t::Zero(n + 1));
411 ic.coeffRef(n) = complex_t{1.0, 0.0};
412 D2ic.row(n + 2) = ic;
413 auto rhs =
eval(info.alloc_, vectorc_t::Zero(n + 3));
414 rhs.coeffRef(n + 1) = dy0;
415 rhs.coeffRef(n + 2) = y0;
416 auto y1 =
eval(info.alloc_, D2ic.colPivHouseholderQr().solve(rhs));
417 auto dy1 =
eval(info.alloc_, 2.0 / h * (D * y1));
418 return std::make_tuple(std::move(y1), std::move(dy1), std::move(x_scaled));
#define RICCATI_ALWAYS_INLINE
RICCATI_ALWAYS_INLINE auto fft(T &&x)
RICCATI_ALWAYS_INLINE auto coeffs_to_cheby_nodes(Mat &&coeffs)
Convert the Chebyshev coefficient representation of a set of polynomials P_j to their values at Cheby...
auto eval(arena_allocator< T, arena_alloc > &arena, const Expr &expr) noexcept
RICCATI_ALWAYS_INLINE auto spectral_chebyshev(SolverInfo &&info, Scalar x0, Scalar h, YScalar y0, YScalar dy0, Integral niter)
Applies a spectral collocation method based on Chebyshev nodes for solving differential equations.
RICCATI_ALWAYS_INLINE auto quad_weights(Integral n)
Calculates Clenshaw-Curtis quadrature weights.
RICCATI_ALWAYS_INLINE auto integration_matrix(Integral n)
Constructs a Chebyshev integration matrix.
Eigen::Matrix< Scalar, -1, -1 > matrix_t
auto gamma(SolverInfo &&info, const Scalar &x)
auto omega(SolverInfo &&info, const Scalar &x)
auto scale(Vector &&x, Scalar x0, Scalar h)
Scales and shifts a vector of Chebyshev nodes.
RICCATI_ALWAYS_INLINE auto cheby_nodes_to_coeffs(Mat &&values)
Convert a matrix of values of m polynomials evaluated at n+1 Chebyshev nodes of the second kind to th...
Eigen::Matrix< Scalar, -1, 1 > vector_t
RICCATI_ALWAYS_INLINE auto chebyshev(Integral n)
Computes the Chebyshev differentiation matrix and Chebyshev nodes.
RICCATI_ALWAYS_INLINE auto coeffs_and_cheby_nodes(Mat &&values)
RICCATI_ALWAYS_INLINE auto interpolate(Vec1 &&s, Vec2 &&t, Allocator &&alloc)
Creates an interpolation matrix from an array of source nodes to target nodes.