Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
Toeplitz.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] R. M. Gray, "Toeplitz and Circulant Matrices: A Review", Foundations and
12// Trends in Communications and Information Theory, 2(3), 2006.
13// [2] G. H. Golub and C. F. Van Loan, "Matrix Computations", 4th ed., Johns
14// Hopkins University Press, 2013, chapter 4.8 (fast Toeplitz products via
15// circulant embedding and the FFT).
16
17#ifndef EIGEN_STRUCTURED_TOEPLITZ_H
18#define EIGEN_STRUCTURED_TOEPLITZ_H
19
20// IWYU pragma: private
21#include "./InternalHeaderCheck.h"
22
23namespace Eigen {
24
25template <typename Scalar_, int Rows_ = Dynamic, int Cols_ = Dynamic>
26class Toeplitz;
27
28namespace internal {
29
30template <typename Scalar_, int Rows_, int Cols_>
31struct traits<Toeplitz<Scalar_, Rows_, Cols_>> {
32 using Scalar = Scalar_;
33 using StorageKind = Dense;
34 using XprKind = MatrixXpr;
35 using StorageIndex = int;
36 static constexpr int RowsAtCompileTime = Rows_;
37 static constexpr int ColsAtCompileTime = Cols_;
38 static constexpr int MaxRowsAtCompileTime = Rows_;
39 static constexpr int MaxColsAtCompileTime = Cols_;
40 // Deliberately no NestByRefBit: transpose(), conjugate() and adjoint() return
41 // owning temporaries, so Product must nest the operator by value for a
42 // delayed-evaluated product expression to keep its left factor alive. The copy
43 // is O(m+n), negligible against the O(n log n) product evaluation.
44 static constexpr int Flags = 0;
45};
46
47template <typename Scalar_, int Rows_, int Cols_>
48struct evaluator_traits<Toeplitz<Scalar_, Rows_, Cols_>> {
49 using Kind = IndexBased;
50 using Shape = StructuredShape;
51};
52
53} // namespace internal
54
80template <typename Scalar_, int Rows_, int Cols_>
81class Toeplitz : public EigenBase<Toeplitz<Scalar_, Rows_, Cols_>> {
82 public:
83 using Scalar = Scalar_;
84 using RealScalar = typename NumTraits<Scalar>::Real;
85 using StorageIndex = int;
86 using Complex = std::complex<RealScalar>;
87 using ColGeneratorType = Matrix<Scalar, Rows_, 1>;
88 using RowGeneratorType = Matrix<Scalar, Cols_, 1>;
89 using ComplexVector = Matrix<Complex, Dynamic, 1>;
90
91 static constexpr int RowsAtCompileTime = Rows_;
92 static constexpr int ColsAtCompileTime = Cols_;
93 static constexpr int MaxRowsAtCompileTime = Rows_;
94 static constexpr int MaxColsAtCompileTime = Cols_;
95 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(Rows_, Cols_);
96 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
97 static constexpr bool IsRowMajor = false;
98 // Deliberately no IsVectorAtCompileTime: Ref<const Toeplitz>'s default StrideType
99 // argument reads it, so its absence makes internal::is_ref_compatible SFINAE to
100 // false and keeps the iterative solvers on their matrix-free path.
101
111 template <typename ColDerived, typename RowDerived>
112 Toeplitz(const MatrixBase<ColDerived>& col, const MatrixBase<RowDerived>& row) : m_col(col), m_row(row) {
113 EIGEN_STATIC_ASSERT_VECTOR_ONLY(ColDerived)
114 EIGEN_STATIC_ASSERT_VECTOR_ONLY(RowDerived)
115 eigen_assert(m_col.size() > 0 && m_row.size() > 0 && "Toeplitz generators must be non-empty");
116 // Below the threshold every product takes the direct path and the symbol has
117 // no consumer, so skip the embedding altogether.
118 if (rows() > internal::structured_direct_threshold() || cols() > internal::structured_direct_threshold())
119 m_symbol = computeSymbol();
120 }
121
122 EIGEN_DEVICE_FUNC Index rows() const { return m_col.size(); }
123 EIGEN_DEVICE_FUNC Index cols() const { return m_row.size(); }
124
126 const ColGeneratorType& column() const { return m_col; }
128 const RowGeneratorType& row() const { return m_row; }
129
134 ComplexVector symbol() const { return m_symbol.size() > 0 ? m_symbol : computeSymbol(); }
135
137 Scalar coeff(Index row, Index col) const {
138 Index k = row - col;
139 return k >= 0 ? m_col.coeff(k) : m_row.coeff(-k);
140 }
141
147 Toeplitz<Scalar, Cols_, Rows_> transpose() const {
148 using TransposeType = Toeplitz<Scalar, Cols_, Rows_>;
149 const Index n = cols();
150 typename TransposeType::ColGeneratorType col(n);
151 col[0] = m_col[0];
152 if (n > 1) col.tail(n - 1) = m_row.tail(n - 1);
153 return TransposeType(col, m_col, internal::structured_reverse_symbol(m_symbol));
154 }
155
159 Toeplitz conjugate() const {
160 return Toeplitz(m_col.conjugate(), m_row.conjugate(), internal::structured_reverse_symbol(m_symbol).conjugate());
161 }
162
166 Toeplitz<Scalar, Cols_, Rows_> adjoint() const {
167 using AdjointType = Toeplitz<Scalar, Cols_, Rows_>;
168 const Index n = cols();
169 typename AdjointType::ColGeneratorType col(n);
170 col[0] = numext::conj(m_col[0]);
171 if (n > 1) col.tail(n - 1) = m_row.tail(n - 1).conjugate();
172 return AdjointType(col, m_col.conjugate(), m_symbol.conjugate());
173 }
174
179 template <typename Dest>
180 void evalTo(Dest& dst) const {
181 const Index m = rows(), n = cols();
182 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
183 for (Index i = 0; i < m; ++i) {
184 const Index h = numext::mini(i + 1, n);
185 dst.row(i).head(h) = m_col.segment(i - h + 1, h).reverse().transpose();
186 if (i + 1 < n) dst.row(i).tail(n - i - 1) = m_row.segment(1, n - i - 1).transpose();
187 }
188 return;
189 }
190 for (Index j = 0; j < n; ++j) {
191 const Index h = numext::mini(j, m);
192 dst.col(j).head(h) = m_row.segment(j - h + 1, h).reverse();
193 if (j < m) dst.col(j).tail(m - j) = m_col.head(m - j);
194 }
195 }
196
198 template <typename Dest>
199 void addTo(Dest& dst) const {
200 const Index m = rows(), n = cols();
201 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
202 for (Index i = 0; i < m; ++i) {
203 const Index h = numext::mini(i + 1, n);
204 dst.row(i).head(h) += m_col.segment(i - h + 1, h).reverse().transpose();
205 if (i + 1 < n) dst.row(i).tail(n - i - 1) += m_row.segment(1, n - i - 1).transpose();
206 }
207 return;
208 }
209 for (Index j = 0; j < n; ++j) {
210 const Index h = numext::mini(j, m);
211 dst.col(j).head(h) += m_row.segment(j - h + 1, h).reverse();
212 if (j < m) dst.col(j).tail(m - j) += m_col.head(m - j);
213 }
214 }
215
217 template <typename Dest>
218 void subTo(Dest& dst) const {
219 const Index m = rows(), n = cols();
220 EIGEN_IF_CONSTEXPR (Dest::IsRowMajor) {
221 for (Index i = 0; i < m; ++i) {
222 const Index h = numext::mini(i + 1, n);
223 dst.row(i).head(h) -= m_col.segment(i - h + 1, h).reverse().transpose();
224 if (i + 1 < n) dst.row(i).tail(n - i - 1) -= m_row.segment(1, n - i - 1).transpose();
225 }
226 return;
227 }
228 for (Index j = 0; j < n; ++j) {
229 const Index h = numext::mini(j, m);
230 dst.col(j).head(h) -= m_row.segment(j - h + 1, h).reverse();
231 if (j < m) dst.col(j).tail(m - j) -= m_col.head(m - j);
232 }
233 }
234
240 template <typename Rhs>
242 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
243 int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
244 INVALID_MATRIX_PRODUCT)
245 eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
246 return Product<Toeplitz, Rhs>(*this, x.derived());
247 }
248
252 template <typename Dest, typename Rhs, typename ProductScalar>
253 void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
254 const Index m = rows(), n = cols();
255 eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
256 const bool small = m <= internal::structured_direct_threshold() && n <= internal::structured_direct_threshold();
257 if (small)
258 directProduct(dst, rhs, alpha);
259 else
260 internal::structured_fft_apply(dst, m_symbol, m, rhs, alpha);
261 }
262
263 private:
264 // Grants transpose() and adjoint() access to the private constructor of the
265 // dimension-swapped instantiation.
266 template <typename OtherScalar, int OtherRows, int OtherCols>
267 friend class Toeplitz;
268
273 Toeplitz(const ColGeneratorType& col, const RowGeneratorType& row, const ComplexVector& symbol)
274 : m_col(col), m_row(row), m_symbol(symbol) {}
275
279 template <typename Dest, typename Rhs, typename ProductScalar>
280 void directProductColumn(Dest& dst, const Rhs& rhs, Index k, const ProductScalar& alpha) const {
281 const Index m = rows(), n = cols();
282 // A unit alpha must not multiply: even the identity complex scalar (1,0)
283 // pollutes an (Inf,0) value with NaN through the 0*Inf cross term.
284 const bool unitAlpha = alpha == ProductScalar(1);
285 if (m <= internal::structured_scalar_threshold() && n <= internal::structured_scalar_threshold()) {
286 // Tiny sizes: a plain scalar loop beats the segment-based path below, whose
287 // per-segment setup dominates when segments hold only a few entries.
288 for (Index i = 0; i < m; ++i) {
289 ProductScalar acc(0);
290 for (Index j = 0; j < n; ++j) acc += coeff(i, j) * rhs.coeff(j, k);
291 dst.coeffRef(i, k) += unitAlpha ? acc : ProductScalar(alpha * acc);
292 }
293 return;
294 }
295
296 // Split each column at its diagonal into contiguous generator slices.
297 auto dstCol = dst.col(k);
298 for (Index j = 0; j < n; ++j) {
299 const ProductScalar xj = unitAlpha ? ProductScalar(rhs.coeff(j, k)) : ProductScalar(alpha * rhs.coeff(j, k));
300 const Index h = numext::mini(j, m);
301 dstCol.head(h) += xj * m_row.segment(j - h + 1, h).reverse();
302 if (j < m) dstCol.tail(m - j) += xj * m_col.head(m - j);
303 }
304 }
305
308 template <typename Dest, typename Rhs, typename ProductScalar>
309 void directProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
310 for (Index k = 0; k < rhs.cols(); ++k) directProductColumn(dst, rhs, k, alpha);
311 }
312
314 ComplexVector computeSymbol() const {
315 const Index m = rows(), n = cols();
316 const Index p = internal::fft_next_good_size(m + n - 1);
317 ComplexVector embedding = ComplexVector::Zero(p);
318 embedding.head(m) = m_col.template cast<Complex>();
319 embedding.tail(n - 1) = m_row.tail(n - 1).reverse().template cast<Complex>();
320 if (p == 1) return embedding; // the DFT of a single sample is the identity
321 ComplexVector symbol(p);
322 auto&& fft = internal::structured_fft_engine<RealScalar>();
323 fft.fwd(symbol, embedding, p);
324 return symbol;
325 }
326
327 ColGeneratorType m_col;
328 RowGeneratorType m_row;
329 ComplexVector m_symbol;
330};
331
335template <typename ColDerived, typename RowDerived>
340
341namespace internal {
342
343template <typename Scalar_, int Rows_, int Cols_, typename Rhs, int ProductTag>
344struct generic_product_impl<Toeplitz<Scalar_, Rows_, Cols_>, Rhs, StructuredShape, DenseShape, ProductTag>
345 : structured_product_impl<Toeplitz<Scalar_, Rows_, Cols_>, Rhs> {};
346
347} // namespace internal
348
349} // namespace Eigen
350
351#endif // EIGEN_STRUCTURED_TOEPLITZ_H
An m x n Toeplitz matrix represented by its first column and row.
Definition Toeplitz.h:81
Toeplitz< Scalar, Cols_, Rows_ > transpose() const
Definition Toeplitz.h:147
Scalar coeff(Index row, Index col) const
Definition Toeplitz.h:137
Toeplitz(const MatrixBase< ColDerived > &col, const MatrixBase< RowDerived > &row)
Definition Toeplitz.h:112
Toeplitz< Scalar, Cols_, Rows_ > adjoint() const
Definition Toeplitz.h:166
ComplexVector symbol() const
Definition Toeplitz.h:134
Product< Toeplitz, Rhs > operator*(const MatrixBase< Rhs > &x) const
Definition Toeplitz.h:241
Toeplitz conjugate() const
Definition Toeplitz.h:159
const ColGeneratorType & column() const
Definition Toeplitz.h:126
const RowGeneratorType & row() const
Definition Toeplitz.h:128
Toeplitz< typename ColDerived::Scalar, ColDerived::SizeAtCompileTime, RowDerived::SizeAtCompileTime > makeToeplitz(const MatrixBase< ColDerived > &col, const MatrixBase< RowDerived > &row)
Definition Toeplitz.h:336
Namespace containing all symbols from the Eigen library.