1use crate::op_registry::{CpuKernel, CpuTensorMut, CpuTensorRef};
17use crate::spd;
18use rlx_ir::SpdMatFn;
19
20fn dim(t: &CpuTensorRef<'_>, i: usize) -> usize {
22 t.shape().dim(i).unwrap_static()
23}
24
25pub struct BiMapKernel;
27impl CpuKernel for BiMapKernel {
28 fn name(&self) -> &str {
29 "core.bimap"
30 }
31 fn execute(
32 &self,
33 inputs: &[CpuTensorRef<'_>],
34 output: CpuTensorMut<'_>,
35 _attrs: &[u8],
36 ) -> Result<(), String> {
37 let w = inputs[0].expect_f64("bimap W")?;
38 let x = inputs[1].expect_f64("bimap X")?;
39 let (m, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
40 let out = output.expect_f64_mut("bimap Y")?;
41 out.copy_from_slice(&spd::bimap(w, x, m, n));
42 Ok(())
43 }
44}
45
46pub struct ReEigKernel {
50 pub eps: f64,
51}
52impl CpuKernel for ReEigKernel {
53 fn name(&self) -> &str {
54 "core.reeig"
55 }
56 fn execute(
57 &self,
58 inputs: &[CpuTensorRef<'_>],
59 output: CpuTensorMut<'_>,
60 _attrs: &[u8],
61 ) -> Result<(), String> {
62 let x = inputs[0].expect_f64("reeig X")?;
63 let n = dim(&inputs[0], 0);
64 let eps = self.eps;
65 let out = output.expect_f64_mut("reeig packed")?;
66 out.copy_from_slice(&spd::spectral_forward_packed(x, n, |l| l.max(eps)));
67 Ok(())
68 }
69}
70
71pub struct LogEigKernel {
73 pub eps: f64,
74}
75impl CpuKernel for LogEigKernel {
76 fn name(&self) -> &str {
77 "core.logeig"
78 }
79 fn execute(
80 &self,
81 inputs: &[CpuTensorRef<'_>],
82 output: CpuTensorMut<'_>,
83 _attrs: &[u8],
84 ) -> Result<(), String> {
85 let x = inputs[0].expect_f64("logeig X")?;
86 let n = dim(&inputs[0], 0);
87 let eps = self.eps;
88 let out = output.expect_f64_mut("logeig packed")?;
89 out.copy_from_slice(&spd::spectral_forward_packed(x, n, |l| l.max(eps).ln()));
90 Ok(())
91 }
92}
93
94pub struct SpdBatchNormKernel {
96 pub eps: f64,
97}
98impl CpuKernel for SpdBatchNormKernel {
99 fn name(&self) -> &str {
100 "core.spd_batch_norm"
101 }
102 fn execute(
103 &self,
104 inputs: &[CpuTensorRef<'_>],
105 output: CpuTensorMut<'_>,
106 _attrs: &[u8],
107 ) -> Result<(), String> {
108 let x = inputs[0].expect_f64("spd_bn X")?;
109 let mean = inputs[1].expect_f64("spd_bn mean")?;
110 let g = inputs[2].expect_f64("spd_bn G")?;
111 let (batch, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
112 let out = output.expect_f64_mut("spd_bn Y")?;
113 out.copy_from_slice(&spd::spd_bn_transport(x, mean, g, batch, n, self.eps));
114 Ok(())
115 }
116}
117
118pub struct SpdKarcherMeanKernel {
120 pub iters: usize,
121 pub tol: f64,
122}
123impl CpuKernel for SpdKarcherMeanKernel {
124 fn name(&self) -> &str {
125 "core.spd_karcher_mean"
126 }
127 fn execute(
128 &self,
129 inputs: &[CpuTensorRef<'_>],
130 output: CpuTensorMut<'_>,
131 _attrs: &[u8],
132 ) -> Result<(), String> {
133 let x = inputs[0].expect_f64("karcher X")?;
134 let (batch, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
135 let covs: Vec<Vec<f64>> = (0..batch)
136 .map(|bi| x[bi * n * n..(bi + 1) * n * n].to_vec())
137 .collect();
138 let out = output.expect_f64_mut("karcher mean")?;
139 out.copy_from_slice(&spd::karcher_mean(&covs, n, self.iters, self.tol));
140 Ok(())
141 }
142}
143
144pub struct SpdKarcherMeanWeightedKernel {
146 pub iters: usize,
147 pub tol: f64,
148}
149impl CpuKernel for SpdKarcherMeanWeightedKernel {
150 fn name(&self) -> &str {
151 "core.spd_karcher_mean_weighted"
152 }
153 fn execute(
154 &self,
155 inputs: &[CpuTensorRef<'_>],
156 output: CpuTensorMut<'_>,
157 _attrs: &[u8],
158 ) -> Result<(), String> {
159 let x = inputs[0].expect_f64("karcher_w X")?;
160 let w = inputs[1].expect_f64("karcher_w weights")?;
161 let (batch, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
162 let covs: Vec<Vec<f64>> = (0..batch)
163 .map(|bi| x[bi * n * n..(bi + 1) * n * n].to_vec())
164 .collect();
165 let out = output.expect_f64_mut("karcher_w mean")?;
166 out.copy_from_slice(&spd::karcher_mean_weighted(
167 &covs, w, n, self.iters, self.tol,
168 ));
169 Ok(())
170 }
171}
172
173pub struct SpdLogMapKernel;
175impl CpuKernel for SpdLogMapKernel {
176 fn name(&self) -> &str {
177 "core.spd_log_map"
178 }
179 fn execute(
180 &self,
181 inputs: &[CpuTensorRef<'_>],
182 output: CpuTensorMut<'_>,
183 _attrs: &[u8],
184 ) -> Result<(), String> {
185 let base = inputs[0].expect_f64("log_map base")?;
186 let x = inputs[1].expect_f64("log_map X")?;
187 let n = dim(&inputs[0], 0);
188 let out = output.expect_f64_mut("log_map V")?;
189 out.copy_from_slice(&spd::log_map(base, x, n));
190 Ok(())
191 }
192}
193
194pub struct SpdExpMapKernel;
196impl CpuKernel for SpdExpMapKernel {
197 fn name(&self) -> &str {
198 "core.spd_exp_map"
199 }
200 fn execute(
201 &self,
202 inputs: &[CpuTensorRef<'_>],
203 output: CpuTensorMut<'_>,
204 _attrs: &[u8],
205 ) -> Result<(), String> {
206 let base = inputs[0].expect_f64("exp_map base")?;
207 let v = inputs[1].expect_f64("exp_map V")?;
208 let n = dim(&inputs[0], 0);
209 let out = output.expect_f64_mut("exp_map Y")?;
210 out.copy_from_slice(&spd::exp_map(base, v, n));
211 Ok(())
212 }
213}
214
215pub struct SpdParallelTransportKernel;
217impl CpuKernel for SpdParallelTransportKernel {
218 fn name(&self) -> &str {
219 "core.spd_parallel_transport"
220 }
221 fn execute(
222 &self,
223 inputs: &[CpuTensorRef<'_>],
224 output: CpuTensorMut<'_>,
225 _attrs: &[u8],
226 ) -> Result<(), String> {
227 let from = inputs[0].expect_f64("transport from")?;
228 let to = inputs[1].expect_f64("transport to")?;
229 let v = inputs[2].expect_f64("transport V")?;
230 let n = dim(&inputs[0], 0);
231 let out = output.expect_f64_mut("transport out")?;
232 out.copy_from_slice(&spd::parallel_transport(from, to, v, n));
233 Ok(())
234 }
235}
236
237pub struct SpdMatrixFnBatchKernel {
239 pub kind: SpdMatFn,
240}
241impl CpuKernel for SpdMatrixFnBatchKernel {
242 fn name(&self) -> &str {
243 "core.spd_matrix_fn_batch"
244 }
245 fn execute(
246 &self,
247 inputs: &[CpuTensorRef<'_>],
248 output: CpuTensorMut<'_>,
249 _attrs: &[u8],
250 ) -> Result<(), String> {
251 let x = inputs[0].expect_f64("spd_matfn X")?;
252 let (batch, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
253 let covs: Vec<Vec<f64>> = (0..batch)
254 .map(|bi| x[bi * n * n..(bi + 1) * n * n].to_vec())
255 .collect();
256 let res = match self.kind {
257 SpdMatFn::Logm => spd::logm_batch(&covs, n),
258 SpdMatFn::Expm => spd::expm_batch(&covs, n),
259 SpdMatFn::Sqrtm => spd::sqrtm_batch(&covs, n),
260 SpdMatFn::Invsqrtm => spd::invsqrtm_batch(&covs, n),
261 };
262 let out = output.expect_f64_mut("spd_matfn Y")?;
263 out.copy_from_slice(&res.concat());
264 Ok(())
265 }
266}
267
268pub struct SpdLogMapBackwardKernel;
271impl CpuKernel for SpdLogMapBackwardKernel {
272 fn name(&self) -> &str {
273 "core.spd_log_map_backward"
274 }
275 fn execute(
276 &self,
277 inputs: &[CpuTensorRef<'_>],
278 output: CpuTensorMut<'_>,
279 _attrs: &[u8],
280 ) -> Result<(), String> {
281 let base = inputs[0].expect_f64("log_map_bwd base")?;
282 let x = inputs[1].expect_f64("log_map_bwd x")?;
283 let dy = inputs[2].expect_f64("log_map_bwd dY")?;
284 let n = dim(&inputs[0], 0);
285 let (d_base, d_x) = spd::log_map_backward(base, x, dy, n);
286 let out = output.expect_f64_mut("log_map_bwd packed")?;
287 out[..n * n].copy_from_slice(&d_base);
288 out[n * n..].copy_from_slice(&d_x);
289 Ok(())
290 }
291}
292
293pub struct SpdExpMapBackwardKernel;
296impl CpuKernel for SpdExpMapBackwardKernel {
297 fn name(&self) -> &str {
298 "core.spd_exp_map_backward"
299 }
300 fn execute(
301 &self,
302 inputs: &[CpuTensorRef<'_>],
303 output: CpuTensorMut<'_>,
304 _attrs: &[u8],
305 ) -> Result<(), String> {
306 let base = inputs[0].expect_f64("exp_map_bwd base")?;
307 let v = inputs[1].expect_f64("exp_map_bwd v")?;
308 let dy = inputs[2].expect_f64("exp_map_bwd dY")?;
309 let n = dim(&inputs[0], 0);
310 let (d_base, d_v) = spd::exp_map_backward(base, v, dy, n);
311 let out = output.expect_f64_mut("exp_map_bwd packed")?;
312 out[..n * n].copy_from_slice(&d_base);
313 out[n * n..].copy_from_slice(&d_v);
314 Ok(())
315 }
316}
317
318pub struct SpdParallelTransportBackwardKernel;
321impl CpuKernel for SpdParallelTransportBackwardKernel {
322 fn name(&self) -> &str {
323 "core.spd_parallel_transport_backward"
324 }
325 fn execute(
326 &self,
327 inputs: &[CpuTensorRef<'_>],
328 output: CpuTensorMut<'_>,
329 _attrs: &[u8],
330 ) -> Result<(), String> {
331 let from = inputs[0].expect_f64("transport_bwd from")?;
332 let to = inputs[1].expect_f64("transport_bwd to")?;
333 let v = inputs[2].expect_f64("transport_bwd v")?;
334 let dy = inputs[3].expect_f64("transport_bwd dY")?;
335 let n = dim(&inputs[0], 0);
336 let (d_from, d_to, d_v) = spd::parallel_transport_backward(from, to, v, dy, n);
337 let out = output.expect_f64_mut("transport_bwd packed")?;
338 out[..n * n].copy_from_slice(&d_from);
339 out[n * n..2 * n * n].copy_from_slice(&d_to);
340 out[2 * n * n..].copy_from_slice(&d_v);
341 Ok(())
342 }
343}
344
345pub struct SpdMatrixFnBatchBackwardKernel {
347 pub kind: SpdMatFn,
348}
349impl CpuKernel for SpdMatrixFnBatchBackwardKernel {
350 fn name(&self) -> &str {
351 "core.spd_matrix_fn_batch_backward"
352 }
353 fn execute(
354 &self,
355 inputs: &[CpuTensorRef<'_>],
356 output: CpuTensorMut<'_>,
357 _attrs: &[u8],
358 ) -> Result<(), String> {
359 let x = inputs[0].expect_f64("spd_matfn_bwd X")?;
360 let dy = inputs[1].expect_f64("spd_matfn_bwd dY")?;
361 let n = dim(&inputs[0], 1);
362 let dx = match self.kind {
364 SpdMatFn::Logm => spd::matrix_fn_batch_backward(
365 x,
366 dy,
367 n,
368 |l| l.max(1e-12).ln(),
369 |l| 1.0 / l.max(1e-12),
370 ),
371 SpdMatFn::Expm => spd::matrix_fn_batch_backward(x, dy, n, |l| l.exp(), |l| l.exp()),
372 SpdMatFn::Sqrtm => spd::matrix_fn_batch_backward(
373 x,
374 dy,
375 n,
376 |l| l.max(0.0).sqrt(),
377 |l| 0.5 / l.max(1e-12).sqrt(),
378 ),
379 SpdMatFn::Invsqrtm => spd::matrix_fn_batch_backward(
380 x,
381 dy,
382 n,
383 |l| 1.0 / l.max(1e-12).sqrt(),
384 |l| -0.5 * l.max(1e-12).powf(-1.5),
385 ),
386 };
387 let out = output.expect_f64_mut("spd_matfn_bwd dX")?;
388 out.copy_from_slice(&dx);
389 Ok(())
390 }
391}
392
393fn n_from_packed(len: usize) -> usize {
395 (((1 + 4 * len) as f64).sqrt().round() as usize - 1) / 2
396}
397
398pub struct EighKernel;
400impl CpuKernel for EighKernel {
401 fn name(&self) -> &str {
402 "core.eigh"
403 }
404 fn execute(
405 &self,
406 inputs: &[CpuTensorRef<'_>],
407 output: CpuTensorMut<'_>,
408 _attrs: &[u8],
409 ) -> Result<(), String> {
410 let a = inputs[0].expect_f64("eigh A")?;
411 let n = dim(&inputs[0], 0);
412 let out = output.expect_f64_mut("eigh packed")?;
413 out.copy_from_slice(&spd::eigh_packed(a, n));
414 Ok(())
415 }
416}
417
418pub struct EighBackwardKernel;
420impl CpuKernel for EighBackwardKernel {
421 fn name(&self) -> &str {
422 "core.eigh_backward"
423 }
424 fn execute(
425 &self,
426 inputs: &[CpuTensorRef<'_>],
427 output: CpuTensorMut<'_>,
428 _attrs: &[u8],
429 ) -> Result<(), String> {
430 let fwd = inputs[0].expect_f64("eigh_bwd fwd")?;
431 let bar = inputs[1].expect_f64("eigh_bwd bar")?;
432 let n = n_from_packed(fwd.len());
433 let out = output.expect_f64_mut("eigh_bwd dA")?;
434 out.copy_from_slice(&spd::eigh_backward_packed(fwd, bar, n));
435 Ok(())
436 }
437}
438
439pub struct EighBatchKernel;
441impl CpuKernel for EighBatchKernel {
442 fn name(&self) -> &str {
443 "core.eigh_batch"
444 }
445 fn execute(
446 &self,
447 inputs: &[CpuTensorRef<'_>],
448 output: CpuTensorMut<'_>,
449 _attrs: &[u8],
450 ) -> Result<(), String> {
451 let a = inputs[0].expect_f64("eigh_batch A")?;
452 let (b, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
453 let covs: Vec<Vec<f64>> = (0..b)
454 .map(|bi| a[bi * n * n..(bi + 1) * n * n].to_vec())
455 .collect();
456 let out = output.expect_f64_mut("eigh_batch packed")?;
457 out.copy_from_slice(&spd::eigh_batch_packed(&covs, n).concat());
458 Ok(())
459 }
460}
461
462pub struct EighBatchBackwardKernel;
464impl CpuKernel for EighBatchBackwardKernel {
465 fn name(&self) -> &str {
466 "core.eigh_batch_backward"
467 }
468 fn execute(
469 &self,
470 inputs: &[CpuTensorRef<'_>],
471 output: CpuTensorMut<'_>,
472 _attrs: &[u8],
473 ) -> Result<(), String> {
474 use rayon::prelude::*;
475 let fwd = inputs[0].expect_f64("eigh_batch_bwd fwd")?;
476 let bar = inputs[1].expect_f64("eigh_batch_bwd bar")?;
477 let (b, packed) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
478 let n = n_from_packed(packed);
479 let da: Vec<f64> = (0..b)
480 .into_par_iter()
481 .flat_map(|bi| {
482 let f = &fwd[bi * packed..(bi + 1) * packed];
483 let bb = &bar[bi * packed..(bi + 1) * packed];
484 spd::eigh_backward_packed(f, bb, n)
485 })
486 .collect();
487 let out = output.expect_f64_mut("eigh_batch_bwd dA")?;
488 out.copy_from_slice(&da);
489 Ok(())
490 }
491}
492
493pub struct ReEigBackwardKernel {
496 pub eps: f64,
497}
498impl CpuKernel for ReEigBackwardKernel {
499 fn name(&self) -> &str {
500 "core.reeig_backward"
501 }
502 fn execute(
503 &self,
504 inputs: &[CpuTensorRef<'_>],
505 output: CpuTensorMut<'_>,
506 _attrs: &[u8],
507 ) -> Result<(), String> {
508 let lam = inputs[0].expect_f64("reeig_bwd λ")?;
509 let u = inputs[1].expect_f64("reeig_bwd U")?;
510 let dy = inputs[2].expect_f64("reeig_bwd dY")?;
511 let n = lam.len();
512 let out = output.expect_f64_mut("reeig_bwd dX")?;
513 out.copy_from_slice(&spd::reeig_backward_precomputed(lam, u, dy, n, self.eps));
514 Ok(())
515 }
516}
517
518pub struct LogEigBackwardKernel {
520 pub eps: f64,
521}
522impl CpuKernel for LogEigBackwardKernel {
523 fn name(&self) -> &str {
524 "core.logeig_backward"
525 }
526 fn execute(
527 &self,
528 inputs: &[CpuTensorRef<'_>],
529 output: CpuTensorMut<'_>,
530 _attrs: &[u8],
531 ) -> Result<(), String> {
532 let lam = inputs[0].expect_f64("logeig_bwd λ")?;
533 let u = inputs[1].expect_f64("logeig_bwd U")?;
534 let dy = inputs[2].expect_f64("logeig_bwd dY")?;
535 let n = lam.len();
536 let out = output.expect_f64_mut("logeig_bwd dX")?;
537 out.copy_from_slice(&spd::logeig_backward_precomputed(lam, u, dy, n, self.eps));
538 Ok(())
539 }
540}
541
542pub struct SpdBnBackwardXKernel {
544 pub eps: f64,
545}
546impl CpuKernel for SpdBnBackwardXKernel {
547 fn name(&self) -> &str {
548 "core.spd_batch_norm_backward_x"
549 }
550 fn execute(
551 &self,
552 inputs: &[CpuTensorRef<'_>],
553 output: CpuTensorMut<'_>,
554 _attrs: &[u8],
555 ) -> Result<(), String> {
556 let mean = inputs[0].expect_f64("spd_bn_bwd_x mean")?;
557 let g = inputs[1].expect_f64("spd_bn_bwd_x G")?;
558 let dy = inputs[2].expect_f64("spd_bn_bwd_x dY")?;
559 let (batch, n) = (dim(&inputs[2], 0), dim(&inputs[0], 0));
560 let out = output.expect_f64_mut("spd_bn_bwd_x dX")?;
561 out.copy_from_slice(&spd::spd_bn_backward_x(mean, g, dy, batch, n, self.eps));
562 Ok(())
563 }
564}
565
566pub struct SpdBnBackwardGKernel {
568 pub eps: f64,
569}
570impl CpuKernel for SpdBnBackwardGKernel {
571 fn name(&self) -> &str {
572 "core.spd_batch_norm_backward_g"
573 }
574 fn execute(
575 &self,
576 inputs: &[CpuTensorRef<'_>],
577 output: CpuTensorMut<'_>,
578 _attrs: &[u8],
579 ) -> Result<(), String> {
580 let x = inputs[0].expect_f64("spd_bn_bwd_g X")?;
581 let mean = inputs[1].expect_f64("spd_bn_bwd_g mean")?;
582 let g = inputs[2].expect_f64("spd_bn_bwd_g G")?;
583 let dy = inputs[3].expect_f64("spd_bn_bwd_g dY")?;
584 let (batch, n) = (dim(&inputs[0], 0), dim(&inputs[0], 1));
585 let out = output.expect_f64_mut("spd_bn_bwd_g dG")?;
586 out.copy_from_slice(&spd::spd_bn_backward_g(x, mean, g, dy, batch, n, self.eps));
587 Ok(())
588 }
589}