| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2025 Charlie Schlosser <cs.schlosser@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_REALVIEW_H | |
| #define EIGEN_REALVIEW_H | |
| // IWYU pragma: private | |
| #include "./InternalHeaderCheck.h" | |
| namespace Eigen { | |
| namespace internal { | |
| // Write access and vectorization requires array-oriented access to the real and imaginary components. | |
| // From https://en.cppreference.com/w/cpp/numeric/complex.html: | |
| // For any pointer to an element of an array of std::complex<T> named p and any valid array index i, | |
| // reinterpret_cast<T*>(p)[2 * i] is the real part of the complex number p[i], and | |
| // reinterpret_cast<T*>(p)[2 * i + 1] is the imaginary part of the complex number p[i]. | |
| template <typename T> | |
| struct complex_array_access : std::false_type {}; | |
| template <typename T> | |
| struct complex_array_access<std::complex<T>> : std::true_type {}; | |
| template <typename Xpr> | |
| struct traits<RealView<Xpr>> : public traits<Xpr> { | |
| template <typename T> | |
| static constexpr int double_size(T size, bool times_two) { | |
| int size_as_int = int(size); | |
| if (size_as_int == Dynamic) return Dynamic; | |
| return times_two ? (2 * size_as_int) : size_as_int; | |
| } | |
| using Base = traits<Xpr>; | |
| using ComplexScalar = typename Base::Scalar; | |
| using Scalar = typename NumTraits<ComplexScalar>::Real; | |
| static constexpr bool ArrayAccess = complex_array_access<ComplexScalar>::value; | |
| // Real and imaginary parts interleave with a uniform stride only when the complex coefficients are adjacent. | |
| static constexpr int ActualDirectAccessBit = | |
| ArrayAccess && inner_stride_at_compile_time<Xpr>::value == 1 ? DirectAccessBit : 0; | |
| static constexpr int ActualLvalueBit = !std::is_const<Xpr>::value && ArrayAccess ? LvalueBit : 0; | |
| static constexpr int ActualPacketAccessBit = packet_traits<Scalar>::Vectorizable ? PacketAccessBit : 0; | |
| static constexpr int FlagMask = | |
| ActualDirectAccessBit | ActualLvalueBit | ActualPacketAccessBit | HereditaryBits | LinearAccessBit; | |
| static constexpr int BaseFlags = int(evaluator<Xpr>::Flags) | int(Base::Flags); | |
| static constexpr int Flags = BaseFlags & FlagMask; | |
| static constexpr bool IsRowMajor = Flags & RowMajorBit; | |
| static constexpr int RowsAtCompileTime = double_size(Base::RowsAtCompileTime, !IsRowMajor); | |
| static constexpr int ColsAtCompileTime = double_size(Base::ColsAtCompileTime, IsRowMajor); | |
| static constexpr int SizeAtCompileTime = size_at_compile_time(RowsAtCompileTime, ColsAtCompileTime); | |
| static constexpr int MaxRowsAtCompileTime = double_size(Base::MaxRowsAtCompileTime, !IsRowMajor); | |
| static constexpr int MaxColsAtCompileTime = double_size(Base::MaxColsAtCompileTime, IsRowMajor); | |
| static constexpr int MaxSizeAtCompileTime = size_at_compile_time(MaxRowsAtCompileTime, MaxColsAtCompileTime); | |
| static constexpr int OuterStrideAtCompileTime = double_size(outer_stride_at_compile_time<Xpr>::value, true); | |
| static constexpr int InnerStrideAtCompileTime = inner_stride_at_compile_time<Xpr>::value; | |
| }; | |
| template <typename Xpr> | |
| struct evaluator<RealView<Xpr>> : private evaluator<Xpr> { | |
| using BaseEvaluator = evaluator<Xpr>; | |
| using XprType = RealView<Xpr>; | |
| using ExpressionTraits = traits<XprType>; | |
| using ComplexScalar = typename ExpressionTraits::ComplexScalar; | |
| using Scalar = typename ExpressionTraits::Scalar; | |
| static constexpr int Flags = ExpressionTraits::Flags; | |
| static constexpr int CoeffReadCost = BaseEvaluator::CoeffReadCost; | |
| static constexpr int Alignment = BaseEvaluator::Alignment; | |
| static constexpr bool IsRowMajor = ExpressionTraits::IsRowMajor; | |
| // DenseCoeffsBase::CoeffReturnType is a reference for LvalueBit as well as DirectAccessBit; returning a value | |
| // there would dangle. | |
| static constexpr bool CoeffByReference = (Flags & (LvalueBit | DirectAccessBit)) != 0; | |
| using ComplexCoeffReturnType = std::conditional_t<CoeffByReference, const ComplexScalar&, ComplexScalar>; | |
| using CoeffReturnType = std::conditional_t<CoeffByReference, const Scalar&, Scalar>; | |
| EIGEN_DEVICE_FUNC explicit evaluator(XprType realView) : BaseEvaluator(realView.m_xpr) {} | |
| template <bool Enable = CoeffByReference, std::enable_if_t<!Enable, bool> = true> | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const { | |
| Index r = IsRowMajor ? row : row / 2; | |
| Index c = IsRowMajor ? col / 2 : col; | |
| bool p = (IsRowMajor ? col : row) & 1; | |
| ComplexScalar ccoeff = BaseEvaluator::coeff(r, c); | |
| return p ? numext::imag(ccoeff) : numext::real(ccoeff); | |
| } | |
| template <bool Enable = CoeffByReference, std::enable_if_t<Enable, bool> = true> | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index row, Index col) const { | |
| Index r = IsRowMajor ? row : row / 2; | |
| Index c = IsRowMajor ? col / 2 : col; | |
| Index p = (IsRowMajor ? col : row) & 1; | |
| ComplexCoeffReturnType ccoeff = BaseEvaluator::coeff(r, c); | |
| return reinterpret_cast<const Scalar(&)[2]>(ccoeff)[p]; | |
| } | |
| template <bool Enable = CoeffByReference, std::enable_if_t<!Enable, bool> = true> | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index index) const { | |
| ComplexScalar ccoeff = BaseEvaluator::coeff(index / 2); | |
| bool p = index & 1; | |
| return p ? numext::imag(ccoeff) : numext::real(ccoeff); | |
| } | |
| template <bool Enable = CoeffByReference, std::enable_if_t<Enable, bool> = true> | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const { | |
| ComplexCoeffReturnType ccoeff = BaseEvaluator::coeff(index / 2); | |
| Index p = index & 1; | |
| return reinterpret_cast<const Scalar(&)[2]>(ccoeff)[p]; | |
| } | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index row, Index col) { | |
| Index r = IsRowMajor ? row : row / 2; | |
| Index c = IsRowMajor ? col / 2 : col; | |
| Index p = (IsRowMajor ? col : row) & 1; | |
| ComplexScalar& ccoeffRef = BaseEvaluator::coeffRef(r, c); | |
| return reinterpret_cast<Scalar(&)[2]>(ccoeffRef)[p]; | |
| } | |
| constexpr EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index index) { | |
| ComplexScalar& ccoeffRef = BaseEvaluator::coeffRef(index / 2); | |
| Index p = index & 1; | |
| return reinterpret_cast<Scalar(&)[2]>(ccoeffRef)[p]; | |
| } | |
| // If the first index is odd (imaginary), discard the first scalar | |
| // in 'result' and assign the missing scalar. | |
| // This operation is safe as the real component of the first scalar must exist. | |
| template <int LoadMode, typename PacketType> | |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packet(Index row, Index col) const { | |
| constexpr int RealPacketSize = unpacket_traits<PacketType>::size; | |
| using ComplexPacket = typename find_packet_by_size<ComplexScalar, RealPacketSize / 2>::type; | |
| EIGEN_STATIC_ASSERT((find_packet_by_size<ComplexScalar, RealPacketSize / 2>::value), | |
| MISSING COMPATIBLE COMPLEX PACKET TYPE) | |
| Index r = IsRowMajor ? row : row / 2; | |
| Index c = IsRowMajor ? col / 2 : col; | |
| bool p = (IsRowMajor ? col : row) & 1; | |
| ComplexPacket cresult = BaseEvaluator::template packet<LoadMode, ComplexPacket>(r, c); | |
| PacketType result = preinterpret<PacketType>(cresult); | |
| if (p) { | |
| Scalar aux[RealPacketSize + 1]; | |
| pstoreu(aux, result); | |
| Index lastr = IsRowMajor ? row : row + RealPacketSize - 1; | |
| Index lastc = IsRowMajor ? col + RealPacketSize - 1 : col; | |
| aux[RealPacketSize] = coeff(lastr, lastc); | |
| result = ploadu<PacketType>(aux + 1); | |
| } | |
| return result; | |
| } | |
| template <int LoadMode, typename PacketType> | |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packet(Index index) const { | |
| constexpr int RealPacketSize = unpacket_traits<PacketType>::size; | |
| using ComplexPacket = typename find_packet_by_size<ComplexScalar, RealPacketSize / 2>::type; | |
| EIGEN_STATIC_ASSERT((find_packet_by_size<ComplexScalar, RealPacketSize / 2>::value), | |
| MISSING COMPATIBLE COMPLEX PACKET TYPE) | |
| ComplexPacket cresult = BaseEvaluator::template packet<LoadMode, ComplexPacket>(index / 2); | |
| PacketType result = preinterpret<PacketType>(cresult); | |
| bool p = index & 1; | |
| if (p) { | |
| Scalar aux[RealPacketSize + 1]; | |
| pstoreu(aux, result); | |
| aux[RealPacketSize] = coeff(index + RealPacketSize - 1); | |
| result = ploadu<PacketType>(aux + 1); | |
| } | |
| return result; | |
| } | |
| // The requested real packet segment forms the half-open interval [begin, end), where 'end' = 'begin' + 'count'. | |
| // In order to access the underlying complex array, even indices must be aligned with the real components | |
| // of the complex scalars. 'begin' and 'count' must be modified as follows: | |
| // a) 'begin' must be rounded down to the nearest even number; and | |
| // b) 'end' must be rounded up to the nearest even number. | |
| template <int LoadMode, typename PacketType> | |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packetSegment(Index row, Index col, Index begin, Index count) const { | |
| constexpr int RealPacketSize = unpacket_traits<PacketType>::size; | |
| using ComplexPacket = typename find_packet_by_size<ComplexScalar, RealPacketSize / 2>::type; | |
| EIGEN_STATIC_ASSERT((find_packet_by_size<ComplexScalar, RealPacketSize / 2>::value), | |
| MISSING COMPATIBLE COMPLEX PACKET TYPE) | |
| Index actualBegin = numext::round_down(begin, 2); | |
| Index actualEnd = numext::round_down(begin + count + 1, 2); | |
| Index actualCount = actualEnd - actualBegin; | |
| Index r = IsRowMajor ? row : row / 2; | |
| Index c = IsRowMajor ? col / 2 : col; | |
| ComplexPacket cresult = | |
| BaseEvaluator::template packetSegment<LoadMode, ComplexPacket>(r, c, actualBegin / 2, actualCount / 2); | |
| PacketType result = preinterpret<PacketType>(cresult); | |
| bool p = (IsRowMajor ? col : row) & 1; | |
| if (p) { | |
| Scalar aux[RealPacketSize + 1] = {}; | |
| pstoreu(aux, result); | |
| Index lastr = IsRowMajor ? row : row + actualEnd - 1; | |
| Index lastc = IsRowMajor ? col + actualEnd - 1 : col; | |
| aux[actualEnd] = coeff(lastr, lastc); | |
| result = ploadu<PacketType>(aux + 1); | |
| } | |
| return result; | |
| } | |
| template <int LoadMode, typename PacketType> | |
| EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packetSegment(Index index, Index begin, Index count) const { | |
| constexpr int RealPacketSize = unpacket_traits<PacketType>::size; | |
| using ComplexPacket = typename find_packet_by_size<ComplexScalar, RealPacketSize / 2>::type; | |
| EIGEN_STATIC_ASSERT((find_packet_by_size<ComplexScalar, RealPacketSize / 2>::value), | |
| MISSING COMPATIBLE COMPLEX PACKET TYPE) | |
| Index actualBegin = numext::round_down(begin, 2); | |
| Index actualEnd = numext::round_down(begin + count + 1, 2); | |
| Index actualCount = actualEnd - actualBegin; | |
| ComplexPacket cresult = | |
| BaseEvaluator::template packetSegment<LoadMode, ComplexPacket>(index / 2, actualBegin / 2, actualCount / 2); | |
| PacketType result = preinterpret<PacketType>(cresult); | |
| bool p = index & 1; | |
| if (p) { | |
| Scalar aux[RealPacketSize + 1] = {}; | |
| pstoreu(aux, result); | |
| aux[actualEnd] = coeff(index + actualEnd - 1); | |
| result = ploadu<PacketType>(aux + 1); | |
| } | |
| return result; | |
| } | |
| }; | |
| } // namespace internal | |
| template <typename Xpr> | |
| class RealView : public internal::dense_xpr_base<RealView<Xpr>>::type { | |
| using ExpressionTraits = internal::traits<RealView>; | |
| EIGEN_STATIC_ASSERT(NumTraits<typename Xpr::Scalar>::IsComplex, SCALAR MUST BE COMPLEX) | |
| public: | |
| using Scalar = typename ExpressionTraits::Scalar; | |
| using Nested = RealView; | |
| EIGEN_DEVICE_FUNC explicit RealView(Xpr& xpr) : m_xpr(xpr) {} | |
| EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept { return Xpr::IsRowMajor ? m_xpr.rows() : 2 * m_xpr.rows(); } | |
| EIGEN_DEVICE_FUNC constexpr Index cols() const noexcept { return Xpr::IsRowMajor ? 2 * m_xpr.cols() : m_xpr.cols(); } | |
| EIGEN_DEVICE_FUNC constexpr Index size() const noexcept { return 2 * m_xpr.size(); } | |
| EIGEN_DEVICE_FUNC constexpr Index innerStride() const noexcept { return m_xpr.innerStride(); } | |
| EIGEN_DEVICE_FUNC constexpr Index outerStride() const noexcept { return 2 * m_xpr.outerStride(); } | |
| EIGEN_DEVICE_FUNC void resize(Index rows, Index cols) { | |
| m_xpr.resize(Xpr::IsRowMajor ? rows : rows / 2, Xpr::IsRowMajor ? cols / 2 : cols); | |
| } | |
| EIGEN_DEVICE_FUNC void resize(Index size) { m_xpr.resize(size / 2); } | |
| using ScalarWithConstIfNotLvalue = std::conditional_t<internal::is_lvalue<Xpr>::value, Scalar, const Scalar>; | |
| EIGEN_DEVICE_FUNC ScalarWithConstIfNotLvalue* data() { | |
| return reinterpret_cast<ScalarWithConstIfNotLvalue*>(m_xpr.data()); | |
| } | |
| EIGEN_DEVICE_FUNC const Scalar* data() const { return reinterpret_cast<const Scalar*>(m_xpr.data()); } | |
| EIGEN_DEVICE_FUNC RealView(const RealView&) = default; | |
| EIGEN_DEVICE_FUNC RealView& operator=(const RealView& other); | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC RealView& operator=(const RealView<OtherDerived>& other); | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC RealView& operator=(const DenseBase<OtherDerived>& other); | |
| protected: | |
| friend struct internal::evaluator<RealView>; | |
| Xpr& m_xpr; | |
| }; | |
| template <typename Xpr> | |
| EIGEN_DEVICE_FUNC RealView<Xpr>& RealView<Xpr>::operator=(const RealView& other) { | |
| internal::call_assignment(*this, other); | |
| return *this; | |
| } | |
| template <typename Xpr> | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC RealView<Xpr>& RealView<Xpr>::operator=(const RealView<OtherDerived>& other) { | |
| internal::call_assignment(*this, other); | |
| return *this; | |
| } | |
| template <typename Xpr> | |
| template <typename OtherDerived> | |
| EIGEN_DEVICE_FUNC RealView<Xpr>& RealView<Xpr>::operator=(const DenseBase<OtherDerived>& other) { | |
| internal::call_assignment(*this, other.derived()); | |
| return *this; | |
| } | |
| template <typename Derived> | |
| EIGEN_DEVICE_FUNC typename DenseBase<Derived>::RealViewReturnType DenseBase<Derived>::realView() { | |
| return RealViewReturnType(derived()); | |
| } | |
| template <typename Derived> | |
| EIGEN_DEVICE_FUNC typename DenseBase<Derived>::ConstRealViewReturnType DenseBase<Derived>::realView() const { | |
| return ConstRealViewReturnType(derived()); | |
| } | |
| } // namespace Eigen | |
| #endif // EIGEN_REALVIEW_H |