riccaticpp
Loading...
Searching...
No Matches
chebyshev.hpp
Go to the documentation of this file.
1#ifndef INCLUDE_RICCATI_CHEBYSHEV_HPP
2#define INCLUDE_RICCATI_CHEBYSHEV_HPP
3
5#include <riccati/memory.hpp>
6#include <riccati/utils.hpp>
7#include <unsupported/Eigen/FFT>
8
9namespace riccati {
10
11namespace internal {
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) {
19 if (Fwd) {
20 res.col(j) = fft.fwd(Eigen::Matrix<std::complex<Scalar>, -1, 1>(x.col(j)))
21 .real();
22 } else {
23 res.col(j) = fft.inv(Eigen::Matrix<std::complex<Scalar>, -1, 1>(x.col(j)))
24 .real();
25 }
26 }
27 return res;
28}
29} // namespace internal
30
57template <typename Mat>
59 using Scalar = typename std::decay_t<Mat>::Scalar;
60 const auto n = coeffs.rows();
61 using Mat_t = matrix_t<Scalar>;
62 if (n <= 1) {
63 return Mat_t(coeffs);
64 } else {
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();
70 return Mat_t(internal::fft<true>(fwd_values).topRows(n).eval());
71 }
72}
73
104template <typename Mat>
106 using Scalar = typename std::decay_t<Mat>::Scalar;
107 using Mat_t = matrix_t<Scalar>;
108 const auto n = values.rows();
109 if (n <= 1) {
110 return Mat_t(Mat_t::Zero(values.rows(), values.cols()));
111 } else {
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();
116 auto rev_ret = Mat_t(internal::fft<false>(rev_values).topRows(n)).eval();
117 rev_ret.block(1, 0, n - 2, n).array() *= 2.0;
118 return rev_ret;
119 }
120}
121
131template <typename Mat>
133 using Scalar = typename std::decay_t<Mat>::Scalar;
134 using Mat_t = matrix_t<Scalar>;
135 const auto n = values.rows();
136 if (n <= 1) {
137 return std::make_pair(Mat_t(values),
138 Mat_t(Mat_t::Zero(values.rows(), values.cols())));
139 } else {
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();
145 auto fwd_val = Mat_t(internal::fft<true>(fwd_values).topRows(n).eval());
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();
150 auto rev_ret = Mat_t(internal::fft<false>(rev_values).topRows(n)).eval();
151 rev_ret.block(1, 0, n - 2, n).array() *= 2.0;
152 return std::make_pair(fwd_val, rev_ret);
153 }
154}
155
177template <typename Scalar, typename Integral>
179 auto ident = matrix_t<Scalar>::Identity(n, n).eval();
180 auto coeffs_pair = coeffs_and_cheby_nodes(ident);
181 auto&& T = coeffs_pair.first;
182 auto&& T_inverse = coeffs_pair.second;
183 n--;
184 auto k = vector_t<Scalar>::LinSpaced(n, 1.0, n).eval();
185 auto k2 = eval(2 * (k.array() - 1));
186 k2.coeffRef(0) = 1.0;
187 // B = np.diag(1 / (2 * k), -1) - np.diag(1 / k2, 1)
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) {
193 v.coeffRef(i) = -1;
194 }
195 auto tmp = (v.asDiagonal() * B.block(1, 0, n, n + 1)).eval();
196 B.row(0) = (tmp).colwise().sum();
197 B.col(0) *= 2.0;
198 auto Q = matrix_t<Scalar>(T * B * T_inverse);
199 Q.bottomRows(1).setZero();
200 return Q;
201}
202
224template <typename Scalar, typename Integral>
227 if (n == 0) {
228 return w;
229 } else {
230 auto a = vector_t<Scalar>::LinSpaced(n + 1, 0, pi<Scalar>()).eval();
231 auto v = vector_t<Scalar>::Ones(n - 1).eval();
232 // TODO: Smarter way to do this
233 if (n % 2 == 0) {
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()
238 / (4.0 * i * i - 1);
239 }
240 v.array() -= (n * a.segment(1, n - 1).array()).cos() / (n * n - 1);
241 } else {
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()
246 / (4.0 * i * i - 1);
247 }
248 v.array() -= (n * a.segment(1, n - 1).array()).cos() / (n * n - 1);
249 }
250 w.segment(1, n - 1).array() = (2.0 * v.array()) / n;
251 return w;
252 }
253}
254
277template <typename Scalar, typename Integral>
279 if (n == 0) {
280 return std::make_pair(matrix_t<Scalar>::Zero(1, 1).eval(),
282 } else {
283 auto a = vector_t<Scalar>::LinSpaced(n + 1, 0.0, pi<Scalar>());
284 auto b = vector_t<Scalar>::Ones(n + 1).eval();
285 b.coeffRef(0) = 2.0;
286 b.coeffRef(b.size() - 1) = 2.0;
287 auto d = vector_t<Scalar>::Ones(n + 1).eval();
288 for (Integral i = 1; i < n + 1; i += 2) {
289 d.coeffRef(i) = -1;
290 }
291 auto x = a.array().cos().eval();
292 auto X = (x.matrix() * vector_t<Scalar>::Ones(n + 1).transpose())
293 .matrix()
294 .eval();
295 auto dX = (X - X.transpose()).eval();
296 auto c = (b.array() * d.array()).eval();
297
298 auto D = ((c.matrix() * (1.0 / c).matrix().transpose()).array()
299 / (dX + matrix_t<Scalar>::Identity(n + 1, n + 1)).array())
300 .matrix()
301 .eval();
302 D -= D.rowwise().sum().asDiagonal();
303 return std::make_pair(matrix_t<Scalar>(D), vector_t<Scalar>(x));
304 }
305}
306
331template <typename Vec1, typename Vec2, typename Allocator>
332RICCATI_ALWAYS_INLINE auto interpolate(Vec1&& s, Vec2&& t, Allocator&& alloc) {
333 const auto r = s.size();
334 const auto q = t.size();
335 auto V
336 = eval(alloc, matrix_t<typename std::decay_t<Vec1>::Scalar>::Ones(r, r));
337 auto 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();
342 }
343 /*
344 return V.transpose().jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV)
345 .solve(R.transpose())
346 .transpose()
347 .eval();
348 */
349 // Eigen::PartialPivLU<std::decay_t<decltype(V)>> lu(V.transpose());
350 return V.transpose().partialPivLu().solve(R.transpose()).transpose().eval();
351}
352
391template <typename SolverInfo, typename Scalar, typename YScalar,
392 typename Integral>
394 Scalar h, YScalar y0,
395 YScalar dy0, Integral niter) {
396 using complex_t = std::complex<Scalar>;
397 using vectorc_t = vector_t<complex_t>;
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]));
407 auto D2ic = eval(info.alloc_, matrix_t<complex_t>::Zero(n + 3, n + 1));
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));
419}
420
421} // namespace riccati
422
423#endif
#define RICCATI_ALWAYS_INLINE
Definition macros.hpp:57
RICCATI_ALWAYS_INLINE auto fft(T &&x)
Definition chebyshev.hpp:13
auto array(T x)
Definition utils.hpp:201
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...
Definition chebyshev.hpp:58
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.
auto square(T x)
Definition utils.hpp:191
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
Definition utils.hpp:48
auto gamma(SolverInfo &&info, const Scalar &x)
Definition solver.hpp:25
auto omega(SolverInfo &&info, const Scalar &x)
Definition solver.hpp:33
auto scale(Vector &&x, Scalar x0, Scalar h)
Scales and shifts a vector of Chebyshev nodes.
Definition utils.hpp:40
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
Definition utils.hpp:50
constexpr T pi()
Definition utils.hpp:63
RICCATI_ALWAYS_INLINE auto chebyshev(Integral n)
Computes the Chebyshev differentiation matrix and Chebyshev nodes.
auto matrix(T x)
Definition utils.hpp:211
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.