Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
Cauchy.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9//
10// References:
11// [1] J. J. Dongarra, J. R. Bunch, C. B. Moler and G. W. Stewart, "LINPACK
12// Users' Guide", SIAM, 1979. determinant()'s balanced accumulation follows
13// the convention of its xGEDI routines, which return determinants as a
14// (fraction, exponent) pair to avoid spurious overflow/underflow.
15// [2] P. H. Sterbenz, "Floating-Point Computation", Prentice-Hall, 1974.
16// Scaling by a power of two is exact, the property the balanced
17// accumulation relies on.
18
19#ifndef EIGEN_STRUCTURED_CAUCHY_H
20#define EIGEN_STRUCTURED_CAUCHY_H
21
22// IWYU pragma: private
23#include "./InternalHeaderCheck.h"
24
25namespace Eigen {
26
27template <typename Scalar_, int Rows_ = Dynamic, int Cols_ = Dynamic>
28class Cauchy;
29
30template <typename Scalar_>
31class CauchyLU;
32
33namespace internal {
34
35template <typename Scalar_, int Rows_, int Cols_>
36struct traits<Cauchy<Scalar_, Rows_, Cols_>> {
37 using Scalar = Scalar_;
38 using StorageKind = Dense;
39 using XprKind = MatrixXpr;
40 using StorageIndex = int;
41 static constexpr int RowsAtCompileTime = Rows_;
42 static constexpr int ColsAtCompileTime = Cols_;
43 static constexpr int MaxRowsAtCompileTime = Rows_;
44 static constexpr int MaxColsAtCompileTime = Cols_;
45 // Deliberately no NestByRefBit: transpose(), conjugate() and adjoint() return
46 // owning temporaries, so Product must nest the operator by value for a
47 // delayed-evaluated product expression to keep its left factor alive. The copy
48 // is O(m+n), negligible against the O(mn) product evaluation.
49 static constexpr unsigned int Flags = 0;
50};
51
52template <typename Scalar_, int Rows_, int Cols_>
53struct evaluator_traits<Cauchy<Scalar_, Rows_, Cols_>> {
54 using Kind = IndexBased;
55 using Shape = StructuredShape;
56};
57
58template <typename Scalar_>
59struct traits<CauchyLU<Scalar_>> : traits<Matrix<Scalar_, Dynamic, Dynamic>> {
60 using XprKind = MatrixXpr;
61 using StorageKind = SolverStorage;
62 using StorageIndex = int;
63 using BaseTraits = traits<Matrix<Scalar_, Dynamic, Dynamic>>;
64 static constexpr unsigned int Flags = BaseTraits::Flags & RowMajorBit;
65 static constexpr int CoeffReadCost = Dynamic;
66};
67
68} // namespace internal
69
109template <typename Scalar_, int Rows_, int Cols_>
110class Cauchy : public EigenBase<Cauchy<Scalar_, Rows_, Cols_>> {
111 public:
112 using Scalar = Scalar_;
113 using RealScalar = typename NumTraits<Scalar>::Real;
114 using StorageIndex = int;
115 using RowNodeVector = Matrix<Scalar, Rows_, 1>;
116 using ColNodeVector = Matrix<Scalar, Cols_, 1>;
117
118 EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(bool(RowNodeVector::NeedsToAlign || ColNodeVector::NeedsToAlign))
119
120 static constexpr int RowsAtCompileTime = Rows_;
121 static constexpr int ColsAtCompileTime = Cols_;
122 static constexpr int MaxRowsAtCompileTime = Rows_;
123 static constexpr int MaxColsAtCompileTime = Cols_;
124 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(Rows_, Cols_);
125 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
126 static constexpr bool IsRowMajor = false;
127 // Deliberately no IsVectorAtCompileTime: Ref<const Cauchy>'s default StrideType
128 // argument reads it, so its absence makes internal::is_ref_compatible SFINAE to
129 // false and keeps the iterative solvers on their matrix-free path.
130
133 template <typename XDerived, typename YDerived>
134 Cauchy(const MatrixBase<XDerived>& x, const MatrixBase<YDerived>& y) : m_x(x), m_y(y) {
135 EIGEN_STATIC_ASSERT_VECTOR_ONLY(XDerived)
136 EIGEN_STATIC_ASSERT_VECTOR_ONLY(YDerived)
137 eigen_assert(m_x.size() > 0 && m_y.size() > 0 && "Cauchy node vectors must be non-empty");
138 }
139
140 EIGEN_DEVICE_FUNC Index rows() const { return m_x.size(); }
141 EIGEN_DEVICE_FUNC Index cols() const { return m_y.size(); }
142
144 const RowNodeVector& rowNodes() const { return m_x; }
146 const ColNodeVector& colNodes() const { return m_y; }
147
149 Scalar coeff(Index row, Index col) const { return Scalar(1) / (m_x.coeff(row) - m_y.coeff(col)); }
150
154
157 Cauchy conjugate() const { return Cauchy(m_x.conjugate(), m_y.conjugate()); }
158
162 return Cauchy<Scalar, Cols_, Rows_>(-m_y.conjugate(), -m_x.conjugate());
163 }
164
177 Scalar determinant() const {
178 eigen_assert(rows() == cols() && "Cauchy::determinant requires a square matrix");
179 const Index n = rows();
180 Scalar det(1);
181 Index exponent = 0;
182 for (Index j = 1; j < n; ++j)
183 for (Index i = 0; i < j; ++i) {
184 const Scalar xDiff = internal::structured_balance(Scalar(m_x.coeff(j) - m_x.coeff(i)), exponent);
185 det = internal::structured_balance(Scalar(det * xDiff), exponent);
186 const Scalar yDiff = internal::structured_balance(Scalar(m_y.coeff(i) - m_y.coeff(j)), exponent);
187 det = internal::structured_balance(Scalar(det * yDiff), exponent);
188 }
189 for (Index j = 0; j < n; ++j)
190 for (Index i = 0; i < n; ++i) {
191 Index denomExponent = 0;
192 const Scalar d = internal::structured_balance(Scalar(m_x.coeff(i) - m_y.coeff(j)), denomExponent);
193 exponent -= denomExponent;
194 det = internal::structured_balance(Scalar(det / d), exponent);
195 }
196 return internal::structured_ldexp_clamped(det, exponent);
197 }
198
201 template <typename Dest>
202 void evalTo(Dest& dst) const {
203 applyAssignment(dst, internal::assign_op<typename Dest::Scalar, Scalar>());
204 }
205
207 template <typename Dest>
208 void addTo(Dest& dst) const {
209 applyAssignment(dst, internal::add_assign_op<typename Dest::Scalar, Scalar>());
210 }
211
213 template <typename Dest>
214 void subTo(Dest& dst) const {
215 applyAssignment(dst, internal::sub_assign_op<typename Dest::Scalar, Scalar>());
216 }
217
223 template <typename Rhs>
225 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
226 int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
227 INVALID_MATRIX_PRODUCT)
228 eigen_assert(v.rows() == cols() && "invalid product: dimensions do not match");
229 return Product<Cauchy, Rhs>(*this, v.derived());
230 }
231
238 template <typename Dest, typename Rhs, typename ProductScalar>
239 void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
240 const Index n = cols();
241 eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
242 // A unit alpha must not multiply: even the identity complex scalar (1,0)
243 // pollutes an (Inf,0) value with NaN through the 0*Inf cross term.
244 const bool unitAlpha = alpha == ProductScalar(1);
245 const auto weight = [&](Index j, Index k) {
246 return unitAlpha ? ProductScalar(rhs.coeff(j, k)) : ProductScalar(alpha * rhs.coeff(j, k));
247 };
248 // Reuse saves (r-1)/r of the divisions. A real operator applied to a complex
249 // right-hand side gains nothing: the complex updates dominate its real divisions.
250 if (rhs.cols() > 1 && std::is_same<ProductScalar, Scalar>::value) {
251 // Row blocks keep the r destination columns of a block in cache across all
252 // n reciprocal columns; the floor keeps the per-column updates long.
253 constexpr Index kBlockBytes = Index(1) << 19;
254 constexpr Index kMinBlockRows = 1024;
255 const Index m = rows(), r = rhs.cols();
256 const Index blockRows = numext::mini(m, numext::maxi(kMinBlockRows, kBlockBytes / (r * Index(sizeof(Scalar)))));
257 Matrix<Scalar, Dynamic, 1, ColMajor, Rows_> reciprocals(blockRows);
258 for (Index i = 0; i < m; i += blockRows) {
259 const Index b = numext::mini(blockRows, m - i);
260 for (Index j = 0; j < n; ++j) {
261 reciprocals.head(b) = (m_x.segment(i, b).array() - m_y.coeff(j)).inverse();
262 for (Index k = 0; k < r; ++k) dst.col(k).segment(i, b) += weight(j, k) * reciprocals.head(b);
263 }
264 }
265 return;
266 }
267 for (Index k = 0; k < rhs.cols(); ++k)
268 for (Index j = 0; j < n; ++j) dst.col(k) += weight(j, k) * (m_x.array() - m_y.coeff(j)).inverse().matrix();
269 }
270
271 private:
272 template <typename Dest, typename Assignment>
273 void applyAssignment(Dest& dst, const Assignment& assignment) const {
274 for (Index j = 0; j < cols(); ++j) {
275 auto dstColumn = dst.col(j);
276 internal::call_assignment_no_alias(dstColumn, (m_x.array() - m_y.coeff(j)).inverse().matrix(), assignment);
277 }
278 }
279
280 RowNodeVector m_x;
281 ColNodeVector m_y;
282};
283
287template <typename XDerived, typename YDerived>
292
327template <typename Scalar_>
328class CauchyLU : public SolverBase<CauchyLU<Scalar_>> {
329 public:
330 using Base = SolverBase<CauchyLU>;
331 friend class SolverBase<CauchyLU>;
332 EIGEN_GENERIC_PUBLIC_INTERFACE(CauchyLU)
333 using DenseMatrix = Matrix<Scalar, Dynamic, Dynamic>;
334 using DenseVector = Matrix<Scalar, Dynamic, 1>;
335
337 CauchyLU() : m_isInitialized(false), m_info(InvalidInput) {}
338
340 template <int Rows_, int Cols_>
341 explicit CauchyLU(const Cauchy<Scalar, Rows_, Cols_>& C) : m_isInitialized(false), m_info(InvalidInput) {
342 compute(C);
343 }
344
352 template <int Rows_, int Cols_>
354 eigen_assert(C.rows() == C.cols() && "CauchyLU requires a square Cauchy matrix");
355 const Index n = C.rows();
356 DenseVector x = C.rowNodes();
357 const DenseVector y = C.colNodes();
358 DenseVector a = DenseVector::Ones(n);
359 DenseVector b = DenseVector::Ones(n);
360 m_lu.resize(n, n);
361 m_perm.resize(static_cast<std::size_t>(n));
362 m_info = Success;
363
364 for (Index k = 0; k < n; ++k) {
365 Index piv = k;
366 RealScalar best(-1);
367 for (Index i = k; i < n; ++i) {
368 m_lu(i, k) = a[i] * b[k] / (x[i] - y[k]);
369 const RealScalar mag = numext::abs(m_lu(i, k));
370 if ((numext::isnan)(mag) || mag > best) {
371 best = mag;
372 piv = i;
373 }
374 }
375 if (piv != k) {
376 m_lu.row(piv).head(k + 1).swap(m_lu.row(k).head(k + 1));
377 std::swap(x[piv], x[k]);
378 std::swap(a[piv], a[k]);
379 }
380 m_perm[static_cast<std::size_t>(k)] = piv;
381 const Scalar pivot = m_lu(k, k);
382 if (pivot == Scalar(0) || !(numext::isfinite)(pivot)) {
383 m_info = NumericalIssue;
384 m_lu.row(k).tail(n - k - 1).setZero();
385 m_lu.col(k).tail(n - k - 1).setZero();
386 continue;
387 }
388 m_lu.col(k).tail(n - k - 1) /= pivot;
389 for (Index j = k + 1; j < n; ++j) {
390 m_lu(k, j) = a[k] * b[j] / (x[k] - y[j]);
391 if (!(numext::isfinite)(m_lu(k, j))) m_info = NumericalIssue;
392 }
393 for (Index i = k + 1; i < n; ++i) a[i] *= (x[i] - x[k]) / (x[i] - y[k]);
394 for (Index j = k + 1; j < n; ++j) b[j] *= (y[j] - y[k]) / (y[j] - x[k]);
395 }
396 m_isInitialized = true;
397 return *this;
398 }
399
400 Index rows() const noexcept { return m_lu.rows(); }
401 Index cols() const noexcept { return m_lu.cols(); }
402
406 eigen_assert(m_isInitialized && "CauchyLU is not initialized.");
407 return m_info;
408 }
409
410#ifdef EIGEN_PARSED_BY_DOXYGEN
415 template <typename Rhs>
416 inline const Solve<CauchyLU, Rhs> solve(const MatrixBase<Rhs>& b) const;
417#endif
418
419#ifndef EIGEN_PARSED_BY_DOXYGEN
421 template <typename RhsType, typename DstType>
422 void _solve_impl(const RhsType& rhs, DstType& dst) const {
423 dst = rhs;
424 for (Index k = 0; k < rows(); ++k) {
425 const Index piv = m_perm[static_cast<std::size_t>(k)];
426 if (piv != k) dst.row(k).swap(dst.row(piv));
427 }
428 m_lu.template triangularView<UnitLower>().solveInPlace(dst);
429 m_lu.template triangularView<Upper>().solveInPlace(dst);
430 }
431
435 template <bool Conjugate, typename RhsType, typename DstType>
436 void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
437 dst = rhs.template conjugateIf<Conjugate>();
438 m_lu.template triangularView<Upper>().transpose().solveInPlace(dst);
439 m_lu.template triangularView<UnitLower>().transpose().solveInPlace(dst);
440 for (Index k = rows() - 1; k >= 0; --k) {
441 const Index piv = m_perm[static_cast<std::size_t>(k)];
442 if (piv != k) dst.row(k).swap(dst.row(piv));
443 }
444 if (Conjugate) dst = dst.conjugate().eval();
445 }
446#endif
447
448 private:
449 DenseMatrix m_lu;
450 std::vector<Index> m_perm; // transposition applied at each elimination step
451 bool m_isInitialized;
452 ComputationInfo m_info;
453};
454
455namespace internal {
456
457// Single product specialization covering every product tag; see the note in
458// Circulant.h.
459template <typename Scalar_, int Rows_, int Cols_, typename Rhs, int ProductTag>
460struct generic_product_impl<Cauchy<Scalar_, Rows_, Cols_>, Rhs, StructuredShape, DenseShape, ProductTag>
461 : structured_product_impl<Cauchy<Scalar_, Rows_, Cols_>, Rhs> {};
462
463} // namespace internal
464
465} // namespace Eigen
466
467#endif // EIGEN_STRUCTURED_CAUCHY_H
Partially pivoted O(n^2) LU solver for square Cauchy systems (Gohberg-Kailath-Olshevsky).
Definition Cauchy.h:328
CauchyLU()
Definition Cauchy.h:337
const Solve< CauchyLU, Rhs > solve(const MatrixBase< Rhs > &b) const
ComputationInfo info() const
Definition Cauchy.h:405
CauchyLU(const Cauchy< Scalar, Rows_, Cols_ > &C)
Definition Cauchy.h:341
CauchyLU & compute(const Cauchy< Scalar, Rows_, Cols_ > &C)
Definition Cauchy.h:353
An m x n Cauchy matrix represented by its two node vectors.
Definition Cauchy.h:110
Scalar determinant() const
Definition Cauchy.h:177
Product< Cauchy, Rhs > operator*(const MatrixBase< Rhs > &v) const
Definition Cauchy.h:224
const ColNodeVector & colNodes() const
Definition Cauchy.h:146
Scalar coeff(Index row, Index col) const
Definition Cauchy.h:149
Cauchy< Scalar, Cols_, Rows_ > transpose() const
Definition Cauchy.h:153
Cauchy< Scalar, Cols_, Rows_ > adjoint() const
Definition Cauchy.h:161
Cauchy conjugate() const
Definition Cauchy.h:157
const RowNodeVector & rowNodes() const
Definition Cauchy.h:144
Cauchy(const MatrixBase< XDerived > &x, const MatrixBase< YDerived > &y)
Definition Cauchy.h:134
constexpr const Scalar & coeff(Index index) const
Cauchy< typename XDerived::Scalar, XDerived::SizeAtCompileTime, YDerived::SizeAtCompileTime > makeCauchy(const MatrixBase< XDerived > &x, const MatrixBase< YDerived > &y)
Definition Cauchy.h:288
ComputationInfo
NumericalIssue
constexpr unsigned int RowMajorBit
Namespace containing all symbols from the Eigen library.