Kokkos Core Kernels Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Kokkos_Complex.hpp
1 //@HEADER
2 // ************************************************************************
3 //
4 // Kokkos v. 4.0
5 // Copyright (2022) National Technology & Engineering
6 // Solutions of Sandia, LLC (NTESS).
7 //
8 // Under the terms of Contract DE-NA0003525 with NTESS,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12 // See https://kokkos.org/LICENSE for license information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //@HEADER
16 #ifndef KOKKOS_COMPLEX_HPP
17 #define KOKKOS_COMPLEX_HPP
18 #ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
19 #define KOKKOS_IMPL_PUBLIC_INCLUDE
20 #define KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_COMPLEX
21 #endif
22 
23 #include <Kokkos_Atomic.hpp>
24 #include <Kokkos_MathematicalFunctions.hpp>
25 #include <Kokkos_NumericTraits.hpp>
26 #include <Kokkos_ReductionIdentity.hpp>
27 #include <impl/Kokkos_Error.hpp>
28 #include <complex>
29 #include <type_traits>
30 #include <iosfwd>
31 #include <tuple>
32 
33 namespace Kokkos {
34 
42 template <class RealType>
43 class
44 #ifdef KOKKOS_ENABLE_COMPLEX_ALIGN
45  alignas(2 * sizeof(RealType))
46 #endif
47  complex {
48  static_assert(std::is_floating_point_v<RealType> &&
49  std::is_same_v<RealType, std::remove_cv_t<RealType>>,
50  "Kokkos::complex can only be instantiated for a cv-unqualified "
51  "floating point type");
52 
53  private:
54  RealType re_{};
55  RealType im_{};
56 
57  public:
59  using value_type = RealType;
60 
62  KOKKOS_DEFAULTED_FUNCTION
63  complex() = default;
64 
66  KOKKOS_DEFAULTED_FUNCTION
67  complex(const complex&) noexcept = default;
68 
69  KOKKOS_DEFAULTED_FUNCTION
70  complex& operator=(const complex&) noexcept = default;
71 
73  template <class RType,
74  std::enable_if_t<std::is_convertible_v<RType, RealType>, int> = 0>
75  KOKKOS_INLINE_FUNCTION complex(const complex<RType>& other) noexcept
76  // Intentionally do the conversions implicitly here so that users don't
77  // get any warnings about narrowing, etc., that they would expect to get
78  // otherwise.
79  : re_(other.real()), im_(other.imag()) {}
80 
86  KOKKOS_INLINE_FUNCTION
87  complex(const std::complex<RealType>& src) noexcept
88  // We can use this aspect of the standard to avoid calling
89  // non-device-marked functions `std::real` and `std::imag`: "For any
90  // object z of type complex<T>, reinterpret_cast<T(&)[2]>(z)[0] is the
91  // real part of z and reinterpret_cast<T(&)[2]>(z)[1] is the imaginary
92  // part of z." Now we don't have to provide a whole bunch of the overloads
93  // of things taking either Kokkos::complex or std::complex
94  : re_(reinterpret_cast<const RealType (&)[2]>(src)[0]),
95  im_(reinterpret_cast<const RealType (&)[2]>(src)[1]) {}
96 
102  // TODO: make explicit. DJS 2019-08-28
103  operator std::complex<RealType>() const noexcept {
104  return std::complex<RealType>(re_, im_);
105  }
106 
109  KOKKOS_INLINE_FUNCTION constexpr complex(const RealType& val) noexcept
110  : re_(val), im_(static_cast<RealType>(0)) {}
111 
113  KOKKOS_INLINE_FUNCTION
114  constexpr complex(const RealType& re, const RealType& im) noexcept
115  : re_(re), im_(im) {}
116 
118  KOKKOS_INLINE_FUNCTION constexpr complex& operator=(
119  const RealType& val) noexcept {
120  re_ = val;
121  im_ = RealType(0);
122  return *this;
123  }
124 
130  complex& operator=(const std::complex<RealType>& src) noexcept {
131  *this = complex(src);
132  return *this;
133  }
134 
136  KOKKOS_INLINE_FUNCTION
137  constexpr RealType& imag() noexcept { return im_; }
138 
140  KOKKOS_INLINE_FUNCTION
141  constexpr RealType& real() noexcept { return re_; }
142 
144  KOKKOS_INLINE_FUNCTION
145  constexpr RealType imag() const noexcept { return im_; }
146 
148  KOKKOS_INLINE_FUNCTION
149  constexpr RealType real() const noexcept { return re_; }
150 
152  KOKKOS_INLINE_FUNCTION
153  constexpr void imag(RealType v) noexcept { im_ = v; }
154 
156  KOKKOS_INLINE_FUNCTION
157  constexpr void real(RealType v) noexcept { re_ = v; }
158 
159  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
160  const complex<RealType>& src) noexcept {
161  re_ += src.re_;
162  im_ += src.im_;
163  return *this;
164  }
165 
166  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
167  const RealType& src) noexcept {
168  re_ += src;
169  return *this;
170  }
171 
172  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
173  const complex<RealType>& src) noexcept {
174  re_ -= src.re_;
175  im_ -= src.im_;
176  return *this;
177  }
178 
179  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
180  const RealType& src) noexcept {
181  re_ -= src;
182  return *this;
183  }
184 
185  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
186  const complex<RealType>& src) noexcept {
187  const RealType realPart = re_ * src.re_ - im_ * src.im_;
188  const RealType imagPart = re_ * src.im_ + im_ * src.re_;
189  re_ = realPart;
190  im_ = imagPart;
191  return *this;
192  }
193 
194  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
195  const RealType& src) noexcept {
196  re_ *= src;
197  im_ *= src;
198  return *this;
199  }
200 
201  // Conditional noexcept, just in case RType throws on divide-by-zero
202  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
203  const complex<RealType>& y) noexcept(noexcept(RealType{} / RealType{})) {
204  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
205  // If the real part is +/-Inf and the imaginary part is -/+Inf,
206  // this won't change the result.
207  const RealType s = fabs(y.real()) + fabs(y.imag());
208 
209  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
210  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
211  // because y/s is NaN.
212  // TODO mark this branch unlikely
213  if (s == RealType(0)) {
214  this->re_ /= s;
215  this->im_ /= s;
216  } else {
217  const complex x_scaled(this->re_ / s, this->im_ / s);
218  const complex y_conj_scaled(y.re_ / s, -(y.im_) / s);
219  const RealType y_scaled_abs =
220  y_conj_scaled.re_ * y_conj_scaled.re_ +
221  y_conj_scaled.im_ * y_conj_scaled.im_; // abs(y) == abs(conj(y))
222  *this = x_scaled * y_conj_scaled;
223  *this /= y_scaled_abs;
224  }
225  return *this;
226  }
227 
228  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
229  const std::complex<RealType>& y) noexcept(noexcept(RealType{} /
230  RealType{})) {
231  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
232  // If the real part is +/-Inf and the imaginary part is -/+Inf,
233  // this won't change the result.
234  const RealType s = fabs(y.real()) + fabs(y.imag());
235 
236  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
237  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
238  // because y/s is NaN.
239  if (s == RealType(0)) {
240  this->re_ /= s;
241  this->im_ /= s;
242  } else {
243  const complex x_scaled(this->re_ / s, this->im_ / s);
244  const complex y_conj_scaled(y.re_ / s, -(y.im_) / s);
245  const RealType y_scaled_abs =
246  y_conj_scaled.re_ * y_conj_scaled.re_ +
247  y_conj_scaled.im_ * y_conj_scaled.im_; // abs(y) == abs(conj(y))
248  *this = x_scaled * y_conj_scaled;
249  *this /= y_scaled_abs;
250  }
251  return *this;
252  }
253 
254  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
255  const RealType& src) noexcept(noexcept(RealType{} / RealType{})) {
256  re_ /= src;
257  im_ /= src;
258  return *this;
259  }
260 
261  template <size_t I, typename RT>
262  friend constexpr const RT& get(const complex<RT>&) noexcept;
263 
264  template <size_t I, typename RT>
265  friend constexpr const RT&& get(const complex<RT>&&) noexcept;
266 
267 #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
268  template <class RType,
270  std::enable_if_t<std::is_convertible_v<RType, RealType>, int> = 0>
271  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION
272  complex(const volatile complex<RType>& src) noexcept
273  // Intentionally do the conversions implicitly here so that users don't
274  // get any warnings about narrowing, etc., that they would expect to get
275  // otherwise.
276  : re_(src.re_), im_(src.im_) {}
277 
287  //
288  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
289  // Intended to behave as
290  // void operator=(const complex&) volatile noexcept
291  //
292  // Use cases:
293  // complex r;
294  // const complex cr;
295  // volatile complex vl;
296  // vl = r;
297  // vl = cr;
298  template <class Complex,
299  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
300  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator=(
301  const Complex& src) volatile noexcept {
302  re_ = src.re_;
303  im_ = src.im_;
304  // We deliberately do not return anything here. See explanation
305  // in public documentation above.
306  }
307 
309  // TODO Should this return void like the other volatile assignment operators?
310  //
311  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
312  // Intended to behave as
313  // volatile complex& operator=(const volatile complex&) volatile noexcept
314  //
315  // Use cases:
316  // volatile complex vr;
317  // const volatile complex cvr;
318  // volatile complex vl;
319  // vl = vr;
320  // vl = cvr;
321  template <class Complex,
322  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
323  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile complex& operator=(
324  const volatile Complex& src) volatile noexcept {
325  re_ = src.re_;
326  im_ = src.im_;
327  return *this;
328  }
329 
331  //
332  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
333  // Intended to behave as
334  // complex& operator=(const volatile complex&) noexcept
335  //
336  // Use cases:
337  // volatile complex vr;
338  // const volatile complex cvr;
339  // complex l;
340  // l = vr;
341  // l = cvr;
342  //
343  template <class Complex,
344  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
345  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
346  const volatile Complex& src) noexcept {
347  re_ = src.re_;
348  im_ = src.im_;
349  return *this;
350  }
351 
352  // Mirroring the behavior of the assignment operators from complex RHS in the
353  // RealType RHS versions.
354 
356  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator=(
357  const volatile RealType& val) noexcept {
358  re_ = val;
359  im_ = RealType(0);
360  // We deliberately do not return anything here. See explanation
361  // in public documentation above.
362  }
363 
365  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
366  const RealType& val) volatile noexcept {
367  re_ = val;
368  im_ = RealType(0);
369  return *this;
370  }
371 
373  // TODO Should this return void like the other volatile assignment operators?
374  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
375  const volatile RealType& val) volatile noexcept {
376  re_ = val;
377  im_ = RealType(0);
378  return *this;
379  }
380 
382  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile RealType&
383  imag() volatile noexcept {
384  return im_;
385  }
386 
388  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile RealType&
389  real() volatile noexcept {
390  return re_;
391  }
392 
394  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION RealType imag() const
395  volatile noexcept {
396  return im_;
397  }
398 
400  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION RealType real() const
401  volatile noexcept {
402  return re_;
403  }
404 
405  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator+=(
406  const volatile complex<RealType>& src) volatile noexcept {
407  re_ += src.re_;
408  im_ += src.im_;
409  }
410 
411  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator+=(
412  const volatile RealType& src) volatile noexcept {
413  re_ += src;
414  }
415 
416  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator*=(
417  const volatile complex<RealType>& src) volatile noexcept {
418  const RealType realPart = re_ * src.re_ - im_ * src.im_;
419  const RealType imagPart = re_ * src.im_ + im_ * src.re_;
420 
421  re_ = realPart;
422  im_ = imagPart;
423  }
424 
425  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator*=(
426  const volatile RealType& src) volatile noexcept {
427  re_ *= src;
428  im_ *= src;
429  }
430 #endif // KOKKOS_ENABLE_DEPRECATED_CODE_4
431 };
432 
433 } // namespace Kokkos
434 
435 // Tuple protocol for complex based on https://wg21.link/P2819R2 (voted into
436 // the C++26 working draft on 2023-11)
437 
438 template <typename RealType>
439 struct std::tuple_size<Kokkos::complex<RealType>>
440  : std::integral_constant<size_t, 2> {};
441 
442 template <size_t I, typename RealType>
443 struct std::tuple_element<I, Kokkos::complex<RealType>> {
444  static_assert(I < 2);
445  using type = RealType;
446 };
447 
448 namespace Kokkos {
449 
450 // get<...>(...) defined here so as not to be hidden friends, as per P2819R2
451 
452 template <size_t I, typename RealType>
453 KOKKOS_FUNCTION constexpr RealType& get(complex<RealType>& z) noexcept {
454  static_assert(I < 2);
455  if constexpr (I == 0)
456  return z.real();
457  else
458  return z.imag();
459 }
460 
461 template <size_t I, typename RealType>
462 KOKKOS_FUNCTION constexpr RealType&& get(complex<RealType>&& z) noexcept {
463  static_assert(I < 2);
464  if constexpr (I == 0)
465  return std::move(z.real());
466  else
467  return std::move(z.imag());
468 }
469 
470 template <size_t I, typename RealType>
471 KOKKOS_FUNCTION constexpr const RealType& get(
472  const complex<RealType>& z) noexcept {
473  static_assert(I < 2);
474  if constexpr (I == 0)
475  return z.re_;
476  else
477  return z.im_;
478 }
479 
480 template <size_t I, typename RealType>
481 KOKKOS_FUNCTION constexpr const RealType&& get(
482  const complex<RealType>&& z) noexcept {
483  static_assert(I < 2);
484  if constexpr (I == 0)
485  return std::move(z.re_);
486  else
487  return std::move(z.im_);
488 }
489 
490 //==============================================================================
491 // <editor-fold desc="Equality and inequality"> {{{1
492 
493 // Note that this is not the same behavior as std::complex, which doesn't allow
494 // implicit conversions, but since this is the way we had it before, we have
495 // to do it this way now.
496 
498 template <class RealType1, class RealType2>
499 KOKKOS_INLINE_FUNCTION bool operator==(complex<RealType1> const& x,
500  complex<RealType2> const& y) noexcept {
501  using common_type = std::common_type_t<RealType1, RealType2>;
502  return common_type(x.real()) == common_type(y.real()) &&
503  common_type(x.imag()) == common_type(y.imag());
504 }
505 
506 // TODO (here and elsewhere) decide if we should convert to a Kokkos::complex
507 // and do the comparison in a device-marked function
509 template <class RealType1, class RealType2>
510 inline bool operator==(std::complex<RealType1> const& x,
511  complex<RealType2> const& y) noexcept {
512  using common_type = std::common_type_t<RealType1, RealType2>;
513  return common_type(x.real()) == common_type(y.real()) &&
514  common_type(x.imag()) == common_type(y.imag());
515 }
516 
518 template <class RealType1, class RealType2>
519 inline bool operator==(complex<RealType1> const& x,
520  std::complex<RealType2> const& y) noexcept {
521  using common_type = std::common_type_t<RealType1, RealType2>;
522  return common_type(x.real()) == common_type(y.real()) &&
523  common_type(x.imag()) == common_type(y.imag());
524 }
525 
527 template <
528  class RealType1, class RealType2,
529  // Constraints to avoid participation in oparator==() for every possible RHS
530  std::enable_if_t<std::is_convertible_v<RealType2, RealType1>, int> = 0>
531 KOKKOS_INLINE_FUNCTION bool operator==(complex<RealType1> const& x,
532  RealType2 const& y) noexcept {
533  using common_type = std::common_type_t<RealType1, RealType2>;
534  return common_type(x.real()) == common_type(y) &&
535  common_type(x.imag()) == common_type(0);
536 }
537 
539 template <
540  class RealType1, class RealType2,
541  // Constraints to avoid participation in oparator==() for every possible RHS
542  std::enable_if_t<std::is_convertible_v<RealType1, RealType2>, int> = 0>
543 KOKKOS_INLINE_FUNCTION bool operator==(RealType1 const& x,
544  complex<RealType2> const& y) noexcept {
545  using common_type = std::common_type_t<RealType1, RealType2>;
546  return common_type(x) == common_type(y.real()) &&
547  common_type(0) == common_type(y.imag());
548 }
549 
551 template <class RealType1, class RealType2>
552 KOKKOS_INLINE_FUNCTION bool operator!=(complex<RealType1> const& x,
553  complex<RealType2> const& y) noexcept {
554  using common_type = std::common_type_t<RealType1, RealType2>;
555  return common_type(x.real()) != common_type(y.real()) ||
556  common_type(x.imag()) != common_type(y.imag());
557 }
558 
560 template <class RealType1, class RealType2>
561 inline bool operator!=(std::complex<RealType1> const& x,
562  complex<RealType2> const& y) noexcept {
563  using common_type = std::common_type_t<RealType1, RealType2>;
564  return common_type(x.real()) != common_type(y.real()) ||
565  common_type(x.imag()) != common_type(y.imag());
566 }
567 
569 template <class RealType1, class RealType2>
570 inline bool operator!=(complex<RealType1> const& x,
571  std::complex<RealType2> const& y) noexcept {
572  using common_type = std::common_type_t<RealType1, RealType2>;
573  return common_type(x.real()) != common_type(y.real()) ||
574  common_type(x.imag()) != common_type(y.imag());
575 }
576 
578 template <
579  class RealType1, class RealType2,
580  // Constraints to avoid participation in oparator==() for every possible RHS
581  std::enable_if_t<std::is_convertible_v<RealType2, RealType1>, int> = 0>
582 KOKKOS_INLINE_FUNCTION bool operator!=(complex<RealType1> const& x,
583  RealType2 const& y) noexcept {
584  using common_type = std::common_type_t<RealType1, RealType2>;
585  return common_type(x.real()) != common_type(y) ||
586  common_type(x.imag()) != common_type(0);
587 }
588 
590 template <
591  class RealType1, class RealType2,
592  // Constraints to avoid participation in oparator==() for every possible RHS
593  std::enable_if_t<std::is_convertible_v<RealType1, RealType2>, int> = 0>
594 KOKKOS_INLINE_FUNCTION bool operator!=(RealType1 const& x,
595  complex<RealType2> const& y) noexcept {
596  using common_type = std::common_type_t<RealType1, RealType2>;
597  return common_type(x) != common_type(y.real()) ||
598  common_type(0) != common_type(y.imag());
599 }
600 
601 // </editor-fold> end Equality and inequality }}}1
602 //==============================================================================
603 
605 template <class RealType1, class RealType2>
606 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
607 operator+(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
608  return complex<std::common_type_t<RealType1, RealType2>>(x.real() + y.real(),
609  x.imag() + y.imag());
610 }
611 
613 template <class RealType1, class RealType2>
614 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
615 operator+(const complex<RealType1>& x, const RealType2& y) noexcept {
616  return complex<std::common_type_t<RealType1, RealType2>>(x.real() + y,
617  x.imag());
618 }
619 
621 template <class RealType1, class RealType2>
622 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
623 operator+(const RealType1& x, const complex<RealType2>& y) noexcept {
624  return complex<std::common_type_t<RealType1, RealType2>>(x + y.real(),
625  y.imag());
626 }
627 
629 template <class RealType>
630 KOKKOS_INLINE_FUNCTION complex<RealType> operator+(
631  const complex<RealType>& x) noexcept {
632  return complex<RealType>{+x.real(), +x.imag()};
633 }
634 
636 template <class RealType1, class RealType2>
637 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
638 operator-(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
639  return complex<std::common_type_t<RealType1, RealType2>>(x.real() - y.real(),
640  x.imag() - y.imag());
641 }
642 
644 template <class RealType1, class RealType2>
645 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
646 operator-(const complex<RealType1>& x, const RealType2& y) noexcept {
647  return complex<std::common_type_t<RealType1, RealType2>>(x.real() - y,
648  x.imag());
649 }
650 
652 template <class RealType1, class RealType2>
653 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
654 operator-(const RealType1& x, const complex<RealType2>& y) noexcept {
655  return complex<std::common_type_t<RealType1, RealType2>>(x - y.real(),
656  -y.imag());
657 }
658 
660 template <class RealType>
661 KOKKOS_INLINE_FUNCTION complex<RealType> operator-(
662  const complex<RealType>& x) noexcept {
663  return complex<RealType>(-x.real(), -x.imag());
664 }
665 
667 template <class RealType1, class RealType2>
668 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
669 operator*(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
670  return complex<std::common_type_t<RealType1, RealType2>>(
671  x.real() * y.real() - x.imag() * y.imag(),
672  x.real() * y.imag() + x.imag() * y.real());
673 }
674 
683 template <class RealType1, class RealType2>
684 inline complex<std::common_type_t<RealType1, RealType2>> operator*(
685  const std::complex<RealType1>& x, const complex<RealType2>& y) {
686  return complex<std::common_type_t<RealType1, RealType2>>(
687  x.real() * y.real() - x.imag() * y.imag(),
688  x.real() * y.imag() + x.imag() * y.real());
689 }
690 
695 template <class RealType1, class RealType2>
696 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
697 operator*(const RealType1& x, const complex<RealType2>& y) noexcept {
698  return complex<std::common_type_t<RealType1, RealType2>>(x * y.real(),
699  x * y.imag());
700 }
701 
706 template <class RealType1, class RealType2>
707 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
708 operator*(const complex<RealType1>& y, const RealType2& x) noexcept {
709  return complex<std::common_type_t<RealType1, RealType2>>(x * y.real(),
710  x * y.imag());
711 }
712 
714 template <class RealType>
715 KOKKOS_INLINE_FUNCTION RealType imag(const complex<RealType>& x) noexcept {
716  return x.imag();
717 }
718 
719 template <class ArithmeticType>
720 KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> imag(
721  ArithmeticType) {
722  return ArithmeticType();
723 }
724 
726 template <class RealType>
727 KOKKOS_INLINE_FUNCTION RealType real(const complex<RealType>& x) noexcept {
728  return x.real();
729 }
730 
731 template <class ArithmeticType>
732 KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> real(
733  ArithmeticType x) {
734  return x;
735 }
736 
738 template <class T>
739 KOKKOS_INLINE_FUNCTION complex<T> polar(const T& r, const T& theta = T()) {
740  KOKKOS_EXPECTS(r >= 0);
741  return complex<T>(r * cos(theta), r * sin(theta));
742 }
743 
745 template <class RealType>
746 KOKKOS_INLINE_FUNCTION RealType abs(const complex<RealType>& x) {
747  return hypot(x.real(), x.imag());
748 }
749 
751 template <class T>
752 KOKKOS_INLINE_FUNCTION complex<T> pow(const complex<T>& x, const T& y) {
753  T r = abs(x);
754  T theta = atan2(x.imag(), x.real());
755  return polar(pow(r, y), y * theta);
756 }
757 
758 template <class T>
759 KOKKOS_INLINE_FUNCTION complex<T> pow(const T& x, const complex<T>& y) {
760  return pow(complex<T>(x), y);
761 }
762 
763 template <class T>
764 KOKKOS_INLINE_FUNCTION complex<T> pow(const complex<T>& x,
765  const complex<T>& y) {
766  return x == T() ? T() : exp(y * log(x));
767 }
768 
769 template <class T, class U, class = std::enable_if_t<std::is_arithmetic_v<T>>>
770 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(
771  const T& x, const complex<U>& y) {
772  using type = Impl::promote_2_t<T, U>;
773  return pow(type(x), complex<type>(y));
774 }
775 
776 template <class T, class U, class = std::enable_if_t<std::is_arithmetic_v<U>>>
777 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(const complex<T>& x,
778  const U& y) {
779  using type = Impl::promote_2_t<T, U>;
780  return pow(complex<type>(x), type(y));
781 }
782 
783 template <class T, class U>
784 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(
785  const complex<T>& x, const complex<U>& y) {
786  using type = Impl::promote_2_t<T, U>;
787  return pow(complex<type>(x), complex<type>(y));
788 }
789 
792 template <class RealType>
793 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sqrt(
794  const complex<RealType>& x) {
795  RealType r = x.real();
796  RealType i = x.imag();
797 
798  if (r == RealType()) {
799  RealType t = sqrt(fabs(i) / 2);
800  return Kokkos::complex<RealType>(t, i < RealType() ? -t : t);
801  } else {
802  RealType t = sqrt(2 * (abs(x) + fabs(r)));
803  RealType u = t / 2;
804  return r > RealType() ? Kokkos::complex<RealType>(u, i / t)
805  : Kokkos::complex<RealType>(fabs(i) / t,
806  i < RealType() ? -u : u);
807  }
808 }
809 
811 template <class RealType>
812 KOKKOS_INLINE_FUNCTION complex<RealType> conj(
813  const complex<RealType>& x) noexcept {
814  return complex<RealType>(real(x), -imag(x));
815 }
816 
817 template <class ArithmeticType>
818 KOKKOS_INLINE_FUNCTION constexpr complex<Impl::promote_t<ArithmeticType>> conj(
819  ArithmeticType x) {
820  using type = Impl::promote_t<ArithmeticType>;
821  return complex<type>(x, -type());
822 }
823 
825 template <class RealType>
826 KOKKOS_INLINE_FUNCTION complex<RealType> exp(const complex<RealType>& x) {
827  return exp(x.real()) * complex<RealType>(cos(x.imag()), sin(x.imag()));
828 }
829 
831 template <class RealType>
832 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> log(
833  const complex<RealType>& x) {
834  RealType phi = atan2(x.imag(), x.real());
835  return Kokkos::complex<RealType>(log(abs(x)), phi);
836 }
837 
839 template <class RealType>
840 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> log10(
841  const complex<RealType>& x) {
842  return log(x) / log(RealType(10));
843 }
844 
846 template <class RealType>
847 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sin(
848  const complex<RealType>& x) {
849  return Kokkos::complex<RealType>(sin(x.real()) * cosh(x.imag()),
850  cos(x.real()) * sinh(x.imag()));
851 }
852 
854 template <class RealType>
855 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> cos(
856  const complex<RealType>& x) {
857  return Kokkos::complex<RealType>(cos(x.real()) * cosh(x.imag()),
858  -sin(x.real()) * sinh(x.imag()));
859 }
860 
862 template <class RealType>
863 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> tan(
864  const complex<RealType>& x) {
865  return sin(x) / cos(x);
866 }
867 
869 template <class RealType>
870 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sinh(
871  const complex<RealType>& x) {
872  return Kokkos::complex<RealType>(sinh(x.real()) * cos(x.imag()),
873  cosh(x.real()) * sin(x.imag()));
874 }
875 
877 template <class RealType>
878 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> cosh(
879  const complex<RealType>& x) {
880  return Kokkos::complex<RealType>(cosh(x.real()) * cos(x.imag()),
881  sinh(x.real()) * sin(x.imag()));
882 }
883 
885 template <class RealType>
886 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> tanh(
887  const complex<RealType>& x) {
888  return sinh(x) / cosh(x);
889 }
890 
892 template <class RealType>
893 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> asinh(
894  const complex<RealType>& x) {
895  return log(x + sqrt(x * x + RealType(1.0)));
896 }
897 
899 template <class RealType>
900 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> acosh(
901  const complex<RealType>& x) {
902  return RealType(2.0) * log(sqrt(RealType(0.5) * (x + RealType(1.0))) +
903  sqrt(RealType(0.5) * (x - RealType(1.0))));
904 }
905 
907 template <class RealType>
908 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> atanh(
909  const complex<RealType>& x) {
910  const RealType i2 = x.imag() * x.imag();
911  const RealType r = RealType(1.0) - i2 - x.real() * x.real();
912 
913  RealType p = RealType(1.0) + x.real();
914  RealType m = RealType(1.0) - x.real();
915 
916  p = i2 + p * p;
917  m = i2 + m * m;
918 
919  RealType phi = atan2(RealType(2.0) * x.imag(), r);
920  return Kokkos::complex<RealType>(RealType(0.25) * (log(p) - log(m)),
921  RealType(0.5) * phi);
922 }
923 
925 template <class RealType>
926 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> asin(
927  const complex<RealType>& x) {
929  asinh(Kokkos::complex<RealType>(-x.imag(), x.real()));
930  return Kokkos::complex<RealType>(t.imag(), -t.real());
931 }
932 
934 template <class RealType>
935 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> acos(
936  const complex<RealType>& x) {
937  Kokkos::complex<RealType> t = asin(x);
938  RealType pi_2 = acos(RealType(0.0));
939  return Kokkos::complex<RealType>(pi_2 - t.real(), -t.imag());
940 }
941 
943 template <class RealType>
944 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> atan(
945  const complex<RealType>& x) {
946  const RealType r2 = x.real() * x.real();
947  const RealType i = RealType(1.0) - r2 - x.imag() * x.imag();
948 
949  RealType p = x.imag() + RealType(1.0);
950  RealType m = x.imag() - RealType(1.0);
951 
952  p = r2 + p * p;
953  m = r2 + m * m;
954 
956  RealType(0.5) * atan2(RealType(2.0) * x.real(), i),
957  RealType(0.25) * log(p / m));
958 }
959 
963 template <class RealType>
964 inline complex<RealType> exp(const std::complex<RealType>& c) {
965  return complex<RealType>(std::exp(c.real()) * std::cos(c.imag()),
966  std::exp(c.real()) * std::sin(c.imag()));
967 }
968 
970 template <class RealType1, class RealType2>
971 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
972 operator/(const complex<RealType1>& x,
973  const RealType2& y) noexcept(noexcept(RealType1{} / RealType2{})) {
974  return complex<std::common_type_t<RealType1, RealType2>>(real(x) / y,
975  imag(x) / y);
976 }
977 
979 template <class RealType1, class RealType2>
980 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
981 operator/(const complex<RealType1>& x,
982  const complex<RealType2>& y) noexcept(noexcept(RealType1{} /
983  RealType2{})) {
984  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
985  // If the real part is +/-Inf and the imaginary part is -/+Inf,
986  // this won't change the result.
987  using common_real_type = std::common_type_t<RealType1, RealType2>;
988  const common_real_type s = fabs(real(y)) + fabs(imag(y));
989 
990  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
991  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
992  // because y/s is NaN.
993  if (s == 0.0) {
994  return complex<common_real_type>(real(x) / s, imag(x) / s);
995  } else {
996  const complex<common_real_type> x_scaled(real(x) / s, imag(x) / s);
997  const complex<common_real_type> y_conj_scaled(real(y) / s, -imag(y) / s);
998  const RealType1 y_scaled_abs =
999  real(y_conj_scaled) * real(y_conj_scaled) +
1000  imag(y_conj_scaled) * imag(y_conj_scaled); // abs(y) == abs(conj(y))
1001  complex<common_real_type> result = x_scaled * y_conj_scaled;
1002  result /= y_scaled_abs;
1003  return result;
1004  }
1005 }
1006 
1008 template <class RealType1, class RealType2>
1009 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
1010 operator/(const RealType1& x,
1011  const complex<RealType2>& y) noexcept(noexcept(RealType1{} /
1012  RealType2{})) {
1013  return complex<std::common_type_t<RealType1, RealType2>>(x) / y;
1014 }
1015 
1016 template <class RealType>
1017 std::ostream& operator<<(std::ostream& os, const complex<RealType>& x) {
1018  const std::complex<RealType> x_std(Kokkos::real(x), Kokkos::imag(x));
1019  os << x_std;
1020  return os;
1021 }
1022 
1023 template <class RealType>
1024 std::istream& operator>>(std::istream& is, complex<RealType>& x) {
1025  std::complex<RealType> x_std;
1026  is >> x_std;
1027  x = x_std; // only assigns on success of above
1028  return is;
1029 }
1030 
1031 template <class T>
1032 struct reduction_identity<Kokkos::complex<T>> {
1033  using t_red_ident = reduction_identity<T>;
1034  KOKKOS_FORCEINLINE_FUNCTION constexpr static Kokkos::complex<T>
1035  sum() noexcept {
1036  return Kokkos::complex<T>(t_red_ident::sum(), t_red_ident::sum());
1037  }
1038  KOKKOS_FORCEINLINE_FUNCTION constexpr static Kokkos::complex<T>
1039  prod() noexcept {
1040  return Kokkos::complex<T>(t_red_ident::prod(), t_red_ident::sum());
1041  }
1042 };
1043 
1044 } // namespace Kokkos
1045 
1046 #ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_COMPLEX
1047 #undef KOKKOS_IMPL_PUBLIC_INCLUDE
1048 #undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_COMPLEX
1049 #endif
1050 #endif // KOKKOS_COMPLEX_HPP
KOKKOS_INLINE_FUNCTION constexpr RealType & imag() noexcept
Partial reimplementation of std::complex that works as the result of a Kokkos::parallel_reduce.
KOKKOS_INLINE_FUNCTION constexpr RealType & real() noexcept