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