polydat_core/numeric/
vector.rs1pub fn dot_scalar(a: &[f32], b: &[f32]) -> f32 {
14 a.iter().zip(b).map(|(x, y)| x * y).sum()
15}
16
17pub fn l2sq_scalar(a: &[f32], b: &[f32]) -> f32 {
19 a.iter().zip(b).map(|(x, y)| (x - y) * (x - y)).sum()
20}
21
22pub fn check_lens(name: &str, a: usize, b: usize) {
24 if a != b {
25 panic!("{name}: operand lengths differ ({a} vs {b})");
26 }
27}
28
29pub fn dot_f32(a: &[f32], b: &[f32]) -> f32 {
39 #[cfg(feature = "jit")]
40 if let Some(k) = crate::compile::jit::simd::kernels() {
41 return unsafe { (k.dot_f32)(a.as_ptr(), b.as_ptr(), a.len() as u64) };
44 }
45 dot_scalar(a, b)
46}
47
48pub fn l2sq_f32(a: &[f32], b: &[f32]) -> f32 {
50 #[cfg(feature = "jit")]
51 if let Some(k) = crate::compile::jit::simd::kernels() {
52 return unsafe { (k.l2sq_f32)(a.as_ptr(), b.as_ptr(), a.len() as u64) };
53 }
54 l2sq_scalar(a, b)
55}
56
57pub fn add_f32_into(a: &[f32], b: &[f32], out: &mut Vec<f32>) {
59 out.clear();
60 out.resize(a.len(), 0.0);
61 #[cfg(feature = "jit")]
62 if let Some(k) = crate::compile::jit::simd::kernels() {
63 unsafe { (k.add_f32)(a.as_ptr(), b.as_ptr(), out.as_mut_ptr(), a.len() as u64) };
65 return;
66 }
67 for i in 0..a.len() {
68 out[i] = a[i] + b[i];
69 }
70}
71
72pub fn scale_f32_into(a: &[f32], k_val: f32, out: &mut Vec<f32>) {
74 out.clear();
75 out.resize(a.len(), 0.0);
76 #[cfg(feature = "jit")]
77 if let Some(k) = crate::compile::jit::simd::kernels() {
78 unsafe { (k.scale_f32)(a.as_ptr(), k_val, out.as_mut_ptr(), a.len() as u64) };
79 return;
80 }
81 for i in 0..a.len() {
82 out[i] = a[i] * k_val;
83 }
84}
85
86pub fn add_f32(a: &[f32], b: &[f32]) -> Vec<f32> {
88 let mut out = Vec::new();
89 add_f32_into(a, b, &mut out);
90 out
91}
92
93pub fn scale_f32(a: &[f32], k_val: f32) -> Vec<f32> {
95 let mut out = Vec::new();
96 scale_f32_into(a, k_val, &mut out);
97 out
98}
99
100pub fn norm_f32_into(a: &[f32], out: &mut Vec<f32>) {
103 let mag = (dot_f32(a, a) as f64).sqrt();
104 if mag == 0.0 {
105 out.clear();
106 out.extend_from_slice(a);
107 } else {
108 scale_f32_into(a, (1.0 / mag) as f32, out);
109 }
110}
111
112pub fn cosine_f32(a: &[f32], b: &[f32]) -> f64 {
114 let dot = dot_f32(a, b) as f64;
115 let na = (dot_f32(a, a) as f64).sqrt();
116 let nb = (dot_f32(b, b) as f64).sqrt();
117 if na == 0.0 || nb == 0.0 {
118 0.0
119 } else {
120 dot / (na * nb)
121 }
122}
123
124pub fn lid_mle_of(distances: &[f32], k: f64) -> f64 {
126 let k = (k as usize).min(distances.len());
127 if k < 2 {
128 return 0.0;
129 }
130 let r_k = distances[k - 1] as f64;
131 if r_k <= 0.0 {
132 return 0.0;
133 }
134 let ln_rk = r_k.ln();
135 let mut logsum = 0.0_f64;
136 let mut terms = 0u32;
137 for &r_j in &distances[..k - 1] {
138 let r_j = r_j as f64;
139 if r_j > 0.0 {
140 logsum += ln_rk - r_j.ln(); terms += 1;
142 }
143 }
144 if terms == 0 || logsum <= 0.0 {
145 0.0
146 } else {
147 terms as f64 / logsum
148 }
149}
150
151pub fn hash_vec_into(seed: u64, dim: u64, out: &mut Vec<f32>) {
153 let dim = dim as usize;
154 out.clear();
155 out.reserve(dim);
156 for i in 0..dim {
157 let h = crate::numeric::hash::splitmix64_u64(
158 seed.wrapping_add((i as u64).wrapping_mul(0x9e3779b97f4a7c15)),
159 );
160 out.push((h as f64 / u64::MAX as f64 * 2.0 - 1.0) as f32);
161 }
162}
163
164pub fn xxhash3_vec_into(seed: u64, dim: u64, out: &mut Vec<f32>) {
166 let dim = dim as usize;
167 out.clear();
168 out.reserve(dim);
169 for i in 0..dim {
170 let mut key = [0u8; 16];
171 key[..8].copy_from_slice(&seed.to_le_bytes());
172 key[8..].copy_from_slice(&(i as u64).to_le_bytes());
173 let h = xxhash_rust::xxh3::xxh3_64(&key);
174 out.push((h as f64 / u64::MAX as f64 * 2.0 - 1.0) as f32);
175 }
176}