| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2006-2008, 2010 Benoit Jacob <jacob.benoit.1@gmail.com> |
| // |
| // 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-License-Identifier: MPL-2.0 |
| |
| #ifndef EIGEN_DOT_H |
| #define EIGEN_DOT_H |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| |
| namespace internal { |
| |
| // Accumulate low-precision norms in float without changing the public result type. |
| template <typename RealScalar> |
| struct stable_norm_accumulator { |
| using type = RealScalar; |
| }; |
| |
| template <> |
| struct stable_norm_accumulator<half> { |
| using type = float; |
| }; |
| |
| template <> |
| struct stable_norm_accumulator<bfloat16> { |
| using type = float; |
| }; |
| |
| template <typename RealScalar, typename Accumulator> |
| struct stable_normalization_normal_min { |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator run() { |
| return static_cast<Accumulator>((numext::numeric_limits<RealScalar>::min)()); |
| } |
| }; |
| |
| // The half and bfloat16 numeric_limits functions are not device functions. |
| template <typename Accumulator> |
| struct stable_normalization_normal_min<half, Accumulator> { |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator run() { return Accumulator(1) / Accumulator(16384); } |
| }; |
| |
| template <typename Accumulator> |
| struct stable_normalization_normal_min<bfloat16, Accumulator> { |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator run() { |
| return static_cast<Accumulator>((numext::numeric_limits<float>::min)()); |
| } |
| }; |
| |
| template <typename RealScalar, typename Accumulator, bool = std::is_floating_point<Accumulator>::value> |
| struct stable_normalization_use_reciprocal { |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE bool run(const Accumulator&) { return false; } |
| }; |
| |
| template <typename RealScalar, typename Accumulator> |
| struct stable_normalization_use_reciprocal<RealScalar, Accumulator, true> { |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE bool run(const Accumulator& scale) { |
| const Accumulator normal_min = stable_normalization_normal_min<RealScalar, Accumulator>::run(); |
| const Accumulator normal_max = Accumulator(1) / normal_min; |
| return scale >= normal_min && scale <= normal_max; |
| } |
| }; |
| |
| template <typename VectorType, typename Accumulator, |
| bool = bool(traits<VectorType>::Flags & DirectAccessBit) && |
| (int(inner_stride_at_compile_time<VectorType>::value) != 1)> |
| struct stable_normalization_dispatch { |
| using Scalar = typename traits<VectorType>::Scalar; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| // Only complex_array_access scalars have a writable component view. |
| using HasWritableRealView = bool_constant<!NumTraits<Scalar>::IsComplex || complex_array_access<Scalar>::value>; |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator max_abs(const VectorType& vec) { |
| return vec.realView().template cast<Accumulator>().cwiseAbs().template maxCoeff<PropagateNaN>(); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator scaled_squared_norm(const VectorType& vec, |
| const Accumulator& factor) { |
| return (vec.realView().template cast<Accumulator>() * factor).squaredNorm(); |
| } |
| |
| template <typename ResultType> |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void assign_scaled(ResultType& result, const VectorType& vec, |
| const Accumulator& factor) { |
| assign_scaled_impl(result, vec, factor, HasWritableRealView()); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void scale_in_place(VectorType& vec, const Accumulator& factor) { |
| scale_in_place_impl(vec, factor, HasWritableRealView()); |
| } |
| |
| private: |
| template <typename ResultType> |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void assign_scaled_impl(ResultType& result, const VectorType& vec, |
| const Accumulator& factor, std::true_type) { |
| result.realView() = (vec.realView().template cast<Accumulator>() * factor).template cast<RealScalar>(); |
| } |
| |
| template <typename ResultType> |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void assign_scaled_impl(ResultType& result, const VectorType& vec, |
| const Accumulator& factor, std::false_type) { |
| result = vec * static_cast<RealScalar>(factor); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void scale_in_place_impl(VectorType& vec, const Accumulator& factor, |
| std::true_type) { |
| vec.realView() = (vec.realView().template cast<Accumulator>() * factor).template cast<RealScalar>(); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void scale_in_place_impl(VectorType& vec, const Accumulator& factor, |
| std::false_type) { |
| vec = vec * static_cast<RealScalar>(factor); |
| } |
| }; |
| |
| // Runtime-contiguous expressions can still use packet traversal. |
| template <typename VectorType, typename Accumulator> |
| struct stable_normalization_dispatch<VectorType, Accumulator, true> { |
| using Scalar = typename traits<VectorType>::Scalar; |
| using RealScalar = typename NumTraits<Scalar>::Real; |
| // A dynamic map avoids over-unrolling fixed-size normalization paths. |
| using PlainVector = Matrix<Scalar, Dynamic, 1>; |
| using ConstContiguousMap = Map<const PlainVector, evaluator<VectorType>::Alignment>; |
| using ContiguousMap = Map<PlainVector, evaluator<VectorType>::Alignment>; |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE bool is_contiguous(const VectorType& vec) { |
| return vec.innerStride() == 1 && (vec.outerSize() == 1 || vec.outerStride() == vec.innerSize()); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator max_abs(const VectorType& vec) { |
| if (is_contiguous(vec)) { |
| const ConstContiguousMap contiguous(vec.data(), vec.size()); |
| return stable_normalization_dispatch<ConstContiguousMap, Accumulator, false>::max_abs(contiguous); |
| } |
| return stable_normalization_dispatch<VectorType, Accumulator, false>::max_abs(vec); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Accumulator scaled_squared_norm(const VectorType& vec, |
| const Accumulator& factor) { |
| if (is_contiguous(vec)) { |
| const ConstContiguousMap contiguous(vec.data(), vec.size()); |
| return stable_normalization_dispatch<ConstContiguousMap, Accumulator, false>::scaled_squared_norm(contiguous, |
| factor); |
| } |
| return stable_normalization_dispatch<VectorType, Accumulator, false>::scaled_squared_norm(vec, factor); |
| } |
| |
| template <typename ResultType> |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void assign_scaled(ResultType& result, const VectorType& vec, |
| const Accumulator& factor) { |
| if (is_contiguous(vec)) { |
| const ConstContiguousMap contiguous(vec.data(), vec.size()); |
| Map<PlainVector> output(result.data(), result.size()); |
| stable_normalization_dispatch<ConstContiguousMap, Accumulator, false>::assign_scaled(output, contiguous, factor); |
| return; |
| } |
| stable_normalization_dispatch<VectorType, Accumulator, false>::assign_scaled(result, vec, factor); |
| } |
| |
| EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE void scale_in_place(VectorType& vec, const Accumulator& factor) { |
| if (is_contiguous(vec)) { |
| ContiguousMap contiguous(vec.data(), vec.size()); |
| stable_normalization_dispatch<ContiguousMap, Accumulator, false>::scale_in_place(contiguous, factor); |
| return; |
| } |
| stable_normalization_dispatch<VectorType, Accumulator, false>::scale_in_place(vec, factor); |
| } |
| }; |
| |
| // Prevent fast-math from merging normal scale factors into a subnormal factor. |
| template <typename VectorType, typename Accumulator> |
| EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void stable_normalization_scale_in_place(VectorType& vec, |
| const Accumulator& factor) { |
| stable_normalization_dispatch<VectorType, Accumulator>::scale_in_place(vec, factor); |
| } |
| |
| template <typename VectorType, typename Divisor> |
| EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void stable_normalization_divide_in_place(VectorType& vec, const Divisor& divisor) { |
| vec /= divisor; |
| } |
| |
| // squaredNorm() reduces realView().cwiseAbs2(), a cwise expression with no direct access, so when |
| // the underlying expression has an inner stride that is not statically 1 (a dynamic-inner-stride |
| // Map/Ref, a row of a 1xN matrix, ...) the reduction falls back to a scalar traversal even though |
| // the data is frequently contiguous at runtime. This trait flags the cases where a runtime |
| // contiguity check is worthwhile; it mirrors the reduction fast path in Redux.h (redux_dispatch). |
| // bool is excluded: its squared norm is any(), handled by a dedicated specialization below. |
| template <typename Xpr> |
| struct squared_norm_runtime_unit_stride { |
| using Scalar = typename traits<Xpr>::Scalar; |
| static constexpr bool value = |
| bool(traits<Xpr>::Flags & DirectAccessBit) && bool(packet_traits<Scalar>::Vectorizable) && |
| !bool(internal::is_same<Scalar, bool>::value) && (int(inner_stride_at_compile_time<Xpr>::value) != 1); |
| }; |
| |
| template <typename Derived, typename Scalar = typename traits<Derived>::Scalar, typename Enable = void> |
| struct squared_norm_impl { |
| using Real = typename NumTraits<Scalar>::Real; |
| static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Real run(const Derived& a) { |
| return a.realView().cwiseAbs2().sum(); |
| } |
| }; |
| |
| template <typename Derived> |
| struct squared_norm_impl<Derived, bool, void> { |
| static EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE bool run(const Derived& a) { return a.any(); } |
| }; |
| |
| // Runtime contiguity fast path: when the data is contiguous at runtime (inner stride 1, and a |
| // single inner panel or no gap between inner panels), reduce the underlying buffer as a contiguous |
| // vector, recovering vectorization of the abs2 reduction. |
| template <typename Derived, typename Scalar> |
| struct squared_norm_impl<Derived, Scalar, std::enable_if_t<squared_norm_runtime_unit_stride<Derived>::value>> { |
| using Real = typename NumTraits<Scalar>::Real; |
| static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Real run(const Derived& a) { |
| if (a.innerStride() == 1 && (a.outerSize() == 1 || a.outerStride() == a.innerSize())) { |
| using PlainVector = Matrix<Scalar, Dynamic, 1>; |
| Map<const PlainVector, evaluator<Derived>::Alignment> contiguous(a.data(), a.size()); |
| return contiguous.realView().cwiseAbs2().sum(); |
| } |
| return a.realView().cwiseAbs2().sum(); |
| } |
| }; |
| |
| } // end namespace internal |
| |
| /** \fn MatrixBase::dot |
| * \returns the dot product of *this with other. |
| * |
| * \only_for_vectors |
| * |
| * \note If the scalar type is complex numbers, then this function returns the hermitian |
| * (sesquilinear) dot product, conjugate-linear in the first variable and linear in the |
| * second variable. |
| * |
| * \sa squaredNorm(), norm() |
| */ |
| template <typename Derived> |
| template <typename OtherDerived> |
| EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE |
| typename ScalarBinaryOpTraits<typename internal::traits<Derived>::Scalar, |
| typename internal::traits<OtherDerived>::Scalar>::ReturnType |
| MatrixBase<Derived>::dot(const MatrixBase<OtherDerived>& other) const { |
| return internal::dot_impl<Derived, OtherDerived>::run(derived(), other.derived()); |
| } |
| |
| //---------- implementation of L2 norm and related functions ---------- |
| |
| /** \returns, for vectors, the squared \em l2 norm of \c *this, and for matrices the squared Frobenius norm. |
| * In both cases, it consists in the sum of the square of all the matrix entries. |
| * For vectors, this is also equal to the dot product of \c *this with itself. |
| * |
| * \sa dot(), norm(), lpNorm() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE typename NumTraits<typename internal::traits<Derived>::Scalar>::Real |
| MatrixBase<Derived>::squaredNorm() const { |
| return internal::squared_norm_impl<Derived>::run(derived()); |
| } |
| |
| /** \returns, for vectors, the \em l2 norm of \c *this, and for matrices the Frobenius norm. |
| * In both cases, it consists in the square root of the sum of the square of all the matrix entries. |
| * For vectors, this is also equal to the square root of the dot product of \c *this with itself. |
| * |
| * \sa lpNorm(), dot(), squaredNorm() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE typename NumTraits<typename internal::traits<Derived>::Scalar>::Real |
| MatrixBase<Derived>::norm() const { |
| return numext::sqrt(squaredNorm()); |
| } |
| |
| /** \returns an expression of the quotient of \c *this by its own norm. |
| * |
| * \warning If the input vector is too small (i.e., this->norm()==0), |
| * then this function returns a copy of the input. |
| * |
| * \only_for_vectors |
| * |
| * \sa norm(), normalize() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase<Derived>::PlainObject MatrixBase<Derived>::normalized() |
| const { |
| using Nested_ = typename internal::nested_eval<Derived, 2>::type; |
| Nested_ n(derived()); |
| RealScalar z = n.squaredNorm(); |
| // NOTE: after extensive benchmarking, this conditional does not impact performance, at least on recent x86 CPU |
| if (z > RealScalar(0)) |
| return n / numext::sqrt(z); |
| else |
| return n; |
| } |
| |
| /** Normalizes the vector, i.e. divides it by its own norm. |
| * |
| * \only_for_vectors |
| * |
| * \warning If the input vector is too small (i.e., this->norm()==0), then \c *this is left unchanged. |
| * |
| * \sa norm(), normalized() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase<Derived>::normalize() { |
| RealScalar z = squaredNorm(); |
| // NOTE: after extensive benchmarking, this conditional does not impact performance, at least on recent x86 CPU |
| if (z > RealScalar(0)) derived() /= numext::sqrt(z); |
| } |
| |
| /** \returns an expression of the quotient of \c *this by its own norm while avoiding underflow and overflow. |
| * |
| * \only_for_vectors |
| * |
| * This method is analogue to the normalized() method, but it reduces the risk of |
| * underflow and overflow when computing the norm. |
| * |
| * \warning If the input vector is too small (i.e., this->norm()==0), |
| * then this function returns a copy of the input. |
| * |
| * \sa stableNorm(), stableNormalize(), normalized() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename MatrixBase<Derived>::PlainObject |
| MatrixBase<Derived>::stableNormalized() const { |
| using Nested_ = typename internal::nested_eval<Derived, 3>::type; |
| using NestedClean = internal::remove_all_t<Nested_>; |
| using Accumulator = typename internal::stable_norm_accumulator<RealScalar>::type; |
| using Dispatch = internal::stable_normalization_dispatch<NestedClean, Accumulator>; |
| Nested_ vec(derived()); |
| if (EIGEN_PREDICT_FALSE(vec.size() == 0)) return vec; |
| |
| // Component-wise scaling stays finite when a finite complex value has an |
| // overflowing magnitude, and avoids a hypot per coefficient. |
| const Accumulator w = Dispatch::max_abs(vec); |
| const Accumulator highest = static_cast<Accumulator>(NumTraits<RealScalar>::highest()); |
| if (EIGEN_PREDICT_FALSE(!(w > Accumulator(0)) || !(w <= highest))) return vec; |
| |
| if (EIGEN_PREDICT_TRUE((internal::stable_normalization_use_reciprocal<RealScalar, Accumulator>::run(w)))) { |
| // Here w and its reciprocal are normal, so multiplication is safe. |
| const Accumulator inv_w = Accumulator(1) / w; |
| const Accumulator z = Dispatch::scaled_squared_norm(vec, inv_w); |
| if (z > Accumulator(0)) { |
| const Accumulator sqrt_z = numext::sqrt(z); |
| const Accumulator factor = inv_w / sqrt_z; |
| PlainObject normalized(rows(), cols()); |
| const Accumulator accumulator_normal_min = |
| internal::stable_normalization_normal_min<Accumulator, Accumulator>::run(); |
| if (EIGEN_PREDICT_TRUE(factor >= accumulator_normal_min)) { |
| Dispatch::assign_scaled(normalized, vec, factor); |
| } else { |
| // inv_w and sqrt_z are normal even though their quotient is not. |
| Dispatch::assign_scaled(normalized, vec, inv_w); |
| internal::stable_normalization_divide_in_place(normalized, static_cast<RealScalar>(sqrt_z)); |
| } |
| return normalized; |
| } |
| return vec; |
| } |
| |
| // Two normal divisors avoid an exceptional reciprocal and fast-math |
| // reassociation into multiplication by 1 / w. |
| const Accumulator sqrt_w = numext::sqrt(w); |
| const RealScalar scale1 = static_cast<RealScalar>(sqrt_w); |
| const RealScalar scale2 = static_cast<RealScalar>(w / sqrt_w); |
| PlainObject normalized = vec; |
| internal::stable_normalization_divide_in_place(normalized, scale1); |
| internal::stable_normalization_divide_in_place(normalized, scale2); |
| const Accumulator z = normalized.realView().template cast<Accumulator>().squaredNorm(); |
| if (z > Accumulator(0)) { |
| internal::stable_normalization_scale_in_place(normalized, Accumulator(1) / numext::sqrt(z)); |
| } |
| return normalized; |
| } |
| |
| /** Normalizes the vector while avoid underflow and overflow |
| * |
| * \only_for_vectors |
| * |
| * This method is analogue to the normalize() method, but it reduces the risk of |
| * underflow and overflow when computing the norm. |
| * |
| * \warning If the input vector is too small (i.e., this->norm()==0), then \c *this is left unchanged. |
| * |
| * \sa stableNorm(), stableNormalized(), normalize() |
| */ |
| template <typename Derived> |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void MatrixBase<Derived>::stableNormalize() { |
| using Accumulator = typename internal::stable_norm_accumulator<RealScalar>::type; |
| using Dispatch = internal::stable_normalization_dispatch<Derived, Accumulator>; |
| if (EIGEN_PREDICT_FALSE(size() == 0)) return; |
| |
| const Accumulator w = Dispatch::max_abs(derived()); |
| const Accumulator highest = static_cast<Accumulator>(NumTraits<RealScalar>::highest()); |
| if (EIGEN_PREDICT_FALSE(!(w > Accumulator(0)) || !(w <= highest))) return; |
| |
| if (EIGEN_PREDICT_TRUE((internal::stable_normalization_use_reciprocal<RealScalar, Accumulator>::run(w)))) { |
| const Accumulator inv_w = Accumulator(1) / w; |
| const Accumulator z = Dispatch::scaled_squared_norm(derived(), inv_w); |
| if (z > Accumulator(0)) { |
| const Accumulator sqrt_z = numext::sqrt(z); |
| const Accumulator factor = inv_w / sqrt_z; |
| const Accumulator accumulator_normal_min = |
| internal::stable_normalization_normal_min<Accumulator, Accumulator>::run(); |
| if (EIGEN_PREDICT_TRUE(factor >= accumulator_normal_min)) { |
| Dispatch::scale_in_place(derived(), factor); |
| } else { |
| internal::stable_normalization_scale_in_place(derived(), inv_w); |
| internal::stable_normalization_divide_in_place(derived(), static_cast<RealScalar>(sqrt_z)); |
| } |
| } |
| return; |
| } |
| |
| const Accumulator sqrt_w = numext::sqrt(w); |
| const RealScalar scale1 = static_cast<RealScalar>(sqrt_w); |
| const RealScalar scale2 = static_cast<RealScalar>(w / sqrt_w); |
| internal::stable_normalization_divide_in_place(derived(), scale1); |
| internal::stable_normalization_divide_in_place(derived(), scale2); |
| const Accumulator z = derived().realView().template cast<Accumulator>().squaredNorm(); |
| if (z > Accumulator(0)) { |
| internal::stable_normalization_scale_in_place(derived(), Accumulator(1) / numext::sqrt(z)); |
| } |
| } |
| |
| //---------- implementation of other norms ---------- |
| |
| namespace internal { |
| |
| template <typename Derived, int p> |
| struct lpNorm_selector { |
| using RealScalar = typename NumTraits<typename traits<Derived>::Scalar>::Real; |
| EIGEN_DEVICE_FUNC static inline RealScalar run(const MatrixBase<Derived>& m) { |
| EIGEN_USING_STD(pow) |
| return pow(m.cwiseAbs().array().pow(p).sum(), RealScalar(1) / p); |
| } |
| }; |
| |
| template <typename Derived> |
| struct lpNorm_selector<Derived, 1> { |
| EIGEN_DEVICE_FUNC static inline typename NumTraits<typename traits<Derived>::Scalar>::Real run( |
| const MatrixBase<Derived>& m) { |
| return m.cwiseAbs().sum(); |
| } |
| }; |
| |
| template <typename Derived> |
| struct lpNorm_selector<Derived, 2> { |
| EIGEN_DEVICE_FUNC static inline typename NumTraits<typename traits<Derived>::Scalar>::Real run( |
| const MatrixBase<Derived>& m) { |
| return m.norm(); |
| } |
| }; |
| |
| template <typename Derived> |
| struct lpNorm_selector<Derived, Infinity> { |
| using RealScalar = typename NumTraits<typename traits<Derived>::Scalar>::Real; |
| EIGEN_DEVICE_FUNC static inline RealScalar run(const MatrixBase<Derived>& m) { |
| if (Derived::SizeAtCompileTime == 0 || (Derived::SizeAtCompileTime == Dynamic && m.size() == 0)) |
| return RealScalar(0); |
| return m.cwiseAbs().maxCoeff(); |
| } |
| }; |
| |
| } // end namespace internal |
| |
| /** \returns the \b coefficient-wise \f$ \ell^p \f$ norm of \c *this, that is, returns the p-th root of the sum of the |
| * p-th powers of the absolute values of the coefficients of \c *this. If \a p is the special value \a Eigen::Infinity, |
| * this function returns the \f$ \ell^\infty \f$ norm, that is the maximum of the absolute values of the coefficients of |
| * \c *this. |
| * |
| * In all cases, if \c *this is empty, then the value 0 is returned. |
| * |
| * \note For matrices, this function does not compute the <a |
| * href="https://en.wikipedia.org/wiki/Operator_norm">operator-norm</a>. That is, if \c *this is a matrix, then its |
| * coefficients are interpreted as a 1D vector. Nonetheless, you can easily compute the 1-norm and \f$\infty\f$-norm |
| * matrix operator norms using \link TutorialReductionsVisitorsBroadcastingReductionsNorm partial reductions \endlink. |
| * |
| * \sa norm() |
| */ |
| template <typename Derived> |
| template <int p> |
| #ifndef EIGEN_PARSED_BY_DOXYGEN |
| EIGEN_DEVICE_FUNC inline typename NumTraits<typename internal::traits<Derived>::Scalar>::Real |
| #else |
| EIGEN_DEVICE_FUNC MatrixBase<Derived>::RealScalar |
| #endif |
| MatrixBase<Derived>::lpNorm() const { |
| return internal::lpNorm_selector<Derived, p>::run(*this); |
| } |
| |
| //---------- implementation of isOrthogonal / isUnitary ---------- |
| |
| /** \returns true if *this is approximately orthogonal to \a other, |
| * within the precision given by \a prec. |
| * |
| * Example: \include MatrixBase_isOrthogonal.cpp |
| * Output: \verbinclude MatrixBase_isOrthogonal.out |
| */ |
| template <typename Derived> |
| template <typename OtherDerived> |
| bool MatrixBase<Derived>::isOrthogonal(const MatrixBase<OtherDerived>& other, const RealScalar& prec) const { |
| typename internal::nested_eval<Derived, 2>::type nested(derived()); |
| typename internal::nested_eval<OtherDerived, 2>::type otherNested(other.derived()); |
| return numext::abs2(nested.dot(otherNested)) <= prec * prec * nested.squaredNorm() * otherNested.squaredNorm(); |
| } |
| |
| /** \returns true if *this is approximately an unitary matrix, |
| * within the precision given by \a prec. In the case where the \a Scalar |
| * type is real numbers, a unitary matrix is an orthogonal matrix, whence the name. |
| * |
| * \note This can be used to check whether a family of vectors forms an orthonormal basis. |
| * Indeed, \c m.isUnitary() returns true if and only if the columns (equivalently, the rows) of m form an |
| * orthonormal basis. |
| * |
| * Example: \include MatrixBase_isUnitary.cpp |
| * Output: \verbinclude MatrixBase_isUnitary.out |
| */ |
| template <typename Derived> |
| bool MatrixBase<Derived>::isUnitary(const RealScalar& prec) const { |
| typename internal::nested_eval<Derived, 1>::type self(derived()); |
| for (Index i = 0; i < cols(); ++i) { |
| if (!internal::isApprox(self.col(i).squaredNorm(), static_cast<RealScalar>(1), prec)) return false; |
| for (Index j = 0; j < i; ++j) |
| if (!internal::isMuchSmallerThan(self.col(i).dot(self.col(j)), static_cast<Scalar>(1), prec)) return false; |
| } |
| return true; |
| } |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_DOT_H |