Skip to main content

trueno/vector/ops/transcendental/
exp_log.rs

1//! Exponential and logarithmic functions: `exp`, `ln`, `log2`, `log10`
2
3use crate::backends::VectorBackend;
4use crate::vector::Vector;
5use crate::{dispatch_unary_op, Result};
6
7impl Vector<f32> {
8    /// Element-wise exponential: result\[i\] = e^x\[i\]
9    ///
10    /// Computes the natural exponential (e^x) for each element.
11    /// Uses Rust's optimized f32::exp() method.
12    ///
13    /// # Examples
14    ///
15    /// ```
16    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
17    /// use trueno::Vector;
18    ///
19    /// let v = Vector::from_slice(&[0.0, 1.0, 2.0]);
20    /// let result = v.exp()?;
21    /// // result ≈ [1.0, 2.718, 7.389]
22    /// # Ok(())
23    /// # }
24    /// ```
25    ///
26    /// # Special Cases
27    ///
28    /// - `exp(0.0)` returns 1.0
29    /// - `exp(1.0)` returns e ≈ 2.71828
30    /// - `exp(-∞)` returns 0.0
31    /// - `exp(+∞)` returns +∞
32    ///
33    /// # Applications
34    ///
35    /// - Machine learning: Softmax activation, sigmoid, exponential loss
36    /// - Statistics: Exponential distribution, log-normal distribution
37    /// - Physics: Radioactive decay, population growth models
38    /// - Signal processing: Exponential smoothing, envelope detection
39    /// - Numerical methods: Solving differential equations
40    pub fn exp(&self) -> Result<Vector<f32>> {
41        // Uninit: backend writes every element before any read.
42        let n = self.len();
43        let mut result_data: Vec<f32> = Vec::with_capacity(n);
44        // SAFETY: Backend writes all elements before any read.
45        unsafe {
46            result_data.set_len(n);
47        }
48
49        if !self.data.is_empty() {
50            // Use parallel processing for large arrays
51            #[cfg(feature = "parallel")]
52            {
53                const PARALLEL_THRESHOLD: usize = 100_000;
54                const CHUNK_SIZE: usize = 65536;
55
56                if self.len() >= PARALLEL_THRESHOLD {
57                    use rayon::prelude::*;
58
59                    self.data
60                        .par_chunks(CHUNK_SIZE)
61                        .zip(result_data.par_chunks_mut(CHUNK_SIZE))
62                        .for_each(|(chunk_in, chunk_out)| {
63                            dispatch_unary_op!(self.backend, exp, chunk_in, chunk_out);
64                        });
65
66                    return Ok(Vector { data: result_data, backend: self.backend });
67                }
68            }
69
70            dispatch_unary_op!(self.backend, exp, &self.data, &mut result_data);
71        }
72
73        Ok(Vector { data: result_data, backend: self.backend })
74    }
75
76    /// Element-wise natural logarithm: result\[i\] = ln(x\[i\])
77    ///
78    /// Computes the natural logarithm (base e) for each element.
79    /// Uses Rust's optimized f32::ln() method.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
85    /// use trueno::Vector;
86    ///
87    /// let v = Vector::from_slice(&[1.0, std::f32::consts::E, std::f32::consts::E.powi(2)]);
88    /// let result = v.ln()?;
89    /// // result ≈ [0.0, 1.0, 2.0]
90    /// # Ok(())
91    /// # }
92    /// ```
93    ///
94    /// # Special Cases
95    ///
96    /// - `ln(1.0)` returns 0.0
97    /// - `ln(e)` returns 1.0
98    /// - `ln(x)` for x ≤ 0 returns NaN
99    /// - `ln(0.0)` returns -∞
100    /// - `ln(+∞)` returns +∞
101    ///
102    /// # Applications
103    ///
104    /// - Machine learning: Log loss, log-likelihood, softmax normalization
105    /// - Statistics: Log-normal distribution, log transformation for skewed data
106    /// - Information theory: Entropy calculation, mutual information
107    /// - Economics: Log returns, elasticity calculations
108    /// - Signal processing: Decibel conversion, log-frequency analysis
109    pub fn ln(&self) -> Result<Vector<f32>> {
110        // Uninit: backend writes every element before any read.
111        let n = self.len();
112        let mut result_data: Vec<f32> = Vec::with_capacity(n);
113        // SAFETY: Backend writes all elements before any read.
114        unsafe {
115            result_data.set_len(n);
116        }
117
118        if !self.data.is_empty() {
119            dispatch_unary_op!(self.backend, ln, &self.data, &mut result_data);
120        }
121
122        Ok(Vector { data: result_data, backend: self.backend })
123    }
124
125    /// Element-wise base-2 logarithm: result\[i\] = log₂(x\[i\])
126    ///
127    /// Computes the base-2 logarithm for each element.
128    /// Uses Rust's optimized f32::log2() method.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
134    /// use trueno::Vector;
135    ///
136    /// let v = Vector::from_slice(&[1.0, 2.0, 4.0, 8.0]);
137    /// let result = v.log2()?;
138    /// // result ≈ [0.0, 1.0, 2.0, 3.0]
139    /// # Ok(())
140    /// # }
141    /// ```
142    ///
143    /// # Special Cases
144    ///
145    /// - `log2(1.0)` returns 0.0
146    /// - `log2(2.0)` returns 1.0
147    /// - `log2(x)` for x ≤ 0 returns NaN
148    /// - `log2(0.0)` returns -∞
149    /// - `log2(+∞)` returns +∞
150    ///
151    /// # Applications
152    ///
153    /// - Information theory: Entropy in bits, mutual information
154    /// - Computer science: Bit manipulation, binary search complexity
155    /// - Audio: Octave calculations, pitch detection
156    /// - Data compression: Huffman coding, arithmetic coding
157    pub fn log2(&self) -> Result<Vector<f32>> {
158        // Uninit: backend writes every element before any read.
159        let n = self.len();
160        let mut result_data: Vec<f32> = Vec::with_capacity(n);
161        // SAFETY: Backend writes all elements before any read.
162        unsafe {
163            result_data.set_len(n);
164        }
165
166        if !self.data.is_empty() {
167            dispatch_unary_op!(self.backend, log2, &self.data, &mut result_data);
168        }
169
170        Ok(Vector { data: result_data, backend: self.backend })
171    }
172
173    /// Element-wise base-10 logarithm: result\[i\] = log₁₀(x\[i\])
174    ///
175    /// Computes the base-10 (common) logarithm for each element.
176    /// Uses Rust's optimized f32::log10() method.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
182    /// use trueno::Vector;
183    ///
184    /// let v = Vector::from_slice(&[1.0, 10.0, 100.0, 1000.0]);
185    /// let result = v.log10()?;
186    /// // result ≈ [0.0, 1.0, 2.0, 3.0]
187    /// # Ok(())
188    /// # }
189    /// ```
190    ///
191    /// # Special Cases
192    ///
193    /// - `log10(1.0)` returns 0.0
194    /// - `log10(10.0)` returns 1.0
195    /// - `log10(x)` for x ≤ 0 returns NaN
196    /// - `log10(0.0)` returns -∞
197    /// - `log10(+∞)` returns +∞
198    ///
199    /// # Applications
200    ///
201    /// - Audio: Decibel calculations (dB = 20 * log10(amplitude))
202    /// - Chemistry: pH calculations (-log10(H+ concentration))
203    /// - Seismology: Richter scale
204    /// - Scientific notation: Order of magnitude calculations
205    pub fn log10(&self) -> Result<Vector<f32>> {
206        // Uninit: backend writes every element before any read.
207        let n = self.len();
208        let mut result_data: Vec<f32> = Vec::with_capacity(n);
209        // SAFETY: Backend writes all elements before any read.
210        unsafe {
211            result_data.set_len(n);
212        }
213
214        if !self.data.is_empty() {
215            dispatch_unary_op!(self.backend, log10, &self.data, &mut result_data);
216        }
217
218        Ok(Vector { data: result_data, backend: self.backend })
219    }
220}