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 complex(const RealType& val) noexcept
110  : re_(val), im_(static_cast<RealType>(0)) {}
111 
113  KOKKOS_INLINE_FUNCTION
114  complex(const RealType& re, const RealType& im) noexcept : re_(re), im_(im) {}
115 
117  KOKKOS_INLINE_FUNCTION complex& operator=(const RealType& val) noexcept {
118  re_ = val;
119  im_ = RealType(0);
120  return *this;
121  }
122 
128  complex& operator=(const std::complex<RealType>& src) noexcept {
129  *this = complex(src);
130  return *this;
131  }
132 
134  KOKKOS_INLINE_FUNCTION
135  constexpr RealType& imag() noexcept { return im_; }
136 
138  KOKKOS_INLINE_FUNCTION
139  constexpr RealType& real() noexcept { return re_; }
140 
142  KOKKOS_INLINE_FUNCTION
143  constexpr RealType imag() const noexcept { return im_; }
144 
146  KOKKOS_INLINE_FUNCTION
147  constexpr RealType real() const noexcept { return re_; }
148 
150  KOKKOS_INLINE_FUNCTION
151  constexpr void imag(RealType v) noexcept { im_ = v; }
152 
154  KOKKOS_INLINE_FUNCTION
155  constexpr void real(RealType v) noexcept { re_ = v; }
156 
157  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
158  const complex<RealType>& src) noexcept {
159  re_ += src.re_;
160  im_ += src.im_;
161  return *this;
162  }
163 
164  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
165  const RealType& src) noexcept {
166  re_ += src;
167  return *this;
168  }
169 
170  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
171  const complex<RealType>& src) noexcept {
172  re_ -= src.re_;
173  im_ -= src.im_;
174  return *this;
175  }
176 
177  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
178  const RealType& src) noexcept {
179  re_ -= src;
180  return *this;
181  }
182 
183  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
184  const complex<RealType>& src) noexcept {
185  const RealType realPart = re_ * src.re_ - im_ * src.im_;
186  const RealType imagPart = re_ * src.im_ + im_ * src.re_;
187  re_ = realPart;
188  im_ = imagPart;
189  return *this;
190  }
191 
192  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
193  const RealType& src) noexcept {
194  re_ *= src;
195  im_ *= src;
196  return *this;
197  }
198 
199  // Conditional noexcept, just in case RType throws on divide-by-zero
200  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
201  const complex<RealType>& y) noexcept(noexcept(RealType{} / RealType{})) {
202  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
203  // If the real part is +/-Inf and the imaginary part is -/+Inf,
204  // this won't change the result.
205  const RealType s = fabs(y.real()) + fabs(y.imag());
206 
207  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
208  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
209  // because y/s is NaN.
210  // TODO mark this branch unlikely
211  if (s == RealType(0)) {
212  this->re_ /= s;
213  this->im_ /= s;
214  } else {
215  const complex x_scaled(this->re_ / s, this->im_ / s);
216  const complex y_conj_scaled(y.re_ / s, -(y.im_) / s);
217  const RealType y_scaled_abs =
218  y_conj_scaled.re_ * y_conj_scaled.re_ +
219  y_conj_scaled.im_ * y_conj_scaled.im_; // abs(y) == abs(conj(y))
220  *this = x_scaled * y_conj_scaled;
221  *this /= y_scaled_abs;
222  }
223  return *this;
224  }
225 
226  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
227  const std::complex<RealType>& y) noexcept(noexcept(RealType{} /
228  RealType{})) {
229  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
230  // If the real part is +/-Inf and the imaginary part is -/+Inf,
231  // this won't change the result.
232  const RealType s = fabs(y.real()) + fabs(y.imag());
233 
234  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
235  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
236  // because y/s is NaN.
237  if (s == RealType(0)) {
238  this->re_ /= s;
239  this->im_ /= s;
240  } else {
241  const complex x_scaled(this->re_ / s, this->im_ / s);
242  const complex y_conj_scaled(y.re_ / s, -(y.im_) / s);
243  const RealType y_scaled_abs =
244  y_conj_scaled.re_ * y_conj_scaled.re_ +
245  y_conj_scaled.im_ * y_conj_scaled.im_; // abs(y) == abs(conj(y))
246  *this = x_scaled * y_conj_scaled;
247  *this /= y_scaled_abs;
248  }
249  return *this;
250  }
251 
252  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
253  const RealType& src) noexcept(noexcept(RealType{} / RealType{})) {
254  re_ /= src;
255  im_ /= src;
256  return *this;
257  }
258 
259  template <size_t I, typename RT>
260  friend constexpr const RT& get(const complex<RT>&) noexcept;
261 
262  template <size_t I, typename RT>
263  friend constexpr const RT&& get(const complex<RT>&&) noexcept;
264 
265 #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
266  template <class RType,
268  std::enable_if_t<std::is_convertible_v<RType, RealType>, int> = 0>
269  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION
270  complex(const volatile complex<RType>& src) noexcept
271  // Intentionally do the conversions implicitly here so that users don't
272  // get any warnings about narrowing, etc., that they would expect to get
273  // otherwise.
274  : re_(src.re_), im_(src.im_) {}
275 
285  //
286  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
287  // Intended to behave as
288  // void operator=(const complex&) volatile noexcept
289  //
290  // Use cases:
291  // complex r;
292  // const complex cr;
293  // volatile complex vl;
294  // vl = r;
295  // vl = cr;
296  template <class Complex,
297  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
298  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator=(
299  const Complex& src) volatile noexcept {
300  re_ = src.re_;
301  im_ = src.im_;
302  // We deliberately do not return anything here. See explanation
303  // in public documentation above.
304  }
305 
307  // TODO Should this return void like the other volatile assignment operators?
308  //
309  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
310  // Intended to behave as
311  // volatile complex& operator=(const volatile complex&) volatile noexcept
312  //
313  // Use cases:
314  // volatile complex vr;
315  // const volatile complex cvr;
316  // volatile complex vl;
317  // vl = vr;
318  // vl = cvr;
319  template <class Complex,
320  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
321  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile complex& operator=(
322  const volatile Complex& src) volatile noexcept {
323  re_ = src.re_;
324  im_ = src.im_;
325  return *this;
326  }
327 
329  //
330  // Templated, so as not to be a copy assignment operator (Kokkos issue #2577)
331  // Intended to behave as
332  // complex& operator=(const volatile complex&) noexcept
333  //
334  // Use cases:
335  // volatile complex vr;
336  // const volatile complex cvr;
337  // complex l;
338  // l = vr;
339  // l = cvr;
340  //
341  template <class Complex,
342  std::enable_if_t<std::is_same_v<Complex, complex>, int> = 0>
343  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
344  const volatile Complex& src) noexcept {
345  re_ = src.re_;
346  im_ = src.im_;
347  return *this;
348  }
349 
350  // Mirroring the behavior of the assignment operators from complex RHS in the
351  // RealType RHS versions.
352 
354  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator=(
355  const volatile RealType& val) noexcept {
356  re_ = val;
357  im_ = RealType(0);
358  // We deliberately do not return anything here. See explanation
359  // in public documentation above.
360  }
361 
363  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
364  const RealType& val) volatile noexcept {
365  re_ = val;
366  im_ = RealType(0);
367  return *this;
368  }
369 
371  // TODO Should this return void like the other volatile assignment operators?
372  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION complex& operator=(
373  const volatile RealType& val) volatile noexcept {
374  re_ = val;
375  im_ = RealType(0);
376  return *this;
377  }
378 
380  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile RealType&
381  imag() volatile noexcept {
382  return im_;
383  }
384 
386  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION volatile RealType&
387  real() volatile noexcept {
388  return re_;
389  }
390 
392  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION RealType imag() const
393  volatile noexcept {
394  return im_;
395  }
396 
398  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION RealType real() const
399  volatile noexcept {
400  return re_;
401  }
402 
403  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator+=(
404  const volatile complex<RealType>& src) volatile noexcept {
405  re_ += src.re_;
406  im_ += src.im_;
407  }
408 
409  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator+=(
410  const volatile RealType& src) volatile noexcept {
411  re_ += src;
412  }
413 
414  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator*=(
415  const volatile complex<RealType>& src) volatile noexcept {
416  const RealType realPart = re_ * src.re_ - im_ * src.im_;
417  const RealType imagPart = re_ * src.im_ + im_ * src.re_;
418 
419  re_ = realPart;
420  im_ = imagPart;
421  }
422 
423  KOKKOS_DEPRECATED KOKKOS_INLINE_FUNCTION void operator*=(
424  const volatile RealType& src) volatile noexcept {
425  re_ *= src;
426  im_ *= src;
427  }
428 #endif // KOKKOS_ENABLE_DEPRECATED_CODE_4
429 };
430 
431 } // namespace Kokkos
432 
433 // Tuple protocol for complex based on https://wg21.link/P2819R2 (voted into
434 // the C++26 working draft on 2023-11)
435 
436 template <typename RealType>
437 struct std::tuple_size<Kokkos::complex<RealType>>
438  : std::integral_constant<size_t, 2> {};
439 
440 template <size_t I, typename RealType>
441 struct std::tuple_element<I, Kokkos::complex<RealType>> {
442  static_assert(I < 2);
443  using type = RealType;
444 };
445 
446 namespace Kokkos {
447 
448 // get<...>(...) defined here so as not to be hidden friends, as per P2819R2
449 
450 template <size_t I, typename RealType>
451 KOKKOS_FUNCTION constexpr RealType& get(complex<RealType>& z) noexcept {
452  static_assert(I < 2);
453  if constexpr (I == 0)
454  return z.real();
455  else
456  return z.imag();
457 #ifdef KOKKOS_COMPILER_INTEL
458  __builtin_unreachable();
459 #endif
460 }
461 
462 template <size_t I, typename RealType>
463 KOKKOS_FUNCTION constexpr RealType&& get(complex<RealType>&& z) noexcept {
464  static_assert(I < 2);
465  if constexpr (I == 0)
466  return std::move(z.real());
467  else
468  return std::move(z.imag());
469 #ifdef KOKKOS_COMPILER_INTEL
470  __builtin_unreachable();
471 #endif
472 }
473 
474 template <size_t I, typename RealType>
475 KOKKOS_FUNCTION constexpr const RealType& get(
476  const complex<RealType>& z) noexcept {
477  static_assert(I < 2);
478  if constexpr (I == 0)
479  return z.re_;
480  else
481  return z.im_;
482 #ifdef KOKKOS_COMPILER_INTEL
483  __builtin_unreachable();
484 #endif
485 }
486 
487 template <size_t I, typename RealType>
488 KOKKOS_FUNCTION constexpr const RealType&& get(
489  const complex<RealType>&& z) noexcept {
490  static_assert(I < 2);
491  if constexpr (I == 0)
492  return std::move(z.re_);
493  else
494  return std::move(z.im_);
495 #ifdef KOKKOS_COMPILER_INTEL
496  __builtin_unreachable();
497 #endif
498 }
499 
500 //==============================================================================
501 // <editor-fold desc="Equality and inequality"> {{{1
502 
503 // Note that this is not the same behavior as std::complex, which doesn't allow
504 // implicit conversions, but since this is the way we had it before, we have
505 // to do it this way now.
506 
508 template <class RealType1, class RealType2>
509 KOKKOS_INLINE_FUNCTION bool operator==(complex<RealType1> const& x,
510  complex<RealType2> const& y) noexcept {
511  using common_type = std::common_type_t<RealType1, RealType2>;
512  return common_type(x.real()) == common_type(y.real()) &&
513  common_type(x.imag()) == common_type(y.imag());
514 }
515 
516 // TODO (here and elsewhere) decide if we should convert to a Kokkos::complex
517 // and do the comparison in a device-marked function
519 template <class RealType1, class RealType2>
520 inline bool operator==(std::complex<RealType1> const& x,
521  complex<RealType2> const& y) noexcept {
522  using common_type = std::common_type_t<RealType1, RealType2>;
523  return common_type(x.real()) == common_type(y.real()) &&
524  common_type(x.imag()) == common_type(y.imag());
525 }
526 
528 template <class RealType1, class RealType2>
529 inline bool operator==(complex<RealType1> const& x,
530  std::complex<RealType2> const& y) noexcept {
531  using common_type = std::common_type_t<RealType1, RealType2>;
532  return common_type(x.real()) == common_type(y.real()) &&
533  common_type(x.imag()) == common_type(y.imag());
534 }
535 
537 template <
538  class RealType1, class RealType2,
539  // Constraints to avoid participation in oparator==() for every possible RHS
540  std::enable_if_t<std::is_convertible_v<RealType2, RealType1>, int> = 0>
541 KOKKOS_INLINE_FUNCTION bool operator==(complex<RealType1> const& x,
542  RealType2 const& y) noexcept {
543  using common_type = std::common_type_t<RealType1, RealType2>;
544  return common_type(x.real()) == common_type(y) &&
545  common_type(x.imag()) == common_type(0);
546 }
547 
549 template <
550  class RealType1, class RealType2,
551  // Constraints to avoid participation in oparator==() for every possible RHS
552  std::enable_if_t<std::is_convertible_v<RealType1, RealType2>, int> = 0>
553 KOKKOS_INLINE_FUNCTION bool operator==(RealType1 const& x,
554  complex<RealType2> const& y) noexcept {
555  using common_type = std::common_type_t<RealType1, RealType2>;
556  return common_type(x) == common_type(y.real()) &&
557  common_type(0) == common_type(y.imag());
558 }
559 
561 template <class RealType1, class RealType2>
562 KOKKOS_INLINE_FUNCTION bool operator!=(complex<RealType1> const& x,
563  complex<RealType2> const& y) noexcept {
564  using common_type = std::common_type_t<RealType1, RealType2>;
565  return common_type(x.real()) != common_type(y.real()) ||
566  common_type(x.imag()) != common_type(y.imag());
567 }
568 
570 template <class RealType1, class RealType2>
571 inline bool operator!=(std::complex<RealType1> const& x,
572  complex<RealType2> const& y) noexcept {
573  using common_type = std::common_type_t<RealType1, RealType2>;
574  return common_type(x.real()) != common_type(y.real()) ||
575  common_type(x.imag()) != common_type(y.imag());
576 }
577 
579 template <class RealType1, class RealType2>
580 inline bool operator!=(complex<RealType1> const& x,
581  std::complex<RealType2> const& y) noexcept {
582  using common_type = std::common_type_t<RealType1, RealType2>;
583  return common_type(x.real()) != common_type(y.real()) ||
584  common_type(x.imag()) != common_type(y.imag());
585 }
586 
588 template <
589  class RealType1, class RealType2,
590  // Constraints to avoid participation in oparator==() for every possible RHS
591  std::enable_if_t<std::is_convertible_v<RealType2, RealType1>, int> = 0>
592 KOKKOS_INLINE_FUNCTION bool operator!=(complex<RealType1> const& x,
593  RealType2 const& y) noexcept {
594  using common_type = std::common_type_t<RealType1, RealType2>;
595  return common_type(x.real()) != common_type(y) ||
596  common_type(x.imag()) != common_type(0);
597 }
598 
600 template <
601  class RealType1, class RealType2,
602  // Constraints to avoid participation in oparator==() for every possible RHS
603  std::enable_if_t<std::is_convertible_v<RealType1, RealType2>, int> = 0>
604 KOKKOS_INLINE_FUNCTION bool operator!=(RealType1 const& x,
605  complex<RealType2> const& y) noexcept {
606  using common_type = std::common_type_t<RealType1, RealType2>;
607  return common_type(x) != common_type(y.real()) ||
608  common_type(0) != common_type(y.imag());
609 }
610 
611 // </editor-fold> end Equality and inequality }}}1
612 //==============================================================================
613 
615 template <class RealType1, class RealType2>
616 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
617 operator+(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
618  return complex<std::common_type_t<RealType1, RealType2>>(x.real() + y.real(),
619  x.imag() + y.imag());
620 }
621 
623 template <class RealType1, class RealType2>
624 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
625 operator+(const complex<RealType1>& x, const RealType2& y) noexcept {
626  return complex<std::common_type_t<RealType1, RealType2>>(x.real() + y,
627  x.imag());
628 }
629 
631 template <class RealType1, class RealType2>
632 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
633 operator+(const RealType1& x, const complex<RealType2>& y) noexcept {
634  return complex<std::common_type_t<RealType1, RealType2>>(x + y.real(),
635  y.imag());
636 }
637 
639 template <class RealType>
640 KOKKOS_INLINE_FUNCTION complex<RealType> operator+(
641  const complex<RealType>& x) noexcept {
642  return complex<RealType>{+x.real(), +x.imag()};
643 }
644 
646 template <class RealType1, class RealType2>
647 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
648 operator-(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
649  return complex<std::common_type_t<RealType1, RealType2>>(x.real() - y.real(),
650  x.imag() - y.imag());
651 }
652 
654 template <class RealType1, class RealType2>
655 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
656 operator-(const complex<RealType1>& x, const RealType2& y) noexcept {
657  return complex<std::common_type_t<RealType1, RealType2>>(x.real() - y,
658  x.imag());
659 }
660 
662 template <class RealType1, class RealType2>
663 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
664 operator-(const RealType1& x, const complex<RealType2>& y) noexcept {
665  return complex<std::common_type_t<RealType1, RealType2>>(x - y.real(),
666  -y.imag());
667 }
668 
670 template <class RealType>
671 KOKKOS_INLINE_FUNCTION complex<RealType> operator-(
672  const complex<RealType>& x) noexcept {
673  return complex<RealType>(-x.real(), -x.imag());
674 }
675 
677 template <class RealType1, class RealType2>
678 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
679 operator*(const complex<RealType1>& x, const complex<RealType2>& y) noexcept {
680  return complex<std::common_type_t<RealType1, RealType2>>(
681  x.real() * y.real() - x.imag() * y.imag(),
682  x.real() * y.imag() + x.imag() * y.real());
683 }
684 
693 template <class RealType1, class RealType2>
694 inline complex<std::common_type_t<RealType1, RealType2>> operator*(
695  const std::complex<RealType1>& x, const complex<RealType2>& y) {
696  return complex<std::common_type_t<RealType1, RealType2>>(
697  x.real() * y.real() - x.imag() * y.imag(),
698  x.real() * y.imag() + x.imag() * y.real());
699 }
700 
705 template <class RealType1, class RealType2>
706 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
707 operator*(const RealType1& x, const complex<RealType2>& y) noexcept {
708  return complex<std::common_type_t<RealType1, RealType2>>(x * y.real(),
709  x * y.imag());
710 }
711 
716 template <class RealType1, class RealType2>
717 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
718 operator*(const complex<RealType1>& y, const RealType2& x) noexcept {
719  return complex<std::common_type_t<RealType1, RealType2>>(x * y.real(),
720  x * y.imag());
721 }
722 
724 template <class RealType>
725 KOKKOS_INLINE_FUNCTION RealType imag(const complex<RealType>& x) noexcept {
726  return x.imag();
727 }
728 
729 template <class ArithmeticType>
730 KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> imag(
731  ArithmeticType) {
732  return ArithmeticType();
733 }
734 
736 template <class RealType>
737 KOKKOS_INLINE_FUNCTION RealType real(const complex<RealType>& x) noexcept {
738  return x.real();
739 }
740 
741 template <class ArithmeticType>
742 KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> real(
743  ArithmeticType x) {
744  return x;
745 }
746 
748 template <class T>
749 KOKKOS_INLINE_FUNCTION complex<T> polar(const T& r, const T& theta = T()) {
750  KOKKOS_EXPECTS(r >= 0);
751  return complex<T>(r * cos(theta), r * sin(theta));
752 }
753 
755 template <class RealType>
756 KOKKOS_INLINE_FUNCTION RealType abs(const complex<RealType>& x) {
757  return hypot(x.real(), x.imag());
758 }
759 
761 template <class T>
762 KOKKOS_INLINE_FUNCTION complex<T> pow(const complex<T>& x, const T& y) {
763  T r = abs(x);
764  T theta = atan2(x.imag(), x.real());
765  return polar(pow(r, y), y * theta);
766 }
767 
768 template <class T>
769 KOKKOS_INLINE_FUNCTION complex<T> pow(const T& x, const complex<T>& y) {
770  return pow(complex<T>(x), y);
771 }
772 
773 template <class T>
774 KOKKOS_INLINE_FUNCTION complex<T> pow(const complex<T>& x,
775  const complex<T>& y) {
776  return x == T() ? T() : exp(y * log(x));
777 }
778 
779 template <class T, class U, class = std::enable_if_t<std::is_arithmetic_v<T>>>
780 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(
781  const T& x, const complex<U>& y) {
782  using type = Impl::promote_2_t<T, U>;
783  return pow(type(x), complex<type>(y));
784 }
785 
786 template <class T, class U, class = std::enable_if_t<std::is_arithmetic_v<U>>>
787 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(const complex<T>& x,
788  const U& y) {
789  using type = Impl::promote_2_t<T, U>;
790  return pow(complex<type>(x), type(y));
791 }
792 
793 template <class T, class U>
794 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(
795  const complex<T>& x, const complex<U>& y) {
796  using type = Impl::promote_2_t<T, U>;
797  return pow(complex<type>(x), complex<type>(y));
798 }
799 
802 template <class RealType>
803 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sqrt(
804  const complex<RealType>& x) {
805  RealType r = x.real();
806  RealType i = x.imag();
807 
808  if (r == RealType()) {
809  RealType t = sqrt(fabs(i) / 2);
810  return Kokkos::complex<RealType>(t, i < RealType() ? -t : t);
811  } else {
812  RealType t = sqrt(2 * (abs(x) + fabs(r)));
813  RealType u = t / 2;
814  return r > RealType() ? Kokkos::complex<RealType>(u, i / t)
815  : Kokkos::complex<RealType>(fabs(i) / t,
816  i < RealType() ? -u : u);
817  }
818 }
819 
821 template <class RealType>
822 KOKKOS_INLINE_FUNCTION complex<RealType> conj(
823  const complex<RealType>& x) noexcept {
824  return complex<RealType>(real(x), -imag(x));
825 }
826 
827 template <class ArithmeticType>
828 KOKKOS_INLINE_FUNCTION constexpr complex<Impl::promote_t<ArithmeticType>> conj(
829  ArithmeticType x) {
830  using type = Impl::promote_t<ArithmeticType>;
831  return complex<type>(x, -type());
832 }
833 
835 template <class RealType>
836 KOKKOS_INLINE_FUNCTION complex<RealType> exp(const complex<RealType>& x) {
837  return exp(x.real()) * complex<RealType>(cos(x.imag()), sin(x.imag()));
838 }
839 
841 template <class RealType>
842 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> log(
843  const complex<RealType>& x) {
844  RealType phi = atan2(x.imag(), x.real());
845  return Kokkos::complex<RealType>(log(abs(x)), phi);
846 }
847 
849 template <class RealType>
850 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> log10(
851  const complex<RealType>& x) {
852  return log(x) / log(RealType(10));
853 }
854 
856 template <class RealType>
857 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sin(
858  const complex<RealType>& x) {
859  return Kokkos::complex<RealType>(sin(x.real()) * cosh(x.imag()),
860  cos(x.real()) * sinh(x.imag()));
861 }
862 
864 template <class RealType>
865 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> cos(
866  const complex<RealType>& x) {
867  return Kokkos::complex<RealType>(cos(x.real()) * cosh(x.imag()),
868  -sin(x.real()) * sinh(x.imag()));
869 }
870 
872 template <class RealType>
873 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> tan(
874  const complex<RealType>& x) {
875  return sin(x) / cos(x);
876 }
877 
879 template <class RealType>
880 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> sinh(
881  const complex<RealType>& x) {
882  return Kokkos::complex<RealType>(sinh(x.real()) * cos(x.imag()),
883  cosh(x.real()) * sin(x.imag()));
884 }
885 
887 template <class RealType>
888 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> cosh(
889  const complex<RealType>& x) {
890  return Kokkos::complex<RealType>(cosh(x.real()) * cos(x.imag()),
891  sinh(x.real()) * sin(x.imag()));
892 }
893 
895 template <class RealType>
896 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> tanh(
897  const complex<RealType>& x) {
898  return sinh(x) / cosh(x);
899 }
900 
902 template <class RealType>
903 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> asinh(
904  const complex<RealType>& x) {
905  return log(x + sqrt(x * x + RealType(1.0)));
906 }
907 
909 template <class RealType>
910 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> acosh(
911  const complex<RealType>& x) {
912  return RealType(2.0) * log(sqrt(RealType(0.5) * (x + RealType(1.0))) +
913  sqrt(RealType(0.5) * (x - RealType(1.0))));
914 }
915 
917 template <class RealType>
918 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> atanh(
919  const complex<RealType>& x) {
920  const RealType i2 = x.imag() * x.imag();
921  const RealType r = RealType(1.0) - i2 - x.real() * x.real();
922 
923  RealType p = RealType(1.0) + x.real();
924  RealType m = RealType(1.0) - x.real();
925 
926  p = i2 + p * p;
927  m = i2 + m * m;
928 
929  RealType phi = atan2(RealType(2.0) * x.imag(), r);
930  return Kokkos::complex<RealType>(RealType(0.25) * (log(p) - log(m)),
931  RealType(0.5) * phi);
932 }
933 
935 template <class RealType>
936 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> asin(
937  const complex<RealType>& x) {
939  asinh(Kokkos::complex<RealType>(-x.imag(), x.real()));
940  return Kokkos::complex<RealType>(t.imag(), -t.real());
941 }
942 
944 template <class RealType>
945 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> acos(
946  const complex<RealType>& x) {
947  Kokkos::complex<RealType> t = asin(x);
948  RealType pi_2 = acos(RealType(0.0));
949  return Kokkos::complex<RealType>(pi_2 - t.real(), -t.imag());
950 }
951 
953 template <class RealType>
954 KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> atan(
955  const complex<RealType>& x) {
956  const RealType r2 = x.real() * x.real();
957  const RealType i = RealType(1.0) - r2 - x.imag() * x.imag();
958 
959  RealType p = x.imag() + RealType(1.0);
960  RealType m = x.imag() - RealType(1.0);
961 
962  p = r2 + p * p;
963  m = r2 + m * m;
964 
966  RealType(0.5) * atan2(RealType(2.0) * x.real(), i),
967  RealType(0.25) * log(p / m));
968 }
969 
973 template <class RealType>
974 inline complex<RealType> exp(const std::complex<RealType>& c) {
975  return complex<RealType>(std::exp(c.real()) * std::cos(c.imag()),
976  std::exp(c.real()) * std::sin(c.imag()));
977 }
978 
980 template <class RealType1, class RealType2>
981 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
982 operator/(const complex<RealType1>& x,
983  const RealType2& y) noexcept(noexcept(RealType1{} / RealType2{})) {
984  return complex<std::common_type_t<RealType1, RealType2>>(real(x) / y,
985  imag(x) / y);
986 }
987 
989 template <class RealType1, class RealType2>
990 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
991 operator/(const complex<RealType1>& x,
992  const complex<RealType2>& y) noexcept(noexcept(RealType1{} /
993  RealType2{})) {
994  // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
995  // If the real part is +/-Inf and the imaginary part is -/+Inf,
996  // this won't change the result.
997  using common_real_type = std::common_type_t<RealType1, RealType2>;
998  const common_real_type s = fabs(real(y)) + fabs(imag(y));
999 
1000  // If s is 0, then y is zero, so x/y == real(x)/0 + i*imag(x)/0.
1001  // In that case, the relation x/y == (x/s) / (y/s) doesn't hold,
1002  // because y/s is NaN.
1003  if (s == 0.0) {
1004  return complex<common_real_type>(real(x) / s, imag(x) / s);
1005  } else {
1006  const complex<common_real_type> x_scaled(real(x) / s, imag(x) / s);
1007  const complex<common_real_type> y_conj_scaled(real(y) / s, -imag(y) / s);
1008  const RealType1 y_scaled_abs =
1009  real(y_conj_scaled) * real(y_conj_scaled) +
1010  imag(y_conj_scaled) * imag(y_conj_scaled); // abs(y) == abs(conj(y))
1011  complex<common_real_type> result = x_scaled * y_conj_scaled;
1012  result /= y_scaled_abs;
1013  return result;
1014  }
1015 }
1016 
1018 template <class RealType1, class RealType2>
1019 KOKKOS_INLINE_FUNCTION complex<std::common_type_t<RealType1, RealType2>>
1020 operator/(const RealType1& x,
1021  const complex<RealType2>& y) noexcept(noexcept(RealType1{} /
1022  RealType2{})) {
1023  return complex<std::common_type_t<RealType1, RealType2>>(x) / y;
1024 }
1025 
1026 template <class RealType>
1027 std::ostream& operator<<(std::ostream& os, const complex<RealType>& x) {
1028  const std::complex<RealType> x_std(Kokkos::real(x), Kokkos::imag(x));
1029  os << x_std;
1030  return os;
1031 }
1032 
1033 template <class RealType>
1034 std::istream& operator>>(std::istream& is, complex<RealType>& x) {
1035  std::complex<RealType> x_std;
1036  is >> x_std;
1037  x = x_std; // only assigns on success of above
1038  return is;
1039 }
1040 
1041 template <class T>
1042 struct reduction_identity<Kokkos::complex<T>> {
1043  using t_red_ident = reduction_identity<T>;
1044  KOKKOS_FORCEINLINE_FUNCTION constexpr static Kokkos::complex<T>
1045  sum() noexcept {
1046  return Kokkos::complex<T>(t_red_ident::sum(), t_red_ident::sum());
1047  }
1048  KOKKOS_FORCEINLINE_FUNCTION constexpr static Kokkos::complex<T>
1049  prod() noexcept {
1050  return Kokkos::complex<T>(t_red_ident::prod(), t_red_ident::sum());
1051  }
1052 };
1053 
1054 } // namespace Kokkos
1055 
1056 #ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_COMPLEX
1057 #undef KOKKOS_IMPL_PUBLIC_INCLUDE
1058 #undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_COMPLEX
1059 #endif
1060 #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