| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // | |
| // References: | |
| // [1] J. J. Dongarra, J. R. Bunch, C. B. Moler and G. W. Stewart, "LINPACK | |
| // Users' Guide", SIAM, 1979. determinant()'s balanced accumulation follows | |
| // the convention of its xGEDI routines, which return determinants as a | |
| // (fraction, exponent) pair to avoid spurious overflow/underflow. | |
| // [2] P. H. Sterbenz, "Floating-Point Computation", Prentice-Hall, 1974. | |
| // Scaling by a power of two is exact, the property the balanced | |
| // accumulation relies on. | |
| #ifndef EIGEN_STRUCTURED_CAUCHY_H | |
| #define EIGEN_STRUCTURED_CAUCHY_H | |
| // IWYU pragma: private | |
| #include "./InternalHeaderCheck.h" | |
| namespace Eigen { | |
| template <typename Scalar_, int Rows_ = Dynamic, int Cols_ = Dynamic> | |
| class Cauchy; | |
| template <typename Scalar_> | |
| class CauchyLU; | |
| namespace internal { | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| struct traits<Cauchy<Scalar_, Rows_, Cols_>> { | |
| using Scalar = Scalar_; | |
| using StorageKind = Dense; | |
| using XprKind = MatrixXpr; | |
| using StorageIndex = int; | |
| static constexpr int RowsAtCompileTime = Rows_; | |
| static constexpr int ColsAtCompileTime = Cols_; | |
| static constexpr int MaxRowsAtCompileTime = Rows_; | |
| static constexpr int MaxColsAtCompileTime = Cols_; | |
| // Deliberately no NestByRefBit: transpose(), conjugate() and adjoint() return | |
| // owning temporaries, so Product must nest the operator by value for a | |
| // delayed-evaluated product expression to keep its left factor alive. The copy | |
| // is O(m+n), negligible against the O(mn) product evaluation. | |
| static constexpr unsigned int Flags = 0; | |
| }; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| struct evaluator_traits<Cauchy<Scalar_, Rows_, Cols_>> { | |
| using Kind = IndexBased; | |
| using Shape = StructuredShape; | |
| }; | |
| template <typename Scalar_> | |
| struct traits<CauchyLU<Scalar_>> : traits<Matrix<Scalar_, Dynamic, Dynamic>> { | |
| using XprKind = MatrixXpr; | |
| using StorageKind = SolverStorage; | |
| using StorageIndex = int; | |
| using BaseTraits = traits<Matrix<Scalar_, Dynamic, Dynamic>>; | |
| static constexpr unsigned int Flags = BaseTraits::Flags & RowMajorBit; | |
| static constexpr int CoeffReadCost = Dynamic; | |
| }; | |
| } // namespace internal | |
| /** \ingroup StructuredMatrices_Module | |
| * \class Cauchy | |
| * \brief An \c m x \c n Cauchy matrix represented by its two node vectors. | |
| * | |
| * A Cauchy matrix has entry \c (i,j) equal to \f$ 1/(x_i - y_j) \f$; the class | |
| * stores only the \c m + \c n nodes. Products are evaluated directly at O(mn) | |
| * operations -- the same cost as a dense product, but with O(m+n) storage and | |
| * without ever forming the matrix. (Fast approximate products via multipole | |
| * expansions are out of scope.) | |
| * | |
| * The class is closed under transposition: \f$ C(x,y)^T = C(-y,-x) \f$, and the | |
| * determinant of a square Cauchy matrix has the classical closed form | |
| * \f$ \prod_{i<j}(x_j-x_i)(y_i-y_j) \big/ \prod_{i,j}(x_i-y_j) \f$. | |
| * | |
| * Because \c operator* returns an Eigen product expression, a \c Cauchy also | |
| * drops into the matrix-free iterative solvers, and it can be assigned to a | |
| * dense matrix when an explicit representation is needed. As with any | |
| * matrix-free operator, the iterative solvers must be instantiated with | |
| * \c IdentityPreconditioner (e.g. | |
| * \c GMRES<Cauchy<double>,IdentityPreconditioner>): the default preconditioners | |
| * read individual coefficients through \c col() or \c InnerIterator, which the | |
| * structured operators do not expose. | |
| * | |
| * Square systems are solved in O(n^2) by \ref CauchyLU, a partially pivoted LU | |
| * factorization computed through the displacement structure | |
| * (Gohberg-Kailath-Olshevsky): unlike the Toeplitz/Levinson world, Cauchy | |
| * structure survives row permutations, which is what makes fast \em pivoted | |
| * factorization possible. The Hilbert matrix is the Cauchy matrix with | |
| * \c x_i = i+1, \c y_j = -j. | |
| * | |
| * All nodes \c x_i must differ from all nodes \c y_j (entries would be infinite | |
| * otherwise); this is not checked. | |
| * | |
| * \tparam Scalar_ the scalar type, real or complex. | |
| * \tparam Rows_ the number of rows at compile time, or \c Dynamic (the default). | |
| * \tparam Cols_ the number of columns at compile time, or \c Dynamic (the default). | |
| * | |
| * \sa class CauchyLU, makeCauchy() | |
| */ | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| class Cauchy : public EigenBase<Cauchy<Scalar_, Rows_, Cols_>> { | |
| public: | |
| using Scalar = Scalar_; | |
| using RealScalar = typename NumTraits<Scalar>::Real; | |
| using StorageIndex = int; | |
| using RowNodeVector = Matrix<Scalar, Rows_, 1>; | |
| using ColNodeVector = Matrix<Scalar, Cols_, 1>; | |
| EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(bool(RowNodeVector::NeedsToAlign || ColNodeVector::NeedsToAlign)) | |
| static constexpr int RowsAtCompileTime = Rows_; | |
| static constexpr int ColsAtCompileTime = Cols_; | |
| static constexpr int MaxRowsAtCompileTime = Rows_; | |
| static constexpr int MaxColsAtCompileTime = Cols_; | |
| static constexpr int SizeAtCompileTime = internal::size_at_compile_time(Rows_, Cols_); | |
| static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime; | |
| static constexpr bool IsRowMajor = false; | |
| // Deliberately no IsVectorAtCompileTime: Ref<const Cauchy>'s default StrideType | |
| // argument reads it, so its absence makes internal::is_ref_compatible SFINAE to | |
| // false and keeps the iterative solvers on their matrix-free path. | |
| /** Builds a Cauchy matrix from the row nodes \a x and the column nodes \a y: | |
| * entry \c (i,j) is \c 1/(x[i] - y[j]). */ | |
| template <typename XDerived, typename YDerived> | |
| Cauchy(const MatrixBase<XDerived>& x, const MatrixBase<YDerived>& y) : m_x(x), m_y(y) { | |
| EIGEN_STATIC_ASSERT_VECTOR_ONLY(XDerived) | |
| EIGEN_STATIC_ASSERT_VECTOR_ONLY(YDerived) | |
| EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_x, "Cauchy node vectors must be non-empty"); | |
| EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_y, "Cauchy node vectors must be non-empty"); | |
| } | |
| EIGEN_DEVICE_FUNC Index rows() const { return m_x.size(); } | |
| EIGEN_DEVICE_FUNC Index cols() const { return m_y.size(); } | |
| /** \returns the row node vector \c x. */ | |
| const RowNodeVector& rowNodes() const { return m_x; } | |
| /** \returns the column node vector \c y. */ | |
| const ColNodeVector& colNodes() const { return m_y; } | |
| /** \returns the coefficient at row \a row and column \a col. */ | |
| Scalar coeff(Index row, Index col) const { return Scalar(1) / (m_x.coeff(row) - m_y.coeff(col)); } | |
| /** \returns the transpose of \c *this, itself a Cauchy operator: | |
| * \f$ C(x,y)^T = C(-y,-x) \f$. */ | |
| Cauchy<Scalar, Cols_, Rows_> transpose() const { return Cauchy<Scalar, Cols_, Rows_>(-m_y, -m_x); } | |
| /** \returns the complex conjugate of \c *this, itself a Cauchy operator (the | |
| * one with conjugated nodes). */ | |
| Cauchy conjugate() const { return Cauchy(m_x.conjugate(), m_y.conjugate()); } | |
| /** \returns the adjoint of \c *this, itself a Cauchy operator: | |
| * \f$ C(x,y)^H = C(-\bar y, -\bar x) \f$. */ | |
| Cauchy<Scalar, Cols_, Rows_> adjoint() const { | |
| return Cauchy<Scalar, Cols_, Rows_>(-m_y.conjugate(), -m_x.conjugate()); | |
| } | |
| /** \returns the determinant of a \b square Cauchy matrix through the classical | |
| * closed form \f$ \prod_{i<j}(x_j-x_i)(y_i-y_j) \big/ \prod_{i,j}(x_i-y_j) \f$, | |
| * in O(n^2) operations. All factors enter a single accumulation kept in the | |
| * balanced form \c m * 2^e (the split fraction/exponent determinant | |
| * convention of LINPACK's xGEDI [1]) -- every factor and the running value | |
| * are renormalized to unit magnitude with the power of two tracked separately | |
| * (exact frexp/ldexp rescaling [2]), numerator factors multiplied in and | |
| * denominator factors divided out -- so no partial product can overflow or | |
| * underflow when the determinant itself is representable. Zero factors | |
| * (coincident \c x or \c y nodes) and non-finite factors propagate exactly; | |
| * a node difference that overflows makes its factor infinite, which turns the | |
| * determinant into NaN when it meets a zero or another infinite factor. */ | |
| Scalar determinant() const { | |
| EIGEN_INTERNAL_ASSERT_SQUARE_MSG(*this, "Cauchy::determinant requires a square matrix"); | |
| const Index n = rows(); | |
| Scalar det(1); | |
| Index exponent = 0; | |
| for (Index j = 1; j < n; ++j) | |
| for (Index i = 0; i < j; ++i) { | |
| const Scalar xDiff = internal::structured_balance(Scalar(m_x.coeff(j) - m_x.coeff(i)), exponent); | |
| det = internal::structured_balance(Scalar(det * xDiff), exponent); | |
| const Scalar yDiff = internal::structured_balance(Scalar(m_y.coeff(i) - m_y.coeff(j)), exponent); | |
| det = internal::structured_balance(Scalar(det * yDiff), exponent); | |
| } | |
| for (Index j = 0; j < n; ++j) | |
| for (Index i = 0; i < n; ++i) { | |
| Index denomExponent = 0; | |
| const Scalar d = internal::structured_balance(Scalar(m_x.coeff(i) - m_y.coeff(j)), denomExponent); | |
| exponent -= denomExponent; | |
| det = internal::structured_balance(Scalar(det / d), exponent); | |
| } | |
| return internal::structured_ldexp_clamped(det, exponent); | |
| } | |
| /** \internal Writes the dense representation into \a dst, one vectorized | |
| * column at a time. Invoked through \c dense = cauchy; */ | |
| template <typename Dest> | |
| void evalTo(Dest& dst) const { | |
| applyAssignment(dst, internal::assign_op<typename Dest::Scalar, Scalar>()); | |
| } | |
| /** \internal Computes \c dst += (*this), see evalTo(). */ | |
| template <typename Dest> | |
| void addTo(Dest& dst) const { | |
| applyAssignment(dst, internal::add_assign_op<typename Dest::Scalar, Scalar>()); | |
| } | |
| /** \internal Computes \c dst -= (*this), see evalTo(). */ | |
| template <typename Dest> | |
| void subTo(Dest& dst) const { | |
| applyAssignment(dst, internal::sub_assign_op<typename Dest::Scalar, Scalar>()); | |
| } | |
| /** \returns the product expression \c (*this) * \a v, evaluated directly at | |
| * O(mn) operations per right-hand side and at most O(m) extra storage. The | |
| * expression carries the default product tag, so assigning it behaves like any | |
| * dense product: a temporary resolves aliasing between the destination and | |
| * \a v, and \c .noalias() skips it. */ | |
| template <typename Rhs> | |
| Product<Cauchy, Rhs> operator*(const MatrixBase<Rhs>& v) const { | |
| return Product<Cauchy, Rhs>(*this, v.derived()); | |
| } | |
| /** \internal Computes \c dst += alpha * (*this) * rhs. \c ProductScalar is the | |
| * promoted scalar of the product (complex when a real operator is applied to a | |
| * complex right-hand side); the accumulation runs in the promoted type. Each | |
| * column of the operator is formed on the fly, vectorized over the rows; for | |
| * several right-hand sides of the operator's own scalar type it is formed once | |
| * per block of rows and reused across them, with identical results. */ | |
| template <typename Dest, typename Rhs, typename ProductScalar> | |
| void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const { | |
| const Index n = cols(); | |
| EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match"); | |
| // A unit alpha must not multiply: even the identity complex scalar (1,0) | |
| // pollutes an (Inf,0) value with NaN through the 0*Inf cross term. | |
| const bool unitAlpha = alpha == ProductScalar(1); | |
| const auto weight = [&](Index j, Index k) { | |
| return unitAlpha ? ProductScalar(rhs.coeff(j, k)) : ProductScalar(alpha * rhs.coeff(j, k)); | |
| }; | |
| // Reuse saves (r-1)/r of the divisions. A real operator applied to a complex | |
| // right-hand side gains nothing: the complex updates dominate its real divisions. | |
| if (rhs.cols() > 1 && std::is_same<ProductScalar, Scalar>::value) { | |
| // Row blocks keep the r destination columns of a block in cache across all | |
| // n reciprocal columns; the floor keeps the per-column updates long. | |
| constexpr Index kBlockBytes = Index(1) << 19; | |
| constexpr Index kMinBlockRows = 1024; | |
| const Index m = rows(), r = rhs.cols(); | |
| const Index blockRows = numext::mini(m, numext::maxi(kMinBlockRows, kBlockBytes / (r * Index(sizeof(Scalar))))); | |
| Matrix<Scalar, Dynamic, 1, ColMajor, Rows_> reciprocals(blockRows); | |
| for (Index i = 0; i < m; i += blockRows) { | |
| const Index b = numext::mini(blockRows, m - i); | |
| for (Index j = 0; j < n; ++j) { | |
| reciprocals.head(b) = (m_x.segment(i, b).array() - m_y.coeff(j)).inverse(); | |
| for (Index k = 0; k < r; ++k) dst.col(k).segment(i, b) += weight(j, k) * reciprocals.head(b); | |
| } | |
| } | |
| return; | |
| } | |
| for (Index k = 0; k < rhs.cols(); ++k) | |
| for (Index j = 0; j < n; ++j) dst.col(k) += weight(j, k) * (m_x.array() - m_y.coeff(j)).inverse().matrix(); | |
| } | |
| private: | |
| template <typename Dest, typename Assignment> | |
| void applyAssignment(Dest& dst, const Assignment& assignment) const { | |
| for (Index j = 0; j < cols(); ++j) { | |
| auto dstColumn = dst.col(j); | |
| internal::call_assignment_no_alias(dstColumn, (m_x.array() - m_y.coeff(j)).inverse().matrix(), assignment); | |
| } | |
| } | |
| RowNodeVector m_x; | |
| ColNodeVector m_y; | |
| }; | |
| /** \ingroup StructuredMatrices_Module | |
| * \returns a \ref Cauchy operator with row nodes \a x and column nodes \a y. The | |
| * compile-time dimensions of the operator are deduced from the node vectors. */ | |
| template <typename XDerived, typename YDerived> | |
| Cauchy<typename XDerived::Scalar, XDerived::SizeAtCompileTime, YDerived::SizeAtCompileTime> makeCauchy( | |
| const MatrixBase<XDerived>& x, const MatrixBase<YDerived>& y) { | |
| return Cauchy<typename XDerived::Scalar, XDerived::SizeAtCompileTime, YDerived::SizeAtCompileTime>(x, y); | |
| } | |
| /** \ingroup StructuredMatrices_Module | |
| * \class CauchyLU | |
| * \brief Partially pivoted O(n^2) LU solver for square Cauchy systems | |
| * (Gohberg-Kailath-Olshevsky). | |
| * | |
| * A Cauchy matrix satisfies the displacement equation | |
| * \f$ D_x C - C D_y = \mathbf{1}\mathbf{1}^T \f$, and -- crucially -- this | |
| * structure survives row permutations. The GKO algorithm exploits it to compute | |
| * the row-pivoted factorization \c P*C = L*U in O(n^2) operations: at each step | |
| * the current column of the Schur complement is generated from O(n) data, the | |
| * largest entry is chosen as pivot, and the Schur complement stays Cauchy-like | |
| * with updated generators. This gives Cauchy systems what the Levinson world | |
| * lacks: genuine partial pivoting at fast-algorithm cost. | |
| * | |
| * Usage follows the usual decomposition style, including the transposed and | |
| * adjoint solves of \c SolverBase (from the same factorization): | |
| * \code | |
| * CauchyLU<double> lu(C); // or lu.compute(C); | |
| * VectorXd u = lu.solve(b); // solve C * u = b | |
| * VectorXd v = lu.transpose().solve(b); // solve C^T * v = b | |
| * VectorXd w = lu.adjoint().solve(b); // solve C^H * w = b | |
| * \endcode | |
| * | |
| * The factors are stored densely (O(n^2) memory, like the dense LU | |
| * decompositions). | |
| * | |
| * References: I. Gohberg, T. Kailath, V. Olshevsky, "Fast Gaussian elimination | |
| * with partial pivoting for matrices with displacement structure," Math. Comp. | |
| * 64 (1995). | |
| * | |
| * \tparam Scalar_ the scalar type, real or complex. | |
| * | |
| * \sa class Cauchy | |
| */ | |
| template <typename Scalar_> | |
| class CauchyLU : public SolverBase<CauchyLU<Scalar_>> { | |
| public: | |
| using Base = SolverBase<CauchyLU>; | |
| friend class SolverBase<CauchyLU>; | |
| EIGEN_GENERIC_PUBLIC_INTERFACE(CauchyLU) | |
| using DenseMatrix = Matrix<Scalar, Dynamic, Dynamic>; | |
| using DenseVector = Matrix<Scalar, Dynamic, 1>; | |
| /** Default constructor; call \ref compute before \ref solve. */ | |
| CauchyLU() : m_isInitialized(false), m_info(InvalidInput) {} | |
| /** Constructs and factorizes from the square Cauchy matrix \a C. */ | |
| template <int Rows_, int Cols_> | |
| explicit CauchyLU(const Cauchy<Scalar, Rows_, Cols_>& C) : m_isInitialized(false), m_info(InvalidInput) { | |
| compute(C); | |
| } | |
| /** Factorizes the square Cauchy matrix \a C as \c P*C = L*U by the GKO | |
| * recursion with partial pivoting. The Schur complement retains the form | |
| * \f[ S_{ij}=\frac{a_i b_j}{x_i-y_j}, \qquad | |
| * a_i\leftarrow a_i\frac{x_i-x_k}{x_i-y_k},\quad | |
| * b_j\leftarrow b_j\frac{y_j-y_k}{y_j-x_k}. \f] | |
| * This follows from the rank-one displacement | |
| * \f$D_x C-C D_y=\mathbf{1}\mathbf{1}^T\f$. \sa solve */ | |
| template <int Rows_, int Cols_> | |
| CauchyLU& compute(const Cauchy<Scalar, Rows_, Cols_>& C) { | |
| EIGEN_INTERNAL_ASSERT_SQUARE_MSG(C, "CauchyLU requires a square Cauchy matrix"); | |
| const Index n = C.rows(); | |
| DenseVector x = C.rowNodes(); | |
| const DenseVector y = C.colNodes(); | |
| DenseVector a = DenseVector::Ones(n); | |
| DenseVector b = DenseVector::Ones(n); | |
| m_lu.resize(n, n); | |
| m_perm.resize(static_cast<std::size_t>(n)); | |
| m_info = Success; | |
| for (Index k = 0; k < n; ++k) { | |
| Index piv = k; | |
| RealScalar best(-1); | |
| for (Index i = k; i < n; ++i) { | |
| m_lu(i, k) = a[i] * b[k] / (x[i] - y[k]); | |
| const RealScalar mag = numext::abs(m_lu(i, k)); | |
| if ((numext::isnan)(mag) || mag > best) { | |
| best = mag; | |
| piv = i; | |
| } | |
| } | |
| if (piv != k) { | |
| m_lu.row(piv).head(k + 1).swap(m_lu.row(k).head(k + 1)); | |
| std::swap(x[piv], x[k]); | |
| std::swap(a[piv], a[k]); | |
| } | |
| m_perm[static_cast<std::size_t>(k)] = piv; | |
| const Scalar pivot = m_lu(k, k); | |
| if (pivot == Scalar(0) || !(numext::isfinite)(pivot)) { | |
| m_info = NumericalIssue; | |
| m_lu.row(k).tail(n - k - 1).setZero(); | |
| m_lu.col(k).tail(n - k - 1).setZero(); | |
| continue; | |
| } | |
| m_lu.col(k).tail(n - k - 1) /= pivot; | |
| for (Index j = k + 1; j < n; ++j) { | |
| m_lu(k, j) = a[k] * b[j] / (x[k] - y[j]); | |
| if (!(numext::isfinite)(m_lu(k, j))) m_info = NumericalIssue; | |
| } | |
| for (Index i = k + 1; i < n; ++i) a[i] *= (x[i] - x[k]) / (x[i] - y[k]); | |
| for (Index j = k + 1; j < n; ++j) b[j] *= (y[j] - y[k]) / (y[j] - x[k]); | |
| } | |
| m_isInitialized = true; | |
| return *this; | |
| } | |
| Index rows() const noexcept { return m_lu.rows(); } | |
| Index cols() const noexcept { return m_lu.cols(); } | |
| /** \returns \c Success, or \c NumericalIssue when the factorization encounters | |
| * a zero or non-finite entry that prevents a usable factorization. */ | |
| ComputationInfo info() const { | |
| eigen_assert(m_isInitialized && "CauchyLU is not initialized."); | |
| return m_info; | |
| } | |
| #ifdef EIGEN_PARSED_BY_DOXYGEN | |
| /** \returns the solution \c u of \c C*u = \a b, as a lazily evaluated | |
| * expression. Supports multiple right-hand sides. The transposed and adjoint | |
| * systems reuse the same factorization through \c transpose().solve(b) and | |
| * \c adjoint().solve(b). \pre \ref compute has been called. */ | |
| template <typename Rhs> | |
| inline const Solve<CauchyLU, Rhs> solve(const MatrixBase<Rhs>& b) const; | |
| #endif | |
| #ifndef EIGEN_PARSED_BY_DOXYGEN | |
| /** \internal P*C = L*U, so C*u = b becomes L*U*u = P*b. */ | |
| template <typename RhsType, typename DstType> | |
| void _solve_impl(const RhsType& rhs, DstType& dst) const { | |
| dst = rhs; | |
| for (Index k = 0; k < rows(); ++k) { | |
| const Index piv = m_perm[static_cast<std::size_t>(k)]; | |
| if (piv != k) dst.row(k).swap(dst.row(piv)); | |
| } | |
| m_lu.template triangularView<UnitLower>().solveInPlace(dst); | |
| m_lu.template triangularView<Upper>().solveInPlace(dst); | |
| } | |
| /** \internal C^T = U^T L^T P, so C^T*w = b becomes U^T (L^T (P w)) = b: solve | |
| * the transposed triangles, then undo the transpositions in reverse order; | |
| * conjugated on the way in and out for the adjoint. */ | |
| template <bool Conjugate, typename RhsType, typename DstType> | |
| void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const { | |
| dst = rhs.template conjugateIf<Conjugate>(); | |
| m_lu.template triangularView<Upper>().transpose().solveInPlace(dst); | |
| m_lu.template triangularView<UnitLower>().transpose().solveInPlace(dst); | |
| for (Index k = rows() - 1; k >= 0; --k) { | |
| const Index piv = m_perm[static_cast<std::size_t>(k)]; | |
| if (piv != k) dst.row(k).swap(dst.row(piv)); | |
| } | |
| if (Conjugate) dst = dst.conjugate().eval(); | |
| } | |
| #endif | |
| private: | |
| DenseMatrix m_lu; | |
| std::vector<Index> m_perm; // transposition applied at each elimination step | |
| bool m_isInitialized; | |
| ComputationInfo m_info; | |
| }; | |
| namespace internal { | |
| // Single product specialization covering every product tag; see the note in | |
| // Circulant.h. | |
| template <typename Scalar_, int Rows_, int Cols_, typename Rhs, int ProductTag> | |
| struct generic_product_impl<Cauchy<Scalar_, Rows_, Cols_>, Rhs, StructuredShape, DenseShape, ProductTag> | |
| : structured_product_impl<Cauchy<Scalar_, Rows_, Cols_>, Rhs> {}; | |
| } // namespace internal | |
| #if EIGEN_COMP_CXXVER < 17 | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::RowsAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::ColsAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::MaxRowsAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::MaxColsAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::SizeAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr int Cauchy<Scalar_, Rows_, Cols_>::MaxSizeAtCompileTime; | |
| template <typename Scalar_, int Rows_, int Cols_> | |
| constexpr bool Cauchy<Scalar_, Rows_, Cols_>::IsRowMajor; | |
| #endif | |
| } // namespace Eigen | |
| #endif // EIGEN_STRUCTURED_CAUCHY_H |