|
1 | 1 | // Element-wise methods for ndarray |
2 | 2 |
|
3 | 3 | #[cfg(feature = "std")] |
4 | | -use num_complex::{Complex32, Complex64}; |
| 4 | +use num_complex::Complex; |
5 | 5 | #[cfg(feature = "std")] |
6 | | -use num_traits::Float; |
| 6 | +use num_traits::{Float, Zero}; |
7 | 7 |
|
8 | 8 | use crate::imp_prelude::*; |
9 | 9 |
|
10 | | -/// Trait for types that can generalize the phase angle (argument) |
11 | | -/// calculation for both real floats and complex numbers. |
12 | | -#[cfg(feature = "std")] |
13 | | -pub trait HasAngle |
14 | | -{ |
15 | | - /// The type of the associated angle. |
16 | | - type Angle; |
17 | | - |
18 | | - /// Return the phase angle (argument) of the value |
19 | | - fn to_angle(&self) -> Self::Angle; |
20 | | -} |
21 | | - |
22 | | -#[cfg(feature = "std")] |
23 | | -impl HasAngle for f32 |
24 | | -{ |
25 | | - type Angle = Self; |
26 | | - |
27 | | - #[inline] |
28 | | - fn to_angle(&self) -> Self |
29 | | - { |
30 | | - 0f32.atan2(*self) |
31 | | - } |
32 | | -} |
33 | | - |
34 | | -#[cfg(feature = "std")] |
35 | | -impl HasAngle for f64 |
36 | | -{ |
37 | | - type Angle = Self; |
38 | | - |
39 | | - #[inline] |
40 | | - fn to_angle(&self) -> Self |
41 | | - { |
42 | | - 0f64.atan2(*self) |
43 | | - } |
44 | | -} |
45 | | - |
46 | | -#[cfg(feature = "std")] |
47 | | -impl HasAngle for Complex32 |
48 | | -{ |
49 | | - type Angle = f32; |
50 | | - |
51 | | - #[inline] |
52 | | - fn to_angle(&self) -> Self::Angle |
53 | | - { |
54 | | - self.im.atan2(self.re) |
55 | | - } |
56 | | -} |
57 | | - |
58 | | -#[cfg(feature = "std")] |
59 | | -impl HasAngle for Complex64 |
60 | | -{ |
61 | | - type Angle = f64; |
62 | | - |
63 | | - #[inline] |
64 | | - fn to_angle(&self) -> Self::Angle |
65 | | - { |
66 | | - self.im.atan2(self.re) |
67 | | - } |
68 | | -} |
69 | | - |
70 | 10 | #[cfg(feature = "std")] |
71 | 11 | macro_rules! boolean_ops { |
72 | 12 | ($(#[$meta1:meta])* fn $func:ident |
@@ -229,20 +169,71 @@ where |
229 | 169 | } |
230 | 170 | } |
231 | 171 |
|
| 172 | +#[cfg(feature = "std")] |
| 173 | +impl<A, D> ArrayRef<A, D> |
| 174 | +where |
| 175 | + D: Dimension, |
| 176 | + A: Clone + Zero, |
| 177 | +{ |
| 178 | + /// Map the array into the real part of a complex array; the imaginary part is 0. |
| 179 | + /// |
| 180 | + /// # Example |
| 181 | + /// ``` |
| 182 | + /// use ndarray::*; |
| 183 | + /// |
| 184 | + /// let arr = array![1.0, -1.0, 0.0]; |
| 185 | + /// let complex = arr.to_complex_re(); |
| 186 | + /// |
| 187 | + /// assert_eq!(complex[0], Complex::new(1.0, 0.0)); |
| 188 | + /// assert_eq!(complex[1], Complex::new(-1.0, 0.0)); |
| 189 | + /// assert_eq!(complex[2], Complex::new(0.0, 0.0)); |
| 190 | + /// ``` |
| 191 | + /// |
| 192 | + /// # See Also |
| 193 | + /// [ArrayRef::to_complex_im] |
| 194 | + #[must_use = "method returns a new array and does not mutate the original value"] |
| 195 | + pub fn to_complex_re(&self) -> Array<Complex<A>, D> |
| 196 | + { |
| 197 | + self.mapv(|v| Complex::new(v, A::zero())) |
| 198 | + } |
| 199 | + |
| 200 | + /// Map the array into the imaginary part of a complex array; the real part is 0. |
| 201 | + /// |
| 202 | + /// # Example |
| 203 | + /// ``` |
| 204 | + /// use ndarray::*; |
| 205 | + /// |
| 206 | + /// let arr = array![1.0, -1.0, 0.0]; |
| 207 | + /// let complex = arr.to_complex_re(); |
| 208 | + /// |
| 209 | + /// assert_eq!(complex[0], Complex::new(0.0, 1.0)); |
| 210 | + /// assert_eq!(complex[1], Complex::new(0.0, -1.0)); |
| 211 | + /// assert_eq!(complex[2], Complex::new(0.0, 0.0)); |
| 212 | + /// ``` |
| 213 | + /// |
| 214 | + /// # See Also |
| 215 | + /// [ArrayRef::to_complex_re] |
| 216 | + #[must_use = "method returns a new array and does not mutate the original value"] |
| 217 | + pub fn to_complex_im(&self) -> Array<Complex<A>, D> |
| 218 | + { |
| 219 | + self.mapv(|v| Complex::new(A::zero(), v)) |
| 220 | + } |
| 221 | +} |
| 222 | + |
232 | 223 | /// # Angle calculation methods for arrays |
233 | 224 | /// |
234 | 225 | /// Methods for calculating phase angles of complex values in arrays. |
235 | 226 | #[cfg(feature = "std")] |
236 | | -impl<A, D> ArrayRef<A, D> |
| 227 | +impl<A, D> ArrayRef<Complex<A>, D> |
237 | 228 | where |
238 | 229 | D: Dimension, |
239 | | - A: HasAngle, |
| 230 | + A: Float, |
240 | 231 | { |
241 | 232 | /// Return the [phase angle (argument)](https://en.wikipedia.org/wiki/Argument_(complex_analysis)) of complex values in the array. |
242 | 233 | /// |
243 | 234 | /// This function always returns the same float type as was provided to it. Leaving the exact precision left to the user. |
244 | 235 | /// The angles are returned in ``radians`` and in the range ``(-π, π]``. |
245 | | - /// To get the angles in degrees, use the `to_degrees()` method on the resulting array. |
| 236 | + /// To get the angles in degrees, use the [`to_degrees()`][ArrayRef::to_degrees] method on the resulting array. |
246 | 237 | /// |
247 | 238 | /// # Examples |
248 | 239 | /// |
@@ -271,9 +262,9 @@ where |
271 | 262 | /// assert!((angles[2] - PI/4.0).abs() < 1e-10); |
272 | 263 | /// ``` |
273 | 264 | #[must_use = "method returns a new array and does not mutate the original value"] |
274 | | - pub fn angle(&self) -> Array<A::Angle, D> |
| 265 | + pub fn angle(&self) -> Array<A, D> |
275 | 266 | { |
276 | | - self.map(A::to_angle) |
| 267 | + self.mapv(|v| v.im.atan2(v.re)) |
277 | 268 | } |
278 | 269 | } |
279 | 270 |
|
@@ -327,28 +318,6 @@ mod angle_tests |
327 | 318 | }}; |
328 | 319 | } |
329 | 320 |
|
330 | | - #[test] |
331 | | - fn test_real_numbers_radians() |
332 | | - { |
333 | | - let arr = Array::from_vec(vec![1.0f64, -1.0, 0.0]); |
334 | | - let angles = arr.angle(); |
335 | | - |
336 | | - assert_approx_eq!(angles[0], 0.0, 1e-10, "angle(1.0) should be 0"); |
337 | | - assert_approx_eq!(angles[1], PI, 1e-10, "angle(-1.0) should be π"); |
338 | | - assert_approx_eq!(angles[2], 0.0, 1e-10, "angle(0.0) should be 0"); |
339 | | - } |
340 | | - |
341 | | - #[test] |
342 | | - fn test_real_numbers_degrees() |
343 | | - { |
344 | | - let arr = Array::from_vec(vec![1.0f64, -1.0, 0.0]); |
345 | | - let angles_deg = arr.angle().to_degrees(); |
346 | | - |
347 | | - assert_approx_eq!(angles_deg[0], 0.0, 1e-10, "angle(1.0) should be 0°"); |
348 | | - assert_approx_eq!(angles_deg[1], 180.0, 1e-10, "angle(-1.0) should be 180°"); |
349 | | - assert_approx_eq!(angles_deg[2], 0.0, 1e-10, "angle(0.0) should be 0°"); |
350 | | - } |
351 | | - |
352 | 321 | #[test] |
353 | 322 | fn test_complex_numbers_radians() |
354 | 323 | { |
@@ -421,18 +390,6 @@ mod angle_tests |
421 | 390 | assert_approx_eq!(a[3], -PI / 2.0, 1e-10); |
422 | 391 | } |
423 | 392 |
|
424 | | - #[test] |
425 | | - fn test_mixed_precision() |
426 | | - { |
427 | | - let arr_f32 = Array::from_vec(vec![1.0f32, -1.0f32]); |
428 | | - let arr_f64 = Array::from_vec(vec![1.0f64, -1.0f64]); |
429 | | - let a32 = arr_f32.angle(); |
430 | | - let a64 = arr_f64.angle(); |
431 | | - |
432 | | - assert_approx_eq!(a32[0] as f64, a64[0], 1e-6); |
433 | | - assert_approx_eq!(a32[1] as f64, a64[1], 1e-6); |
434 | | - } |
435 | | - |
436 | 393 | #[test] |
437 | 394 | fn test_range_validation() |
438 | 395 | { |
|
0 commit comments