1use crate::infer::GraphExt;
34use crate::op::{Activation, Op};
35use crate::{DType, Graph, NodeId, Shape};
36
37pub const SPD_JACOBI_SWEEPS: u32 = 6;
50
51pub fn spd_jacobi_sweeps() -> u32 {
53 crate::env::var("RLX_SPD_JACOBI_SWEEPS")
54 .and_then(|s| s.parse::<u32>().ok())
55 .filter(|&v| v > 0)
56 .unwrap_or(SPD_JACOBI_SWEEPS)
57}
58
59fn const_bytes(xs: &[f64], dtype: DType) -> Vec<u8> {
61 match dtype {
62 DType::F64 => xs.iter().flat_map(|v| v.to_le_bytes()).collect(),
63 _ => xs.iter().flat_map(|v| (*v as f32).to_le_bytes()).collect(),
64 }
65}
66
67fn cmat(g: &mut Graph, xs: &[f64], dims: &[usize], dtype: DType) -> NodeId {
69 g.add_node(
70 Op::Constant {
71 data: const_bytes(xs, dtype),
72 },
73 vec![],
74 Shape::new(dims, dtype),
75 )
76}
77
78fn cscalar(g: &mut Graph, v: f64, dtype: DType) -> NodeId {
80 cmat(g, &[v], &[1, 1], dtype)
81}
82
83fn round_robin_rounds(ne: usize) -> Vec<Vec<(usize, usize)>> {
89 let mut players: Vec<usize> = (0..ne).collect();
90 let mut rounds = Vec::with_capacity(ne - 1);
91 for _ in 0..ne - 1 {
92 let pairs: Vec<(usize, usize)> = (0..ne / 2)
93 .map(|i| {
94 let (a, b) = (players[i], players[ne - 1 - i]);
95 (a.min(b), a.max(b))
96 })
97 .collect();
98 rounds.push(pairs);
99 let last = players[ne - 1];
101 for i in (2..ne).rev() {
102 players[i] = players[i - 1];
103 }
104 players[1] = last;
105 }
106 rounds
107}
108
109fn diag_k(g: &mut Graph, m: NodeId, ident_k: NodeId) -> NodeId {
112 let d = g.mul(m, ident_k);
113 g.sum(d, vec![1], true) }
115
116#[allow(clippy::too_many_arguments)]
124fn one_round(
125 g: &mut Graph,
126 av: NodeId,
127 vv: NodeId,
128 spp: NodeId,
129 sqq: NodeId,
130 ident_n: NodeId,
131 ident_k: NodeId,
132 dt: DType,
133) -> (NodeId, NodeId) {
134 let one = cscalar(g, 1.0, dt);
135 let two = cscalar(g, 2.0, dt);
136 let tiny = cscalar(g, 1e-30, dt);
137 let sbias = cscalar(g, 1e-12, dt);
138 let floor = cscalar(g, 1e-6, dt);
139
140 let sppt = g.transpose_(spp, vec![1, 0]); let sqqt = g.transpose_(sqq, vec![1, 0]);
142 let tpp = g.mm(sppt, av); let m_pp = g.mm(tpp, spp); let app = diag_k(g, m_pp, ident_k); let tqq = g.mm(sqqt, av);
146 let m_qq = g.mm(tqq, sqq);
147 let aqq = diag_k(g, m_qq, ident_k);
148 let m_pq = g.mm(tpp, sqq); let apq = diag_k(g, m_pq, ident_k);
150
151 let num = g.sub(aqq, app);
153 let den0 = g.mul(two, apq);
154 let den0b = g.add(den0, sbias); let den0sq = g.mul(den0b, den0b);
156 let den0abs = g.sqrt(den0sq);
157 let den0abst = g.add(den0abs, tiny);
158 let sgnden = g.div(den0b, den0abst);
159 let sfloor = g.mul(sgnden, floor);
160 let den = g.add(den0, sfloor);
161 let tau = g.div(num, den);
162 let tau2 = g.mul(tau, tau);
163 let atau = g.sqrt(tau2);
164 let satau = g.add(atau, tiny);
165 let sgn = g.div(tau, satau);
166 let tau2p1 = g.add(tau2, one);
167 let rt = g.sqrt(tau2p1);
168 let tden = g.add(atau, rt);
169 let t = g.div(sgn, tden);
170 let t2 = g.mul(t, t);
171 let t2p1 = g.add(t2, one);
172 let rc = g.sqrt(t2p1);
173 let c = g.div(one, rc);
174 let s = g.mul(t, c);
175 let cm1 = g.sub(c, one);
176 let negs = g.neg(s);
177
178 let d_cm1 = g.mul(cm1, ident_k); let d_s = g.mul(s, ident_k);
182 let d_negs = g.mul(negs, ident_k);
183 let pp_diag = {
184 let l = g.mm(spp, d_cm1);
185 g.mm(l, sppt)
186 };
187 let qq_diag = {
188 let l = g.mm(sqq, d_cm1);
189 g.mm(l, sqqt)
190 };
191 let pq = {
192 let l = g.mm(spp, d_s);
193 g.mm(l, sqqt)
194 };
195 let qp = {
196 let l = g.mm(sqq, d_negs);
197 g.mm(l, sppt)
198 };
199 let j1 = g.add(ident_n, pp_diag);
200 let j2 = g.add(j1, qq_diag);
201 let j3 = g.add(j2, pq);
202 let jj = g.add(j3, qp);
203 let jt = g.transpose_(jj, vec![1, 0]);
204 let jta = g.mm(jt, av);
205 let av2 = g.mm(jta, jj);
206 let vv2 = g.mm(vv, jj);
207 (av2, vv2)
208}
209
210fn round_body(n: usize, k: usize, dt: DType) -> Graph {
213 let mut body = Graph::new("jacobi_round");
214 let carry = body.input("carry", Shape::new(&[2 * n, n], dt));
215 let spp = body.input("spp", Shape::new(&[n, k], dt));
216 let sqq = body.input("sqq", Shape::new(&[n, k], dt));
217 let a_in = body.narrow_(carry, 0, 0, n);
218 let v_in = body.narrow_(carry, 0, n, n);
219 let mut ident = vec![0f64; n * n];
220 for i in 0..n {
221 ident[i * n + i] = 1.0;
222 }
223 let ident_n = cmat(&mut body, &ident, &[n, n], dt);
224 let mut idk = vec![0f64; k * k];
225 for i in 0..k {
226 idk[i * k + i] = 1.0;
227 }
228 let ident_k = cmat(&mut body, &idk, &[k, k], dt);
229 let (a_out, v_out) = one_round(&mut body, a_in, v_in, spp, sqq, ident_n, ident_k, dt);
230 let out = body.concat_(vec![a_out, v_out], 0);
231 body.set_outputs(vec![out]);
232 body
233}
234
235fn eigensolve(g: &mut Graph, a: NodeId, n: usize, sweeps: u32, dt: DType) -> (NodeId, NodeId) {
247 let mut ident = vec![0f64; n * n];
248 for i in 0..n {
249 ident[i * n + i] = 1.0;
250 }
251 let iv = cmat(g, &ident, &[n, n], dt);
252 if n < 2 {
253 return (a, iv); }
255 let mut carry = g.concat_(vec![a, iv], 0); let ne = if n.is_multiple_of(2) { n } else { n + 1 };
258 let k = ne / 2;
259 let rounds = round_robin_rounds(ne);
260 let nr = rounds.len(); if crate::env::flag("RLX_SPD_UNROLL") {
281 let mut idk = vec![0f64; k * k];
282 for i in 0..k {
283 idk[i * k + i] = 1.0;
284 }
285 let ident_k = cmat(g, &idk, &[k, k], dt);
286 let sel: Vec<(NodeId, NodeId)> = rounds
287 .iter()
288 .map(|round| {
289 let mut sp = vec![0f64; n * k];
290 let mut sq = vec![0f64; n * k];
291 for (i, &(p, q)) in round.iter().enumerate() {
292 if p < n {
293 sp[p * k + i] = 1.0;
294 }
295 if q < n {
296 sq[q * k + i] = 1.0;
297 }
298 }
299 (cmat(g, &sp, &[n, k], dt), cmat(g, &sq, &[n, k], dt))
300 })
301 .collect();
302 let (mut av, mut vv) = (a, iv);
305 for _ in 0..sweeps {
306 for &(spp_r, sqq_r) in &sel {
307 let (a2, v2) = one_round(g, av, vv, spp_r, sqq_r, iv, ident_k, dt);
308 av = a2;
309 vv = v2;
310 }
311 }
312 return (av, vv);
313 }
314
315 let total = nr * sweeps as usize;
326 let mut spp = vec![0f64; total * n * k];
327 let mut sqq = vec![0f64; total * n * k];
328 for s in 0..sweeps as usize {
329 for (r, round) in rounds.iter().enumerate() {
330 let base = (s * nr + r) * n * k;
331 for (i, &(p, q)) in round.iter().enumerate() {
332 if p < n {
333 spp[base + p * k + i] = 1.0;
334 }
335 if q < n {
336 sqq[base + q * k + i] = 1.0;
337 }
338 }
339 }
340 }
341 let spp_c = cmat(g, &spp, &[total, n, k], dt);
342 let sqq_c = cmat(g, &sqq, &[total, n, k], dt);
343 let body = round_body(n, k, dt);
344 carry = g.scan_with_xs(carry, &[spp_c, sqq_c], body, total as u32);
345 let av = g.narrow_(carry, 0, 0, n);
346 let vv = g.narrow_(carry, 0, n, n);
347 (av, vv)
348}
349
350#[derive(Clone, Copy, PartialEq)]
359pub enum SpectralFn {
360 Re,
361 Log,
362 Sqrt,
363 InvSqrt,
364 Pow(f64),
365}
366
367fn spectral_fmat(
371 g: &mut Graph,
372 av: NodeId,
373 n: usize,
374 eps: f64,
375 f: SpectralFn,
376 dt: DType,
377) -> (NodeId, Vec<NodeId>) {
378 let epsn = cscalar(g, eps, dt);
379 let half = cscalar(g, 0.5, dt);
380 let one = cscalar(g, 1.0, dt);
381 let mut terms: Vec<NodeId> = Vec::with_capacity(n);
382 let mut lam_parts: Vec<NodeId> = Vec::with_capacity(n);
383 for i in 0..n {
384 let ri = g.narrow_(av, 0, i, 1); let lam = g.narrow_(ri, 1, i, 1); lam_parts.push(g.reshape_(lam, vec![1])); let sumv = g.add(lam, epsn);
389 let diff = g.sub(lam, epsn);
390 let d2 = g.mul(diff, diff);
391 let ad = g.sqrt(d2);
392 let sad = g.add(sumv, ad);
393 let mx = g.mul(half, sad);
394 let fl = match f {
395 SpectralFn::Re => mx,
396 SpectralFn::Log => g.activation(Activation::Log, mx, Shape::new(&[1, 1], dt)),
397 SpectralFn::Sqrt => g.sqrt(mx),
398 SpectralFn::Pow(p) => {
401 let l = g.activation(Activation::Log, mx, Shape::new(&[1, 1], dt));
402 let pc = cscalar(g, p, dt);
403 let pl = g.mul(l, pc);
404 g.activation(Activation::Exp, pl, Shape::new(&[1, 1], dt))
405 }
406 SpectralFn::InvSqrt => {
407 let s = g.sqrt(mx);
408 g.div(one, s)
409 }
410 };
411 let mut eii = vec![0f64; n * n];
412 eii[i * n + i] = 1.0;
413 let eii = cmat(g, &eii, &[n, n], dt);
414 terms.push(g.mul(fl, eii));
415 }
416 let mut fmat = terms[0];
417 for t in &terms[1..] {
418 fmat = g.add(fmat, *t);
419 }
420 (fmat, lam_parts)
421}
422
423pub fn spectral_matfn(
427 g: &mut Graph,
428 a: NodeId,
429 n: usize,
430 sweeps: u32,
431 eps: f64,
432 f: SpectralFn,
433) -> NodeId {
434 let dt = g.shape(a).dtype();
435 let (av, vv) = eigensolve(g, a, n, sweeps, dt);
436 let (fmat, _) = spectral_fmat(g, av, n, eps, f, dt);
437 let vt = g.transpose_(vv, vec![1, 0]);
438 let vf = g.mm(vv, fmat);
439 g.mm(vf, vt)
440}
441
442fn ident_mat3(g: &mut Graph, n: usize, dt: DType) -> NodeId {
455 let mut m = vec![0f64; n * n];
456 for i in 0..n {
457 m[i * n + i] = 1.0;
458 }
459 cmat(g, &m, &[1, n, n], dt)
460}
461
462fn jacobi_cs(
465 g: &mut Graph,
466 app: NodeId,
467 aqq: NodeId,
468 apq: NodeId,
469 dt: DType,
470) -> (NodeId, NodeId, NodeId) {
471 let one = cscalar(g, 1.0, dt);
472 let two = cscalar(g, 2.0, dt);
473 let tiny = cscalar(g, 1e-30, dt);
474 let sbias = cscalar(g, 1e-12, dt);
475 let floor = cscalar(g, 1e-6, dt);
476 let num = g.sub(aqq, app);
477 let den0 = g.mul(two, apq);
478 let den0b = g.add(den0, sbias);
479 let den0sq = g.mul(den0b, den0b);
480 let den0abs = g.sqrt(den0sq);
481 let den0abst = g.add(den0abs, tiny);
482 let sgnden = g.div(den0b, den0abst);
483 let sfloor = g.mul(sgnden, floor);
484 let den = g.add(den0, sfloor);
485 let tau = g.div(num, den);
486 let tau2 = g.mul(tau, tau);
487 let atau = g.sqrt(tau2);
488 let satau = g.add(atau, tiny);
489 let sgn = g.div(tau, satau);
490 let tau2p1 = g.add(tau2, one);
491 let rt = g.sqrt(tau2p1);
492 let tden = g.add(atau, rt);
493 let t = g.div(sgn, tden);
494 let t2 = g.mul(t, t);
495 let t2p1 = g.add(t2, one);
496 let rc = g.sqrt(t2p1);
497 let c = g.div(one, rc);
498 let s = g.mul(t, c);
499 let cm1 = g.sub(c, one);
500 let negs = g.neg(s);
501 (cm1, s, negs)
502}
503
504#[allow(clippy::too_many_arguments)]
507fn one_round_b(
508 g: &mut Graph,
509 av: NodeId,
510 vv: NodeId,
511 spp: NodeId,
512 sqq: NodeId,
513 ident_n: NodeId,
514 ident_k: NodeId,
515 dt: DType,
516) -> (NodeId, NodeId) {
517 let sppt = g.transpose_(spp, vec![0, 2, 1]); let sqqt = g.transpose_(sqq, vec![0, 2, 1]);
520 let tpp = g.mm(sppt, av); let m_pp = g.mm(tpp, spp); let app = {
523 let d = g.mul(m_pp, ident_k);
524 g.sum(d, vec![2], true)
525 }; let tqq = g.mm(sqqt, av);
527 let m_qq = g.mm(tqq, sqq);
528 let aqq = {
529 let d = g.mul(m_qq, ident_k);
530 g.sum(d, vec![2], true)
531 };
532 let m_pq = g.mm(tpp, sqq);
533 let apq = {
534 let d = g.mul(m_pq, ident_k);
535 g.sum(d, vec![2], true)
536 };
537 let (cm1, s, negs) = jacobi_cs(g, app, aqq, apq, dt);
538 let d_cm1 = g.mul(cm1, ident_k); let d_s = g.mul(s, ident_k);
540 let d_negs = g.mul(negs, ident_k);
541 let pp_diag = {
542 let l = g.mm(spp, d_cm1);
543 g.mm(l, sppt)
544 };
545 let qq_diag = {
546 let l = g.mm(sqq, d_cm1);
547 g.mm(l, sqqt)
548 };
549 let pq = {
550 let l = g.mm(spp, d_s);
551 g.mm(l, sqqt)
552 };
553 let qp = {
554 let l = g.mm(sqq, d_negs);
555 g.mm(l, sppt)
556 };
557 let j1 = g.add(ident_n, pp_diag);
558 let j2 = g.add(j1, qq_diag);
559 let j3 = g.add(j2, pq);
560 let jj = g.add(j3, qp);
561 let jt = g.transpose_(jj, vec![0, 2, 1]); let jta = g.mm(jt, av);
563 let av2 = g.mm(jta, jj);
564 let vv2 = g.mm(vv, jj);
565 (av2, vv2)
566}
567
568fn round_body_b(batch: usize, n: usize, k: usize, dt: DType) -> Graph {
570 let mut body = Graph::new("jacobi_round_b");
571 let carry = body.input("carry", Shape::new(&[batch, 2 * n, n], dt));
572 let spp = body.input("spp", Shape::new(&[n, k], dt));
573 let sqq = body.input("sqq", Shape::new(&[n, k], dt));
574 let a_in = body.narrow_(carry, 1, 0, n); let v_in = body.narrow_(carry, 1, n, n);
576 let _ = batch;
579 let spp3 = body.reshape_(spp, vec![1, n as i64, k as i64]);
580 let sqq3 = body.reshape_(sqq, vec![1, n as i64, k as i64]);
581 let ident_n = ident_mat3(&mut body, n, dt);
582 let ident_k = ident_mat3(&mut body, k, dt);
583 let (a_out, v_out) = one_round_b(&mut body, a_in, v_in, spp3, sqq3, ident_n, ident_k, dt);
584 let out = body.concat_(vec![a_out, v_out], 1); body.set_outputs(vec![out]);
586 body
587}
588
589fn eigensolve_b(
591 g: &mut Graph,
592 a: NodeId,
593 batch: usize,
594 n: usize,
595 sweeps: u32,
596 dt: DType,
597) -> (NodeId, NodeId) {
598 let mut ivdata = vec![0f64; batch * n * n];
601 for b in 0..batch {
602 for i in 0..n {
603 ivdata[b * n * n + i * n + i] = 1.0;
604 }
605 }
606 let iv = cmat(g, &ivdata, &[batch, n, n], dt);
607 if n < 2 {
608 return (a, iv);
609 }
610 let mut carry = g.concat_(vec![a, iv], 1); let ne = if n.is_multiple_of(2) { n } else { n + 1 };
613 let k = ne / 2;
614 let rounds = round_robin_rounds(ne);
615 let nr = rounds.len();
616 let mut spp = vec![0f64; nr * n * k];
617 let mut sqq = vec![0f64; nr * n * k];
618 for (r, round) in rounds.iter().enumerate() {
619 for (i, &(p, q)) in round.iter().enumerate() {
620 if p < n {
621 spp[r * n * k + p * k + i] = 1.0;
622 }
623 if q < n {
624 sqq[r * n * k + q * k + i] = 1.0;
625 }
626 }
627 }
628 let spp_c = cmat(g, &spp, &[nr, n, k], dt);
629 let sqq_c = cmat(g, &sqq, &[nr, n, k], dt);
630 let xs = [spp_c, sqq_c];
631 for _ in 0..sweeps {
632 let body = round_body_b(batch, n, k, dt);
633 carry = g.scan_with_xs(carry, &xs, body, nr as u32);
634 }
635 let av = g.narrow_(carry, 1, 0, n);
636 let vv = g.narrow_(carry, 1, n, n);
637 (av, vv)
638}
639
640pub fn spectral_matfn_batched(
643 g: &mut Graph,
644 a: NodeId,
645 batch: usize,
646 n: usize,
647 sweeps: u32,
648 eps: f64,
649 f: SpectralFn,
650) -> NodeId {
651 let dt = g.shape(a).dtype();
652 let ident_n = ident_mat3(g, n, dt); let (av, vv) = eigensolve_b(g, a, batch, n, sweeps, dt);
654 let epsn = cscalar(g, eps, dt);
656 let half = cscalar(g, 0.5, dt);
657 let one = cscalar(g, 1.0, dt);
658 let diag_av = {
659 let d = g.mul(av, ident_n);
660 g.sum(d, vec![2], true)
661 }; let sumv = g.add(diag_av, epsn);
663 let diff = g.sub(diag_av, epsn);
664 let d2 = g.mul(diff, diff);
665 let ad = g.sqrt(d2);
666 let sad = g.add(sumv, ad);
667 let mx = g.mul(half, sad); let fl = match f {
669 SpectralFn::Re => mx,
670 SpectralFn::Log => g.activation(Activation::Log, mx, Shape::new(&[batch, n, 1], dt)),
671 SpectralFn::Sqrt => g.sqrt(mx),
672 SpectralFn::Pow(p) => {
673 let l = g.activation(Activation::Log, mx, Shape::new(&[batch, n, 1], dt));
674 let pc = cscalar(g, p, dt);
675 let pl = g.mul(l, pc);
676 g.activation(Activation::Exp, pl, Shape::new(&[batch, n, 1], dt))
677 }
678 SpectralFn::InvSqrt => {
679 let s = g.sqrt(mx);
680 g.div(one, s)
681 }
682 };
683 let fmat = g.mul(fl, ident_n); let vt = g.transpose_(vv, vec![0, 2, 1]);
685 let vf = g.mm(vv, fmat);
686 g.mm(vf, vt)
687}
688
689impl Graph {
690 pub fn spd_reeig_batched(
693 &mut self,
694 x: NodeId,
695 batch: usize,
696 n: usize,
697 sweeps: u32,
698 eps: f64,
699 ) -> NodeId {
700 spectral_matfn_batched(self, x, batch, n, sweeps, eps, SpectralFn::Re)
701 }
702
703 pub fn spd_poweig_batched(
707 &mut self,
708 x: NodeId,
709 batch: usize,
710 n: usize,
711 sweeps: u32,
712 eps: f64,
713 power: f64,
714 ) -> NodeId {
715 spectral_matfn_batched(self, x, batch, n, sweeps, eps, SpectralFn::Pow(power))
716 }
717
718 pub fn spd_sqrt_batched(
720 &mut self,
721 x: NodeId,
722 batch: usize,
723 n: usize,
724 sweeps: u32,
725 eps: f64,
726 ) -> NodeId {
727 spectral_matfn_batched(self, x, batch, n, sweeps, eps, SpectralFn::Sqrt)
728 }
729
730 pub fn spd_invsqrt_batched(
732 &mut self,
733 x: NodeId,
734 batch: usize,
735 n: usize,
736 sweeps: u32,
737 eps: f64,
738 ) -> NodeId {
739 spectral_matfn_batched(self, x, batch, n, sweeps, eps, SpectralFn::InvSqrt)
740 }
741
742 pub fn spd_logeig_batched(
744 &mut self,
745 x: NodeId,
746 batch: usize,
747 n: usize,
748 sweeps: u32,
749 eps: f64,
750 ) -> NodeId {
751 spectral_matfn_batched(self, x, batch, n, sweeps, eps, SpectralFn::Log)
752 }
753}
754
755pub fn bimap(g: &mut Graph, w: NodeId, x: NodeId) -> NodeId {
758 let wt = g.transpose_(w, vec![1, 0]);
759 let wx = g.mm(w, x);
760 g.mm(wx, wt)
761}
762
763pub fn spd_batch_norm_transport(
767 g: &mut Graph,
768 x: NodeId,
769 mean: NodeId,
770 g_: NodeId,
771 n: usize,
772 batch: usize,
773 sweeps: u32,
774 eps: f64,
775) -> NodeId {
776 let ms = spectral_matfn(g, mean, n, sweeps, eps, SpectralFn::InvSqrt); let gs = spectral_matfn(g, g_, n, sweeps, eps, SpectralFn::Sqrt); let mut slices = Vec::with_capacity(batch);
779 for bi in 0..batch {
780 let xi = g.narrow_(x, 0, bi, 1);
781 let xi = g.reshape_(xi, vec![n as i64, n as i64]);
782 let mx = g.mm(ms, xi); let ci = g.mm(mx, ms); let gc = g.mm(gs, ci); let yi = g.mm(gc, gs); slices.push(g.reshape_(yi, vec![1, n as i64, n as i64]));
787 }
788 g.concat_(slices, 0)
789}
790
791pub fn spectral_reeig(g: &mut Graph, a: NodeId, n: usize, sweeps: u32, eps: f64) -> NodeId {
794 spectral_matfn(g, a, n, sweeps, eps, SpectralFn::Re)
795}
796
797pub fn spectral_logeig(g: &mut Graph, a: NodeId, n: usize, sweeps: u32, eps: f64) -> NodeId {
800 spectral_matfn(g, a, n, sweeps, eps, SpectralFn::Log)
801}
802
803pub fn spectral_packed(
808 g: &mut Graph,
809 a: NodeId,
810 n: usize,
811 sweeps: u32,
812 eps: f64,
813 log: bool,
814) -> NodeId {
815 let dt = g.shape(a).dtype();
816 let (av, vv) = eigensolve(g, a, n, sweeps, dt);
817 let f = if log { SpectralFn::Log } else { SpectralFn::Re };
818 let (fmat, lam_parts) = spectral_fmat(g, av, n, eps, f, dt);
819 let vt = g.transpose_(vv, vec![1, 0]);
820 let vf = g.mm(vv, fmat);
821 let y = g.mm(vf, vt); let y_flat = g.reshape_(y, vec![(n * n) as i64]); let lam = g.concat_(lam_parts, 0); let u_flat = g.reshape_(vv, vec![(n * n) as i64]); g.concat_(vec![y_flat, lam, u_flat], 0) }
827
828impl Graph {
829 pub fn spectral_reeig(&mut self, x: NodeId, sweeps: u32, eps: f64) -> NodeId {
832 let n = self.shape(x).dim(0).unwrap_static();
833 spectral_reeig(self, x, n, sweeps, eps)
834 }
835
836 pub fn spectral_logeig(&mut self, x: NodeId, sweeps: u32, eps: f64) -> NodeId {
839 let n = self.shape(x).dim(0).unwrap_static();
840 spectral_logeig(self, x, n, sweeps, eps)
841 }
842}