Eigen  5.0.1
 
Loading...
Searching...
No Matches
MatrixNorms.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// SPDX-FileCopyrightText: The Eigen Authors
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_MATRIX_NORMS_H
12#define EIGEN_MATRIX_NORMS_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18namespace internal {
19
20// *_abs_sum sums magnitudes of stored coefficients; *_l1_norm takes the maximum
21// absolute column sum of the represented matrix, including any implicit entries.
22
23// Sum |a_ij| over i <= j + ExtraDiagonals (Upper), or j <= i + ExtraDiagonals (Lower).
24template <unsigned int UpLo, int ExtraDiagonals, typename Derived>
25EIGEN_DEVICE_FUNC typename Derived::RealScalar triangular_band_abs_sum(const MatrixBase<Derived>& matrix) {
26 static_assert(UpLo == Upper || UpLo == Lower, "UpLo must be Upper or Lower");
27 static_assert(ExtraDiagonals == 0 || ExtraDiagonals == 1, "Expected a triangular or Hessenberg envelope");
28 using RealScalar = typename Derived::RealScalar;
29 RealScalar result(0);
30 if (matrix.rows() == 0 || matrix.cols() == 0) return result;
31
32 constexpr bool Leading = (UpLo == Upper) != bool(Derived::IsRowMajor);
33 const Index inner = matrix.innerSize();
34 // Inner-vector slices preserve packet access for contiguous storage and also work for strided expressions.
35 for (Index j = 0; j < matrix.outerSize(); ++j) {
36 const Index start = Leading ? Index(0) : numext::mini(inner, numext::maxi(Index(0), j - ExtraDiagonals));
37 const Index end = Leading ? numext::mini(inner, j + ExtraDiagonals + 1) : inner;
38 result += matrix.derived().innerVector(j).segment(start, end - start).template lpNorm<1>();
39 }
40 return result;
41}
42
43// Coefficient-wise l1 norm of the stored upper/lower triangle, including the diagonal.
44// Entries outside the selected triangle are never read; rectangular and empty expressions are supported.
45template <unsigned int UpLo, typename Derived>
46EIGEN_DEVICE_FUNC typename Derived::RealScalar triangular_abs_sum(const MatrixBase<Derived>& matrix) {
47 return triangular_band_abs_sum<UpLo, 0>(matrix);
48}
49
50// Coefficient-wise l1 norm of the upper/lower Hessenberg envelope, including its one extra off-diagonal.
51template <unsigned int UpLo, typename Derived>
52EIGEN_DEVICE_FUNC typename Derived::RealScalar hessenberg_abs_sum(const MatrixBase<Derived>& matrix) {
53 return triangular_band_abs_sum<UpLo, 1>(matrix);
54}
55
56// Column step of the self-adjoint 1-norm, on two columns sharing a range of rows: sums[i] +=
57// |m(i, j0)| + |m(i, j1)|, and each column's own sum of those rows goes to sums[j0] and sums[j1].
58// Walking two columns at once halves the traffic on sums, and one packet pass does everything, so
59// short columns pay no per-expression setup.
60//
61// Real scalars use pabs. Complex ones have no packet abs, so |z| = sqrt(re^2 + im^2) is computed
62// on the real lanes of the complex packet. That leaves |z|^2 in both lanes of each slot, so one
63// square root serves both columns: even lanes from j0 and odd lanes from j1 give |u0| |v0| |u1|
64// |v1| ..., whose sum with its flip is the update of sums, and whose reduction as a complex packet
65// is (sum |u|, sum |v|). The accumulator has the matrix's scalar type and only its real parts are
66// read, so what lands in the imaginary lanes is harmless. Squaring overflows above sqrt(max) and
67// loses precision below sqrt(min), so the pass also records the largest component it has seen and
68// the caller recomputes the norm through numext::abs when that is out of range. The record is
69// exact and the range test finite, so neither depends on infinities surviving fast-math.
70template <typename Scalar_>
71struct selfadjoint_l1norm_real_lanes {
72 using Scalar = Scalar_;
73 using Real = Scalar_;
74 using Packet = typename packet_traits<Scalar>::type;
75 using RPacket = Packet;
76 static constexpr Index PacketSize = unpacket_traits<Packet>::size;
77 // Up to this size the per-column form (mirrored term read as a row) beats the column pass, whose
78 // accumulator costs more to set up than these columns cost to read.
79 static constexpr Index PerColumnUpTo = 4;
80 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RPacket lanes(const Packet& p) { return p; }
81 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real abs(const Scalar& x) { return numext::abs(x); }
82 // Nothing to record: pabs is exact.
83 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real component(const Scalar&) { return Real(0); }
84 static EIGEN_DEVICE_FUNC bool inRange(Real, Index) { return true; }
85
86 // The running sums of a pass over two columns.
87 struct Pass {
88 // pset1 rather than pzero(RPacket()): nvc++ 26.1 crashes on a value-initialized __m512 (#3193).
89 RPacket acc0 = pset1<RPacket>(Real(0));
90 RPacket acc1 = pset1<RPacket>(Real(0));
91 template <typename SumsEvaluator>
92 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void step(const RPacket& u, const RPacket& v, SumsEvaluator& s, Index i) {
93 RPacket a = pabs(u);
94 RPacket b = pabs(v);
95 acc0 = padd(acc0, a);
96 acc1 = padd(acc1, b);
97 s.template writePacket<Unaligned>(i, padd(s.template packet<Unaligned, Packet>(i), padd(a, b)));
98 }
99 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real sum0() const { return predux(acc0); }
100 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real sum1() const { return predux(acc1); }
101 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real peak() const { return Real(0); }
102 };
103};
104
105template <typename T>
106struct selfadjoint_l1norm_complex_lanes {
107 using Scalar = std::complex<T>;
108 using Real = T;
109 using Packet = typename packet_traits<Scalar>::type;
110 using RPacket = typename unpacket_traits<Packet>::as_real;
111 static constexpr Index PacketSize = unpacket_traits<Packet>::size;
112 static constexpr Index PerColumnUpTo = 0;
113 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RPacket lanes(const Packet& p) { return p.v; }
114 // Same formula as the packets, for the diagonal and the tails: hypot costs more than the packets
115 // spend on the rest of a short column.
116 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real abs(const Scalar& z) { return numext::sqrt(numext::abs2(z)); }
117 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real component(const Scalar& z) {
118 return numext::maxi(numext::abs(numext::real(z)), numext::abs(numext::imag(z)));
119 }
120 // Components this large overflow when squared, and below the lower bound the squares lose
121 // precision the sum of n of them cannot hide.
122 static EIGEN_DEVICE_FUNC bool inRange(Real peak, Index n) {
123 Real tiny = Real(n) * numext::sqrt((std::numeric_limits<Real>::min)()) / NumTraits<Real>::epsilon();
124 Real huge = numext::sqrt(NumTraits<Real>::highest()) / Real(2);
125 return peak > tiny && peak < huge;
126 }
127
128 struct Pass {
129 RPacket acc = pset1<RPacket>(Real(0)); // |u| in the even lanes, |v| in the odd ones
130 RPacket peak_ = pset1<RPacket>(Real(0)); // the largest component seen
131 template <typename SumsEvaluator>
132 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void step(const RPacket& u, const RPacket& v, SumsEvaluator& s, Index i) {
133 peak_ = pmax(peak_, pmax(pabs(u), pabs(v)));
134 RPacket r = psqrt(pselect(peven_mask(u), abs2(u), abs2(v))); // |u0| |v0| |u1| |v1| ...
135 acc = padd(acc, r);
136 s.template writePacket<Unaligned>(i,
137 Packet(padd(lanes(s.template packet<Unaligned, Packet>(i)), padd(r, flip(r)))));
138 }
139 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real sum0() const { return numext::real(predux(Packet(acc))); }
140 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real sum1() const { return numext::imag(predux(Packet(acc))); }
141 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real peak() const { return predux_max(peak_); }
142 };
143
144 private:
145 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RPacket flip(const RPacket& r) { return pcplxflip(Packet(r)).v; }
146 // |z|^2 in both lanes of its slot.
147 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RPacket abs2(const RPacket& v) {
148 RPacket s = pmul(v, v);
149 return padd(s, flip(s));
150 }
151};
152
153// The pass over two columns: packets over the shared rows, coefficients for the tail. An instance
154// remembers the largest component it has seen, for inRange().
155template <typename Lanes>
156struct selfadjoint_l1norm_packet_impl : Lanes {
157 using Lanes::PacketSize;
158 using typename Lanes::Packet;
159 using typename Lanes::Real;
160 using typename Lanes::RPacket;
161 using typename Lanes::Scalar;
162
163 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real abs(const Scalar& x) {
164 m_peak = numext::maxi(m_peak, Lanes::component(x));
165 return Lanes::abs(x);
166 }
167 EIGEN_DEVICE_FUNC bool inRange(Index n) const { return Lanes::inRange(m_peak, n); }
168
169 template <typename SumsDerived, typename Derived>
170 EIGEN_DEVICE_FUNC void accumulate(DenseBase<SumsDerived>& sums, const DenseBase<Derived>& m, Index j0, Index j1,
171 Index begin, Index end) {
172 accumulateCast(sums, j0, j1, begin, m.col(j0).segment(begin, end - begin).template cast<Scalar>(),
173 m.col(j1).segment(begin, end - begin).template cast<Scalar>());
174 }
175
176 private:
177 Real m_peak = Real(0);
178
179 template <typename SumsDerived, typename Derived0, typename Derived1>
180 EIGEN_DEVICE_FUNC void accumulateCast(DenseBase<SumsDerived>& sums, Index j0, Index j1, Index begin,
181 const DenseBase<Derived0>& x0, const DenseBase<Derived1>& x1) {
182 using SumsEvaluator = evaluator<SumsDerived>;
183 using Evaluator0 = evaluator<Derived0>;
184 using Evaluator1 = evaluator<Derived1>;
185 constexpr int Needed = PacketAccessBit | LinearAccessBit;
186 constexpr bool Vectorize = (SumsEvaluator::Flags & Needed) == Needed && (Evaluator0::Flags & Needed) == Needed &&
187 (Evaluator1::Flags & Needed) == Needed;
188 SumsEvaluator s(sums.derived());
189 accumulate(s, j0, j1, begin, Evaluator0(x0.derived()), Evaluator1(x1.derived()), x0.size(),
190 bool_constant<Vectorize>());
191 }
192 template <typename Evaluator>
193 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RPacket load(const Evaluator& x, Index i) {
194 return Lanes::lanes(x.template packet<Unaligned, Packet>(i));
195 }
196 template <typename SumsEvaluator, typename Evaluator0, typename Evaluator1>
197 EIGEN_DEVICE_FUNC void accumulate(SumsEvaluator& s, Index j0, Index j1, Index begin, const Evaluator0& x0,
198 const Evaluator1& x1, Index from, Index to) {
199 Real sum0 = Real(0);
200 Real sum1 = Real(0);
201 for (Index i = from; i < to; ++i) {
202 Real a = abs(x0.coeff(i));
203 Real b = abs(x1.coeff(i));
204 s.coeffRef(begin + i) += a + b;
205 sum0 += a;
206 sum1 += b;
207 }
208 s.coeffRef(j0) += sum0;
209 s.coeffRef(j1) += sum1;
210 }
211 template <typename SumsEvaluator, typename Evaluator0, typename Evaluator1>
212 EIGEN_DEVICE_FUNC void accumulate(SumsEvaluator& s, Index j0, Index j1, Index begin, const Evaluator0& x0,
213 const Evaluator1& x1, Index n, std::false_type) {
214 accumulate(s, j0, j1, begin, x0, x1, Index(0), n);
215 }
216 template <typename SumsEvaluator, typename Evaluator0, typename Evaluator1>
217 EIGEN_DEVICE_FUNC void accumulate(SumsEvaluator& s, Index j0, Index j1, Index begin, const Evaluator0& x0,
218 const Evaluator1& x1, Index n, std::true_type) {
219 if (n < PacketSize) return accumulate(s, j0, j1, begin, x0, x1, Index(0), n);
220 typename Lanes::Pass pass;
221 Index i = 0;
222 for (; i + PacketSize <= n; i += PacketSize) pass.step(load(x0, i), load(x1, i), s, begin + i);
223 accumulate(s, j0, j1, begin, x0, x1, i, n);
224 s.coeffRef(j0) += pass.sum0();
225 s.coeffRef(j1) += pass.sum1();
226 m_peak = numext::maxi(m_peak, pass.peak());
227 }
228};
229
230// Coefficient fallback: custom complex types, or complex packets without a plain real view.
231template <typename Scalar_, typename Enable = void>
232struct selfadjoint_l1norm_impl {
233 using Scalar = Scalar_;
234 using Real = typename NumTraits<Scalar>::Real;
235 static constexpr Index PerColumnUpTo = 16;
236 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real abs(const Scalar& x) const { return numext::abs(x); }
237 EIGEN_DEVICE_FUNC bool inRange(Index) const { return true; }
238 template <typename SumsDerived, typename Derived>
239 EIGEN_DEVICE_FUNC void accumulate(DenseBase<SumsDerived>& sums, const DenseBase<Derived>& m, Index j0, Index j1,
240 Index begin, Index end) const {
241 Real sum0 = Real(0);
242 Real sum1 = Real(0);
243 for (Index i = begin; i < end; ++i) {
244 Real a = numext::abs(m.coeff(i, j0));
245 Real b = numext::abs(m.coeff(i, j1));
246 sums.coeffRef(i) += Scalar(a + b);
247 sum0 += a;
248 sum1 += b;
249 }
250 sums.coeffRef(j0) += Scalar(sum0);
251 sums.coeffRef(j1) += Scalar(sum1);
252 }
253};
254// half and bfloat16 accumulate in float, as stableNorm does.
255template <typename Scalar>
256struct selfadjoint_l1norm_impl<Scalar, std::enable_if_t<!NumTraits<Scalar>::IsComplex>>
257 : selfadjoint_l1norm_packet_impl<selfadjoint_l1norm_real_lanes<typename stable_norm_accumulator<Scalar>::type>> {};
258// The real view must lay the components out one per lane, which the lane masks assume.
259template <typename Packet, typename Enable = void>
260struct selfadjoint_l1norm_plain_real_view : std::false_type {};
261template <typename Packet>
262struct selfadjoint_l1norm_plain_real_view<Packet, void_t<typename unpacket_traits<Packet>::as_real>>
263 : bool_constant<sizeof(typename unpacket_traits<Packet>::as_real) ==
264 unpacket_traits<Packet>::size * sizeof(typename unpacket_traits<Packet>::type)> {};
265template <typename T>
266struct selfadjoint_l1norm_impl<
267 std::complex<T>,
268 std::enable_if_t<selfadjoint_l1norm_plain_real_view<typename packet_traits<std::complex<T>>::type>::value>>
269 : selfadjoint_l1norm_packet_impl<selfadjoint_l1norm_complex_lanes<T>> {};
270
271template <typename MatrixType, unsigned int UpLo>
272class selfadjoint_l1norm {
273 using Scalar = typename MatrixType::Scalar;
274 using RealScalar = typename MatrixType::RealScalar;
275
276 public:
277 EIGEN_DEVICE_FUNC explicit selfadjoint_l1norm(const MatrixType& matrix) : m_matrix(matrix) {}
278
279 EIGEN_DEVICE_FUNC RealScalar run() const {
280#ifdef EIGEN_GPU_COMPILE_PHASE
281 // No per-thread accumulator on a device.
282 return l1NormPerColumn();
283#else
284 if (m_matrix.rows() <= L1NormImpl::PerColumnUpTo) return l1NormPerColumn();
285 // The stored triangle of a row-major matrix is the complementary triangle of its column-major
286 // transpose, which has the same norm.
287 EIGEN_IF_CONSTEXPR (bool(MatrixType::IsRowMajor)) {
288 return l1NormStreaming<(UpLo == Lower ? Upper : Lower)>(m_matrix.transpose());
289 } else {
290 return l1NormStreaming<UpLo>(m_matrix);
291 }
292#endif
293 }
294
295 private:
296 using L1NormImpl = internal::selfadjoint_l1norm_impl<Scalar>;
297 // float for half and bfloat16, Scalar otherwise.
298 using L1NormScalar = typename L1NormImpl::Scalar;
299 using L1NormAccumulator = typename L1NormImpl::Real;
300
301 // Each column is read once, top to bottom, two at a time: |a_ij| goes to column j's sum and, as
302 // the mirrored a_ji, to sums[i]. Lower walks the columns forward and Upper backward so that
303 // sums[j] is complete when column j is reached. Of a pair (j0, j1) only j0's element in row j1
304 // lies outside the rows the two share.
305 template <int Mode, typename Mat>
306 RealScalar l1NormStreaming(const Mat& m) const {
307 const Index n = m.rows();
308 // The accumulator lives in the object for bounded sizes and on the stack otherwise, so that
309 // neither fixed-size nor preallocated dynamic-size decompositions allocate.
310 internal::gemv_static_vector_if<L1NormScalar, Mat::RowsAtCompileTime, Mat::MaxRowsAtCompileTime, true> static_sums;
311 ei_declare_aligned_stack_constructed_variable(L1NormScalar, sums_data, n, static_sums.data());
312 Map<Matrix<L1NormScalar, Dynamic, 1>> sums(sums_data, n);
313 sums.setZero();
314 L1NormImpl impl;
315 L1NormAccumulator norm = L1NormAccumulator(0);
316 Index k = 0;
317 for (; k + 1 < n; k += 2) {
318 Index j0 = Mode == Lower ? k : n - 1 - k;
319 Index j1 = Mode == Lower ? j0 + 1 : j0 - 1;
320 Index rowBegin = Mode == Lower ? j1 + 1 : 0;
321 Index rowEnd = Mode == Lower ? n : j1;
322 impl.accumulate(sums, m, j0, j1, rowBegin, rowEnd);
323 // The element of j0 in row j1 lies outside the shared rows: it counts for both columns.
324 L1NormAccumulator boundary = impl.abs(m.coeff(j1, j0));
325 // Totals are materialized so that maxi compares two accumulators (an integer sum promotes,
326 // an autodiff sum is an expression).
327 L1NormAccumulator col0 = numext::real(sums.coeff(j0)) + impl.abs(m.coeff(j0, j0)) + boundary;
328 L1NormAccumulator col1 = numext::real(sums.coeff(j1)) + impl.abs(m.coeff(j1, j1)) + boundary;
329 norm = numext::maxi(norm, col0);
330 norm = numext::maxi(norm, col1);
331 }
332 if (k < n) {
333 Index j = Mode == Lower ? k : 0;
334 L1NormAccumulator col = numext::real(sums.coeff(j)) + impl.abs(m.coeff(j, j));
335 norm = numext::maxi(norm, col);
336 }
337 return impl.inRange(n) ? RealScalar(norm) : l1NormPerColumn();
338 }
339
340 // One column at a time, the mirrored term read as a row; no workspace.
341 EIGEN_DEVICE_FUNC RealScalar l1NormPerColumn() const {
342 L1NormAccumulator norm = L1NormAccumulator(0);
343 const Index n = m_matrix.rows();
344 for (Index col = 0; col < n; ++col) {
345 L1NormAccumulator abs_col_sum;
346 EIGEN_IF_CONSTEXPR (UpLo == Lower) {
347 abs_col_sum = m_matrix.col(col).tail(n - col).template cast<L1NormScalar>().template lpNorm<1>() +
348 m_matrix.row(col).head(col).template cast<L1NormScalar>().template lpNorm<1>();
349 } else {
350 abs_col_sum = m_matrix.col(col).head(col).template cast<L1NormScalar>().template lpNorm<1>() +
351 m_matrix.row(col).tail(n - col).template cast<L1NormScalar>().template lpNorm<1>();
352 }
353 norm = numext::maxi(norm, abs_col_sum);
354 }
355 return RealScalar(norm);
356 }
357
358 const MatrixType& m_matrix;
359};
360
361// Matrix 1-norm of the implicit full self-adjoint matrix; only the stored triangle is read.
362template <unsigned int UpLo, typename Derived>
363EIGEN_DEVICE_FUNC typename Derived::RealScalar selfadjoint_l1_norm(const MatrixBase<Derived>& matrix) {
364 static_assert(UpLo == Upper || UpLo == Lower, "UpLo must be Upper or Lower");
365 eigen_assert(matrix.rows() == matrix.cols());
366 return selfadjoint_l1norm<Derived, UpLo>(matrix.derived()).run();
367}
368
369} // namespace internal
370} // namespace Eigen
371
372#endif // EIGEN_MATRIX_NORMS_H
@ Lower
Definition Constants.h:212
@ Upper
Definition Constants.h:214
constexpr unsigned int PacketAccessBit
Definition Constants.h:98
constexpr unsigned int LinearAccessBit
Definition Constants.h:134