127 enum DenseAssignment { SetAssignment, AddAssignment, SubAssignment };
130 using Scalar = Scalar_;
132 using StorageIndex = int;
133 using Complex = std::complex<RealScalar>;
140 using GeneratorType =
141 Matrix<Scalar, BlockSize_, NumBlocks_, (BlockSize_ == 1 && NumBlocks_ != 1) ?
int(
RowMajor) : int(
ColMajor)>;
148 static constexpr int RowsAtCompileTime = internal::bccb_dim(BlockSize_, NumBlocks_);
149 static constexpr int ColsAtCompileTime = RowsAtCompileTime;
150 static constexpr int MaxRowsAtCompileTime = RowsAtCompileTime;
151 static constexpr int MaxColsAtCompileTime = RowsAtCompileTime;
152 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(RowsAtCompileTime, ColsAtCompileTime);
153 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
154 static constexpr bool IsRowMajor =
false;
166 template <
typename Derived>
168 eigen_assert(m_g.rows() > 0 && m_g.cols() > 0 &&
"Bccb generator must be non-empty");
169 if (m_g.size() > internal::structured_direct_threshold()) {
170 m_symbol = computeSymbol();
171 m_prodSymbol = computeProdSymbol(m_g);
175 EIGEN_DEVICE_FUNC Index rows()
const {
return m_g.size(); }
176 EIGEN_DEVICE_FUNC Index cols()
const {
return m_g.size(); }
190 ComplexArray
symbol()
const {
return m_symbol.size() > 0 ? m_symbol : computeSymbol(); }
195 Index k2 = row % n2 - col % n2;
196 if (k2 < 0) k2 += n2;
197 Index k1 = row / n2 - col / n2;
199 return m_g.coeff(k2, k1);
208 Bccb transpose()
const {
return Bccb(reverse2(m_g), reverse2(m_symbol), reverse2(m_prodSymbol)); }
229 template <
typename Rhs>
231 EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
232 int(RowsAtCompileTime) ==
int(Rhs::RowsAtCompileTime),
233 YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
234 const Index N = rows();
235 eigen_assert(b.rows() == N &&
"right-hand side has the wrong number of rows");
236 const ComplexArray s =
symbol();
237 const RealArray mods = s.cwiseAbs();
238 const ComplexArray sinv = internal::structured_pinv_symbol(s, mods, internal::structured_rank_threshold(s, mods));
240 applySymbol(x, sinv, b.derived(), Scalar(1),
false);
251 const ComplexArray s =
symbol();
252 const RealArray mods = s.cwiseAbs();
253 return (!(mods.array() < internal::structured_rank_threshold(s, mods))).count();
261 ComplexArray sinv =
symbol().cwiseInverse();
262 const GeneratorType ginv = generatorFromSymbol(sinv);
263 const bool cache = rows() > internal::structured_direct_threshold();
264 return Bccb(ginv, cache ? sinv : ComplexArray(), cache ? computeProdSymbol(ginv) : ComplexArray());
276 const ComplexArray s =
symbol();
279 for (
Index k1 = 0; k1 < s.cols(); ++k1)
280 for (
Index k2 = 0; k2 < s.rows(); ++k2)
281 det = internal::structured_balance(Complex(det * internal::structured_balance(s(k2, k1), exponent)), exponent);
282 return toScalar(internal::structured_ldexp_clamped(det, exponent), std::is_same<RealScalar, Scalar>());
298 ComplexMatrix F(N, N);
299 for (
Index f1 = 0; f1 < n1; ++f1)
300 for (
Index f2 = 0; f2 < n2; ++f2) fourierColumn(F, f1, f2, f1 * n2 + f2);
309 const RealVector mods = s.cwiseAbs();
310 const std::vector<Index> perm = internal::structured_svd_permutation(mods);
311 RealVector sv(s.size());
312 for (
Index t = 0; t < s.size(); ++t) sv[t] = mods[perm[t]];
322 const RealVector mods = s.cwiseAbs();
323 const std::vector<Index> perm = internal::structured_svd_permutation(mods);
324 ComplexMatrix U(N, N);
325 for (
Index t = 0; t < N; ++t) {
326 fourierColumn(U, perm[t] / n2, perm[t] % n2, t);
327 const RealScalar a = mods[perm[t]];
328 if (a > RealScalar(0)) U.col(t) *= s[perm[t]] / a;
339 const std::vector<Index> perm = internal::structured_svd_permutation(RealVector(s.cwiseAbs()));
340 ComplexMatrix V(N, N);
341 for (
Index t = 0; t < N; ++t) fourierColumn(V, perm[t] / n2, perm[t] % n2, t);
349 template <
typename Dest>
350 void evalTo(Dest& dst)
const {
351 assignTo<SetAssignment>(dst);
355 template <
typename Dest>
356 void addTo(Dest& dst)
const {
357 assignTo<AddAssignment>(dst);
361 template <
typename Dest>
362 void subTo(Dest& dst)
const {
363 assignTo<SubAssignment>(dst);
371 template <
typename Rhs>
373 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
374 int(ColsAtCompileTime) ==
int(Rhs::RowsAtCompileTime),
375 INVALID_MATRIX_PRODUCT)
376 eigen_assert(x.rows() == cols() &&
"invalid product: dimensions do not match");
383 template <
typename Dest,
typename Rhs,
typename ProductScalar>
384 void addProduct(Dest& dst,
const Rhs& rhs,
const ProductScalar& alpha)
const {
385 const Index N = rows();
386 eigen_assert(rhs.rows() == N &&
"invalid product: dimensions do not match");
387 if (N <= internal::structured_direct_threshold()) {
388 directProduct(dst, rhs, alpha);
394 const ComplexArray& s = m_prodSymbol.size() > 0 ? m_prodSymbol : m_symbol;
395 applySymbol(dst, s, rhs, alpha,
true);
401 template <DenseAssignment Assignment,
typename Dest>
402 void assignTo(Dest& dst)
const {
404 for (
Index c1 = 0; c1 < n1; ++c1)
405 for (
Index j2 = 0; j2 < n2; ++j2) {
406 auto col = dst.col(c1 * n2 + j2);
407 for (
Index b1 = 0; b1 < n1; ++b1) {
409 if (k1 < 0) k1 += n1;
410 EIGEN_IF_CONSTEXPR (Assignment == SetAssignment) {
411 col.segment(b1 * n2, j2) = m_g.col(k1).tail(j2);
412 col.segment(b1 * n2 + j2, n2 - j2) = m_g.col(k1).head(n2 - j2);
413 }
else EIGEN_IF_CONSTEXPR (Assignment == AddAssignment) {
414 col.segment(b1 * n2, j2) += m_g.col(k1).tail(j2);
415 col.segment(b1 * n2 + j2, n2 - j2) += m_g.col(k1).head(n2 - j2);
417 col.segment(b1 * n2, j2) -= m_g.col(k1).tail(j2);
418 col.segment(b1 * n2 + j2, n2 - j2) -= m_g.col(k1).head(n2 - j2);
430 Bccb(
const GeneratorType& g,
const ComplexArray&
symbol,
const ComplexArray& prodSymbol)
431 : m_g(g), m_symbol(
symbol), m_prodSymbol(prodSymbol) {}
451 ComplexArray computeProdSymbol(
const GeneratorType& g)
const {
452 const Index n2 = g.rows(), n1 = g.cols();
453 const Index p2 = internal::fft_next_good_size(n2) == n2 ? n2 : internal::fft_next_good_size(2 * n2 - 1);
454 const Index p1 = internal::fft_next_good_size(n1) == n1 ? n1 : internal::fft_next_good_size(2 * n1 - 1);
455 if (p2 == n2 && p1 == n1)
return ComplexArray();
456 ComplexArray Ge = ComplexArray::Zero(p2, p1);
457 Ge.topLeftCorner(n2, n1) = g.template cast<Complex>();
458 if (p2 != n2) Ge.bottomLeftCorner(n2 - 1, n1) = g.bottomRows(n2 - 1).template cast<Complex>();
459 if (p1 != n1) Ge.topRightCorner(n2, n1 - 1) = g.rightCols(n1 - 1).template cast<Complex>();
460 if (p2 != n2 && p1 != n1)
461 Ge.bottomRightCorner(n2 - 1, n1 - 1) = g.bottomRightCorner(n2 - 1, n1 - 1).template cast<Complex>();
471 template <
typename Dest,
typename Rhs,
typename ProductScalar>
472 void directProductColumn(Dest& dst,
const Rhs& rhs,
Index k,
const ProductScalar& alpha)
const {
473 const Index N = rows();
476 const bool unitAlpha = alpha == ProductScalar(1);
477 for (
Index i = 0; i < N; ++i) {
478 ProductScalar acc(0);
479 for (
Index j = 0; j < N; ++j) acc +=
coeff(i, j) * rhs.coeff(j, k);
480 dst.coeffRef(i, k) += unitAlpha ? acc : ProductScalar(alpha * acc);
486 template <
typename Dest,
typename Rhs,
typename ProductScalar>
487 void directProduct(Dest& dst,
const Rhs& rhs,
const ProductScalar& alpha)
const {
488 for (
Index k = 0; k < rhs.cols(); ++k) directProductColumn(dst, rhs, k, alpha);
500 template <
typename Dest,
typename Rhs,
typename ProductScalar>
501 void applySymbol(Dest& dst,
const ComplexArray& s,
const Rhs& rhs,
const ProductScalar& alpha,
502 bool accumulate)
const {
504 const Index p2 = s.rows(), p1 = s.cols();
505 const bool padded = p2 != n2 || p1 != n1;
506 ComplexVector xc(rows());
507 ComplexArray X(p2, p1), Xn;
508 for (
Index k = 0; k < rhs.cols(); ++k) {
512 xc = rhs.col(k).template cast<Complex>();
515 X.topLeftCorner(n2, n1) = xc.reshaped(n2, n1);
517 X = xc.reshaped(n2, n1);
520 X.array() *= s.array();
522 if (padded) Xn = X.topLeftCorner(n2, n1);
523 const auto out = internal::structured_scalar_part_impl<ProductScalar>::run((padded ? Xn : X).reshaped());
525 dst.col(k) += alpha * out;
527 dst.col(k) = alpha * out;
535 template <
bool Inverse>
536 void transform2(ComplexArray& X)
const {
537 auto&& fft = internal::structured_fft_engine<RealScalar>();
538 const Index n2 = X.rows(), n1 = X.cols();
543 ComplexVector tmp, rowv;
545 for (
Index k1 = 0; k1 < n1; ++k1) {
546 EIGEN_IF_CONSTEXPR (Inverse) {
547 fft.inv(tmp, X.col(k1), n2);
549 fft.fwd(tmp, X.col(k1), n2);
555 for (
Index k2 = 0; k2 < n2; ++k2) {
556 rowv = X.row(k2).transpose();
557 EIGEN_IF_CONSTEXPR (Inverse) {
558 fft.inv(tmp, rowv, n1);
560 fft.fwd(tmp, rowv, n1);
562 X.row(k2) = tmp.transpose();
568 void fft2(ComplexArray& X)
const { transform2<false>(X); }
571 void ifft2(ComplexArray& X)
const { transform2<true>(X); }
574 GeneratorType generatorFromSymbol(ComplexArray
symbol)
const {
576 GeneratorType
generator = internal::structured_scalar_part_impl<Scalar>::run(
symbol);
581 ComplexArray computeSymbol()
const {
582 ComplexArray s = m_g.template cast<Complex>();
590 template <
typename MatType>
591 static MatType reverse2(
const MatType& M) {
592 const Index r = M.rows(), c = M.cols();
594 if (M.size() == 0)
return R;
596 if (c > 1) R.row(0).tail(c - 1) = M.row(0).tail(c - 1).reverse();
597 if (r > 1) R.col(0).tail(r - 1) = M.col(0).tail(r - 1).reverse();
598 if (r > 1 && c > 1) R.bottomRightCorner(r - 1, c - 1) = M.bottomRightCorner(r - 1, c - 1).reverse();
607 void fourierColumn(ComplexMatrix& F,
Index f1,
Index f2,
Index dstCol)
const {
609 const RealScalar scale = RealScalar(1) / numext::sqrt(RealScalar(rows()));
610 ComplexVector w2(n2);
612 for (
Index i2 = 0; i2 < n2; ++i2) {
613 w2[i2] = std::polar(scale, RealScalar(2 * EIGEN_PI) * RealScalar(jf) / RealScalar(n2));
615 if (jf >= n2) jf -= n2;
618 for (
Index b1 = 0; b1 < n1; ++b1) {
619 const Complex w1 = std::polar(RealScalar(1), RealScalar(2 * EIGEN_PI) * RealScalar(bf) / RealScalar(n1));
620 F.col(dstCol).segment(b1 * n2, n2) = w1 * w2;
622 if (bf >= n1) bf -= n1;
627 static Scalar toScalar(
const Complex& z, std::true_type ) {
return numext::real(z); }
628 static Scalar toScalar(
const Complex& z, std::false_type ) {
return z; }
631 ComplexArray m_symbol;
634 ComplexArray m_prodSymbol;