Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
Hankel.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10// References:
11// [1] G. H. Golub and C. F. Van Loan, "Matrix Computations", 4th ed., Johns
12// Hopkins University Press, 2013, chapter 4.7 (Toeplitz, Hankel and
13// related systems) and chapter 4.8 (fast products via circulant embedding
14// and the FFT).
15// [2] R. H. Chan and X.-Q. Jin, "An Introduction to Iterative Toeplitz
16// Solvers", SIAM, 2007 (a Hankel matrix is a column-reversed Toeplitz
17// matrix, so its products and solves reduce to the Toeplitz machinery).
18
19#ifndef EIGEN_STRUCTURED_HANKEL_H
20#define EIGEN_STRUCTURED_HANKEL_H
21
22// IWYU pragma: private
23#include "./InternalHeaderCheck.h"
24
25namespace Eigen {
26
27template <typename Scalar_, int Rows_ = Dynamic, int Cols_ = Dynamic>
28class Hankel;
29
30namespace internal {
31
32template <typename Scalar_, int Rows_, int Cols_>
33struct traits<Hankel<Scalar_, Rows_, Cols_>> {
34 using Scalar = Scalar_;
35 using StorageKind = Dense;
36 using XprKind = MatrixXpr;
37 using StorageIndex = int;
38 static constexpr int RowsAtCompileTime = Rows_;
39 static constexpr int ColsAtCompileTime = Cols_;
40 static constexpr int MaxRowsAtCompileTime = Rows_;
41 static constexpr int MaxColsAtCompileTime = Cols_;
42 // Deliberately no NestByRefBit: transpose(), conjugate() and adjoint() return
43 // owning temporaries, so Product must nest the operator by value for a
44 // delayed-evaluated product expression to keep its left factor alive. The copy
45 // is O(n), negligible against the O(n log n) product evaluation.
46 static constexpr unsigned int Flags = 0;
47};
48
49template <typename Scalar_, int Rows_, int Cols_>
50struct evaluator_traits<Hankel<Scalar_, Rows_, Cols_>> {
51 using Kind = IndexBased;
52 using Shape = StructuredShape;
53};
54
55} // namespace internal
56
97template <typename Scalar_, int Rows_, int Cols_>
98class Hankel : public EigenBase<Hankel<Scalar_, Rows_, Cols_>> {
99 public:
100 using Scalar = Scalar_;
101 using RealScalar = typename NumTraits<Scalar>::Real;
102 using StorageIndex = int;
103 using Complex = std::complex<RealScalar>;
104 using ColGeneratorType = Matrix<Scalar, Rows_, 1>;
105 using RowGeneratorType = Matrix<Scalar, Cols_, 1>;
106 using ComplexVector = Matrix<Complex, Dynamic, 1>;
107
108 static constexpr int RowsAtCompileTime = Rows_;
109 static constexpr int ColsAtCompileTime = Cols_;
110 static constexpr int MaxRowsAtCompileTime = Rows_;
111 static constexpr int MaxColsAtCompileTime = Cols_;
112 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(Rows_, Cols_);
113 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
114 static constexpr bool IsRowMajor = false;
115 // Deliberately no IsVectorAtCompileTime: Ref<const Hankel>'s default StrideType
116 // argument reads it, so its absence makes internal::is_ref_compatible SFINAE to
117 // false and keeps the iterative solvers on their matrix-free path.
118
120 static constexpr int GenSizeAtCompileTime = (Rows_ == Dynamic || Cols_ == Dynamic) ? Dynamic : (Rows_ + Cols_ - 1);
121 using GeneratorType = Matrix<Scalar, GenSizeAtCompileTime, 1>;
122
131 template <typename ColDerived, typename RowDerived>
133 : m_rows(col.size()), m_cols(row.size()) {
134 EIGEN_STATIC_ASSERT_VECTOR_ONLY(ColDerived)
135 EIGEN_STATIC_ASSERT_VECTOR_ONLY(RowDerived)
136 const Index m = col.size(), n = row.size();
137 eigen_assert(m > 0 && n > 0 && "Hankel generators must be non-empty");
138 m_h.resize(m + n - 1);
139 m_h.head(m) = col;
140 if (n > 1) m_h.tail(n - 1) = row.tail(n - 1);
141 if (m > 1 && n > 1 && (m > internal::structured_direct_threshold() || n > internal::structured_direct_threshold()))
142 m_symbol = computeSymbol();
143 }
144
145 EIGEN_DEVICE_FUNC Index rows() const { return m_rows.value(); }
146 EIGEN_DEVICE_FUNC Index cols() const { return m_cols.value(); }
147
150 const GeneratorType& generator() const { return m_h; }
151
153 ColGeneratorType column() const { return m_h.head(rows()); }
154
156 RowGeneratorType lastRow() const { return m_h.segment(rows() - 1, cols()); }
157
163 ComplexVector symbol() const { return m_symbol.size() > 0 ? m_symbol : computeSymbol(); }
164
166 Scalar coeff(Index row, Index col) const { return m_h.coeff(row + col); }
167
173 Hankel<Scalar, Cols_, Rows_> transpose() const {
174 return Hankel<Scalar, Cols_, Rows_>(m_h, cols(), rows(), transposedSymbol());
175 }
176
180 Hankel conjugate() const {
181 return Hankel(m_h.conjugate(), rows(), cols(), internal::structured_reverse_symbol(m_symbol).conjugate());
182 }
183
187 Hankel<Scalar, Cols_, Rows_> adjoint() const { return conjugate().transpose(); }
188
194 ColGeneratorType c = m_h.segment(cols() - 1, rows());
195 RowGeneratorType r = m_h.head(cols()).reverse();
197 }
198
202 static constexpr int SolveRowsAtCompileTime = Cols_ != Dynamic ? Cols_ : Rows_;
203
211 template <typename Rhs>
213 EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
214 int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
215 YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
216 eigen_assert(rows() == cols() && "Hankel::solve requires a square matrix");
217 eigen_assert(b.rows() == rows() && "right-hand side has the wrong number of rows");
219 eigen_assert(levinson.info() == Success &&
220 "Hankel::solve: the Levinson recursion broke down; "
221 "use LookAheadLevinson on toToeplitz() for diagnostics");
222 return levinson.solve(b).colwise().reverse();
223 }
224
228 template <typename Dest>
229 void evalTo(Dest& dst) const {
230 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
231 for (Index i = 0; i < rows(); ++i) dst.row(i) = m_h.segment(i, cols()).transpose();
232 return;
233 }
234 for (Index j = 0; j < cols(); ++j) dst.col(j) = m_h.segment(j, rows());
235 }
236
238 template <typename Dest>
239 void addTo(Dest& dst) const {
240 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
241 for (Index i = 0; i < rows(); ++i) dst.row(i) += m_h.segment(i, cols()).transpose();
242 return;
243 }
244 for (Index j = 0; j < cols(); ++j) dst.col(j) += m_h.segment(j, rows());
245 }
246
248 template <typename Dest>
249 void subTo(Dest& dst) const {
250 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
251 for (Index i = 0; i < rows(); ++i) dst.row(i) -= m_h.segment(i, cols()).transpose();
252 return;
253 }
254 for (Index j = 0; j < cols(); ++j) dst.col(j) -= m_h.segment(j, rows());
255 }
256
261 template <typename Rhs>
263 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
264 int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
265 INVALID_MATRIX_PRODUCT)
266 eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
267 return Product<Hankel, Rhs>(*this, x.derived());
268 }
269
278 template <typename Dest, typename Rhs, typename ProductScalar>
279 void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
280 const Index m = rows(), n = cols();
281 eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
282 const bool small = m == 1 || n == 1 ||
283 (m <= internal::structured_direct_threshold() && n <= internal::structured_direct_threshold());
284 if (small) {
285 directProduct(dst, rhs, alpha);
286 return;
287 }
288 internal::structured_fft_apply(dst, m_symbol, m, rhs.colwise().reverse(), alpha);
289 }
290
291 private:
292 template <typename OtherScalar, int OtherRows, int OtherCols>
293 friend class Hankel;
294
299 Hankel(const GeneratorType& h, Index rows, Index cols, const ComplexVector& symbol)
300 : m_rows(rows), m_cols(cols), m_h(h), m_symbol(symbol) {}
301
306 template <typename Dest, typename Rhs, typename ProductScalar>
307 void directProductColumn(Dest& dst, const Rhs& rhs, Index k, const ProductScalar& alpha) const {
308 const Index m = rows(), n = cols();
309 // A unit alpha must not multiply: even the identity complex scalar (1,0)
310 // pollutes an (Inf,0) value with NaN through the 0*Inf cross term.
311 const bool unitAlpha = alpha == ProductScalar(1);
312
313 if (n == 1) {
314 // The runtime-sized head keeps other fixed-size shapes well-formed.
315 const ProductScalar xj = unitAlpha ? ProductScalar(rhs.coeff(0, k)) : ProductScalar(alpha * rhs.coeff(0, k));
316 dst.col(k) += xj * m_h.head(m);
317 return;
318 }
319
320 if (m == 1) {
321 const ProductScalar acc = m_h.head(n).cwiseProduct(rhs.col(k)).sum();
322 dst.coeffRef(0, k) += unitAlpha ? acc : ProductScalar(alpha * acc);
323 return;
324 }
325
326 if (m <= internal::structured_scalar_threshold() && n <= internal::structured_scalar_threshold()) {
327 // Tiny sizes: a plain scalar loop beats the segment-based path below, whose
328 // per-segment setup dominates when segments hold only a few entries.
329 for (Index i = 0; i < m; ++i) {
330 ProductScalar acc(0);
331 for (Index j = 0; j < n; ++j) acc += coeff(i, j) * rhs.coeff(j, k);
332 dst.coeffRef(i, k) += unitAlpha ? acc : ProductScalar(alpha * acc);
333 }
334 return;
335 }
336
337 // Column j is the contiguous, vectorizable slice h[j..j+m-1].
338 auto dstCol = dst.col(k);
339 for (Index j = 0; j < n; ++j) {
340 const ProductScalar xj = unitAlpha ? ProductScalar(rhs.coeff(j, k)) : ProductScalar(alpha * rhs.coeff(j, k));
341 dstCol += xj * m_h.segment(j, m);
342 }
343 }
344
347 template <typename Dest, typename Rhs, typename ProductScalar>
348 void directProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
349 for (Index k = 0; k < rhs.cols(); ++k) directProductColumn(dst, rhs, k, alpha);
350 }
351
355 ComplexVector computeSymbol() const {
356 const Index m = rows(), n = cols();
357 const Index p = internal::fft_next_good_size(m + n - 1);
358 ComplexVector embedding = ComplexVector::Zero(p);
359 embedding.head(m) = m_h.tail(m).template cast<Complex>(); // h[n-1 .. m+n-2]
360 embedding.tail(n - 1) = m_h.head(n - 1).template cast<Complex>();
361 if (p == 1) return embedding; // the DFT of a single sample is the identity
362 ComplexVector symbol(p);
363 auto&& fft = internal::structured_fft_engine<RealScalar>();
364 fft.fwd(symbol, embedding, p);
365 return symbol;
366 }
367
373 ComplexVector transposedSymbol() const {
374 ComplexVector sym = m_symbol;
375 const Index p = sym.size();
376 if (p > 0 && rows() != cols()) {
377 Index s = (rows() - cols()) % p;
378 if (s < 0) s += p;
379 // Contiguous real buffers let sin/cos use packets; complex component views
380 // alone have stride two and would force scalar trigonometric evaluation.
381 Array<RealScalar, 64, 1> angles, values;
382 Matrix<Complex, 64, 1> phases;
383 Index fs = 0; // f * s mod p
384 for (Index f = 0; f < p;) {
385 const Index count = numext::mini<Index>(64, p - f);
386 for (Index i = 0; i < count; ++i) {
387 angles[i] = RealScalar(2 * EIGEN_PI) * RealScalar(fs) / RealScalar(p);
388 fs += s;
389 if (fs >= p) fs -= p;
390 }
391 values.head(count) = angles.head(count).cos();
392 phases.head(count).real() = values.head(count);
393 values.head(count) = angles.head(count).sin();
394 phases.head(count).imag() = values.head(count);
395 sym.segment(f, count).array() *= phases.head(count).array();
396 f += count;
397 }
398 }
399 return sym;
400 }
401
402 // Empty (compile-time constant) when the corresponding dimension is fixed; the
403 // dimensions cannot be recovered from m_h alone, whose length is rows()+cols()-1.
404 internal::variable_if_dynamic<Index, RowsAtCompileTime> m_rows;
405 internal::variable_if_dynamic<Index, ColsAtCompileTime> m_cols;
406 GeneratorType m_h;
407 ComplexVector m_symbol;
408};
409
413template <typename ColDerived, typename RowDerived>
418
419namespace internal {
420
421// Single product specialization covering every product tag; see the note in
422// Circulant.h.
423template <typename Scalar_, int Rows_, int Cols_, typename Rhs, int ProductTag>
424struct generic_product_impl<Hankel<Scalar_, Rows_, Cols_>, Rhs, StructuredShape, DenseShape, ProductTag>
425 : structured_product_impl<Hankel<Scalar_, Rows_, Cols_>, Rhs> {};
426
427} // namespace internal
428
429} // namespace Eigen
430
431#endif // EIGEN_STRUCTURED_HANKEL_H
constexpr FixedSegmentReturnType< N >::Type tail(Index n=N)
An m x n Hankel matrix represented by its first column and last row.
Definition Hankel.h:98
Scalar coeff(Index row, Index col) const
Definition Hankel.h:166
Hankel(const MatrixBase< ColDerived > &col, const MatrixBase< RowDerived > &row)
Definition Hankel.h:132
ColGeneratorType column() const
Definition Hankel.h:153
Matrix< Scalar, SolveRowsAtCompileTime, Rhs::ColsAtCompileTime > solve(const MatrixBase< Rhs > &b) const
Definition Hankel.h:212
Toeplitz< Scalar, Rows_, Cols_ > toToeplitz() const
Definition Hankel.h:193
ComplexVector symbol() const
Definition Hankel.h:163
Hankel< Scalar, Cols_, Rows_ > adjoint() const
Definition Hankel.h:187
RowGeneratorType lastRow() const
Definition Hankel.h:156
Hankel< Scalar, Cols_, Rows_ > transpose() const
Definition Hankel.h:173
Hankel conjugate() const
Definition Hankel.h:180
Product< Hankel, Rhs > operator*(const MatrixBase< Rhs > &x) const
Definition Hankel.h:262
const GeneratorType & generator() const
Definition Hankel.h:150
Look-ahead Levinson direct solver for general Toeplitz systems.
Definition LookAheadLevinson.h:84
ComputationInfo info() const
Definition LookAheadLevinson.h:158
const Solve< LookAheadLevinson, Rhs > solve(const MatrixBase< Rhs > &b) const
An m x n Toeplitz matrix represented by its first column and row.
Definition Toeplitz.h:81
Hankel< typename ColDerived::Scalar, ColDerived::SizeAtCompileTime, RowDerived::SizeAtCompileTime > makeHankel(const MatrixBase< ColDerived > &col, const MatrixBase< RowDerived > &row)
Definition Hankel.h:414
Namespace containing all symbols from the Eigen library.