11#ifndef EIGEN_STABLENORM_H
12#define EIGEN_STABLENORM_H
15#include "./InternalHeaderCheck.h"
21template <typename Accumulator, bool = std::is_floating_point<Accumulator>::value>
22struct stable_norm_unscaled_predicate {
23 static inline bool run(
const Accumulator&,
const Accumulator&) {
return false; }
26template <
typename Accumulator>
27struct stable_norm_unscaled_predicate<Accumulator, true> {
28 static inline bool run(
const Accumulator& maxCoeff,
const Accumulator& invScale) {
32 static const Accumulator kSqrtMin =
33 sqrt((numext::numeric_limits<Accumulator>::min)() * (Accumulator(16384) / NumTraits<Accumulator>::epsilon()));
34 static const Accumulator kSqrtMax = sqrt(NumTraits<Accumulator>::highest() / Accumulator(16384));
36 static const Accumulator kSqrtNormalMin = sqrt((numext::numeric_limits<Accumulator>::min)());
37 return maxCoeff >= kSqrtMin && maxCoeff <= kSqrtMax && invScale >= kSqrtNormalMin;
41template <
typename ExpressionType,
typename Accumulator>
42inline Accumulator stable_norm_squared_norm(
const ExpressionType& block,
const Accumulator& invScale,
43 const Accumulator& maxCoeff) {
44 if (stable_norm_unscaled_predicate<Accumulator>::run(maxCoeff, invScale)) {
45 return block.realView().template cast<Accumulator>().squaredNorm() * numext::abs2(invScale);
47 return (block.realView().template cast<Accumulator>() * invScale).squaredNorm();
50template <
typename ExpressionType,
typename Accumulator>
51inline void stable_norm_kernel(
const ExpressionType& block, Accumulator& ssq, Accumulator& scale,
52 Accumulator& invScale) {
54 Accumulator maxCoeff = block.realView().template cast<Accumulator>().cwiseAbs().template maxCoeff<PropagateNaN>();
56 if (maxCoeff > scale) {
57 if (maxCoeff > NumTraits<Accumulator>::highest())
59 invScale = Accumulator(1);
62 const auto factors = safe_scaling<Accumulator>::compute_ceiling_factors_with_normal_reciprocal(maxCoeff);
63 if (scale > Accumulator(0)) ssq = ssq * numext::abs2(scale * factors.invScale);
64 scale = factors.scale;
65 invScale = factors.invScale;
67 }
else if (maxCoeff != maxCoeff)
73 if (scale > Accumulator(0))
74 ssq += stable_norm_squared_norm(block, invScale, maxCoeff);
77template <
typename VectorType,
typename Accumulator>
78void stable_norm_impl_inner_step(
const VectorType& vec, Accumulator& ssq, Accumulator& scale, Accumulator& invScale) {
79 const Index blockSize = 4096;
82 Index blockEnd = numext::round_down(n, blockSize);
83 for (Index i = 0; i < blockEnd; i += blockSize) {
84 internal::stable_norm_kernel(vec.template segment<blockSize>(i), ssq, scale, invScale);
87 internal::stable_norm_kernel(vec.tail(n - blockEnd), ssq, scale, invScale);
91template <
typename VectorType,
typename Accumulator,
93 (int(inner_stride_at_compile_time<VectorType>::value) != 1)>
94struct stable_norm_vector_dispatch {
95 static inline void run(
const VectorType& vec, Accumulator& ssq, Accumulator& scale, Accumulator& invScale) {
96 stable_norm_impl_inner_step(vec, ssq, scale, invScale);
100template <
typename VectorType,
typename Accumulator>
101struct stable_norm_vector_dispatch<VectorType, Accumulator, true> {
102 static inline void run(
const VectorType& vec, Accumulator& ssq, Accumulator& scale, Accumulator& invScale) {
103 if (vec.innerStride() == 1) {
104 using Scalar =
typename traits<VectorType>::Scalar;
105 using PlainVector = Matrix<Scalar, VectorType::SizeAtCompileTime, 1, 0, VectorType::MaxSizeAtCompileTime, 1>;
106 using ContiguousMap = Map<const PlainVector, evaluator<VectorType>::Alignment>;
107 const ContiguousMap contiguous(vec.data(), vec.size());
108 stable_norm_impl_inner_step(contiguous, ssq, scale, invScale);
111 stable_norm_impl_inner_step(vec, ssq, scale, invScale);
115template <
typename VectorType,
typename Accumulator>
116inline void stable_norm_impl_inner_dispatch(
const VectorType& vec, Accumulator& ssq, Accumulator& scale,
117 Accumulator& invScale) {
118 stable_norm_vector_dispatch<VectorType, Accumulator>::run(vec, ssq, scale, invScale);
121template <
typename MatrixType,
typename Accumulator>
122inline void stable_norm_impl_outer_steps(
const MatrixType& mat, Accumulator& ssq, Accumulator& scale,
123 Accumulator& invScale) {
124 for (Index j = 0; j < mat.outerSize(); ++j) {
125 stable_norm_impl_inner_dispatch(mat.innerVector(j), ssq, scale, invScale);
129template <
typename MatrixType,
typename Accumulator,
bool =
bool(traits<MatrixType>::Flags & DirectAccessBit)>
130struct stable_norm_matrix_dispatch {
131 static inline void run(
const MatrixType& mat, Accumulator& ssq, Accumulator& scale, Accumulator& invScale) {
132 stable_norm_impl_outer_steps(mat, ssq, scale, invScale);
136template <
typename MatrixType,
typename Accumulator>
137struct stable_norm_matrix_dispatch<MatrixType, Accumulator, true> {
138 static inline void run(
const MatrixType& mat, Accumulator& ssq, Accumulator& scale, Accumulator& invScale) {
139 if (mat.innerStride() == 1 && (mat.outerSize() == 1 || mat.outerStride() == mat.innerSize())) {
140 using Scalar =
typename traits<MatrixType>::Scalar;
141 using PlainVector = Matrix<Scalar, MatrixType::SizeAtCompileTime, 1, 0, MatrixType::MaxSizeAtCompileTime, 1>;
142 using ContiguousMap = Map<const PlainVector, evaluator<MatrixType>::Alignment>;
143 const ContiguousMap contiguous(mat.data(), mat.size());
144 stable_norm_impl_inner_step(contiguous, ssq, scale, invScale);
147 stable_norm_impl_outer_steps(mat, ssq, scale, invScale);
151template <
typename VectorType, std::enable_if_t<VectorType::IsVectorAtCompileTime,
int> = 0>
152typename VectorType::RealScalar stable_norm_impl(
const VectorType& vec) {
155 Index n = vec.size();
156 if (EIGEN_PREDICT_FALSE(n == 1))
return numext::abs(vec.coeff(0));
158 using RealScalar =
typename VectorType::RealScalar;
159 using Accumulator =
typename stable_norm_accumulator<RealScalar>::type;
160 Accumulator scale(0);
161 Accumulator invScale(1);
164 stable_norm_vector_dispatch<VectorType, Accumulator>::run(vec, ssq, scale, invScale);
166 return RealScalar(scale * sqrt(ssq));
169template <
typename MatrixType, std::enable_if_t<!MatrixType::IsVectorAtCompileTime,
int> = 0>
170typename MatrixType::RealScalar stable_norm_impl(
const MatrixType& mat) {
173 using RealScalar =
typename MatrixType::RealScalar;
174 using Accumulator =
typename stable_norm_accumulator<RealScalar>::type;
175 Accumulator scale(0);
176 Accumulator invScale(1);
179 stable_norm_matrix_dispatch<MatrixType, Accumulator>::run(mat, ssq, scale, invScale);
180 return RealScalar(scale * sqrt(ssq));
183inline int stable_norm_floor_div2(
int value) {
return value / 2 - ((value < 0 && value % 2 != 0) ? 1 : 0); }
185inline int stable_norm_ceil_div2(
int value) {
return value / 2 + ((value > 0 && value % 2 != 0) ? 1 : 0); }
187template <
typename Accumulator>
188inline void blue_norm_accumulate_component(
const Accumulator& ax,
const Accumulator& tsml,
const Accumulator& tbig,
189 const Accumulator& ssml,
const Accumulator& sbig,
bool& notBig,
190 Accumulator& asml, Accumulator& amed, Accumulator& abig) {
192 abig += numext::abs2(ax * sbig);
194 }
else if (ax < tsml) {
195 if (notBig) asml += numext::abs2(ax * ssml);
197 amed += numext::abs2(ax);
201template <typename Scalar, typename Accumulator, bool = NumTraits<Scalar>::IsComplex>
202struct blue_norm_accumulate_scalar {
203 static inline void run(
const Scalar& value,
const Accumulator& tsml,
const Accumulator& tbig,
const Accumulator& ssml,
204 const Accumulator& sbig,
bool& notBig, Accumulator& asml, Accumulator& amed,
206 const Accumulator ax = numext::abs(Accumulator(value));
207 blue_norm_accumulate_component(ax, tsml, tbig, ssml, sbig, notBig, asml, amed, abig);
211template <
typename Scalar,
typename Accumulator>
212struct blue_norm_accumulate_scalar<Scalar, Accumulator, true> {
213 static inline void run(
const Scalar& value,
const Accumulator& tsml,
const Accumulator& tbig,
const Accumulator& ssml,
214 const Accumulator& sbig,
bool& notBig, Accumulator& asml, Accumulator& amed,
216 const Accumulator real = numext::abs(Accumulator(numext::real(value)));
217 const Accumulator imag = numext::abs(Accumulator(numext::imag(value)));
218 blue_norm_accumulate_component(real, tsml, tbig, ssml, sbig, notBig, asml, amed, abig);
219 blue_norm_accumulate_component(imag, tsml, tbig, ssml, sbig, notBig, asml, amed, abig);
223template <
typename Derived>
224inline typename NumTraits<typename traits<Derived>::Scalar>::Real blueNorm_impl(
const EigenBase<Derived>& _vec) {
225 using RealScalar =
typename Derived::RealScalar;
226 using Accumulator =
typename stable_norm_accumulator<RealScalar>::type;
227 using Scalar =
typename traits<Derived>::Scalar;
231 const Derived& vec(_vec.derived());
232 if (vec.size() == 0)
return RealScalar(0);
239 static const int ibeta = std::numeric_limits<Accumulator>::radix;
240 static const int it = NumTraits<Accumulator>::digits();
241 static const int iemin = NumTraits<Accumulator>::min_exponent();
242 static const int iemax = NumTraits<Accumulator>::max_exponent();
243 static const Accumulator tsml = Accumulator(pow(Accumulator(ibeta), Accumulator(stable_norm_ceil_div2(iemin - 1))));
244 static const Accumulator tbig =
245 Accumulator(pow(Accumulator(ibeta), Accumulator(stable_norm_floor_div2(iemax - it + 1))));
246 static const Accumulator ssml =
247 Accumulator(pow(Accumulator(ibeta), Accumulator(-stable_norm_floor_div2(iemin - it))));
248 static const Accumulator sbig =
249 Accumulator(pow(Accumulator(ibeta), Accumulator(-stable_norm_ceil_div2(iemax + it - 1))));
256 for (Index j = 0; j < vec.outerSize(); ++j) {
257 for (
typename Derived::InnerIterator iter(vec, j); iter; ++iter) {
258 blue_norm_accumulate_scalar<Scalar, Accumulator>::run(iter.value(), tsml, tbig, ssml, sbig, notBig, asml, amed,
263 Accumulator scale(1);
264 Accumulator sumsq(0);
265 if (abig > Accumulator(0)) {
266 if (amed > Accumulator(0) || amed > NumTraits<Accumulator>::highest() || amed != amed) abig += (amed * sbig) * sbig;
267 scale = Accumulator(1) / sbig;
269 }
else if (asml > Accumulator(0)) {
270 if (amed > Accumulator(0) || amed > NumTraits<Accumulator>::highest() || amed != amed) {
272 asml = sqrt(asml) / ssml;
275 const bool smallIsLarger = asml > amed;
276 const Accumulator ymin = smallIsLarger ? amed : asml;
277 const Accumulator ymax = smallIsLarger ? asml : amed;
278 sumsq = numext::abs2(ymax) * (Accumulator(1) + numext::abs2(ymin / ymax));
280 scale = Accumulator(1) / ssml;
286 return RealScalar(scale * sqrt(sumsq));
302template <
typename Derived>
304 using Nested =
typename internal::nested_eval<Derived, 2>::type;
305 Nested nested(derived());
306 return internal::stable_norm_impl(nested);
319template <
typename Derived>
321 return internal::blueNorm_impl(*
this);
329template <
typename Derived>
331 using Accumulator =
typename internal::stable_norm_accumulator<RealScalar>::type;
332 if (size() == 0)
return RealScalar(0);
RealViewReturnType realView()
Definition RealView.h:289
constexpr CastXpr< NewType >::Type cast() const
Definition DenseBase.h:66
RealScalar blueNorm() const
Definition StableNorm.h:320
RealScalar hypotNorm() const
Definition StableNorm.h:330
RealScalar stableNorm() const
Definition StableNorm.h:303
const CwiseUnaryOp< internal::scalar_abs_op< Scalar >, const Derived > cwiseAbs() const
constexpr unsigned int DirectAccessBit
Definition Constants.h:160
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition NumTraits.h:233