Eigen  5.0.1
 
Loading...
Searching...
No Matches
Complex.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2025 Rasmus Munk Larsen
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_COMPLEX_CLANG_H
12#define EIGEN_COMPLEX_CLANG_H
13
14// IWYU pragma: private
15#include "../../InternalHeaderCheck.h"
16
17namespace Eigen {
18namespace internal {
19
20template <typename RealScalar, int N>
21struct complex_packet_wrapper {
22 using RealPacketT = detail::VectorType<RealScalar, 2 * N>;
23 complex_packet_wrapper() = default;
24 EIGEN_STRONG_INLINE explicit complex_packet_wrapper(const RealPacketT& a) : v(a) {}
25 EIGEN_STRONG_INLINE constexpr std::complex<RealScalar> operator[](Index i) const {
26 return std::complex<RealScalar>(v[2 * i], v[2 * i + 1]);
27 }
28 RealPacketT v;
29};
30
31// --- Primary complex packet aliases ---
32constexpr int kComplexFloatSize = kFloatPacketSize / 2; // 2, 4, or 8
33constexpr int kComplexDoubleSize = kDoublePacketSize / 2; // 1, 2, or 4
34using PacketXcf = complex_packet_wrapper<float, kComplexFloatSize>;
35using PacketXcd = complex_packet_wrapper<double, kComplexDoubleSize>;
36
37struct generic_complex_packet_traits : default_packet_traits {
38 enum {
39 Vectorizable = 1,
40 AlignedOnScalar = 1,
41 HasAdd = 1,
42 HasSub = 1,
43 HasMul = 1,
44 HasDiv = 1,
45 HasNegate = 1,
46 HasAbs = 0,
47 HasAbs2 = 0,
48 HasMin = 0,
49 HasMax = 0,
50 HasArg = 0,
51 HasSetLinear = 0,
52 HasConj = 1,
53 // Math functions
54 HasLog = 1,
55 HasExp = 1,
56 HasSqrt = 1,
57 };
58};
59
60template <>
61struct packet_traits<std::complex<float>> : generic_complex_packet_traits {
62 using type = PacketXcf;
63 using half = PacketXcf;
64 enum {
65 size = kComplexFloatSize,
66 };
67};
68
69template <>
70struct unpacket_traits<PacketXcf> : generic_unpacket_traits {
71 using type = std::complex<float>;
72 using half = PacketXcf;
73 using as_real = PacketXf;
74 enum {
75 size = kComplexFloatSize,
76 };
77};
78
79template <>
80struct packet_traits<std::complex<double>> : generic_complex_packet_traits {
81 using type = PacketXcd;
82 using half = PacketXcd;
83 enum {
84 size = kComplexDoubleSize,
85 };
86};
87
88template <>
89struct unpacket_traits<PacketXcd> : generic_unpacket_traits {
90 using type = std::complex<double>;
91 using half = PacketXcd;
92 using as_real = PacketXd;
93 enum {
94 size = kComplexDoubleSize,
95 };
96};
97
98// ------------ Load and store ops ----------
99#define EIGEN_CLANG_COMPLEX_LOAD_STORE(PACKET_TYPE) \
100 template <> \
101 EIGEN_STRONG_INLINE PACKET_TYPE ploadu<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
102 return PACKET_TYPE(ploadu<typename unpacket_traits<PACKET_TYPE>::as_real>(&numext::real_ref(*from))); \
103 } \
104 template <> \
105 EIGEN_STRONG_INLINE PACKET_TYPE pload<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
106 return PACKET_TYPE(pload<typename unpacket_traits<PACKET_TYPE>::as_real>(&numext::real_ref(*from))); \
107 } \
108 template <> \
109 EIGEN_STRONG_INLINE void pstoreu<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
110 typename unpacket_traits<PACKET_TYPE>::type * to, const PACKET_TYPE& from) { \
111 pstoreu(&numext::real_ref(*to), from.v); \
112 } \
113 template <> \
114 EIGEN_STRONG_INLINE void pstore<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
115 typename unpacket_traits<PACKET_TYPE>::type * to, const PACKET_TYPE& from) { \
116 pstore(&numext::real_ref(*to), from.v); \
117 }
118
119EIGEN_CLANG_COMPLEX_LOAD_STORE(PacketXcf);
120EIGEN_CLANG_COMPLEX_LOAD_STORE(PacketXcd);
121#undef EIGEN_CLANG_COMPLEX_LOAD_STORE
122
123namespace detail {
124
125// Index sequence over the real components -- two per complex value -- of a
126// complex packet. Index Is names component Is % 2 of complex value Is / 2.
127template <typename ComplexPacket>
128using complex_real_indices = vector_indices<typename ComplexPacket::RealPacketT>;
129
130template <typename ComplexPacket, std::size_t... Is>
131EIGEN_STRONG_INLINE ComplexPacket complex_pset1_impl(const typename unpacket_traits<ComplexPacket>::type& from,
132 std::index_sequence<Is...>) {
133 using RealPacket = typename ComplexPacket::RealPacketT;
134 using RealScalar = scalar_type_of_vector_t<RealPacket>;
135 const RealScalar re = numext::real(from);
136 const RealScalar im = numext::imag(from);
137 return ComplexPacket(RealPacket{(Is % 2 == 0 ? re : im)...});
138}
139
140// Negates the imaginary parts by flipping their sign bits with one xor; the
141// negate-and-shuffle alternative costs an extra blend.
142template <typename ComplexPacket, std::size_t... Is>
143EIGEN_STRONG_INLINE ComplexPacket complex_pconj_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
144 using RealScalar = scalar_type_of_vector_t<typename ComplexPacket::RealPacketT>;
145 using IntScalar = std::conditional_t<sizeof(RealScalar) == 4, int32_t, int64_t>;
146 using IntPacket = VectorType<IntScalar, sizeof...(Is)>;
147 const IntPacket sign_mask = {(Is % 2 == 0 ? IntScalar(0) : (std::numeric_limits<IntScalar>::min)())...};
148 return ComplexPacket(
149 reinterpret_cast<typename ComplexPacket::RealPacketT>(reinterpret_cast<IntPacket>(a.v) ^ sign_mask));
150}
151
152// {re, im} -> {im, re}.
153template <typename ComplexPacket, std::size_t... Is>
154EIGEN_STRONG_INLINE ComplexPacket complex_pcplxflip_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
155 return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2) + (1 - Is % 2))...));
156}
157
158// {re, im} -> {re, re}.
159template <typename ComplexPacket, std::size_t... Is>
160EIGEN_STRONG_INLINE ComplexPacket complex_pdupreal_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
161 return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2))...));
162}
163
164// {re, im} -> {im, im}.
165template <typename ComplexPacket, std::size_t... Is>
166EIGEN_STRONG_INLINE ComplexPacket complex_pdupimag_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
167 return ComplexPacket(__builtin_shufflevector(a.v, a.v, (2 * (Is / 2) + 1)...));
168}
169
170// (a + ib)(c + id) = (ac - bd) + i(ad + bc), as an even/odd merge of the
171// difference and sum vectors. Both products are spelled inside one expression
172// each so fp-contract may fuse them, and the merge of a same-operand sub and
173// add is the pattern LLVM folds into a single fmaddsub-style instruction on
174// targets that have one. This matches the handwritten x86 backends, unlike
175// the conjugate-and-flip formulation of the generic pmul_complex.
176template <typename ComplexPacket, std::size_t... Is>
177EIGEN_STRONG_INLINE ComplexPacket complex_pmul_impl(const ComplexPacket& x, const ComplexPacket& y,
178 std::index_sequence<Is...> is) {
179 using RealPacket = typename ComplexPacket::RealPacketT;
180 const RealPacket x_re = complex_pdupreal_impl(x, is).v;
181 const RealPacket t = complex_pdupimag_impl(x, is).v * complex_pcplxflip_impl(y, is).v;
182 const RealPacket sub = x_re * y.v - t;
183 const RealPacket add = x_re * y.v + t;
184 return ComplexPacket(__builtin_shufflevector(sub, add, (Is % 2 == 0 ? Is : sizeof...(Is) + Is)...));
185}
186
187// Loads each complex value Repeat times in a row: Repeat == 2 implements
188// ploaddup and Repeat == 4 implements ploadquad.
189template <std::size_t Repeat, typename ComplexPacket, std::size_t... Is>
190EIGEN_STRONG_INLINE ComplexPacket complex_loadrepeat_impl(const typename unpacket_traits<ComplexPacket>::type* from,
191 std::index_sequence<Is...>) {
192 using RealPacket = typename ComplexPacket::RealPacketT;
193 return ComplexPacket(
194 RealPacket{(Is % 2 == 0 ? numext::real(from[Is / (2 * Repeat)]) : numext::imag(from[Is / (2 * Repeat)]))...});
195}
196
197// Reverses the complex values, keeping each real/imaginary pair together.
198template <typename ComplexPacket, std::size_t... Is>
199EIGEN_STRONG_INLINE ComplexPacket complex_preverse_impl(const ComplexPacket& a, std::index_sequence<Is...>) {
200 constexpr std::size_t kLastValue = sizeof...(Is) - 2;
201 return ComplexPacket(__builtin_shufflevector(a.v, a.v, (kLastValue - 2 * (Is / 2) + Is % 2)...));
202}
203
204} // namespace detail
205
206// --- pset1 for complex ---
207#define EIGEN_CLANG_COMPLEX_SET1(PACKET_TYPE) \
208 template <> \
209 EIGEN_STRONG_INLINE PACKET_TYPE pset1<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type& from) { \
210 return detail::complex_pset1_impl<PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
211 }
212
213EIGEN_CLANG_COMPLEX_SET1(PacketXcf)
214EIGEN_CLANG_COMPLEX_SET1(PacketXcd)
215#undef EIGEN_CLANG_COMPLEX_SET1
216
217// ----------- Unary ops ------------------
218#define DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, OP) \
219 template <> \
220 EIGEN_STRONG_INLINE PACKET_TYPE OP<PACKET_TYPE>(const PACKET_TYPE& a) { \
221 return PACKET_TYPE(OP(a.v)); \
222 }
223
224#define EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PACKET_TYPE) \
225 DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, pnegate) \
226 DELEGATE_UNARY_TO_REAL_OP(PACKET_TYPE, pzero) \
227 template <> \
228 EIGEN_STRONG_INLINE unpacket_traits<PACKET_TYPE>::type pfirst<PACKET_TYPE>(const PACKET_TYPE& a) { \
229 return a[0]; \
230 } \
231 EIGEN_INSTANTIATE_COMPLEX_MATH_FUNCS(PACKET_TYPE)
232
233EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PacketXcf);
234EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS(PacketXcd);
235
236#undef DELEGATE_UNARY_TO_REAL_OP
237#undef EIGEN_CLANG_COMPLEX_UNARY_CWISE_OPS
238
239// --- Operations that rearrange the real and imaginary lanes ---
240#define EIGEN_CLANG_COMPLEX_LANE_OPS(PACKET_TYPE) \
241 template <> \
242 EIGEN_STRONG_INLINE PACKET_TYPE pconj<PACKET_TYPE>(const PACKET_TYPE& a) { \
243 return detail::complex_pconj_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
244 } \
245 template <> \
246 EIGEN_STRONG_INLINE PACKET_TYPE pcplxflip<PACKET_TYPE>(const PACKET_TYPE& a) { \
247 return detail::complex_pcplxflip_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
248 } \
249 template <> \
250 EIGEN_STRONG_INLINE PACKET_TYPE pdupreal<PACKET_TYPE>(const PACKET_TYPE& a) { \
251 return detail::complex_pdupreal_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
252 } \
253 template <> \
254 EIGEN_STRONG_INLINE PACKET_TYPE pdupimag<PACKET_TYPE>(const PACKET_TYPE& a) { \
255 return detail::complex_pdupimag_impl(a, detail::complex_real_indices<PACKET_TYPE>{}); \
256 }
257
258EIGEN_CLANG_COMPLEX_LANE_OPS(PacketXcf)
259EIGEN_CLANG_COMPLEX_LANE_OPS(PacketXcd)
260#undef EIGEN_CLANG_COMPLEX_LANE_OPS
261
262// --- ploaddup and ploadquad ---
263#define EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PACKET_TYPE) \
264 template <> \
265 EIGEN_STRONG_INLINE PACKET_TYPE ploaddup<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
266 return detail::complex_loadrepeat_impl<2, PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
267 } \
268 template <> \
269 EIGEN_STRONG_INLINE PACKET_TYPE ploadquad<PACKET_TYPE>(const unpacket_traits<PACKET_TYPE>::type* from) { \
270 return detail::complex_loadrepeat_impl<4, PACKET_TYPE>(from, detail::complex_real_indices<PACKET_TYPE>{}); \
271 }
272
273EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PacketXcf)
274EIGEN_CLANG_COMPLEX_LOAD_REPEAT(PacketXcd)
275#undef EIGEN_CLANG_COMPLEX_LOAD_REPEAT
276
277// --- preverse ---
278template <>
279EIGEN_STRONG_INLINE PacketXcf preverse<PacketXcf>(const PacketXcf& a) {
280 return detail::complex_preverse_impl(a, detail::complex_real_indices<PacketXcf>{});
281}
282template <>
283EIGEN_STRONG_INLINE PacketXcd preverse<PacketXcd>(const PacketXcd& a) {
284 return detail::complex_preverse_impl(a, detail::complex_real_indices<PacketXcd>{});
285}
286
287// ----------- Binary ops ------------------
288#define DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, OP) \
289 template <> \
290 EIGEN_STRONG_INLINE PACKET_TYPE OP<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
291 return PACKET_TYPE(OP(a.v, b.v)); \
292 }
293
294#define EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PACKET_TYPE) \
295 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, padd) \
296 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, psub) \
297 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pand) \
298 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, por) \
299 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pxor) \
300 DELEGATE_BINARY_TO_REAL_OP(PACKET_TYPE, pandnot) \
301 template <> \
302 EIGEN_STRONG_INLINE PACKET_TYPE pdiv<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
303 return pdiv_complex(a, b); \
304 } \
305 template <> \
306 EIGEN_STRONG_INLINE PACKET_TYPE pcmp_eq<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
307 const PACKET_TYPE t = PACKET_TYPE(pcmp_eq(a.v, b.v)); \
308 return PACKET_TYPE(pand(pdupreal(t).v, pdupimag(t).v)); \
309 }
310
311EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PacketXcf);
312EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS(PacketXcd);
313
314// The sub-packets complex_predux_mul in Reductions.h recurses through call
315// detail::complex_pmul_impl directly, so only the full packets need pmul.
316#define EIGEN_CLANG_COMPLEX_PMUL(PACKET_TYPE) \
317 template <> \
318 EIGEN_STRONG_INLINE PACKET_TYPE pmul<PACKET_TYPE>(const PACKET_TYPE& a, const PACKET_TYPE& b) { \
319 return detail::complex_pmul_impl(a, b, detail::complex_real_indices<PACKET_TYPE>{}); \
320 }
321
322EIGEN_CLANG_COMPLEX_PMUL(PacketXcf);
323EIGEN_CLANG_COMPLEX_PMUL(PacketXcd);
324
325#define EIGEN_CLANG_PACKET_SCATTER_GATHER(PACKET_TYPE) \
326 template <> \
327 EIGEN_STRONG_INLINE void pscatter(unpacket_traits<PACKET_TYPE>::type* to, const PACKET_TYPE& from, Index stride) { \
328 constexpr int size = unpacket_traits<PACKET_TYPE>::size; \
329 for (int i = 0; i < size; ++i) { \
330 to[i * stride] = from[i]; \
331 } \
332 } \
333 template <> \
334 EIGEN_STRONG_INLINE PACKET_TYPE pgather<typename unpacket_traits<PACKET_TYPE>::type, PACKET_TYPE>( \
335 const unpacket_traits<PACKET_TYPE>::type* from, Index stride) { \
336 constexpr int size = unpacket_traits<PACKET_TYPE>::size; \
337 PACKET_TYPE result; \
338 for (int i = 0; i < size; ++i) { \
339 const unpacket_traits<PACKET_TYPE>::type from_i = from[i * stride]; \
340 result.v[2 * i] = numext::real(from_i); \
341 result.v[2 * i + 1] = numext::imag(from_i); \
342 } \
343 return result; \
344 }
345
346EIGEN_CLANG_PACKET_SCATTER_GATHER(PacketXcf);
347EIGEN_CLANG_PACKET_SCATTER_GATHER(PacketXcd);
348#undef EIGEN_CLANG_PACKET_SCATTER_GATHER
349
350#undef DELEGATE_BINARY_TO_REAL_OP
351#undef EIGEN_CLANG_COMPLEX_BINARY_CWISE_OPS
352#undef EIGEN_CLANG_COMPLEX_PMUL
353
354// ------------ ternary ops -------------
355// The mask duplicates each complex value's bits across both real lanes, so
356// selecting per real lane is equivalent to selecting whole complex values.
357template <>
358EIGEN_STRONG_INLINE PacketXcf pselect<PacketXcf>(const PacketXcf& mask, const PacketXcf& a, const PacketXcf& b) {
359 return PacketXcf(pselect(mask.v, a.v, b.v));
360}
361template <>
362EIGEN_STRONG_INLINE PacketXcd pselect<PacketXcd>(const PacketXcd& mask, const PacketXcd& a, const PacketXcd& b) {
363 return PacketXcd(pselect(mask.v, a.v, b.v));
364}
365
366// --- zip_in_place for complex ---
367namespace detail {
368
369// Complex packets interleave whole complex values, so their real and imaginary
370// components move together.
371template <>
372EIGEN_ALWAYS_INLINE void zip_in_place<PacketXcf>(PacketXcf& p1, PacketXcf& p2) {
373 zip_in_place_impl<2>(p1.v, p2.v, complex_real_indices<PacketXcf>{});
374}
375
376#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
377// PacketXcd holds a single complex value at 16 bytes, so there is nothing to interleave.
378template <>
379EIGEN_ALWAYS_INLINE void zip_in_place<PacketXcd>(PacketXcd& p1, PacketXcd& p2) {
380 zip_in_place_impl<2>(p1.v, p2.v, complex_real_indices<PacketXcd>{});
381}
382#endif
383
384} // namespace detail
385
386// --- ptranspose for complex ---
387// PacketXcf: valid block sizes depend on kComplexFloatSize.
388EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 2>& kernel) {
389 detail::ptranspose_impl(kernel);
390}
391#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
392EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 4>& kernel) {
393 detail::ptranspose_impl(kernel);
394}
395#endif
396#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 64
397EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcf, 8>& kernel) {
398 detail::ptranspose_impl(kernel);
399}
400#endif
401
402// PacketXcd: valid block sizes depend on kComplexDoubleSize.
403#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 32
404EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcd, 2>& kernel) {
405 detail::ptranspose_impl(kernel);
406}
407#endif
408#if EIGEN_GENERIC_VECTOR_SIZE_BYTES >= 64
409EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void ptranspose(PacketBlock<PacketXcd, 4>& kernel) {
410 detail::ptranspose_impl(kernel);
411}
412#endif
413
414EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(PacketXcf, PacketXf)
415EIGEN_MAKE_CONJ_HELPER_CPLX_REAL(PacketXcd, PacketXd)
416
417} // end namespace internal
418} // end namespace Eigen
419
420#endif // EIGEN_COMPLEX_CLANG_H