Skip to main content

thermite_complex/math/
specialized.rs

1// The `fn name[..][..](self: Self, ..)` shape is the macro DSL's, as in
2// `thermite::math`, which allows this lint at its module root for the same reason.
3#![allow(clippy::needless_arbitrary_self_type)]
4
5//! The complex math trait family.
6//!
7//! Thermite's math families ([`CoreMath`](thermite::math::CoreMath),
8//! [`TranscendentalMath`](thermite::math::TranscendentalMath),
9//! [`SpatialMath`](thermite::math::SpatialMath),
10//! [`RealMath`](thermite::math::RealMath)) are all `Self -> Self`, a real vector
11//! having nothing else to return. A complex number does: its modulus and argument
12//! are real, and its polar form is a pair of reals. Those operations get their own
13//! family here, built the way the core ones are:
14//!
15//! - [`ComplexVector`] carries the structure: the associated real type
16//!   [`Real`](ComplexVector::Real), the component accessors, and the operations
17//!   that take no [`Policy`] (`conj`, `norm_sqr`, `inv`, `norm_l1`).
18//! - [`SpecializedComplexMath`] carries the algorithms, mirroring
19//!   [`thermite::math::specialized`].
20//! - [`ComplexMathWithPolicy`](crate::math::ComplexMathWithPolicy) and [`ComplexMath`](crate::math::ComplexMath) are generated from it by
21//!   `decl_complex_math!` (a copy of core's `decl_math!`), giving each operation a
22//!   `foo_p::<P>()` and a default-policy `foo()` form.
23//!
24//! So `z.norm_p::<Precision>()` behaves as `x.sin_p::<Precision>()` does, and
25//! generic code bounds on `V: ComplexMath` as it would on `V: TranscendentalMath`.
26
27use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
28
29use thermite::math::policy::Policy;
30use thermite::prelude::*;
31use thermite::vector::ops::{self, MulAddAssignExt, MulAddExt};
32
33use crate::vector::RealFloatVector;
34
35#[cfg(feature = "special")]
36pub use crate::math::special::SpecializedComplexSpecialMath;
37
38/// A vector of complex numbers over a real vector type.
39///
40/// The structural half of the complex math family: it names the underlying real
41/// vector ([`Real`](ComplexVector::Real)) and the operations that take no
42/// [`Policy`]. The policy-dependent ones (modulus, argument, polar form, ...) are
43/// in [`ComplexMath`](crate::math::ComplexMath).
44///
45/// # Mixed complex/real arithmetic
46///
47/// The supertraits promise the binary operators against [`Real`](Self::Real), so
48/// generic code can scale, offset and fuse by a real vector without widening it
49/// into a complex one:
50///
51/// ```
52/// use thermite::prelude::*;
53/// use thermite_complex::prelude::*;
54///
55/// // Horner evaluation of a real-coefficient polynomial at a complex point.
56/// fn horner<T: ComplexVector>(z: T, coeffs: &[T::Real]) -> T {
57///     let mut acc = T::real(coeffs[0]);
58///
59///     for &c in &coeffs[1..] {
60///         acc = acc * z + c; // Complex * Complex, then Complex + Real
61///     }
62///
63///     acc
64/// }
65///
66/// type V = Vector<f64>;
67///
68/// // z^2 + 3 at z = 1 + 2i is -3 + 4i + 3 = 4i
69/// let z = Complex::new(V::splat(1.0), V::splat(2.0));
70/// let r = horner(z, &[V::ONE, V::ZERO, V::splat(3.0)]);
71///
72/// assert_eq!((r.re.extract::<0>(), r.im.extract::<0>()), (0.0, 4.0));
73/// ```
74///
75/// `Mul`/`Div` by a real cost two real multiplies, versus the four multiplies and
76/// two adds of a complex multiply by `Complex::real(r)`, which the compiler cannot
77/// recover from the widened form.
78/// [`MulAddExt<Self::Real, Self>`](thermite::vector::ops::MulAddExt) is promised
79/// for the same reason, and is a true single-rounding FMA (one fused op per
80/// component) where the complex-by-complex FMA cannot be.
81pub trait ComplexVector:
82    FloatVector
83    + Add<Self::Real, Output = Self>
84    + Sub<Self::Real, Output = Self>
85    + Mul<Self::Real, Output = Self>
86    + Div<Self::Real, Output = Self>
87    + Rem<Self::Real, Output = Self>
88    + AddAssign<Self::Real>
89    + SubAssign<Self::Real>
90    + MulAssign<Self::Real>
91    + DivAssign<Self::Real>
92    + RemAssign<Self::Real>
93    + MulAddExt<Self::Real, Self, Output = Self>
94    + MulAddAssignExt<Self::Real, Self>
95    + ops::AddMasked<Self::Mask, Self::Real, Output = Self>
96    + ops::SubMasked<Self::Mask, Self::Real, Output = Self>
97    + ops::MulMasked<Self::Mask, Self::Real, Output = Self>
98    + ops::DivMasked<Self::Mask, Self::Real, Output = Self>
99    + ops::MulAddExtMasked<Self::Mask, Self::Real, Self, Output = Self>
100{
101    /// The real vector type of each component, in which a modulus or an argument
102    /// is measured.
103    ///
104    /// The core [`SpatialMath`] family has no such associated type, so its norms
105    /// must return `Self` and come back as real-valued *complex* numbers.
106    /// [`ComplexMath::norm`](crate::math::ComplexMath::norm) returns this instead.
107    ///
108    /// [`SpatialMath`]: thermite::math::SpatialMath
109    type Real: RealFloatVector;
110
111    /// The real part.
112    fn re(self) -> Self::Real;
113
114    /// The imaginary part.
115    fn im(self) -> Self::Real;
116
117    /// Builds a complex vector from its real and imaginary parts.
118    ///
119    /// Not spelled `new`, tempting as it is to match the inherent
120    /// [`Complex::new`](crate::Complex::new): [`GenericVector::new`] is already in
121    /// scope on every implementor and takes a lane array, so a second `new` is
122    /// ambiguous (E0034) in precisely the generic code this trait exists for.
123    /// [`real`](Self::real) has no such clash and does match its inherent twin.
124    fn from_parts(re: Self::Real, im: Self::Real) -> Self;
125
126    /// Non-temporal store of the whole block to `ptr`, in `Self`'s own memory layout (the
127    /// planar/SoA `[re | im]` layout for `Complex<V>`), bypassing the cache. This is for
128    /// **relocating blocks within a `[Self]` buffer** - e.g. an FFT transpose whose output is
129    /// too large to cache - NOT the AoS boundary (that is [`store`](thermite::prelude::GenericVector::store)
130    /// / [`store_streaming`](thermite::prelude::GenericVector::store_streaming), which interleave re/im).
131    ///
132    /// Weakly ordered: a non-temporal store is not guaranteed visible to a later load until an
133    /// `sfence`, so the caller **must fence before reading the result**. Use it only when the
134    /// destination clearly exceeds last-level cache (NT forfeits cache reuse, so it loses below
135    /// a few MB).
136    ///
137    /// The default is a plain store (correct everywhere, no NT benefit). `Complex<V>` overrides
138    /// it to stream each half with the real [`store_streaming`](thermite::prelude::GenericVector::store_streaming)
139    /// of its component vector (`_mm256_stream_ps` on AVX2; a plain store on backends without NT).
140    ///
141    /// # Safety
142    /// `ptr` must be valid for writes and aligned to `Self` (a `[Self]` slot satisfies this).
143    #[inline(always)]
144    unsafe fn store_streaming_block(self, ptr: *mut Self) {
145        unsafe { ptr.write(self) }
146    }
147
148    /// Builds a complex vector from a real part, with zero imaginary part.
149    #[inline(always)]
150    fn real(re: Self::Real) -> Self {
151        Self::from_parts(re, <Self::Real as NumericVector>::ZERO)
152    }
153
154    /// The complex conjugate `re - im*i`.
155    fn conj(self) -> Self;
156
157    /// The squared modulus `$|z|^2 = re^2 + im^2$`.
158    ///
159    /// Cheaper than [`norm`](crate::math::ComplexMath::norm) (no square root), but it squares
160    /// the range, so it overflows or underflows near the limits of the format.
161    fn norm_sqr(self) -> Self::Real;
162
163    /// The L1 ("Manhattan") norm `|re| + |im|`, a real value.
164    fn norm_l1(self) -> Self::Real;
165
166    /// The multiplicative inverse `$1/z = \bar{z}/|z|^2$`.
167    ///
168    /// Inherits the range limits of [`norm_sqr`](ComplexVector::norm_sqr); the
169    /// scaled form is [`ComplexMath::finv`](crate::math::ComplexMath::finv).
170    fn inv(self) -> Self;
171}
172
173/// Element-parameterized implementations behind [`ComplexMath`](crate::math::ComplexMath).
174///
175/// The complex counterpart of [`thermite::math::specialized`]: implementing this
176/// for a complex vector type gives it [`ComplexMath`](crate::math::ComplexMath) and
177/// [`ComplexMathWithPolicy`](crate::math::ComplexMathWithPolicy), as implementing `SpecializedTranscendentalMath`
178/// gives it `TranscendentalMath`.
179///
180/// Bound on [`ComplexMath`](crate::math::ComplexMath); this trait is for implementors.
181pub trait SpecializedComplexMath<E>: ComplexVector<Element = E> {
182    /// The modulus (magnitude) `|z|`.
183    fn norm<P: Policy>(self) -> Self::Real;
184
185    /// The principal argument `arg(z)`, in `(-pi, pi]`.
186    fn arg<P: Policy>(self) -> Self::Real;
187
188    /// Polar form `(r, theta)`, such that `self == r * exp(i*theta)`.
189    #[inline(always)]
190    fn to_polar<P: Policy>(self) -> (Self::Real, Self::Real) {
191        (self.norm::<P>(), self.arg::<P>())
192    }
193
194    /// Builds a complex number from a polar representation `r * exp(i*theta)`.
195    fn from_polar<P: Policy>(r: Self::Real, theta: Self::Real) -> Self;
196
197    /// Raises `self` to a *real* power.
198    fn powfr<P: Policy>(self, e: Self::Real) -> Self;
199
200    /// Raises a *real* base to the complex power `self`.
201    fn expf<P: Policy>(self, base: Self::Real) -> Self;
202
203    /// The logarithm of `self` in an arbitrary *real* base.
204    fn logr<P: Policy>(self, base: Self::Real) -> Self;
205
206    /// `1/self`, scaling by the modulus and not its square.
207    ///
208    /// Survives the magnitudes where [`inv`](ComplexVector::inv) would have
209    /// `norm_sqr()` overflow to infinity or underflow to zero.
210    fn finv<P: Policy>(self) -> Self;
211
212    /// `self/rhs`, scaling by the modulus and not its square.
213    ///
214    /// Survives the magnitudes where `/` would have `rhs.norm_sqr()` overflow to
215    /// infinity or underflow to zero.
216    #[inline(always)]
217    fn fdiv<P: Policy>(self, rhs: Self) -> Self {
218        self * rhs.finv::<P>()
219    }
220}
221