trueno/vector/ops/transforms/math.rs
1//! Mathematical transformation operations (sqrt, recip, pow)
2
3#[allow(unused_imports)]
4use crate::backends::VectorBackend;
5use crate::dispatch_unary_op;
6use crate::{Result, Vector};
7
8impl Vector<f32> {
9 /// Element-wise square root: result\[i\] = sqrt(self\[i\])
10 ///
11 /// Computes the square root of each element. For negative values, returns NaN
12 /// following IEEE 754 floating-point semantics.
13 ///
14 /// # Returns
15 ///
16 /// A new vector where each element is the square root of the corresponding input element
17 ///
18 /// # Examples
19 ///
20 /// ```
21 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
22 /// use trueno::Vector;
23 ///
24 /// let a = Vector::from_slice(&[4.0, 9.0, 16.0, 25.0]);
25 /// let result = a.sqrt()?;
26 /// assert_eq!(result.as_slice(), &[2.0, 3.0, 4.0, 5.0]);
27 /// # Ok(())
28 /// # }
29 /// ```
30 ///
31 /// Negative values produce NaN:
32 /// ```
33 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
34 /// use trueno::Vector;
35 ///
36 /// let a = Vector::from_slice(&[-1.0, 4.0]);
37 /// let result = a.sqrt()?;
38 /// assert!(result.as_slice()[0].is_nan());
39 /// assert_eq!(result.as_slice()[1], 2.0);
40 /// # Ok(())
41 /// # }
42 /// ```
43 ///
44 /// # Use Cases
45 ///
46 /// - Distance calculations: Euclidean distance computation
47 /// - Statistics: Standard deviation, RMS (root mean square)
48 /// - Machine learning: Normalization, gradient descent with adaptive learning rates
49 /// - Signal processing: Amplitude calculations, power spectrum analysis
50 /// - Physics simulations: Velocity from kinetic energy, wave propagation
51 pub fn sqrt(&self) -> Result<Vector<f32>> {
52 // Uninit allocation: dispatch_unary_op!(sqrt) writes every element.
53 let n = self.len();
54 let mut result_data: Vec<f32> = Vec::with_capacity(n);
55 // SAFETY: dispatch_unary_op writes result_data[i] = sqrt(input[i]) for all i.
56 unsafe {
57 result_data.set_len(n);
58 }
59
60 if !self.as_slice().is_empty() {
61 // Use parallel processing for large arrays
62 #[cfg(feature = "parallel")]
63 {
64 const PARALLEL_THRESHOLD: usize = 100_000;
65 const CHUNK_SIZE: usize = 65536;
66
67 if self.len() >= PARALLEL_THRESHOLD {
68 use rayon::prelude::*;
69
70 self.as_slice()
71 .par_chunks(CHUNK_SIZE)
72 .zip(result_data.par_chunks_mut(CHUNK_SIZE))
73 .for_each(|(chunk_in, chunk_out)| {
74 dispatch_unary_op!(self.backend(), sqrt, chunk_in, chunk_out);
75 });
76
77 return Ok(Vector { data: result_data, backend: self.backend() });
78 }
79 }
80
81 dispatch_unary_op!(self.backend(), sqrt, self.as_slice(), &mut result_data);
82 }
83
84 Ok(Vector { data: result_data, backend: self.backend() })
85 }
86
87 /// Element-wise reciprocal: result\[i\] = 1 / self\[i\]
88 ///
89 /// Computes the reciprocal (multiplicative inverse) of each element.
90 /// For zero values, returns infinity following IEEE 754 floating-point semantics.
91 ///
92 /// # Returns
93 ///
94 /// A new vector where each element is the reciprocal of the corresponding input element
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use trueno::Vector;
100 ///
101 /// let a = Vector::from_slice(&[2.0, 4.0, 5.0, 10.0]);
102 /// let result = a.recip().unwrap();
103 /// assert_eq!(result.as_slice(), &[0.5, 0.25, 0.2, 0.1]);
104 /// ```
105 ///
106 /// Zero values produce infinity:
107 /// ```
108 /// use trueno::Vector;
109 ///
110 /// let a = Vector::from_slice(&[0.0, 2.0]);
111 /// let result = a.recip().unwrap();
112 /// assert!(result.as_slice()[0].is_infinite());
113 /// assert_eq!(result.as_slice()[1], 0.5);
114 /// ```
115 ///
116 /// # Use Cases
117 ///
118 /// - Division optimization: `a / b` -> `a * recip(b)` (multiplication is faster)
119 /// - Neural networks: Learning rate schedules, weight normalization
120 /// - Statistics: Harmonic mean calculations, inverse transformations
121 /// - Physics: Resistance (R = 1/G), optical power (P = 1/f)
122 /// - Signal processing: Frequency to period conversion, filter design
123 pub fn recip(&self) -> Result<Vector<f32>> {
124 // Uninit allocation: dispatch_unary_op!(recip) writes every element.
125 let n = self.len();
126 let mut result_data: Vec<f32> = Vec::with_capacity(n);
127 // SAFETY: dispatch_unary_op writes result_data[i] = 1/input[i] for all i.
128 unsafe {
129 result_data.set_len(n);
130 }
131
132 if !self.as_slice().is_empty() {
133 dispatch_unary_op!(self.backend(), recip, self.as_slice(), &mut result_data);
134 }
135
136 Ok(Vector { data: result_data, backend: self.backend() })
137 }
138
139 /// Element-wise power: result\[i\] = base\[i\]^n
140 ///
141 /// Raises each element to the given power `n`.
142 /// Uses Rust's optimized f32::powf() method.
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// use trueno::Vector;
148 ///
149 /// let v = Vector::from_slice(&[2.0, 3.0, 4.0]);
150 /// let squared = v.pow(2.0).unwrap();
151 /// assert_eq!(squared.as_slice(), &[4.0, 9.0, 16.0]);
152 ///
153 /// let sqrt = v.pow(0.5).unwrap(); // Fractional power = root
154 /// ```
155 ///
156 /// # Special Cases
157 ///
158 /// - `x.pow(0.0)` returns 1.0 for all x (even x=0)
159 /// - `x.pow(1.0)` returns x (identity)
160 /// - `x.pow(-1.0)` returns 1/x (reciprocal)
161 /// - `x.pow(0.5)` returns sqrt(x) (square root)
162 ///
163 /// # Applications
164 ///
165 /// - Statistics: Power transformations (Box-Cox, Yeo-Johnson)
166 /// - Machine learning: Polynomial features, activation functions
167 /// - Physics: Inverse square law (1/r^2), power laws
168 /// - Signal processing: Power spectral density, root mean square
169 pub fn pow(&self, n: f32) -> Result<Vector<f32>> {
170 let pow_data: Vec<f32> = self.as_slice().iter().map(|x| x.powf(n)).collect();
171 Ok(Vector::from_vec(pow_data))
172 }
173}