| // 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] N. J. Higham, "Accuracy and Stability of Numerical Algorithms", 2nd ed., |
| // SIAM, 2002, chapter 27. Avoiding spurious overflow by rescaling with |
| // powers of two, the technique behind determinant()'s balanced |
| // accumulation and the scaled Horner recurrence of addProduct(). |
| // [2] P. H. Sterbenz, "Floating-Point Computation", Prentice-Hall, 1974. |
| // Scaling by a power of two is exact, the property the balanced |
| // accumulation and the scaled Horner recurrence rely on. |
| // [3] 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. |
| // [4] L. Reichel, "Newton Interpolation at Leja Points", BIT 30 (1990), |
| // 332--346. Leja ordering controls growth in the Newton representation; |
| // BjorckPereyra uses it for genuinely complex node sets. |
| |
| #ifndef EIGEN_STRUCTURED_VANDERMONDE_H |
| #define EIGEN_STRUCTURED_VANDERMONDE_H |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| |
| template <typename Scalar_, int Rows_ = Dynamic, int Cols_ = Dynamic> |
| class Vandermonde; |
| |
| template <typename Scalar_> |
| class BjorckPereyra; |
| |
| namespace internal { |
| |
| template <typename Scalar_, int Rows_, int Cols_> |
| struct traits<Vandermonde<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: the makeVandermonde() factories (and any |
| // function returning the operator by value) produce 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), negligible |
| // against the O(mn) product evaluation. |
| static constexpr int Flags = Rows_ == 1 && Cols_ != 1 ? RowMajorBit : 0; |
| }; |
| |
| template <typename Scalar_, int Rows_, int Cols_> |
| struct evaluator_traits<Vandermonde<Scalar_, Rows_, Cols_>> { |
| using Kind = IndexBased; |
| using Shape = StructuredShape; |
| }; |
| |
| // Core rewrites alpha * (lhs * rhs) as (alpha * lhs) * rhs. The scaled |
| // Vandermonde wrapper needs coefficient and BLAS metadata to participate in |
| // that general dense-expression machinery. |
| template <typename Scalar_, int Rows_, int Cols_> |
| struct evaluator<Vandermonde<Scalar_, Rows_, Cols_>> : evaluator_base<Vandermonde<Scalar_, Rows_, Cols_>> { |
| using XprType = Vandermonde<Scalar_, Rows_, Cols_>; |
| using Scalar = Scalar_; |
| static constexpr int CoeffReadCost = HugeCost; |
| static constexpr int Flags = traits<XprType>::Flags; |
| static constexpr int Alignment = 0; |
| |
| EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE explicit evaluator(const XprType& xpr) : m_xpr(xpr) {} |
| |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const { return m_xpr.coeff(row, col); } |
| |
| private: |
| const XprType& m_xpr; |
| }; |
| |
| template <typename Scalar_, int Rows_, int Cols_> |
| struct blas_traits<Vandermonde<Scalar_, Rows_, Cols_>> { |
| using XprType = Vandermonde<Scalar_, Rows_, Cols_>; |
| using Scalar = Scalar_; |
| using ExtractType = const XprType&; |
| using ExtractType_ = XprType; |
| using DirectLinearAccessType = XprType; |
| static constexpr bool IsComplex = NumTraits<Scalar>::IsComplex; |
| static constexpr bool IsTransposed = false; |
| static constexpr bool NeedToConjugate = false; |
| static constexpr bool HasUsableDirectAccess = false; |
| static constexpr bool HasScalarFactor = false; |
| static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE ExtractType extract(const XprType& x) { return x; } |
| static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE Scalar extractScalarFactor(const XprType&) { return Scalar(1); } |
| }; |
| |
| template <typename Scalar_> |
| struct traits<BjorckPereyra<Scalar_>> : traits<Matrix<Scalar_, Dynamic, Dynamic>> { |
| using XprKind = MatrixXpr; |
| using StorageKind = SolverStorage; |
| using StorageIndex = int; |
| using BaseTraits = traits<Matrix<Scalar_, Dynamic, Dynamic>>; |
| static constexpr int Flags = BaseTraits::Flags & RowMajorBit; |
| static constexpr int CoeffReadCost = Dynamic; |
| }; |
| |
| } // namespace internal |
| |
| /** \ingroup StructuredMatrices_Module |
| * \class Vandermonde |
| * \brief An \c m x \c n Vandermonde matrix represented by its node vector. |
| * |
| * A Vandermonde matrix has entry \c (i,j) equal to \f$ x_i^j \f$, where \c x is |
| * the node vector. Thus |
| * \f[ (Va)_i = \sum_{j=0}^{n-1} a_j x_i^j, \qquad |
| * p_{n-1}=a_{n-1},\quad p_j=a_j+x_i p_{j+1}. \f] |
| * The class stores only the \c m nodes; products evaluate this Horner recurrence |
| * rule at O(mn) operations -- the same cost as a dense product, but with O(m) |
| * storage and without ever forming the matrix. |
| * |
| * Square systems are solved in O(n^2) by the Björck-Pereyra algorithm (class |
| * \ref BjorckPereyra), whose \c transpose().solve() form covers the dual |
| * (moment) system. There is no fast transposed \em product, so the class is not |
| * closed under transposition and rectangular least-squares problems are best |
| * handled by a dense QR of the materialized matrix. |
| * |
| * Because \c operator* returns an Eigen product expression, a \c Vandermonde |
| * 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 BiCGSTAB<Vandermonde<double>,IdentityPreconditioner>): the default |
| * preconditioners read individual coefficients through \c col() or |
| * \c InnerIterator, which the structured operators do not expose. |
| * |
| * \warning Vandermonde matrices with real nodes are exponentially |
| * ill-conditioned: the condition number grows at least like \f$ 2^n \f$ for any |
| * real node configuration (Beckermann, 2000). Solves remain surprisingly |
| * accurate for monotone node sets and sign-alternating right-hand sides |
| * (Björck-Pereyra's celebrated property, see Higham, ASNA ch. 22), but forward |
| * errors necessarily scale with the conditioning in general. Complex nodes on |
| * the unit circle are the well-conditioned case: for the n-th roots of unity, |
| * \f$ V/\sqrt{n} \f$ is unitary. |
| * |
| * \tparam Scalar_ a floating-point-like real or complex scalar supporting |
| * Eigen's scalar math hooks, including \c isfinite, \c frexp and \c ldexp (and |
| * \c log for complex solver ordering). Integer types are rejected. |
| * \tparam Rows_ the number of rows (nodes) at compile time, or \c Dynamic. |
| * \tparam Cols_ the number of columns (powers) at compile time, or \c Dynamic. |
| * |
| * \sa class BjorckPereyra, makeVandermonde() |
| */ |
| template <typename Scalar_, int Rows_, int Cols_> |
| class Vandermonde : public EigenBase<Vandermonde<Scalar_, Rows_, Cols_>> { |
| public: |
| using Derived = Vandermonde; |
| using StorageBaseType = Vandermonde; |
| using Scalar = Scalar_; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| using StorageIndex = int; |
| static constexpr int NodeOptions = Rows_ == Dynamic ? AutoAlign : DontAlign; |
| using NodeVector = Matrix<Scalar, Rows_, 1, NodeOptions>; |
| using Nested = Vandermonde; |
| |
| 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 int Flags = internal::traits<Vandermonde>::Flags; |
| static constexpr bool IsRowMajor = (Flags & RowMajorBit) != 0; |
| // Deliberately no IsVectorAtCompileTime: Ref<const Vandermonde>'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. |
| |
| EIGEN_STATIC_ASSERT_NON_INTEGER(RealScalar) |
| EIGEN_MAKE_SCALAR_BINARY_OP_ONTHELEFT(operator*, internal::scalar_product_op) |
| |
| /** Builds an \c m x \a cols Vandermonde matrix from the \c m nodes \a nodes. */ |
| template <typename Derived> |
| Vandermonde(const MatrixBase<Derived>& nodes, Index cols) : m_x(nodes), m_cols(cols) { |
| EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived) |
| eigen_assert(m_x.size() > 0 && m_cols > 0 && "Vandermonde must be non-empty"); |
| eigen_assert((Cols_ == Dynamic || Cols_ == cols) && "cols does not match the compile-time column count"); |
| } |
| |
| /** Builds the square Vandermonde matrix of the nodes \a nodes. */ |
| template <typename Derived> |
| explicit Vandermonde(const MatrixBase<Derived>& nodes) : Vandermonde(nodes, nodes.size()) { |
| EIGEN_STATIC_ASSERT(Rows_ == Dynamic || Cols_ == Dynamic || Rows_ == Cols_, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES) |
| EIGEN_STATIC_ASSERT( |
| Cols_ == Dynamic || Derived::SizeAtCompileTime == Dynamic || Cols_ == Derived::SizeAtCompileTime, |
| YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES) |
| } |
| |
| EIGEN_DEVICE_FUNC Index rows() const { return m_x.size(); } |
| EIGEN_DEVICE_FUNC Index cols() const { return m_cols; } |
| |
| /** \returns the node vector. */ |
| const NodeVector& nodes() const { return m_x; } |
| |
| /** \returns the coefficient at row \a row and column \a col, \f$ x_i^j \f$. */ |
| EIGEN_DEVICE_FUNC Scalar coeff(Index row, Index col) const { |
| Scalar p(1); |
| const Scalar xi = m_x.coeff(row); |
| for (Index t = 0; t < col; ++t) p *= xi; |
| return p; |
| } |
| |
| /** \returns the determinant of a \b square Vandermonde matrix through the |
| * closed form \f$ \prod_{i<j} (x_j - x_i) \f$, in O(n^2) operations. The |
| * product is accumulated in the balanced form \c m * 2^e (the split |
| * fraction/exponent determinant convention of LINPACK's xGEDI [3]) -- every |
| * factor and the running product are renormalized to unit magnitude with the |
| * power of two tracked separately, an exact rescaling [2] -- so the partial |
| * products can neither overflow nor underflow when the determinant itself is |
| * representable, whatever the spread of the nodes. Zero factors (repeated |
| * nodes, giving an exactly singular matrix) and non-finite factors propagate |
| * exactly. */ |
| Scalar determinant() const { |
| eigen_assert(rows() == cols() && "Vandermonde::determinant requires a square matrix"); |
| const Index n = rows(); |
| Scalar det(1); |
| internal::structured_exponent_type exponent = 0; |
| for (Index j = 1; j < n; ++j) |
| for (Index i = 0; i < j; ++i) { |
| // A node difference can overflow even though the determinant is |
| // representable (e.g. nodes near +-max); the guarded difference then |
| // enters at half scale with the factor of two carried by the running |
| // exponent. |
| int shift; |
| const Scalar diff = internal::structured_guarded_diff(m_x.coeff(j), m_x.coeff(i), shift); |
| exponent += shift; |
| det = internal::structured_balance(det * internal::structured_balance(diff, exponent), exponent); |
| } |
| // ldexp saturates cleanly to zero / infinity once the accumulated exponent |
| // leaves the representable range; the clamp only guards the narrowing to int. |
| return internal::structured_ldexp_clamped(det, exponent); |
| } |
| |
| /** \internal Writes the dense representation into \a dst: column \c j is the |
| * elementwise product of column \c j-1 with the nodes, so only cumulative |
| * columnwise products are involved. Invoked through \c dense = vandermonde; */ |
| template <typename Dest> |
| void evalTo(Dest& dst) const { |
| dst.col(0).setOnes(); |
| for (Index j = 1; j < m_cols; ++j) dst.col(j) = dst.col(j - 1).cwiseProduct(m_x); |
| } |
| |
| /** \internal Computes \c dst += (*this), see evalTo(). */ |
| template <typename Dest> |
| void addTo(Dest& dst) const { |
| NodeVector p = NodeVector::Ones(rows()); |
| dst.col(0) += p; |
| for (Index j = 1; j < m_cols; ++j) { |
| p = p.cwiseProduct(m_x); |
| dst.col(j) += p; |
| } |
| } |
| |
| /** \internal Computes \c dst -= (*this), see evalTo(). */ |
| template <typename Dest> |
| void subTo(Dest& dst) const { |
| NodeVector p = NodeVector::Ones(rows()); |
| dst.col(0) -= p; |
| for (Index j = 1; j < m_cols; ++j) { |
| p = p.cwiseProduct(m_x); |
| dst.col(j) -= p; |
| } |
| } |
| |
| /** \returns the product expression \c (*this) * \a a: the polynomial with |
| * ascending coefficients \c a (per column) evaluated at every node by Horner's |
| * rule, at O(mn) operations and O(1) 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 a, and |
| * \c .noalias() skips it. */ |
| template <typename Rhs> |
| Product<Vandermonde, Rhs> operator*(const MatrixBase<Rhs>& a) const { |
| EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic || |
| int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime), |
| INVALID_MATRIX_PRODUCT) |
| eigen_assert(a.rows() == cols() && "invalid product: dimensions do not match"); |
| return Product<Vandermonde, Rhs>(*this, a.derived()); |
| } |
| |
| /** \internal Computes \c dst += alpha * (*this) * rhs by Horner's rule. |
| * \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. |
| * |
| * Horner intermediates can overflow even when the polynomial value itself is |
| * representable (e.g. coefficients near the overflow threshold evaluated at a |
| * node of magnitude 1/2). Each (node, column) pair is therefore screened with |
| * a conservative exponent bound: when no intermediate can overflow -- every |
| * input of moderate magnitude -- the plain Horner loop runs, matching the naive |
| * evaluation step for step, though not necessarily bit for bit: the compiler |
| * may contract one loop's multiply-add into an FMA and not the other's. |
| * Otherwise scaledHorner() keeps the running value in |
| * the balanced form m * 2^e of determinant(). Non-finite nodes or coefficients |
| * also take the plain loop, which propagates Inf/NaN entrywise like a dense |
| * product; and 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. |
| * |
| * The column's finiteness and its exponent bound come from the single |
| * fast-max pass of internal::structured_exponent_bound_finite(), which is not |
| * guaranteed to propagate NaN. That is sufficient here for the same reason as |
| * in the FFT products: an Inf in NaN-free data always surfaces in a fast |
| * maximum, and a column containing NaN yields the NaN results dense-product |
| * semantics require through the plain loop and the scaled recurrence alike |
| * (every Horner step folds the NaN coefficient in, and the balancing helpers |
| * pass non-finite values through), so missing a NaN cannot change the |
| * result. */ |
| template <typename Dest, typename Rhs, typename ProductScalar> |
| void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const { |
| const Index m = rows(), n = m_cols; |
| using Exponent = internal::structured_exponent_type; |
| eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match"); |
| const bool unitAlpha = alpha == ProductScalar(1); |
| int log2n = 0; // n < 2^log2n: bounds the number of addends of the Horner sum |
| for (Index t = n; t > 0; t /= 2) ++log2n; |
| for (Index k = 0; k < rhs.cols(); ++k) { |
| int colExp; // max modulus < 2^colExp; 0 for a zero or non-finite column |
| const bool colFinite = internal::structured_exponent_bound_finite(rhs.col(k), colExp); |
| for (Index i = 0; i < m; ++i) { |
| const Scalar xi = m_x.coeff(i); |
| // |p_j| < 2^(colExp+log2n+(n-1) max(xiExp,0)+1). |
| const Exponent intermediateBound = |
| Exponent(colExp) + Exponent(log2n) + (Exponent(n) - 1) * Exponent(numext::maxi(exponentBound(xi), 0)) + 2; |
| const bool plain = !colFinite || !(numext::isfinite)(xi) || |
| intermediateBound <= Exponent(NumTraits<RealScalar>::max_exponent()); |
| ProductScalar acc; |
| if (plain) { |
| acc = rhs.coeff(n - 1, k); |
| for (Index j = n - 2; j >= 0; --j) acc = acc * xi + rhs.coeff(j, k); |
| } else { |
| acc = scaledHorner<ProductScalar>(xi, rhs, k); |
| } |
| dst.coeffRef(i, k) += unitAlpha ? acc : ProductScalar(alpha * acc); |
| } |
| } |
| } |
| |
| private: |
| /** \internal \returns an exponent bound \c e with \c |z| < 2^e (the modulus |
| * for a complex \a z), or 0 for a zero or non-finite \a z; the |
| * single-coefficient analogue of internal::structured_exponent_bound(). */ |
| template <typename T> |
| static int exponentBound(const T& z) { |
| return exponentBoundImpl(z, internal::bool_constant<NumTraits<T>::IsComplex>()); |
| } |
| |
| template <typename T> |
| static int exponentBoundImpl(const T& z, std::false_type) { |
| if (!(numext::abs(z) > T(0)) || !(numext::isfinite)(z)) return 0; |
| int e; |
| EIGEN_USING_STD(frexp); |
| frexp(z, &e); |
| return e; |
| } |
| |
| template <typename T> |
| static int exponentBoundImpl(const T& z, std::true_type) { |
| using Real = typename NumTraits<T>::Real; |
| const Real mag = numext::maxi(numext::abs(numext::real(z)), numext::abs(numext::imag(z))); |
| if (!(mag > Real(0)) || !(numext::isfinite)(mag)) return 0; |
| int e; |
| EIGEN_USING_STD(frexp); |
| frexp(mag, &e); |
| return e + 1; // the modulus is at most sqrt(2) times the largest component |
| } |
| |
| /** \internal \returns \a z * 2^e computed through two exact half-factors, so |
| * the factors themselves stay representable for the exponent swings the scaled |
| * Horner recurrence produces (|e| up to about twice the scalar's exponent |
| * range on the negative side, at most one exponent range on the positive |
| * side); a factor past the underflow threshold flushes to zero together with |
| * the then-negligible contribution it scales. */ |
| template <typename T> |
| static T twoHalfScale(const T& z, internal::structured_exponent_type e) { |
| using Real = typename NumTraits<T>::Real; |
| constexpr internal::structured_exponent_type kMaxExponent = internal::structured_exponent_type(1) << 24; |
| const int ec = static_cast<int>(numext::mini(numext::maxi(e, -kMaxExponent), kMaxExponent)); |
| const Real h1 = numext::ldexp(Real(1), ec / 2); |
| const Real h2 = numext::ldexp(Real(1), ec - ec / 2); |
| return (z * h1) * h2; |
| } |
| |
| /** \internal Evaluates the polynomial with ascending coefficients |
| * \c rhs.col(k) at the node \a xi, keeping the running value in the balanced |
| * form \c acc * 2^exponent of determinant() (overflow-avoiding power-of-two |
| * rescaling [1], exact by [2]): the node enters through its unit mantissa |
| * with its exponent folded into the running one, the mantissa is |
| * renormalized after every step, and each coefficient is folded into the |
| * running frame scaled by an exact power of two split into two half-factors |
| * (when the coefficient dominates the frame, the frame is rebased onto the |
| * coefficient's exponent instead). Intermediates can therefore neither |
| * overflow nor underflow, and the final ldexp saturates to +-Inf / +-0 exactly |
| * where the true value leaves the representable range. |
| * |
| * An exactly zero mantissa carries no scale, so the frame is reset before |
| * every fold: after an exact cancellation (or a zero node annihilating the |
| * running value) a stale huge frame would otherwise underflow the next small |
| * coefficient to zero. A cancellation that leaves a tiny nonzero mantissa |
| * needs no such care -- the frexp renormalization rebases the frame to the |
| * surviving magnitude (which is a multiple of the operands' unit roundoff, |
| * hence never subnormal for real scalars, and frexp is exact on subnormal |
| * component values regardless). |
| * \pre the node is finite and the column passed the fast-max routing |
| * predicate of addProduct(): it holds no Inf without an accompanying NaN. A |
| * NaN-bearing column can reach the recurrence when the fast maximum misses |
| * the NaN; every helper passes non-finite values through, so it produces the |
| * NaN result dense-product semantics require. */ |
| template <typename ProductScalar, typename Rhs> |
| ProductScalar scaledHorner(const Scalar& xi, const Rhs& rhs, Index k) const { |
| using Exponent = internal::structured_exponent_type; |
| Exponent xiE = 0; |
| const Scalar xiMant = internal::structured_balance(xi, xiE); // xi = xiMant * 2^xiE, exactly |
| ProductScalar acc(0); |
| Exponent exponent = 0; // running value = acc * 2^exponent |
| for (Index j = m_cols - 1; j >= 0; --j) { |
| if (j < m_cols - 1) { |
| exponent += xiE; |
| acc = internal::structured_balance(acc * xiMant, exponent); |
| } |
| // A zero value has no scale: reset the frame so the next coefficient |
| // enters at its own magnitude instead of underflowing in a stale one. |
| if (acc == ProductScalar(0)) exponent = 0; |
| const ProductScalar aj(rhs.coeff(j, k)); |
| if (aj == ProductScalar(0)) continue; |
| const Exponent ajExp = exponentBound(aj); |
| if (exponent < ajExp) { |
| // The coefficient dominates the running frame: rebase onto the |
| // coefficient's exponent. The running value rescales exactly, or |
| // underflows harmlessly once it is negligible against the coefficient. |
| acc = twoHalfScale(acc, exponent - ajExp); |
| exponent = ajExp; |
| } |
| acc = internal::structured_balance(acc + twoHalfScale(aj, -exponent), exponent); |
| } |
| return internal::structured_ldexp_clamped(acc, exponent); |
| } |
| |
| NodeVector m_x; |
| Index m_cols; |
| }; |
| |
| /** \ingroup StructuredMatrices_Module |
| * \returns an \c m x \a cols \ref Vandermonde operator with node vector \a nodes; |
| * the compile-time row count is deduced from \a nodes. */ |
| template <typename Derived> |
| Vandermonde<typename Derived::Scalar, Derived::SizeAtCompileTime, Dynamic> makeVandermonde( |
| const MatrixBase<Derived>& nodes, Index cols) { |
| return Vandermonde<typename Derived::Scalar, Derived::SizeAtCompileTime, Dynamic>(nodes, cols); |
| } |
| |
| /** \ingroup StructuredMatrices_Module |
| * \returns the square \ref Vandermonde operator of the nodes \a nodes. */ |
| template <typename Derived> |
| Vandermonde<typename Derived::Scalar, Derived::SizeAtCompileTime, Derived::SizeAtCompileTime> makeVandermonde( |
| const MatrixBase<Derived>& nodes) { |
| return Vandermonde<typename Derived::Scalar, Derived::SizeAtCompileTime, Derived::SizeAtCompileTime>(nodes); |
| } |
| |
| /** \ingroup StructuredMatrices_Module |
| * \class BjorckPereyra |
| * \brief Björck-Pereyra O(n^2) solver for square Vandermonde systems. |
| * |
| * Solves \c V*a = f -- polynomial interpolation: find the coefficients of the |
| * polynomial taking values \c f at the nodes -- in O(n^2) operations and O(n) |
| * storage, via divided differences in the Newton basis followed by the basis |
| * change to monomials (Björck & Pereyra, 1970; Golub & Van Loan, Alg. 4.6.2). |
| * The transposed (dual, or moment) system \f$ V^T w = b \f$ is solved by the |
| * companion dual recurrences through the standard \c SolverBase idiom: |
| * \code |
| * BjorckPereyra<double> bp(V); // or bp.compute(V); |
| * VectorXd a = bp.solve(f); // solve V * a = f |
| * VectorXd w = bp.transpose().solve(b); // solve V^T * w = b |
| * VectorXd u = bp.adjoint().solve(b); // solve V^H * u = b |
| * \endcode |
| * |
| * There is no factorization: \c compute() stores the nodes (and flags exactly |
| * repeated or non-finite nodes through \c info()), and each solve runs the |
| * O(n^2) recurrences directly. Genuinely complex node sets are put in a |
| * deterministic Leja order to control growth in the Newton representation; |
| * real nodes, including complex scalars with zero imaginary parts, retain their |
| * input order and its useful monotonicity properties. |
| * |
| * Despite the exponential conditioning of real-node Vandermonde matrices, the |
| * computed solution is often far more accurate than the conditioning suggests: |
| * for monotonically ordered nodes and a right-hand side with alternating signs |
| * the forward error is governed by a small relative-perturbation bound |
| * independent of the condition number (Higham, ASNA ch. 22). |
| * |
| * \tparam Scalar_ a floating-point-like real or complex scalar supporting |
| * Eigen's scalar math hooks, including \c isfinite (and \c abs and \c log for |
| * complex node ordering). Integer types are rejected. |
| * |
| * \sa class Vandermonde |
| */ |
| template <typename Scalar_> |
| class BjorckPereyra : public SolverBase<BjorckPereyra<Scalar_>> { |
| public: |
| using Base = SolverBase<BjorckPereyra>; |
| friend class SolverBase<BjorckPereyra>; |
| EIGEN_GENERIC_PUBLIC_INTERFACE(BjorckPereyra) |
| EIGEN_STATIC_ASSERT_NON_INTEGER(RealScalar) |
| using NodeVector = Matrix<Scalar, Dynamic, 1>; |
| |
| /** Default constructor; call \ref compute before \ref solve. */ |
| BjorckPereyra() : m_isInitialized(false), m_info(InvalidInput) {} |
| |
| /** Constructs the solver for the square Vandermonde matrix \a V. */ |
| template <int Rows_, int Cols_> |
| explicit BjorckPereyra(const Vandermonde<Scalar, Rows_, Cols_>& V) : m_isInitialized(false), m_info(InvalidInput) { |
| compute(V); |
| } |
| |
| /** Stores the nodes of the square Vandermonde matrix \a V, checks that they |
| * are finite and distinct, and computes a Leja order for genuinely complex |
| * nodes. */ |
| template <int Rows_, int Cols_> |
| BjorckPereyra& compute(const Vandermonde<Scalar, Rows_, Cols_>& V) { |
| EIGEN_STATIC_ASSERT(Rows_ == Dynamic || Cols_ == Dynamic || Rows_ == Cols_, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES) |
| eigen_assert(V.rows() == V.cols() && "BjorckPereyra requires a square Vandermonde matrix"); |
| m_x = V.nodes(); |
| m_order.clear(); |
| m_info = Success; |
| const Index n = m_x.size(); |
| if (!m_x.allFinite()) m_info = InvalidInput; |
| for (Index j = 1; j < n && m_info == Success; ++j) |
| for (Index i = 0; i < j; ++i) { |
| if (m_x[i] == m_x[j]) { |
| m_info = NumericalIssue; |
| break; |
| } |
| } |
| if (m_info == Success) initializeNodeOrder(m_x, internal::bool_constant<NumTraits<Scalar>::IsComplex>()); |
| m_isInitialized = true; |
| return *this; |
| } |
| |
| Index rows() const noexcept { return m_x.size(); } |
| Index cols() const noexcept { return m_x.size(); } |
| |
| /** \returns \c Success, \c NumericalIssue when the nodes contain an exact |
| * duplicate (the matrix is singular), or \c InvalidInput for a non-finite |
| * node. */ |
| ComputationInfo info() const { |
| eigen_assert(m_isInitialized && "BjorckPereyra is not initialized."); |
| return m_info; |
| } |
| |
| #ifdef EIGEN_PARSED_BY_DOXYGEN |
| /** \returns the solution \c a of \c V*a = \a f, as a lazily evaluated |
| * expression. Supports multiple right-hand sides. The transposed and adjoint |
| * systems are solved through \c transpose().solve(b) and \c adjoint().solve(b). |
| * \pre \ref compute has been called. */ |
| template <typename Rhs> |
| inline const Solve<BjorckPereyra, Rhs> solve(const MatrixBase<Rhs>& f) const; |
| #endif |
| |
| #ifndef EIGEN_PARSED_BY_DOXYGEN |
| /** \internal Primal solve V*a = rhs: divided differences (Newton coefficients), |
| * then the Newton-to-monomial basis change. */ |
| template <typename RhsType, typename DstType> |
| void _solve_impl(const RhsType& rhs, DstType& dst) const { |
| using RhsScalar = typename RhsType::Scalar; |
| using WorkScalar = typename DstType::Scalar; |
| using ProductOp = internal::scalar_product_op<Scalar, RhsScalar>; |
| EIGEN_CHECK_BINARY_COMPATIBILITY(ProductOp, Scalar, RhsScalar) |
| |
| const Index n = m_x.size(); |
| dst = rhs; |
| Matrix<WorkScalar, Dynamic, 1> permuted; |
| if (!m_order.empty()) permuted.resize(n); |
| for (Index k = 0; k < rhs.cols(); ++k) { |
| auto a = dst.col(k); |
| if (!m_order.empty()) { |
| for (Index i = 0; i < n; ++i) permuted[i] = a[m_order[static_cast<std::size_t>(i)]]; |
| a = permuted; |
| } |
| for (Index j = 0; j < n - 1; ++j) |
| for (Index i = n - 1; i > j; --i) a[i] = (a[i] - a[i - 1]) / (m_x[i] - m_x[i - j - 1]); |
| for (Index j = n - 2; j >= 0; --j) |
| for (Index i = j; i < n - 1; ++i) a[i] -= m_x[j] * a[i + 1]; |
| } |
| } |
| |
| /** \internal Transposed (dual) solve V^T*w = rhs: the transposes of the primal |
| * elementary steps, applied 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 { |
| using RhsScalar = typename RhsType::Scalar; |
| using WorkScalar = typename DstType::Scalar; |
| using ProductOp = internal::scalar_product_op<Scalar, RhsScalar>; |
| EIGEN_CHECK_BINARY_COMPATIBILITY(ProductOp, Scalar, RhsScalar) |
| |
| const Index n = m_x.size(); |
| dst = rhs.template conjugateIf<Conjugate>(); |
| Matrix<WorkScalar, Dynamic, 1> permuted; |
| if (!m_order.empty()) permuted.resize(n); |
| for (Index k = 0; k < rhs.cols(); ++k) { |
| auto w = dst.col(k); |
| for (Index j = 0; j < n - 1; ++j) |
| for (Index i = n - 1; i > j; --i) w[i] -= m_x[j] * w[i - 1]; |
| for (Index j = n - 2; j >= 0; --j) { |
| w.tail(n - j - 1).array() /= (m_x.tail(n - j - 1) - m_x.head(n - j - 1)).array(); |
| for (Index i = j; i < n - 1; ++i) w[i] -= w[i + 1]; |
| } |
| |
| if (!m_order.empty()) { |
| permuted = w; |
| for (Index i = 0; i < n; ++i) w[m_order[static_cast<std::size_t>(i)]] = permuted[i]; |
| } |
| } |
| if (Conjugate) dst = dst.conjugate().eval(); |
| } |
| #endif |
| |
| private: |
| static RealScalar lejaLogAbs(const Scalar& z) { |
| const RealScalar re = numext::abs(numext::real(z)); |
| const RealScalar im = numext::abs(numext::imag(z)); |
| const RealScalar scale = numext::maxi(re, im); |
| if (scale == RealScalar(0)) return -NumTraits<RealScalar>::infinity(); |
| const RealScalar scaledRe = re / scale; |
| const RealScalar scaledIm = im / scale; |
| return numext::log(scale) + RealScalar(0.5) * numext::log(scaledRe * scaledRe + scaledIm * scaledIm); |
| } |
| |
| static RealScalar lejaLogDistance(const Scalar& a, const Scalar& b) { |
| int exponent; |
| const Scalar difference = internal::structured_guarded_diff(a, b, exponent); |
| return lejaLogAbs(difference) + RealScalar(exponent) * numext::log(RealScalar(2)); |
| } |
| |
| void initializeNodeOrder(const NodeVector&, std::false_type) {} |
| |
| void initializeNodeOrder(const NodeVector& nodes, std::true_type) { |
| using Real = typename NumTraits<Scalar>::Real; |
| const Index n = nodes.size(); |
| bool genuinelyComplex = false; |
| for (Index i = 0; i < n; ++i) genuinelyComplex = genuinelyComplex || numext::imag(nodes[i]) != Real(0); |
| if (!genuinelyComplex || n < 2) return; |
| |
| const NodeVector original = nodes; |
| |
| m_order.resize(static_cast<std::size_t>(n)); |
| std::vector<char> selected(static_cast<std::size_t>(n), 0); |
| std::vector<RealScalar> scores(static_cast<std::size_t>(n), RealScalar(0)); |
| Index next; // the Leja sequence starts at a node of largest modulus |
| original.unaryExpr(&lejaLogAbs).maxCoeff(&next); |
| |
| for (Index position = 0; position < n; ++position) { |
| m_order[static_cast<std::size_t>(position)] = next; |
| selected[static_cast<std::size_t>(next)] = 1; |
| if (position + 1 == n) break; |
| |
| Index candidate = -1; |
| RealScalar candidateScore = -NumTraits<RealScalar>::infinity(); |
| for (Index i = 0; i < n; ++i) { |
| if (selected[static_cast<std::size_t>(i)]) continue; |
| scores[static_cast<std::size_t>(i)] += lejaLogDistance(original[i], original[next]); |
| if (candidate < 0 || scores[static_cast<std::size_t>(i)] > candidateScore) { |
| candidate = i; |
| candidateScore = scores[static_cast<std::size_t>(i)]; |
| } |
| } |
| next = candidate; |
| } |
| |
| for (Index i = 0; i < n; ++i) m_x[i] = original[m_order[static_cast<std::size_t>(i)]]; |
| } |
| |
| NodeVector m_x; |
| std::vector<Index> m_order; |
| bool m_isInitialized; |
| ComputationInfo m_info; |
| }; |
| |
| namespace internal { |
| |
| /** \internal Solve results use the scalar promoted from the solver and RHS. |
| * Core's generic solve traits retain the RHS scalar, which would discard the |
| * imaginary part when a complex Vandermonde is applied to a real RHS. */ |
| template <typename SolverScalar, typename RhsScalar, |
| bool Compatible = has_ReturnType<ScalarBinaryOpTraits<SolverScalar, RhsScalar>>::value> |
| struct bjorck_pereyra_result_scalar { |
| using type = SolverScalar; |
| }; |
| |
| template <typename SolverScalar, typename RhsScalar> |
| struct bjorck_pereyra_result_scalar<SolverScalar, RhsScalar, true> { |
| using type = typename ScalarBinaryOpTraits<SolverScalar, RhsScalar>::ReturnType; |
| }; |
| |
| template <typename SolverScalar, typename RhsType> |
| struct bjorck_pereyra_solve_traits { |
| using ResultScalar = typename bjorck_pereyra_result_scalar<SolverScalar, typename RhsType::Scalar>::type; |
| using PlainObject = |
| typename make_proper_matrix_type<ResultScalar, Dynamic, RhsType::ColsAtCompileTime, RhsType::PlainObject::Options, |
| Dynamic, RhsType::MaxColsAtCompileTime>::type; |
| }; |
| |
| template <typename Scalar_, typename RhsType> |
| struct solve_traits<BjorckPereyra<Scalar_>, RhsType, Dense> : bjorck_pereyra_solve_traits<Scalar_, RhsType> {}; |
| |
| template <typename Scalar_, typename RhsType> |
| struct solve_traits<Transpose<const BjorckPereyra<Scalar_>>, RhsType, Dense> |
| : bjorck_pereyra_solve_traits<Scalar_, RhsType> {}; |
| |
| template <typename Scalar_, typename RhsType> |
| struct solve_traits<CwiseUnaryOp<scalar_conjugate_op<Scalar_>, const Transpose<const BjorckPereyra<Scalar_>>>, RhsType, |
| Dense> : bjorck_pereyra_solve_traits<Scalar_, RhsType> {}; |
| |
| template <typename Factor, typename Scalar_, int Rows_, int Cols_, typename Plain, typename Rhs> |
| struct scaled_vandermonde_product_impl |
| : generic_product_impl_base< |
| CwiseBinaryOp<scalar_product_op<Factor, Scalar_>, const CwiseNullaryOp<scalar_constant_op<Factor>, Plain>, |
| const Vandermonde<Scalar_, Rows_, Cols_>>, |
| Rhs, scaled_vandermonde_product_impl<Factor, Scalar_, Rows_, Cols_, Plain, Rhs>> { |
| using Op = Vandermonde<Scalar_, Rows_, Cols_>; |
| using ScaledOp = CwiseBinaryOp<scalar_product_op<Factor, Scalar_>, |
| const CwiseNullaryOp<scalar_constant_op<Factor>, Plain>, const Op>; |
| using Scalar = typename Product<ScaledOp, Rhs>::Scalar; |
| |
| template <typename Dest> |
| static void scaleAndAddTo(Dest& dst, const ScaledOp& lhs, const Rhs& rhs, const Scalar& alpha) { |
| using RhsNested = typename nested_eval<Rhs, Rows_>::type; |
| RhsNested actualRhs(rhs); |
| lhs.rhs().addProduct(dst, actualRhs, Scalar(alpha * lhs.lhs().functor().m_other)); |
| } |
| }; |
| |
| // Preserve the Horner kernel after Core introduces the scaled wrapper above; |
| // otherwise the wrapper has DenseShape and falls back to a coefficient product. |
| #define EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(ProductTag) \ |
| template <typename Factor, typename Scalar_, int Rows_, int Cols_, typename Plain, typename Rhs> \ |
| struct generic_product_impl< \ |
| CwiseBinaryOp<scalar_product_op<Factor, Scalar_>, const CwiseNullaryOp<scalar_constant_op<Factor>, Plain>, \ |
| const Vandermonde<Scalar_, Rows_, Cols_>>, \ |
| Rhs, DenseShape, DenseShape, ProductTag> \ |
| : scaled_vandermonde_product_impl<Factor, Scalar_, Rows_, Cols_, Plain, Rhs> {}; |
| |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(CoeffBasedProductMode) |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(LazyCoeffBasedProductMode) |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(OuterProduct) |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(InnerProduct) |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(GemvProduct) |
| EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL(GemmProduct) |
| |
| #undef EIGEN_SCALED_VANDERMONDE_PRODUCT_IMPL |
| |
| template <typename Scalar_, int Rows_, int Cols_, typename Rhs, int ProductTag> |
| struct generic_product_impl<Vandermonde<Scalar_, Rows_, Cols_>, Rhs, StructuredShape, DenseShape, ProductTag> |
| : structured_product_impl<Vandermonde<Scalar_, Rows_, Cols_>, Rhs> {}; |
| |
| } // namespace internal |
| |
| } // namespace Eigen |
| |
| #endif // EIGEN_STRUCTURED_VANDERMONDE_H |