| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| use ndarray::{s, Array1, Array2}; |
|
|
| |
|
|
| |
| |
| fn mother_gaussian(t: f64, sigma0: f64) -> f64 { |
| let norm = 1.0 / (2.0 * std::f64::consts::PI * sigma0.powi(2)).powf(0.25); |
| norm * (-0.5 * t * t / (sigma0 * sigma0)).exp() |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| pub fn build_dictionary( |
| sigma0: f64, |
| a_min: f64, |
| a_max: f64, |
| b_min: f64, |
| b_max: f64, |
| m: usize, |
| t_start: f64, |
| t_end: f64, |
| dt: f64, |
| ) -> Array2<f64> { |
| let n = ((t_end - t_start) / dt).ceil() as usize; |
| let mut psi = Array2::<f64>::zeros((n, m)); |
|
|
| let log_a_min = a_min.ln(); |
| let log_a_max = a_max.ln(); |
| let log_a_step = (log_a_max - log_a_min) / ((m / 2) as f64); |
|
|
| for i in 0..m { |
| |
| let a = if i < m / 2 { |
| (log_a_min + i as f64 * log_a_step).exp() |
| } else { |
| -((log_a_min + (m - 1 - i) as f64 * log_a_step).exp()) |
| }; |
|
|
| |
| let b = b_min + (i as f64) * (b_max - b_min) / ((m - 1) as f64); |
|
|
| |
| let scale = 1.0 / a.abs().sqrt(); |
|
|
| |
| for k in 0..n { |
| let t = t_start + k as f64 * dt; |
| let arg = (t - b) / a; |
| let phi_val = mother_gaussian(arg, sigma0); |
| psi[[k, i]] = scale * phi_val; |
| } |
| } |
|
|
| psi |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| pub fn latent_to_waveform( |
| z: &Array1<f64>, |
| W: &Array2<f64>, |
| psi: &Array2<f64>, |
| ) -> Array1<f64> { |
| |
| let c = if W.ncols() == z.len() { |
| W.dot(z) |
| } else { |
| z.to_owned() |
| }; |
|
|
| |
| psi.dot(&c) |
| } |
|
|
| |
|
|
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use ndarray::Random; |
|
|
| #[test] |
| fn test_linearity() { |
| let sigma0 = 1.0; |
| let (a_min, a_max) = (0.5, 2.0); |
| let (b_min, b_max) = (-5.0, 5.0); |
| let m = 32; |
| let (t_start, t_end, dt) = (-10.0, 10.0, 0.1); |
|
|
| let psi = build_dictionary(sigma0, a_min, a_max, b_min, b_max, m, t_start, t_end, dt); |
| let W = Array2::<f64>::eye(m); |
|
|
| let z1 = Array1::<f64>::random(m, rand::distributions::Uniform::new(-1.0, 1.0)); |
| let z2 = Array1::<f64>::random(m, rand::distributions::Uniform::new(-1.0, 1.0)); |
|
|
| let alpha = 2.5; |
| let beta = -1.3; |
|
|
| let lhs = latent_to_waveform(&(alpha * &z1 + beta * &z2), &W, &psi); |
| let rhs = alpha * latent_to_waveform(&z1, &W, &psi) |
| + beta * latent_to_waveform(&z2, &W, &psi); |
|
|
| let diff = (&lhs - &rhs).mapv(|x| x.abs()).sum(); |
| assert!(diff < 1e-10, "Linearity test failed: diff = {}", diff); |
| } |
|
|
| #[test] |
| fn test_identity_mapping() { |
| let sigma0 = 1.0; |
| let (a_min, a_max) = (0.5, 2.0); |
| let (b_min, b_max) = (-5.0, 5.0); |
| let m = 16; |
| let (t_start, t_end, dt) = (-10.0, 10.0, 0.1); |
|
|
| let psi = build_dictionary(sigma0, a_min, a_max, b_min, b_max, m, t_start, t_end, dt); |
| let W = Array2::<f64>::eye(m); |
|
|
| let z = Array1::<f64>::random(m, rand::distributions::Uniform::new(-1.0, 1.0)); |
| let x = latent_to_waveform(&z, &W, &psi); |
|
|
| |
| assert_eq!(x.len(), psi.nrows()); |
| } |
|
|
| #[test] |
| fn test_energy_bounds() { |
| let sigma0 = 1.0; |
| let (a_min, a_max) = (0.5, 2.0); |
| let (b_min, b_max) = (-5.0, 5.0); |
| let m = 64; |
| let (t_start, t_end, dt) = (-10.0, 10.0, 0.01); |
|
|
| let psi = build_dictionary(sigma0, a_min, a_max, b_min, b_max, m, t_start, t_end, dt); |
| let W = Array2::<f64>::eye(m); |
|
|
| let z = Array1::<f64>::random(m, rand::distributions::Uniform::new(-1.0, 1.0)); |
| let x = latent_to_waveform(&z, &W, &psi); |
|
|
| let energy_x = x.mapv(|v| v * v).sum() * dt; |
| let energy_z = z.mapv(|v| v * v).sum(); |
|
|
| |
| let ratio = energy_x / energy_z; |
| assert!(ratio > 0.0 && ratio.is_finite(), "Energy ratio invalid: {}", ratio); |
| } |
| } |
|
|
| |
|
|
| fn main() { |
| let sigma0 = 1.0; |
| let (a_min, a_max) = (0.5, 2.0); |
| let (b_min, b_max) = (-5.0, 5.0); |
| let m = 64; |
| let (t_start, t_end, dt) = (-10.0, 10.0, 0.01); |
| let d = m; |
|
|
| println!("LW-LGM: Latent-to-Waveform Linear Geometric Map"); |
| println!("================================================"); |
| println!("Parameters:"); |
| println!(" Οβ = {}", sigma0); |
| println!(" a β [{}, {}]", a_min, a_max); |
| println!(" b β [{}, {}]", b_min, b_max); |
| println!(" m = {} atoms", m); |
| println!(" t β [{}, {}] dt={}", t_start, t_end, dt); |
| println!(); |
|
|
| |
| let psi = build_dictionary(sigma0, a_min, a_max, b_min, b_max, m, t_start, t_end, dt); |
| println!("Dictionary Ξ¨: {}Γ{}", psi.nrows(), psi.ncols()); |
|
|
| |
| let W = Array2::<f64>::eye(m); |
|
|
| |
| let z = Array1::<f64>::random(m, rand::distributions::Uniform::new(-1.0, 1.0)); |
| println!("Latent z: {} dimensions", z.len()); |
|
|
| |
| let x = latent_to_waveform(&z, &W, &psi); |
| println!("Output x: {} samples", x.len()); |
| println!("x[0..10] = {:?}", x.slice(s![0..10]).to_vec()); |
|
|
| let energy = x.mapv(|v| v * v).sum() * dt; |
| println!("Signal energy: {:.6}", energy); |
| } |
|
|