| // 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 |