alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
Complex.hpp
Go to the documentation of this file.
1/* Copyright 2024 Sergei Bastrakov, Aurora Perego
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
8#include "alpaka/math.hpp"
10#include "alpaka/trait.hpp"
11#include "math.hpp"
12
13#include <cmath>
14#include <complex>
15#include <iostream>
16#include <type_traits>
17
18namespace alpaka::math
19{
20 namespace internal
21 {
22 //! Implementation of a complex number usable on host and device.
23 //!
24 //! It follows the layout of std::complex and so array-oriented access.
25 //! The class template implements all methods and operators as std::complex<T>.
26 //! Additionally, it provides an implicit conversion to and from std::complex<T>.
27 //! All methods besides operators << and >> are host-device.
28 //! It does not provide non-member functions of std::complex besides the operators.
29 //! Those are provided the same way as alpaka math functions for real numbers.
30 //!
31 //! Note that unlike most of alpaka, this is a concrete type template, and not merely a concept.
32 //!
33 //! Naming and order of the methods match https://en.cppreference.com/w/cpp/numeric/complex in C++17.
34 //! Implementation chose to not extend it e.g. by adding constexpr to some places that would get it in C++20.
35 //! The motivation is that with internal conversion to std::complex<T> for CPU backends, it would define the
36 //! common interface for generic code anyways. So it is more clear to have alpaka's interface exactly matching
37 //! when possible, and not "improving".
38 //!
39 //! @tparam T type of the real and imaginary part: float, double, or long double.
40 template<typename T>
41 class Complex
42 {
43 public:
44 // Make sure the input type is floating-point
45 static_assert(std::is_floating_point_v<T>);
46
47 //! Type of the real and imaginary parts
48 using value_type = T;
49
50 //! Constructor from the given real and imaginary parts
51 constexpr Complex(T const& real = T{}, T const& imag = T{}) : m_real(real), m_imag(imag)
52 {
53 }
54
55 //! Copy constructor
56 constexpr Complex(Complex const& other) = default;
57
58 //! Constructor from Complex of another type
59 template<typename U>
60 constexpr Complex(Complex<U> const& other)
61 : m_real(static_cast<T>(other.real()))
62 , m_imag(static_cast<T>(other.imag()))
63 {
64 }
65
66 //! Constructor from std::complex
67 constexpr Complex(std::complex<T> const& other) : m_real(other.real()), m_imag(other.imag())
68 {
69 }
70
71 //! Conversion to std::complex
72 constexpr operator std::complex<T>() const
73 {
74 return std::complex<T>{m_real, m_imag};
75 }
76
77 //! Assignment
78 Complex& operator=(Complex const&) = default;
79
80 //! Get the real part
81 constexpr T real() const
82 {
83 return m_real;
84 }
85
86 //! Set the real part
87 constexpr void real(T value)
88 {
89 m_real = value;
90 }
91
92 //! Get the imaginary part
93 constexpr T imag() const
94 {
95 return m_imag;
96 }
97
98 //! Set the imaginary part
99 constexpr void imag(T value)
100 {
101 m_imag = value;
102 }
103
104 //! Addition assignment with a real number
105 constexpr Complex& operator+=(T const& other)
106 {
107 m_real += other;
108 return *this;
109 }
110
111 //! Addition assignment with a complex number
112 template<typename U>
113 constexpr Complex& operator+=(Complex<U> const& other)
114 {
115 m_real += static_cast<T>(other.real());
116 m_imag += static_cast<T>(other.imag());
117 return *this;
118 }
119
120 //! Subtraction assignment with a real number
121 constexpr Complex& operator-=(T const& other)
122 {
123 m_real -= other;
124 return *this;
125 }
126
127 //! Subtraction assignment with a complex number
128 template<typename U>
129 constexpr Complex& operator-=(Complex<U> const& other)
130 {
131 m_real -= static_cast<T>(other.real());
132 m_imag -= static_cast<T>(other.imag());
133 return *this;
134 }
135
136 //! Multiplication assignment with a real number
137 constexpr Complex& operator*=(T const& other)
138 {
139 m_real *= other;
140 m_imag *= other;
141 return *this;
142 }
143
144 //! Multiplication assignment with a complex number
145 template<typename U>
146 constexpr Complex& operator*=(Complex<U> const& other)
147 {
148 auto const newReal = m_real * static_cast<T>(other.real()) - m_imag * static_cast<T>(other.imag());
149 auto const newImag = m_imag * static_cast<T>(other.real()) + m_real * static_cast<T>(other.imag());
150 m_real = newReal;
151 m_imag = newImag;
152 return *this;
153 }
154
155 //! Division assignment with a real number
156 constexpr Complex& operator/=(T const& other)
157 {
158 m_real /= other;
159 m_imag /= other;
160 return *this;
161 }
162
163 //! Division assignment with a complex number
164 template<typename U>
165 constexpr Complex& operator/=(Complex<U> const& other)
166 {
167 return *this *= Complex{
168 static_cast<T>(other.real() / (other.real() * other.real() + other.imag() * other.imag())),
169 static_cast<T>(
170 -other.imag() / (other.real() * other.real() + other.imag() * other.imag()))};
171 }
172
173 private:
174 //! Real and imaginary parts, storage enables array-oriented access
175 T m_real, m_imag;
176 };
177
178 //! Host-device arithmetic operations matching std::complex<T>.
179 //!
180 //! They take and return alpaka::math::Complex.
181 //!
182 //! @{
183 //!
184
185 //! Unary plus (added for compatibility with std::complex)
186 template<typename T>
187 constexpr Complex<T> operator+(Complex<T> const& val)
188 {
189 return val;
190 }
191
192 //! Unary minus
193 template<typename T>
194 constexpr Complex<T> operator-(Complex<T> const& val)
195 {
196 return Complex<T>{-val.real(), -val.imag()};
197 }
198
199 //! Addition of two complex numbers
200 template<typename T>
201 constexpr Complex<T> operator+(Complex<T> const& lhs, Complex<T> const& rhs)
202 {
203 return Complex<T>{lhs.real() + rhs.real(), lhs.imag() + rhs.imag()};
204 }
205
206 //! Addition of a complex and a real number
207 template<typename T>
208 constexpr Complex<T> operator+(Complex<T> const& lhs, T const& rhs)
209 {
210 return Complex<T>{lhs.real() + rhs, lhs.imag()};
211 }
212
213 //! Addition of a real and a complex number
214 template<typename T>
215 constexpr Complex<T> operator+(T const& lhs, Complex<T> const& rhs)
216 {
217 return Complex<T>{lhs + rhs.real(), rhs.imag()};
218 }
219
220 //! Subtraction of two complex numbers
221 template<typename T>
222 constexpr Complex<T> operator-(Complex<T> const& lhs, Complex<T> const& rhs)
223 {
224 return Complex<T>{lhs.real() - rhs.real(), lhs.imag() - rhs.imag()};
225 }
226
227 //! Subtraction of a complex and a real number
228 template<typename T>
229 constexpr Complex<T> operator-(Complex<T> const& lhs, T const& rhs)
230 {
231 return Complex<T>{lhs.real() - rhs, lhs.imag()};
232 }
233
234 //! Subtraction of a real and a complex number
235 template<typename T>
236 constexpr Complex<T> operator-(T const& lhs, Complex<T> const& rhs)
237 {
238 return Complex<T>{lhs - rhs.real(), -rhs.imag()};
239 }
240
241 //! Muptiplication of two complex numbers
242 template<typename T>
243 constexpr Complex<T> operator*(Complex<T> const& lhs, Complex<T> const& rhs)
244 {
245 return Complex<T>{
246 lhs.real() * rhs.real() - lhs.imag() * rhs.imag(),
247 lhs.imag() * rhs.real() + lhs.real() * rhs.imag()};
248 }
249
250 //! Muptiplication of a complex and a real number
251 template<typename T>
252 constexpr Complex<T> operator*(Complex<T> const& lhs, T const& rhs)
253 {
254 return Complex<T>{lhs.real() * rhs, lhs.imag() * rhs};
255 }
256
257 //! Muptiplication of a real and a complex number
258 template<typename T>
259 constexpr Complex<T> operator*(T const& lhs, Complex<T> const& rhs)
260 {
261 return Complex<T>{lhs * rhs.real(), lhs * rhs.imag()};
262 }
263
264 //! Division of two complex numbers
265 template<typename T>
266 constexpr Complex<T> operator/(Complex<T> const& lhs, Complex<T> const& rhs)
267 {
268 return Complex<T>{
269 (lhs.real() * rhs.real() + lhs.imag() * rhs.imag())
270 / (rhs.real() * rhs.real() + rhs.imag() * rhs.imag()),
271 (lhs.imag() * rhs.real() - lhs.real() * rhs.imag())
272 / (rhs.real() * rhs.real() + rhs.imag() * rhs.imag())};
273 }
274
275 //! Division of complex and a real number
276 template<typename T>
277 constexpr Complex<T> operator/(Complex<T> const& lhs, T const& rhs)
278 {
279 return Complex<T>{lhs.real() / rhs, lhs.imag() / rhs};
280 }
281
282 //! Division of a real and a complex number
283 template<typename T>
284 constexpr Complex<T> operator/(T const& lhs, Complex<T> const& rhs)
285 {
286 return Complex<T>{
287 lhs * rhs.real() / (rhs.real() * rhs.real() + rhs.imag() * rhs.imag()),
288 -lhs * rhs.imag() / (rhs.real() * rhs.real() + rhs.imag() * rhs.imag())};
289 }
290
291 //! Equality of two complex numbers
292 template<typename T>
293 constexpr bool operator==(Complex<T> const& lhs, Complex<T> const& rhs)
294 {
295 return math::floatEqualExactNoWarning(lhs.real(), rhs.real())
296 && math::floatEqualExactNoWarning(lhs.imag(), rhs.imag());
297 }
298
299 //! Equality of a complex and a real number
300 template<typename T>
301 constexpr bool operator==(Complex<T> const& lhs, T const& rhs)
302 {
303 return math::floatEqualExactNoWarning(lhs.real(), rhs)
304 && math::floatEqualExactNoWarning(lhs.imag(), static_cast<T>(0));
305 }
306
307 //! Equality of a real and a complex number
308 template<typename T>
309 constexpr bool operator==(T const& lhs, Complex<T> const& rhs)
310 {
311 return math::floatEqualExactNoWarning(lhs, rhs.real())
312 && math::floatEqualExactNoWarning(static_cast<T>(0), rhs.imag());
313 }
314
315 //! Inequality of two complex numbers.
316 //!
317 //! @note this and other versions of operator != should be removed since C++20, as so does std::complex
318 template<typename T>
319 constexpr bool operator!=(Complex<T> const& lhs, Complex<T> const& rhs)
320 {
321 return !(lhs == rhs);
322 }
323
324 //! Inequality of a complex and a real number
325 template<typename T>
326 constexpr bool operator!=(Complex<T> const& lhs, T const& rhs)
327 {
328 return !math::floatEqualExactNoWarning(lhs.real(), rhs)
329 || !math::floatEqualExactNoWarning(lhs.imag(), static_cast<T>(0));
330 }
331
332 //! Inequality of a real and a complex number
333 template<typename T>
334 constexpr bool operator!=(T const& lhs, Complex<T> const& rhs)
335 {
336 return !math::floatEqualExactNoWarning(lhs, rhs.real())
337 || !math::floatEqualExactNoWarning(static_cast<T>(0), rhs.imag());
338 }
339
340 //! @}
341
342 //! Host-only output of a complex number
343 template<typename T, typename TChar, typename TTraits>
344 std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& os, Complex<T> const& x)
345 {
346 os << x.operator std::complex<T>();
347 return os;
348 }
349
350 //! Host-only input of a complex number
351 template<typename T, typename TChar, typename TTraits>
352 std::basic_istream<TChar, TTraits>& operator>>(std::basic_istream<TChar, TTraits>& is, Complex<T> const& x)
353 {
354 std::complex<T> z;
355 is >> z;
356 x = z;
357 return is;
358 }
359
360 //! Host-only math functions matching std::complex<T>.
361 //!
362 //! Due to issue #1688, these functions are technically marked host-device and suppress related warnings.
363 //! However, they must be called for host only.
364 //!
365 //! They take and return alpaka::math::Complex (or a real number when appropriate).
366 //! Internally cast, fall back to std::complex implementation and cast back.
367 //! These functions can be used directly on the host side.
368 //! They are also picked up by ADL in math traits for CPU backends.
369 //!
370 //! On the device side, alpaka math traits must be used instead.
371 //! Note that the set of the traits is currently a bit smaller.
372 //!
373 //! @{
374 //!
375
376 //! Absolute value
377 template<typename T>
378 constexpr T abs(Complex<T> const& x)
379 {
380 return std::abs(std::complex<T>(x));
381 }
382
383 //! Arc cosine
384 template<typename T>
385 constexpr Complex<T> acos(Complex<T> const& x)
386 {
387 return std::acos(std::complex<T>(x));
388 }
389
390 //! Arc hyperbolic cosine
391 template<typename T>
392 constexpr Complex<T> acosh(Complex<T> const& x)
393 {
394 return std::acosh(std::complex<T>(x));
395 }
396
397 //! Argument
398 template<typename T>
399 constexpr T arg(Complex<T> const& x)
400 {
401 return std::arg(std::complex<T>(x));
402 }
403
404 //! Arc sine
405 template<typename T>
406 constexpr Complex<T> asin(Complex<T> const& x)
407 {
408 return std::asin(std::complex<T>(x));
409 }
410
411 //! Arc hyperbolic sine
412 template<typename T>
413 constexpr Complex<T> asinh(Complex<T> const& x)
414 {
415 return std::asinh(std::complex<T>(x));
416 }
417
418 //! Arc tangent
419 template<typename T>
420 constexpr Complex<T> atan(Complex<T> const& x)
421 {
422 return std::atan(std::complex<T>(x));
423 }
424
425 //! Arc hyperbolic tangent
426 template<typename T>
427 constexpr Complex<T> atanh(Complex<T> const& x)
428 {
429 return std::atanh(std::complex<T>(x));
430 }
431
432 //! Complex conjugate
433 template<typename T>
434 constexpr Complex<T> conj(Complex<T> const& x)
435 {
436 return std::conj(std::complex<T>(x));
437 }
438
439 //! Cosine
440 template<typename T>
441 constexpr Complex<T> cos(Complex<T> const& x)
442 {
443 return std::cos(std::complex<T>(x));
444 }
445
446 //! Hyperbolic cosine
447 template<typename T>
448 constexpr Complex<T> cosh(Complex<T> const& x)
449 {
450 return std::cosh(std::complex<T>(x));
451 }
452
453 //! Exponential
454 template<typename T>
455 constexpr Complex<T> exp(Complex<T> const& x)
456 {
457 return std::exp(std::complex<T>(x));
458 }
459
460 //! Natural logarithm
461 template<typename T>
462 constexpr Complex<T> log(Complex<T> const& x)
463 {
464 return std::log(std::complex<T>(x));
465 }
466
467 //! Base 10 logarithm
468 template<typename T>
469 constexpr Complex<T> log10(Complex<T> const& x)
470 {
471 return std::log10(std::complex<T>(x));
472 }
473
474 //! Squared magnitude
475 template<typename T>
476 constexpr T norm(Complex<T> const& x)
477 {
478 return std::norm(std::complex<T>(x));
479 }
480
481 //! Get a complex number with given magnitude and phase angle
482 template<typename T>
483 constexpr Complex<T> polar(T const& r, T const& theta = T())
484 {
485 return std::polar(r, theta);
486 }
487
488 //! Complex power of a complex number
489 template<typename T, typename U>
490 constexpr auto pow(Complex<T> const& x, Complex<U> const& y)
491 {
492 // Use same type promotion as std::pow
493 auto const result = std::pow(std::complex<T>(x), std::complex<U>(y));
494 using ValueType = typename decltype(result)::value_type;
495 return Complex<ValueType>(result);
496 }
497
498 //! Real power of a complex number
499 template<typename T, typename U>
500 constexpr auto pow(Complex<T> const& x, U const& y)
501 {
502 return pow(x, Complex<U>(y));
503 }
504
505 //! Complex power of a real number
506 template<typename T, typename U>
507 constexpr auto pow(T const& x, Complex<U> const& y)
508 {
509 return pow(Complex<T>(x), y);
510 }
511
512 //! Projection onto the Riemann sphere
513 template<typename T>
514 constexpr Complex<T> proj(Complex<T> const& x)
515 {
516 return std::proj(std::complex<T>(x));
517 }
518
519 //! Sine
520 template<typename T>
521 constexpr Complex<T> sin(Complex<T> const& x)
522 {
523 return std::sin(std::complex<T>(x));
524 }
525
526 //! Hyperbolic sine
527 template<typename T>
528 constexpr Complex<T> sinh(Complex<T> const& x)
529 {
530 return std::sinh(std::complex<T>(x));
531 }
532
533 //! Square root
534 template<typename T>
535 constexpr Complex<T> sqrt(Complex<T> const& x)
536 {
537 return std::sqrt(std::complex<T>(x));
538 }
539
540 //! Tangent
541 template<typename T>
542 constexpr Complex<T> tan(Complex<T> const& x)
543 {
544 return std::tan(std::complex<T>(x));
545 }
546
547 //! Hyperbolic tangent
548 template<typename T>
549 constexpr Complex<T> tanh(Complex<T> const& x)
550 {
551 return std::tanh(std::complex<T>(x));
552 }
553
554 //! @}
555 } // namespace internal
556
557 using internal::Complex;
558
559#if ALPAKA_LANG_CUDA || ALPAKA_LANG_HIP || ALPAKA_LANG_SYCL
560
561 namespace internal
562 {
563 template<typename T_MathImpl, typename T_Arg>
564 struct Abs::Op<T_MathImpl, Complex<T_Arg>>
565 {
566 constexpr auto operator()(T_MathImpl, Complex<T_Arg> const& arg) const
567 {
568 return math::sqrt(arg.real() * arg.real() + arg.imag() * arg.imag());
569 }
570 };
571
572 //! The acos trait specialization for complex types.
573 template<typename T_MathImpl, typename T>
574 struct Acos::Op<T_MathImpl, Complex<T>>
575 {
576 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
577 {
578 // This holds everywhere, including the branch cuts: acos(z) = -i * ln(z + i * sqrt(1 - z^2))
579 return Complex<T>{static_cast<T>(0.0), static_cast<T>(-1.0)}
580 * math::log(
581 arg
582 + Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)}
583 * math::sqrt(static_cast<T>(1.0) - arg * arg));
584 }
585 };
586
587 //! The acosh trait specialization for complex types.
588 template<typename T_MathImpl, typename T>
589 struct Acosh::Op<T_MathImpl, Complex<T>>
590 {
591 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
592 {
593 // acos(z) = ln(z + sqrt(z-1) * sqrt(z+1))
594 return math::log(arg + math::sqrt(arg - static_cast<T>(1.0)) * math::sqrt(arg + static_cast<T>(1.0)));
595 }
596 };
597
598 //! The arg Complex<T> specialization for complex types.
599 template<typename T_MathImpl, typename T>
600 struct Arg::Op<T_MathImpl, Complex<T>>
601 {
602 constexpr auto operator()(T_MathImpl, Complex<T> const& argument) const
603 {
604 return math::atan2(argument.imag(), argument.real());
605 }
606 };
607
608 //! The asin trait specialization for complex types.
609 template<typename T_MathImpl, typename T>
610 struct Asin::Op<T_MathImpl, Complex<T>>
611 {
612 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
613 {
614 // This holds everywhere, including the branch cuts: asin(z) = i * ln(sqrt(1 - z^2) - i * z)
615 return Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)}
616 * math::log(
617 math::sqrt(static_cast<T>(1.0) - arg * arg)
618 - Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} * arg);
619 }
620 };
621
622 //! The asinh trait specialization for complex types.
623 template<typename T_MathImpl, typename T>
624 struct Asinh::Op<T_MathImpl, Complex<T>>
625 {
626 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
627 {
628 // asinh(z) = ln(z + sqrt(z^2 + 1))
629 return math::log(arg + math::sqrt(arg * arg + static_cast<T>(1.0)));
630 }
631 };
632
633 //! The atan trait specialization for complex types.
634 template<typename T_MathImpl, typename T>
635 struct Atan::Op<T_MathImpl, Complex<T>>
636 {
637 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
638 {
639 // This holds everywhere, including the branch cuts: atan(z) = -i/2 * ln((i - z) / (i + z))
640 return Complex<T>{static_cast<T>(0.0), static_cast<T>(-0.5)}
641 * math::log(
642 (Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} - arg)
643 / (Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} + arg));
644 }
645 };
646
647 //! The atanh trait specialization for complex types.
648 template<typename T_MathImpl, typename T>
649 struct Atanh::Op<T_MathImpl, Complex<T>>
650 {
651 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
652 {
653 // atanh(z) = 0.5 * (ln(1 + z) - ln(1 - z))
654 return static_cast<T>(0.5)
655 * (math::log(static_cast<T>(1.0) + arg) - math::log(static_cast<T>(1.0) - arg));
656 }
657 };
658
659 //! The conj specialization for complex types.
660 template<typename T_MathImpl, typename T>
661 struct Conj::Op<T_MathImpl, Complex<T>>
662 {
663 constexpr auto operator()(T_MathImpl const& /* conj_ctx */, Complex<T> const& arg) const
664 {
665 return Complex<T>{arg.real(), -arg.imag()};
666 }
667 };
668
669 //! The cos trait specialization for complex types.
670 template<typename T_MathImpl, typename T>
671 struct Cos::Op<T_MathImpl, Complex<T>>
672 {
673 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
674 {
675 // cos(z) = 0.5 * (exp(i * z) + exp(-i * z))
676 return T(0.5)
677 * (math::exp(Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} * arg)
678 + math::exp(Complex<T>{static_cast<T>(0.0), static_cast<T>(-1.0)} * arg));
679 }
680 };
681
682 //! The cosh trait specialization for complex types.
683 template<typename T_MathImpl, typename T>
684 struct Cosh::Op<T_MathImpl, Complex<T>>
685 {
686 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
687 {
688 // cosh(z) = 0.5 * (exp(z) + exp(-z))
689 return T(0.5) * (math::exp(arg) + math::exp(static_cast<T>(-1.0) * arg));
690 }
691 };
692
693 //! The exp trait specialization for complex types.
694 template<typename T_MathImpl, typename T>
695 struct Exp::Op<T_MathImpl, Complex<T>>
696 {
697 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
698 {
699 // exp(z) = exp(x + iy) = exp(x) * (cos(y) + i * sin(y))
700 auto re = T{}, im = T{};
701 math::sincos(arg.imag(), im, re);
702 return math::exp(arg.real()) * Complex<T>{re, im};
703 }
704 };
705
706 //! The log trait specialization for complex types.
707 template<typename T_MathImpl, typename T>
708 struct Log::Op<T_MathImpl, Complex<T>>
709 {
710 constexpr auto operator()(T_MathImpl, Complex<T> const& argument) const
711 {
712 // Branch cut along the negative real axis (same as for std::complex),
713 // principal value of ln(z) = ln(|z|) + i * arg(z)
714 return math::log(math::abs(argument))
715 + Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} * math::arg(argument);
716 }
717 };
718
719 //! The log2 trait specialization for complex types.
720 template<typename T_MathImpl, typename T>
721 struct Log2::Op<T_MathImpl, Complex<T>>
722 {
723 constexpr auto operator()(T_MathImpl, Complex<T> const& argument) const
724 {
725 return math::log(argument) / math::log(static_cast<T>(2));
726 }
727 };
728
729 //! The log10 trait specialization for complex types.
730 template<typename T_MathImpl, typename T>
731 struct Log10::Op<T_MathImpl, Complex<T>>
732 {
733 constexpr auto operator()(T_MathImpl, Complex<T> const& argument) const
734 {
735 return math::log(argument) / math::log(static_cast<T>(10));
736 }
737 };
738
739 //! The pow trait specialization for complex types.
740 template<typename T_MathImpl, typename T, typename U>
741 struct Pow::Op<T_MathImpl, Complex<T>, Complex<U>>
742 {
743 constexpr auto operator()(T_MathImpl, Complex<T> const& base, Complex<U> const& exponent) const
744 {
745 // Type promotion matching rules of complex std::pow but simplified given our math only supports float
746 // and double, no long double.
747 using Promoted
748 = Complex<std::conditional_t<is_decayed_v<T, float> && is_decayed_v<U, float>, float, double>>;
749 // pow(z1, z2) = e^(z2 * log(z1))
750 return math::exp(Promoted{exponent} * math::log(Promoted{base}));
751 }
752 };
753
754 //! The pow trait specialization for complex and real types.
755 template<typename T_MathImpl, typename T, typename U>
756 struct Pow::Op<T_MathImpl, Complex<T>, U>
757 {
758 constexpr auto operator()(T_MathImpl, Complex<T> const& base, U const& exponent) const
759 {
760 return math::pow(base, Complex<U>{exponent});
761 }
762 };
763
764 //! The pow trait specialization for real and complex types.
765 template<typename T_MathImpl, typename T, typename U>
766 struct Pow::Op<T_MathImpl, T, Complex<U>>
767 {
768 constexpr auto operator()(T_MathImpl, T const& base, Complex<U> const& exponent) const
769 {
770 return math::pow(Complex<T>{base}, exponent);
771 }
772 };
773
774 //! The rsqrt trait specialization for complex types.
775 template<typename T_MathImpl, typename T>
776 struct Rsqrt::Op<T_MathImpl, Complex<T>>
777 {
778 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
779 {
780 return static_cast<T>(1.0) / math::sqrt(arg);
781 }
782 };
783
784 //! The sin trait specialization for complex types.
785 template<typename T_MathImpl, typename T>
786 struct Sin::Op<T_MathImpl, Complex<T>>
787 {
788 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
789 {
790 // sin(z) = (exp(i * z) - exp(-i * z)) / 2i
791 return (math::exp(Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} * arg)
792 - math::exp(Complex<T>{static_cast<T>(0.0), static_cast<T>(-1.0)} * arg))
793 / Complex<T>{static_cast<T>(0.0), static_cast<T>(2.0)};
794 }
795 };
796
797 //! The sinh trait specialization for complex types.
798 template<typename T_MathImpl, typename T>
799 struct Sinh::Op<T_MathImpl, Complex<T>>
800 {
801 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
802 {
803 // sinh(z) = (exp(z) - exp(-i * z)) / 2
804 return (math::exp(arg) - math::exp(static_cast<T>(-1.0) * arg)) / static_cast<T>(2.0);
805 }
806 };
807
808 //! The sincos trait specialization for complex types.
809 template<typename T_MathImpl, typename T>
810 struct SinCos::Op<T_MathImpl, Complex<T>>
811 {
812 constexpr auto operator()(
813 T_MathImpl,
814 Complex<T> const& arg,
815 Complex<T>& result_sin,
816 Complex<T>& result_cos) const -> void
817 {
818 result_sin = math::sin(arg);
819 result_cos = math::cos(arg);
820 }
821 };
822
823 //! The sqrt trait specialization for complex types.
824 template<typename T_MathImpl, typename T>
825 struct Sqrt::Op<T_MathImpl, Complex<T>>
826 {
827 constexpr auto operator()(T_MathImpl, Complex<T> const& argument) const
828 {
829 // Branch cut along the negative real axis (same as for std::complex),
830 // principal value of sqrt(z) = sqrt(|z|) * e^(i * arg(z) / 2)
831 auto const halfArg = T(0.5) * math::arg(argument);
832 auto re = T{}, im = T{};
833 math::sincos(halfArg, im, re);
834 return math::sqrt(math::abs(argument)) * Complex<T>(re, im);
835 }
836 };
837
838 //! The tan trait specialization for complex types.
839 template<typename T_MathImpl, typename T>
840 struct Tan::Op<T_MathImpl, Complex<T>>
841 {
842 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
843 {
844 // tan(z) = i * (e^-iz - e^iz) / (e^-iz + e^iz) = i * (1 - e^2iz) / (1 + e^2iz)
845 // Warning: this straightforward implementation can easily result in NaN as 0/0 or inf/inf.
846 auto const expValue = math::exp(Complex<T>{static_cast<T>(0.0), static_cast<T>(2.0)} * arg);
847 return Complex<T>{static_cast<T>(0.0), static_cast<T>(1.0)} * (static_cast<T>(1.0) - expValue)
848 / (static_cast<T>(1.0) + expValue);
849 }
850 };
851
852 //! The tanh trait specialization for complex types.
853 template<typename T_MathImpl, typename T>
854 struct Tanh::Op<T_MathImpl, Complex<T>>
855 {
856 constexpr auto operator()(T_MathImpl, Complex<T> const& arg) const
857 {
858 // tanh(z) = (e^z - e^-z)/(e^z+e^-z)
859 return (math::exp(arg) - math::exp(static_cast<T>(-1.0) * arg))
860 / (math::exp(arg) + math::exp(static_cast<T>(-1.0) * arg));
861 }
862 };
863
864 } // namespace internal
865
866#endif
867
868} // namespace alpaka::math
869
870namespace alpaka::trait
871{
872 template<typename T>
873 struct GetValueType<math::internal::Complex<T>>
874 {
875 using type = T;
876 };
877} // namespace alpaka::trait
constexpr auto exp(auto const &arg)
Definition math.hpp:140
constexpr auto sin(auto const &arg)
Definition math.hpp:21
ALPAKA_FN_INLINE constexpr auto floatEqualExactNoWarning(T a, T b) -> bool
Compare two floating point numbers for exact equivalence.
constexpr auto abs(auto const &arg)
Definition math.hpp:15
constexpr auto atan2(auto const &y, auto const &x)
Definition math.hpp:152
constexpr auto arg(auto const &arg)
Definition math.hpp:146
constexpr auto sincos(auto const &arg, auto &result_sin, auto &result_cos)
Definition math.hpp:130
constexpr auto sqrt(auto const &arg)
Definition math.hpp:159
constexpr auto cos(auto const &arg)
Definition math.hpp:178
constexpr auto log(auto const &arg)
Computes the natural (base e) logarithm of arg.
Definition math.hpp:208
constexpr auto pow(auto const &base, auto const &exp)
Definition math.hpp:284
constexpr auto operator-(const Simd< T_Type, T_width, T_Storage > &lhs, const Simd< T_Type, T_width, T_OtherStorage > &rhs)
binary operators
Definition Simd.hpp:580
constexpr auto operator+(const Simd< T_Type, T_width, T_Storage > &lhs, const Simd< T_Type, T_width, T_OtherStorage > &rhs)
binary operators
Definition Simd.hpp:579
constexpr auto is_decayed_v
Provides a decaying wrapper around std::is_same. Example: is_decayed_v<volatile float,...
Definition decay.hpp:13
typename T::value_type type
Definition trait.hpp:46