1#[cfg_attr(feature = "introspection", derive(serde::Serialize))]
19#[derive(Clone, Debug)]
20pub struct IlsqrParams {
21 pub tol: f64,
23 pub max_iter: usize,
25}
26
27impl Default for IlsqrParams {
28 fn default() -> Self {
29 Self {
30 tol: 0.01,
31 max_iter: 50,
32 }
33 }
34}
35
36use std::cell::RefCell;
37use num_complex::Complex64;
38use crate::fft::Fft3dWorkspace;
39use crate::kernels::dipole::dipole_kernel;
40use crate::kernels::smv::smv_kernel;
41use crate::utils::gradient::{fgrad, bdiv};
42use crate::Grid;
43
44pub fn lsqr<F, G>(
63 apply_a: F,
64 apply_at: G,
65 b: &[f64],
66 tol: f64,
67 max_iter: usize,
68) -> Vec<f64>
69where
70 F: Fn(&[f64]) -> Vec<f64>,
71 G: Fn(&[f64]) -> Vec<f64>,
72{
73 let mut u = b.to_vec();
75 let mut beta = norm(&u);
76
77 if beta > 0.0 {
78 scale_inplace(&mut u, 1.0 / beta);
79 }
80
81 let mut v = apply_at(&u);
82 let n = v.len();
83 let mut alpha = norm(&v);
84
85 if alpha > 0.0 {
86 scale_inplace(&mut v, 1.0 / alpha);
87 }
88
89 let mut w = v.clone();
90 let mut x = vec![0.0; n];
91
92 let mut phi_bar = beta;
93 let mut rho_bar = alpha;
94
95 let bnorm = beta;
96
97 for _iter in 0..max_iter {
98 let mut u_new = apply_a(&v);
100 axpy(&mut u_new, -alpha, &u);
101 beta = norm(&u_new);
102
103 if beta > 0.0 {
104 scale_inplace(&mut u_new, 1.0 / beta);
105 }
106 u = u_new;
107
108 let mut v_new = apply_at(&u);
109 axpy(&mut v_new, -beta, &v);
110 alpha = norm(&v_new);
111
112 if alpha > 0.0 {
113 scale_inplace(&mut v_new, 1.0 / alpha);
114 }
115 v = v_new;
116
117 let rho = (rho_bar * rho_bar + beta * beta).sqrt();
119 let c = rho_bar / rho;
120 let s = beta / rho;
121 let theta = s * alpha;
122 rho_bar = -c * alpha;
123 let phi = c * phi_bar;
124 phi_bar *= s;
125
126 let t1 = phi / rho;
128 let t2 = -theta / rho;
129
130 for i in 0..n {
131 x[i] += t1 * w[i];
132 w[i] = v[i] + t2 * w[i];
133 }
134
135 let rel_residual = phi_bar / (bnorm + 1e-20);
137
138 if rel_residual < tol {
139 break;
140 }
141 }
142
143 x
144}
145
146fn norm_complex(x: &[Complex64]) -> f64 {
152 x.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
153}
154
155fn scale_complex_inplace(x: &mut [Complex64], s: f64) {
157 for v in x.iter_mut() {
158 *v *= s;
159 }
160}
161
162fn axpy_complex(y: &mut [Complex64], a: f64, x: &[Complex64]) {
164 for (yi, xi) in y.iter_mut().zip(x.iter()) {
165 *yi += a * xi;
166 }
167}
168
169pub fn lsqr_complex<F, G>(
178 apply_a: F,
179 apply_ah: G,
180 b: &[Complex64],
181 tol: f64,
182 max_iter: usize,
183 verbose: bool,
184) -> Vec<Complex64>
185where
186 F: Fn(&[Complex64]) -> Vec<Complex64>,
187 G: Fn(&[Complex64]) -> Vec<Complex64>,
188{
189 let mut u = b.to_vec();
191 let mut beta = norm_complex(&u);
192
193 if beta > 0.0 {
194 scale_complex_inplace(&mut u, 1.0 / beta);
195 }
196
197 let mut v = apply_ah(&u);
199 let n = v.len();
200 let mut alpha = norm_complex(&v);
201
202 if alpha > 0.0 {
203 scale_complex_inplace(&mut v, 1.0 / alpha);
204 }
205
206 let mut w = v.clone();
207 let mut x = vec![Complex64::new(0.0, 0.0); n];
208
209 let mut phi_bar = beta;
210 let mut rho_bar = alpha;
211
212 let bnorm = beta;
213 let atol = tol;
214 let btol = tol;
215
216 let mut norm_a2 = alpha * alpha;
218
219 let mut xxnorm = 0.0;
222 let mut z_sol = 0.0;
223 let mut cs2 = -1.0;
224 let mut sn2 = 0.0;
225
226 if alpha * beta == 0.0 {
227 return x;
228 }
229
230 for _iter in 0..max_iter {
231 let mut u_new = apply_a(&v);
233 axpy_complex(&mut u_new, -alpha, &u);
234 beta = norm_complex(&u_new);
235
236 if beta > 0.0 {
237 scale_complex_inplace(&mut u_new, 1.0 / beta);
238 }
239 u = u_new;
240
241 let mut v_new = apply_ah(&u);
242 axpy_complex(&mut v_new, -beta, &v);
243 alpha = norm_complex(&v_new);
244
245 if alpha > 0.0 {
246 scale_complex_inplace(&mut v_new, 1.0 / alpha);
247 }
248 v = v_new;
249
250 let rho = (rho_bar * rho_bar + beta * beta).sqrt();
252 let c = rho_bar / rho;
253 let s = beta / rho;
254 let theta = s * alpha;
255 rho_bar = -c * alpha;
256 let phi = c * phi_bar;
257 phi_bar *= s;
258
259 let delta = sn2 * rho;
261 let gambar = -cs2 * rho;
262 let rhs = phi - delta * z_sol;
263 let zbar = rhs / gambar;
264 let xnorm = (xxnorm + zbar * zbar).sqrt();
265 let gamma = (gambar * gambar + theta * theta).sqrt();
266 cs2 = gambar / gamma;
267 sn2 = theta / gamma;
268 z_sol = rhs / gamma;
269 xxnorm += z_sol * z_sol;
270
271 let t1 = phi / rho;
273 let t2 = -theta / rho;
274 for i in 0..n {
275 x[i] += t1 * w[i];
276 w[i] = v[i] + t2 * w[i];
277 }
278
279 let normr = phi_bar;
281 let norm_ar = alpha * (c * phi_bar).abs();
282
283 norm_a2 += beta * beta + alpha * alpha;
284 let norm_a = norm_a2.sqrt();
285
286 let test1 = normr / (bnorm + 1e-20);
303 let test2 = norm_ar / ((norm_a * normr) + 1e-20);
304 let _ = (btol, xnorm);
305
306 if verbose {
307 eprintln!(" LSQR iter {:>3}: ||r||/||b||={:.6e} ||A'r||/(||A||·||r||)={:.6e}",
308 _iter + 1, test1, test2);
309 }
310
311 if test1 <= atol {
312 if verbose {
313 eprintln!(" LSQR converged at iteration {} (relres={:.4e})",
314 _iter + 1, test1);
315 }
316 break;
317 }
318 }
319
320 x
321}
322
323pub fn lsmr<F, G>(
345 apply_a: F,
346 apply_at: G,
347 b: &[f64],
348 n: usize,
349 atol: f64,
350 btol: f64,
351 max_iter: usize,
352 _verbose: bool,
353) -> Vec<f64>
354where
355 F: Fn(&[f64]) -> Vec<f64>,
356 G: Fn(&[f64]) -> Vec<f64>,
357{
358 let mut u = b.to_vec();
364 let mut beta = norm(&u);
365
366 if beta > 0.0 {
367 scale_inplace(&mut u, 1.0 / beta);
368 }
369
370 let mut v = apply_at(&u);
371 let mut alpha = norm(&v);
372
373 if alpha > 0.0 {
374 scale_inplace(&mut v, 1.0 / alpha);
375 }
376
377 let mut alpha_bar = alpha;
379 let mut zeta_bar = alpha * beta;
380 let mut rho = 1.0;
381 let mut rho_bar = 1.0;
382 let mut c_bar = 1.0;
383 let mut s_bar = 0.0;
384
385 let mut h = v.clone();
386 let mut h_bar = vec![0.0; n];
387 let mut x = vec![0.0; n];
388
389 let normb = beta;
391 let mut betadd = beta;
392 let mut betad = 0.0;
393 let mut rhodold = 1.0;
394 let mut tautildeold = 0.0;
395 let mut thetatilde = 0.0;
396 let mut zeta = 0.0;
397 let d = 0.0;
398
399 let mut norm_a2 = alpha * alpha;
401 let mut maxrbar = 0.0f64;
402 let mut minrbar = 1e100f64;
403 let conlim = 1e8;
404 let ctol = if conlim > 0.0 { 1.0 / conlim } else { 0.0 };
405
406 if alpha * beta == 0.0 {
408 return x;
409 }
410
411 for _iter in 0..max_iter {
412 let mut u_new = apply_a(&v);
414 axpy(&mut u_new, -alpha, &u);
415 beta = norm(&u_new);
416
417 if beta > 0.0 {
418 scale_inplace(&mut u_new, 1.0 / beta);
419 }
420 u = u_new;
421
422 let mut v_new = apply_at(&u);
423 axpy(&mut v_new, -beta, &v);
424 alpha = norm(&v_new);
425
426 if alpha > 0.0 {
427 scale_inplace(&mut v_new, 1.0 / alpha);
428 }
429 v = v_new;
430
431 let rho_old = rho;
433 rho = (alpha_bar * alpha_bar + beta * beta).sqrt();
434 let c = alpha_bar / rho;
435 let s = beta / rho;
436 let theta_new = s * alpha;
437 alpha_bar = c * alpha;
438
439 let rho_bar_old = rho_bar;
441 let zeta_old = zeta;
442 let theta_bar = s_bar * rho;
443 let rho_temp = c_bar * rho;
444 rho_bar = (rho_temp * rho_temp + theta_new * theta_new).sqrt();
445 c_bar = rho_temp / rho_bar;
446 s_bar = theta_new / rho_bar;
447 zeta = c_bar * zeta_bar;
448 zeta_bar *= -s_bar;
449
450 for i in 0..n {
452 h_bar[i] = h[i] - (theta_bar * rho / (rho_old * rho_bar_old)) * h_bar[i];
453 x[i] += (zeta / (rho * rho_bar)) * h_bar[i];
454 h[i] = v[i] - (theta_new / rho) * h[i];
455 }
456
457 let betaacute = betadd; let betahat = c * betaacute;
462 betadd = -s * betaacute;
463
464 let thetatildeold = thetatilde;
465 let rhotildeold = (rhodold * rhodold + theta_bar * theta_bar).sqrt();
466 let ctildeold = rhodold / rhotildeold;
467 let stildeold = theta_bar / rhotildeold;
468 thetatilde = stildeold * rho_bar;
469 rhodold = ctildeold * rho_bar;
470 betad = -stildeold * betad + ctildeold * betahat;
471
472 tautildeold = (zeta_old - thetatildeold * tautildeold) / rhotildeold;
473 let taud = (zeta - thetatilde * tautildeold) / rhodold;
474 let normr = (d + (betad - taud).powi(2) + betadd * betadd).sqrt();
476
477 norm_a2 += beta * beta;
479 let norm_a = norm_a2.sqrt();
480 norm_a2 += alpha * alpha;
481
482 maxrbar = maxrbar.max(rho_bar_old);
484 if _iter > 0 {
485 minrbar = minrbar.min(rho_bar_old);
486 }
487 let cond_a = maxrbar.max(rho_temp) / minrbar.min(rho_temp);
488
489 let norm_ar = zeta_bar.abs();
491 let normx = norm(&x);
492
493 let test1 = normr / (normb + 1e-20);
494 let test2 = norm_ar / ((norm_a * normr) + 1e-20);
495 let test3 = 1.0 / (cond_a + 1e-20);
496 let rtol = btol + atol * norm_a * normx / (normb + 1e-20);
497
498 if _verbose {
499 eprintln!(" LSMR iter {:>3}: ||r||/||b||={:.6e} ||A'r||/(||A||·||r||)={:.6e} 1/condA={:.6e} rtol={:.6e}",
500 _iter + 1, test1, test2, test3, rtol);
501 }
502
503 if test3 <= ctol || test2 <= atol || test1 <= rtol {
506 if _verbose {
507 let reason = if test1 <= rtol { "test1 (residual)"
508 } else if test2 <= atol { "test2 (||A'r||)"
509 } else { "test3 (cond(A))" };
510 eprintln!(" LSMR converged at iteration {} via {}", _iter + 1, reason);
511 }
512 break;
513 }
514 }
515
516 x
517}
518
519fn compute_laplacian(
529 f: &[f64],
530 mask: &[u8],
531 nx: usize, ny: usize, nz: usize,
532 vsx: f64, vsy: f64, vsz: f64,
533) -> Vec<f64> {
534 let n_total = nx * ny * nz;
535 let mut lap = vec![0.0; n_total];
536
537 let hx = 1.0 / (vsx * vsx);
538 let hy = 1.0 / (vsy * vsy);
539 let hz = 1.0 / (vsz * vsz);
540
541 let nxny = nx * ny;
542
543 for k in 0..nz {
544 for j in 0..ny {
545 let jk_offset = j * nx + k * nxny;
546 for i in 0..nx {
547 let l = i + jk_offset;
548
549 if mask[l] == 0 {
550 continue;
551 }
552
553 lap[l] += hx * lap1_axis(f, mask, l, 1, nx, i, nx);
555
556 lap[l] += hy * lap1_axis(f, mask, l, nx, nxny, j * nx, nxny);
558
559 lap[l] += hz * lap1_axis(f, mask, l, nxny, n_total, k * nxny, n_total);
561 }
562 }
563 }
564
565 lap
566}
567
568#[inline]
584fn lap1_axis(
585 f: &[f64],
586 mask: &[u8],
587 l: usize,
588 a: usize,
589 n_axis: usize,
590 coord: usize,
591 n_total: usize,
592) -> f64 {
593 let n_end = n_axis - a; let n_interior = n_axis - 2 * a; let stencil = if coord.wrapping_sub(a) < n_interior {
600 2 * (mask[l + a] as u8) + (mask[l - a] as u8)
602 } else {
603 if coord == 0 { 2 } else if coord == n_end { 1 } else { 0 }
605 };
606
607 match stencil {
608 3 => {
609 f[l - a] - 2.0 * f[l] + f[l + a]
611 }
612 2 => {
613 lap1_forward(f, mask, l, a, n_axis, coord, n_total)
615 }
616 1 => {
617 lap1_backward(f, mask, l, a, n_axis, coord, n_total)
619 }
620 _ => 0.0, }
622}
623
624#[inline]
626fn lap1_forward(
627 f: &[f64],
628 mask: &[u8],
629 l: usize,
630 a: usize,
631 n_axis: usize,
632 coord: usize,
633 _n_total: usize,
634) -> f64 {
635 if coord + 3 * a < n_axis && mask[l + 2 * a] != 0 && mask[l + 3 * a] != 0 {
637 2.0 * f[l] - 5.0 * f[l + a] + 4.0 * f[l + 2 * a] - f[l + 3 * a]
638 }
639 else if coord + 2 * a < n_axis && mask[l + 2 * a] != 0 {
641 f[l] - 2.0 * f[l + a] + f[l + 2 * a]
642 }
643 else {
645 f[l + a] - f[l]
646 }
647}
648
649#[inline]
651fn lap1_backward(
652 f: &[f64],
653 mask: &[u8],
654 l: usize,
655 a: usize,
656 n_axis: usize,
657 coord: usize,
658 _n_total: usize,
659) -> f64 {
660 if coord.wrapping_sub(3 * a) < n_axis && mask[l - 3 * a] != 0 && mask[l - 2 * a] != 0 {
662 -f[l - 3 * a] + 4.0 * f[l - 2 * a] - 5.0 * f[l - a] + 2.0 * f[l]
663 }
664 else if coord.wrapping_sub(2 * a) < n_axis && mask[l - 2 * a] != 0 {
666 f[l - 2 * a] - 2.0 * f[l - a] + f[l]
667 }
668 else {
670 f[l - a] - f[l]
671 }
672}
673
674fn laplacian_weights_ilsqr(
678 f: &[f64],
679 mask: &[u8],
680 nx: usize, ny: usize, nz: usize,
681 vsx: f64, vsy: f64, vsz: f64,
682 pmin: f64,
683 pmax: f64,
684) -> Vec<f64> {
685 let n_total = nx * ny * nz;
686 let mut w = vec![0.0; n_total];
687
688 let lap = compute_laplacian(f, mask, nx, ny, nz, vsx, vsy, vsz);
690
691 let mut masked_lap: Vec<f64> = lap.iter()
693 .zip(mask.iter())
694 .filter(|(_, &m)| m > 0)
695 .map(|(&l, _)| l)
696 .collect();
697
698 if masked_lap.is_empty() {
699 return w;
700 }
701
702 masked_lap.sort_by(|a, b| a.partial_cmp(b).unwrap());
704
705 let thr_min = prctile(&masked_lap, pmin);
706 let thr_max = prctile(&masked_lap, pmax);
707
708 let range = thr_max - thr_min;
709
710 for i in 0..n_total {
712 if mask[i] == 0 {
713 continue;
714 }
715
716 let l = lap[i];
717
718 if l < thr_min {
719 w[i] = 1.0;
720 } else if l > thr_max {
721 w[i] = 0.0;
722 } else if range > 1e-10 {
723 w[i] = (thr_max - l) / range;
724 }
725 }
726
727 w
728}
729
730fn dipole_kspace_weights_ilsqr(
734 d: &[f64],
735 n_exp: f64,
736 pa: f64,
737 pb: f64,
738) -> Vec<f64> {
739 let len = d.len();
740 let mut w = vec![0.0; len];
741
742 for i in 0..len {
744 w[i] = d[i].abs().powf(n_exp);
745 }
746
747 let mut vals: Vec<f64> = w.to_vec();
749 vals.sort_by(|a, b| a.partial_cmp(b).unwrap());
750
751 if vals.is_empty() {
752 return vec![0.0; len];
753 }
754
755 let ab_min = prctile(&vals, pa);
756 let ab_max = prctile(&vals, pb);
757
758 let range = ab_max - ab_min;
759
760 for i in 0..len {
762 if range > 1e-20 {
763 w[i] = (w[i] - ab_min) / range;
764 }
765 w[i] = w[i].max(0.0).min(1.0);
766 }
767
768 w
769}
770
771fn fgrad_masked(
777 f: &[f64],
778 mask: &[u8],
779 nx: usize, ny: usize, nz: usize,
780 vsx: f64, vsy: f64, vsz: f64,
781) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
782 let n_total = nx * ny * nz;
783 let mut dx = vec![0.0; n_total];
784 let mut dy = vec![0.0; n_total];
785 let mut dz = vec![0.0; n_total];
786
787 let hx = 1.0 / vsx;
788 let hy = 1.0 / vsy;
789 let hz = 1.0 / vsz;
790
791 let nxny = nx * ny;
792
793 for k in 0..nz {
794 for j in 0..ny {
795 let jk = j * nx + k * nxny;
796 for i in 0..nx {
797 let l = i + jk;
798 if mask[l] == 0 { continue; }
799
800 dx[l] = if i < nx - 1 && mask[l + 1] != 0 {
802 hx * (f[l + 1] - f[l])
803 } else if i > 0 && mask[l - 1] != 0 {
804 hx * (f[l] - f[l - 1])
805 } else {
806 0.0
807 };
808
809 dy[l] = if j < ny - 1 && mask[l + nx] != 0 {
811 hy * (f[l + nx] - f[l])
812 } else if j > 0 && mask[l - nx] != 0 {
813 hy * (f[l] - f[l - nx])
814 } else {
815 0.0
816 };
817
818 dz[l] = if k < nz - 1 && mask[l + nxny] != 0 {
820 hz * (f[l + nxny] - f[l])
821 } else if k > 0 && mask[l - nxny] != 0 {
822 hz * (f[l] - f[l - nxny])
823 } else {
824 0.0
825 };
826 }
827 }
828 }
829
830 (dx, dy, dz)
831}
832
833fn gradient_weights_ilsqr(
835 x: &[f64],
836 mask: &[u8],
837 nx: usize, ny: usize, nz: usize,
838 vsx: f64, vsy: f64, vsz: f64,
839 pmin: f64,
840 pmax: f64,
841) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
842 let (gx, gy, gz) = fgrad_masked(x, mask, nx, ny, nz, vsx, vsy, vsz);
844
845 let wx = gradient_weights_component(&gx, mask, pmin, pmax);
847 let wy = gradient_weights_component(&gy, mask, pmin, pmax);
848 let wz = gradient_weights_component(&gz, mask, pmin, pmax);
849
850 (wx, wy, wz)
851}
852
853fn gradient_weights_component(
854 g: &[f64],
855 mask: &[u8],
856 pmin: f64,
857 pmax: f64,
858) -> Vec<f64> {
859 let len = g.len();
860 let mut w = vec![0.0; len];
861
862 let mut masked_g: Vec<f64> = g.iter()
864 .zip(mask.iter())
865 .filter(|(_, &m)| m > 0)
866 .map(|(&v, _)| v)
867 .collect();
868
869 if masked_g.is_empty() {
870 return w;
871 }
872
873 masked_g.sort_by(|a, b| a.partial_cmp(b).unwrap());
874
875 let thr_min = prctile(&masked_g, pmin);
876 let thr_max = prctile(&masked_g, pmax);
877
878 let range = thr_max - thr_min;
879
880 for i in 0..len {
881 if mask[i] == 0 {
882 continue;
883 }
884
885 let v = g[i];
886
887 if v < thr_min {
888 w[i] = 1.0;
889 } else if v > thr_max {
890 w[i] = 0.0;
891 } else if range > 1e-10 {
892 w[i] = (thr_max - v) / range;
893 }
894
895 w[i] *= mask[i] as f64;
897 }
898
899 w
900}
901
902fn norm(x: &[f64]) -> f64 {
907 x.iter().map(|&v| v * v).sum::<f64>().sqrt()
908}
909
910fn scale_inplace(x: &mut [f64], s: f64) {
911 for v in x.iter_mut() {
912 *v *= s;
913 }
914}
915
916fn axpy(y: &mut [f64], a: f64, x: &[f64]) {
917 for (yi, &xi) in y.iter_mut().zip(x.iter()) {
918 *yi += a * xi;
919 }
920}
921
922fn multiply_elementwise(a: &[f64], b: &[f64]) -> Vec<f64> {
923 a.iter().zip(b.iter()).map(|(&ai, &bi)| ai * bi).collect()
924}
925
926fn sign_array(x: &[f64]) -> Vec<f64> {
927 x.iter().map(|&v| {
928 if v > 0.0 { 1.0 }
929 else if v < 0.0 { -1.0 }
930 else { 0.0 }
931 }).collect()
932}
933
934fn prctile(sorted: &[f64], p: f64) -> f64 {
938 let n = sorted.len();
939 if n == 0 { return 0.0; }
940 if n == 1 { return sorted[0]; }
941 let h = (p / 100.0) * (n - 1) as f64;
942 let lo = h.floor() as usize;
943 let hi = (lo + 1).min(n - 1);
944 let frac = h - lo as f64;
945 sorted[lo] + frac * (sorted[hi] - sorted[lo])
946}
947
948fn lsqr_step(
954 f: &[f64],
955 mask: &[u8],
956 d: &[f64],
957 nx: usize, ny: usize, nz: usize,
958 vsx: f64, vsy: f64, vsz: f64,
959 workspace: &mut Fft3dWorkspace,
960) -> Vec<f64> {
961
962 let pmin = 60.0;
964 let pmax = 99.9;
965 let tol_lsqr = 0.01;
966 let maxit_lsqr = 50;
967
968 let w = laplacian_weights_ilsqr(f, mask, nx, ny, nz, vsx, vsy, vsz, pmin, pmax);
970
971 let wf: Vec<Complex64> = w.iter().zip(f.iter())
973 .map(|(&wi, &fi)| Complex64::new(wi * fi, 0.0))
974 .collect();
975
976 let mut wf_fft = wf.clone();
977 workspace.fft3d(&mut wf_fft);
978
979 let b: Vec<Complex64> = wf_fft.iter().zip(d.iter())
981 .map(|(wfi, &di)| wfi * di)
982 .collect();
983
984 let lsqr_ws = RefCell::new(Fft3dWorkspace::new(nx, ny, nz));
988 let apply_a = |x: &[Complex64]| -> Vec<Complex64> {
989 let dx: Vec<Complex64> = x.iter().zip(d.iter())
991 .map(|(xi, &di)| xi * di)
992 .collect();
993
994 let mut dx_ifft = dx.clone();
996 let mut ws = lsqr_ws.borrow_mut();
997 ws.ifft3d(&mut dx_ifft);
998
999 let wdx: Vec<Complex64> = w.iter().zip(dx_ifft.iter())
1001 .map(|(&wi, dxi)| Complex64::new(wi * dxi.re, 0.0))
1002 .collect();
1003
1004 let mut wdx_fft = wdx.clone();
1006 ws.fft3d(&mut wdx_fft);
1007
1008 wdx_fft.iter().zip(d.iter())
1010 .map(|(wdxi, &di)| wdxi * di)
1011 .collect()
1012 };
1013
1014 let apply_ah = |x: &[Complex64]| -> Vec<Complex64> {
1016 apply_a(x)
1017 };
1018
1019 let x_lsqr = lsqr_complex(apply_a, apply_ah, &b, tol_lsqr, maxit_lsqr, false);
1021
1022 let mut x_ifft = x_lsqr;
1024 workspace.ifft3d(&mut x_ifft);
1025
1026 x_ifft.iter().zip(mask.iter())
1028 .map(|(xi, &mi)| if mi > 0 { xi.re } else { 0.0 })
1029 .collect()
1030}
1031
1032fn fastqsm_step(
1038 f: &[f64],
1039 mask: &[u8],
1040 d: &[f64],
1041 nx: usize, ny: usize, nz: usize,
1042 vsx: f64, vsy: f64, vsz: f64,
1043 workspace: &mut Fft3dWorkspace,
1044) -> Vec<f64> {
1045 let n_total = nx * ny * nz;
1046
1047 let f_complex: Vec<Complex64> = f.iter()
1049 .map(|&v| Complex64::new(v, 0.0))
1050 .collect();
1051
1052 let mut f_fft = f_complex;
1053 workspace.fft3d(&mut f_fft);
1054
1055 let sign_d = sign_array(d);
1057 let x: Vec<Complex64> = f_fft.iter().zip(sign_d.iter())
1058 .map(|(fi, &si)| fi * si)
1059 .collect();
1060
1061 let pa = 1.0;
1063 let pb = 30.0;
1064 let n_exp = 0.001;
1065 let wfs = dipole_kspace_weights_ilsqr(d, n_exp, pa, pb);
1066
1067 let r_smv = 3.0;
1069 let smv_grid = Grid::new(nx, ny, nz, vsx, vsy, vsz);
1070 let h = smv_kernel(&smv_grid, r_smv);
1071
1072 let h_complex: Vec<Complex64> = h.iter()
1074 .map(|&v| Complex64::new(v, 0.0))
1075 .collect();
1076 let mut h_fft_complex = h_complex;
1077 workspace.fft3d(&mut h_fft_complex);
1078 let h_fft: Vec<f64> = h_fft_complex.iter().map(|c| c.re).collect();
1079
1080 let mut x_filtered: Vec<Complex64> = x.iter()
1083 .zip(wfs.iter())
1084 .zip(h_fft.iter())
1085 .map(|((xi, &wi), &hi)| {
1086 xi * wi + xi * hi * (1.0 - wi)
1087 })
1088 .collect();
1089
1090 workspace.ifft3d(&mut x_filtered);
1091
1092 for (xi, &mi) in x_filtered.iter_mut().zip(mask.iter()) {
1094 if mi == 0 {
1095 *xi = Complex64::new(0.0, 0.0);
1096 } else {
1097 *xi = Complex64::new(xi.re, 0.0);
1098 }
1099 }
1100
1101 workspace.fft3d(&mut x_filtered);
1102
1103 let mut x_filtered2: Vec<Complex64> = x_filtered.iter()
1105 .zip(wfs.iter())
1106 .zip(h_fft.iter())
1107 .map(|((xi, &wi), &hi)| {
1108 xi * wi + xi * hi * (1.0 - wi)
1109 })
1110 .collect();
1111
1112 workspace.ifft3d(&mut x_filtered2);
1113
1114 let x_fs: Vec<f64> = x_filtered2.iter().zip(mask.iter())
1115 .map(|(xi, &mi)| if mi > 0 { xi.re } else { 0.0 })
1116 .collect();
1117
1118 let t0 = 1.0 / 8.0;
1120 let mut inv_d = vec![0.0; n_total];
1121 for i in 0..n_total {
1122 if d[i].abs() < t0 {
1123 inv_d[i] = d[i].signum() / t0;
1124 } else {
1125 inv_d[i] = 1.0 / d[i];
1126 }
1127 }
1128
1129 let x_tkd_fft: Vec<Complex64> = f_fft.iter().zip(inv_d.iter())
1130 .map(|(fi, &idi)| fi * idi)
1131 .collect();
1132
1133 let mut x_tkd_complex = x_tkd_fft;
1134 workspace.ifft3d(&mut x_tkd_complex);
1135
1136 let x_tkd: Vec<f64> = x_tkd_complex.iter().zip(mask.iter())
1137 .map(|(xi, &mi)| if mi > 0 { xi.re } else { 0.0 })
1138 .collect();
1139
1140 let sum_xfs: f64 = x_fs.iter().map(|&v| v).sum();
1144 let sum_xtkd: f64 = x_tkd.iter().map(|&v| v).sum();
1145 let sum_xfs2: f64 = x_fs.iter().map(|&v| v * v).sum();
1146 let sum_xfs_xtkd: f64 = x_fs.iter().zip(x_tkd.iter())
1147 .map(|(&xf, &xt)| xf * xt)
1148 .sum();
1149
1150 let n_all: f64 = n_total as f64;
1151
1152 let det = sum_xfs2 * n_all - sum_xfs * sum_xfs;
1154
1155 let (a, b) = if det.abs() > 1e-20 {
1156 let a = (n_all * sum_xfs_xtkd - sum_xfs * sum_xtkd) / det;
1157 let b = (sum_xfs2 * sum_xtkd - sum_xfs * sum_xfs_xtkd) / det;
1158 (a, b)
1159 } else {
1160 (1.0, 0.0)
1161 };
1162
1163 x_fs.iter().zip(mask.iter())
1165 .map(|(&xf, &mi)| if mi > 0 { a * xf + b } else { 0.0 })
1166 .collect()
1167}
1168
1169fn susceptibility_artifacts_step(
1175 x0: &[f64],
1176 xfs: &[f64],
1177 mask: &[u8],
1178 d: &[f64],
1179 nx: usize, ny: usize, nz: usize,
1180 vsx: f64, vsy: f64, vsz: f64,
1181 tol: f64,
1182 maxit: usize,
1183 _workspace: &mut Fft3dWorkspace,
1184) -> Vec<f64> {
1185 let n_total = nx * ny * nz;
1186
1187 let pmin = 50.0;
1189 let pmax = 70.0;
1190 let (wx, wy, wz) = gradient_weights_ilsqr(xfs, mask, nx, ny, nz, vsx, vsy, vsz, pmin, pmax);
1191
1192 let thr = 0.1;
1194 let mic: Vec<f64> = d.iter().map(|&di| if di.abs() < thr { 1.0 } else { 0.0 }).collect();
1195
1196 let step3_grid = Grid::new(nx, ny, nz, vsx, vsy, vsz);
1198 let (dx, dy, dz) = fgrad(x0, &step3_grid);
1199
1200 let bx = multiply_elementwise(&wx, &dx);
1202 let by = multiply_elementwise(&wy, &dy);
1203 let bz = multiply_elementwise(&wz, &dz);
1204
1205 let mut b = Vec::with_capacity(3 * n_total);
1206 b.extend_from_slice(&bx);
1207 b.extend_from_slice(&by);
1208 b.extend_from_slice(&bz);
1209
1210 let lsmr_ws = RefCell::new(Fft3dWorkspace::new(nx, ny, nz));
1213 let apply_a = |x_in: &[f64]| -> Vec<f64> {
1214 let x_complex: Vec<Complex64> = x_in.iter()
1217 .map(|&v| Complex64::new(v, 0.0))
1218 .collect();
1219
1220 let mut x_fft = x_complex;
1221 let mut ws = lsmr_ws.borrow_mut();
1222 ws.fft3d(&mut x_fft);
1223
1224 let x_mic: Vec<Complex64> = x_fft.iter().zip(mic.iter())
1226 .map(|(xi, &mi)| xi * mi)
1227 .collect();
1228
1229 let mut x_ifft = x_mic;
1230 ws.ifft3d(&mut x_ifft);
1231
1232 let x_filtered: Vec<f64> = x_ifft.iter().map(|xi| xi.re).collect();
1233
1234 let (gx, gy, gz) = fgrad(&x_filtered, &step3_grid);
1236
1237 let mut result = Vec::with_capacity(3 * n_total);
1239 result.extend(wx.iter().zip(gx.iter()).map(|(&w, &g)| w * g));
1240 result.extend(wy.iter().zip(gy.iter()).map(|(&w, &g)| w * g));
1241 result.extend(wz.iter().zip(gz.iter()).map(|(&w, &g)| w * g));
1242
1243 result
1244 };
1245
1246 let apply_at = |y_in: &[f64]| -> Vec<f64> {
1248 let yx = &y_in[0..n_total];
1250 let yy = &y_in[n_total..2*n_total];
1251 let yz = &y_in[2*n_total..3*n_total];
1252
1253 let wyx: Vec<f64> = wx.iter().zip(yx.iter()).map(|(&w, &y)| w * y).collect();
1255 let wyy: Vec<f64> = wy.iter().zip(yy.iter()).map(|(&w, &y)| w * y).collect();
1256 let wyz: Vec<f64> = wz.iter().zip(yz.iter()).map(|(&w, &y)| w * y).collect();
1257
1258 let div = bdiv(&wyx, &wyy, &wyz, &step3_grid);
1262
1263 let div_complex: Vec<Complex64> = div.iter()
1265 .map(|&v| Complex64::new(-v, 0.0))
1266 .collect();
1267
1268 let mut div_fft = div_complex;
1269 let mut ws = lsmr_ws.borrow_mut();
1270 ws.fft3d(&mut div_fft);
1271
1272 let div_mic: Vec<Complex64> = div_fft.iter().zip(mic.iter())
1273 .map(|(di, &mi)| di * mi)
1274 .collect();
1275
1276 let mut div_ifft = div_mic;
1277 ws.ifft3d(&mut div_ifft);
1278
1279 div_ifft.iter().map(|di| di.re).collect()
1280 };
1281
1282 let xsa = lsmr(apply_a, apply_at, &b, n_total, tol, tol, maxit, false);
1284
1285 xsa.iter().zip(mask.iter())
1287 .map(|(&x, &m)| if m > 0 { x } else { 0.0 })
1288 .collect()
1289}
1290
1291pub fn ilsqr(
1308 field: &[f64],
1309 mask: &[u8],
1310 grid: &Grid,
1311 bdir: (f64, f64, f64),
1312 params: &IlsqrParams,
1313 mut progress: impl FnMut(usize, usize),
1314) -> (Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>) {
1315 let (nx, ny, nz) = grid.dims;
1316 let (vsx, vsy, vsz) = grid.voxel_size;
1317
1318 let d = dipole_kernel(grid, bdir);
1320
1321 let mut workspace = Fft3dWorkspace::new(nx, ny, nz);
1323
1324 progress(1, 4);
1325
1326 let xlsqr = lsqr_step(field, mask, &d, nx, ny, nz, vsx, vsy, vsz, &mut workspace);
1328
1329 progress(2, 4);
1330
1331 let xfs = fastqsm_step(field, mask, &d, nx, ny, nz, vsx, vsy, vsz, &mut workspace);
1333
1334 progress(3, 4);
1335
1336 let xsa = susceptibility_artifacts_step(
1338 &xlsqr, &xfs, mask, &d,
1339 nx, ny, nz, vsx, vsy, vsz,
1340 params.tol, params.max_iter, &mut workspace
1341 );
1342
1343 progress(4, 4);
1344
1345 let chi: Vec<f64> = xlsqr.iter().zip(xsa.iter()).zip(mask.iter())
1347 .map(|((&xl, &xs), &m)| if m > 0 { xl - xs } else { 0.0 })
1348 .collect();
1349
1350 (chi, xsa, xfs, xlsqr)
1351}
1352
1353#[cfg(test)]
1354mod tests {
1355 use super::*;
1356
1357 #[test]
1358 fn test_lsqr_simple() {
1359 let n = 10;
1361 let diag: Vec<f64> = (1..=n).map(|i| i as f64).collect();
1362 let b: Vec<f64> = diag.iter().map(|&d| d * 2.0).collect(); let apply_a = |x: &[f64]| -> Vec<f64> {
1365 x.iter().zip(diag.iter()).map(|(&xi, &di)| xi * di).collect()
1366 };
1367
1368 let x = lsqr(apply_a, apply_a, &b, 1e-10, 100);
1369
1370 for (i, &xi) in x.iter().enumerate() {
1371 assert!((xi - 2.0).abs() < 1e-6, "x[{}] = {}, expected 2.0", i, xi);
1372 }
1373 }
1374
1375 #[test]
1376 fn test_norm() {
1377 let x = vec![3.0, 4.0];
1378 assert!((norm(&x) - 5.0).abs() < 1e-10);
1379 }
1380
1381 #[test]
1382 fn test_sign_array() {
1383 let x = vec![-2.0, 0.0, 3.0];
1384 let s = sign_array(&x);
1385 assert_eq!(s, vec![-1.0, 0.0, 1.0]);
1386 }
1387
1388 #[test]
1389 fn test_lsqr_complex_relres_stop() {
1390 let diag = vec![1.0, 10.0, 100.0, 1000.0];
1401 let expected: Vec<Complex64> =
1402 diag.iter().map(|&d| Complex64::new(d, 0.0)).collect();
1403 let b: Vec<Complex64> = expected
1404 .iter()
1405 .zip(diag.iter())
1406 .map(|(&xi, &di)| xi * di)
1407 .collect();
1408
1409 let da = diag.clone();
1410 let apply = move |x: &[Complex64]| -> Vec<Complex64> {
1411 x.iter().zip(da.iter()).map(|(&xi, &di)| xi * di).collect()
1412 };
1413 let apply2 = apply.clone();
1414 let tol = 1e-6;
1415 let x = lsqr_complex(apply, apply2, &b, tol, 200, false);
1416
1417 let ax: Vec<Complex64> =
1419 x.iter().zip(diag.iter()).map(|(&xi, &di)| xi * di).collect();
1420 let r: f64 = ax
1421 .iter()
1422 .zip(b.iter())
1423 .map(|(a, bb)| (a - bb).norm_sqr())
1424 .sum::<f64>()
1425 .sqrt();
1426 let bn: f64 = b.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
1427 assert!(
1428 r / bn <= tol * 10.0,
1429 "relres {} should reach ~tol {}; premature-stop regression",
1430 r / bn,
1431 tol
1432 );
1433 }
1434
1435 #[test]
1436 fn test_lsqr_complex_diagonal() {
1437 let diag = vec![1.0, 2.0, 3.0];
1440 let expected = vec![
1441 Complex64::new(1.0, 1.0),
1442 Complex64::new(2.0, 1.0),
1443 Complex64::new(3.0, 1.0),
1444 ];
1445 let b: Vec<Complex64> = expected.iter().zip(diag.iter())
1446 .map(|(&xi, &di)| xi * di)
1447 .collect();
1448
1449 let diag_a = diag.clone();
1450 let diag_ah = diag.clone();
1451 let apply_a = move |x: &[Complex64]| -> Vec<Complex64> {
1452 x.iter().zip(diag_a.iter()).map(|(&xi, &di)| xi * di).collect()
1453 };
1454 let apply_ah = move |x: &[Complex64]| -> Vec<Complex64> {
1455 x.iter().zip(diag_ah.iter()).map(|(&xi, &di)| xi * di).collect()
1456 };
1457
1458 let x = lsqr_complex(apply_a, apply_ah, &b, 1e-10, 100, false);
1459
1460 for (i, (xi, ei)) in x.iter().zip(expected.iter()).enumerate() {
1461 assert!((xi.re - ei.re).abs() < 1e-6,
1462 "x[{}].re = {}, expected {}", i, xi.re, ei.re);
1463 assert!((xi.im - ei.im).abs() < 1e-6,
1464 "x[{}].im = {}, expected {}", i, xi.im, ei.im);
1465 }
1466 }
1467
1468 #[test]
1469 fn test_lsmr_diagonal() {
1470 let b = vec![3.0, 5.0, 7.0];
1474
1475 let apply_a = |x: &[f64]| -> Vec<f64> { x.to_vec() };
1476 let apply_at = |x: &[f64]| -> Vec<f64> { x.to_vec() };
1477
1478 let x = lsmr(apply_a, apply_at, &b, 3, 1e-6, 1e-6, 200, false);
1479
1480 assert_eq!(x.len(), 3);
1482 for (i, &xi) in x.iter().enumerate() {
1483 assert!(xi.is_finite(), "x[{}] = {} is not finite", i, xi);
1484 }
1485
1486 let residual: f64 = x.iter().zip(b.iter())
1488 .map(|(&xi, &bi)| (xi - bi).powi(2))
1489 .sum::<f64>()
1490 .sqrt();
1491 let bnorm: f64 = b.iter().map(|&bi| bi * bi).sum::<f64>().sqrt();
1492 assert!(residual < bnorm,
1493 "residual {} should be less than ||b|| = {}", residual, bnorm);
1494 }
1495
1496 #[test]
1497 fn test_laplacian_weights() {
1498 let (nx, ny, nz) = (4, 4, 4);
1501 let n_total = nx * ny * nz;
1502 let mut mask = vec![0u8; n_total];
1503 let mut field = vec![0.0; n_total];
1504
1505 for k in 0..nz {
1507 for j in 0..ny {
1508 for i in 0..nx {
1509 let idx = i + j * nx + k * nx * ny;
1510 let ci = i as f64 - 1.5;
1511 let cj = j as f64 - 1.5;
1512 let ck = k as f64 - 1.5;
1513 let r2 = ci * ci + cj * cj + ck * ck;
1514 if r2 < 2.5 {
1515 mask[idx] = 1;
1516 field[idx] = 5.0; }
1518 }
1519 }
1520 }
1521
1522 let w = laplacian_weights_ilsqr(&field, &mask, nx, ny, nz, 1.0, 1.0, 1.0, 10.0, 90.0);
1523
1524 for (i, &wi) in w.iter().enumerate() {
1526 assert!(wi.is_finite(), "weight[{}] is not finite", i);
1527 assert!(wi >= 0.0 && wi <= 1.0, "weight[{}] = {} out of [0,1]", i, wi);
1528 }
1529
1530 for i in 0..n_total {
1532 if mask[i] == 0 {
1533 assert_eq!(w[i], 0.0, "weight outside mask should be 0 at index {}", i);
1534 }
1535 }
1536 }
1537
1538 #[test]
1539 fn test_dipole_kspace_weights() {
1540 let d = vec![0.0, 0.01, 0.1, 0.3, 0.5, 0.7, 1.0, -0.5, -1.0, 0.0];
1542
1543 let w = dipole_kspace_weights_ilsqr(&d, 1.0, 1.0, 90.0);
1544
1545 for (i, &wi) in w.iter().enumerate() {
1547 assert!(wi >= 0.0 && wi <= 1.0,
1548 "weight[{}] = {} out of [0,1]", i, wi);
1549 }
1550
1551 assert!(w[0] <= 1e-10, "weight at D=0 should be ~0, got {}", w[0]);
1553
1554 assert!(w[6] > 0.5, "weight at |D|=1.0 should be large, got {}", w[6]);
1557 assert!(w[8] > 0.5, "weight at |D|=1.0 should be large, got {}", w[8]);
1558 }
1559
1560 #[test]
1561 fn test_gradient_weights() {
1562 let (nx, ny, nz) = (4, 4, 4);
1564 let n_total = nx * ny * nz;
1565
1566 let mask = vec![1u8; n_total];
1568
1569 let mut field = vec![0.0; n_total];
1571 for k in 0..nz {
1572 for j in 0..ny {
1573 for i in 0..nx {
1574 let idx = i + j * nx + k * nx * ny;
1575 field[idx] = i as f64; }
1577 }
1578 }
1579
1580 let (wx, wy, wz) = gradient_weights_ilsqr(
1581 &field, &mask, nx, ny, nz, 1.0, 1.0, 1.0, 10.0, 90.0
1582 );
1583
1584 for i in 0..n_total {
1586 assert!(wx[i].is_finite(), "wx[{}] is not finite", i);
1587 assert!(wy[i].is_finite(), "wy[{}] is not finite", i);
1588 assert!(wz[i].is_finite(), "wz[{}] is not finite", i);
1589 assert!(wx[i] >= 0.0 && wx[i] <= 1.0, "wx[{}] = {} out of [0,1]", i, wx[i]);
1590 assert!(wy[i] >= 0.0 && wy[i] <= 1.0, "wy[{}] = {} out of [0,1]", i, wy[i]);
1591 assert!(wz[i] >= 0.0 && wz[i] <= 1.0, "wz[{}] = {} out of [0,1]", i, wz[i]);
1592 }
1593
1594 let wy_sum: f64 = wy.iter().sum();
1597 let wz_sum: f64 = wz.iter().sum();
1598 assert!(wy_sum.is_finite(), "wy sum is not finite");
1599 assert!(wz_sum.is_finite(), "wz sum is not finite");
1600 }
1601
1602 #[test]
1603 fn test_ilsqr_small() {
1604 let (nx, ny, nz) = (8, 8, 8);
1608 let n_total = nx * ny * nz;
1609 let vsx = 1.0;
1610 let vsy = 1.0;
1611 let vsz = 1.0;
1612 let bdir = (0.0, 0.0, 1.0);
1613
1614 let mut mask = vec![0u8; n_total];
1616 let cx = (nx as f64 - 1.0) / 2.0;
1617 let cy = (ny as f64 - 1.0) / 2.0;
1618 let cz = (nz as f64 - 1.0) / 2.0;
1619 let radius = 3.0;
1620
1621 for k in 0..nz {
1622 for j in 0..ny {
1623 for i in 0..nx {
1624 let idx = i + j * nx + k * nx * ny;
1625 let di = i as f64 - cx;
1626 let dj = j as f64 - cy;
1627 let dk = k as f64 - cz;
1628 if di * di + dj * dj + dk * dk < radius * radius {
1629 mask[idx] = 1;
1630 }
1631 }
1632 }
1633 }
1634
1635 let mut field = vec![0.0; n_total];
1638 for k in 0..nz {
1639 for j in 0..ny {
1640 for i in 0..nx {
1641 let idx = i + j * nx + k * nx * ny;
1642 if mask[idx] > 0 {
1643 let di = i as f64 - cx;
1644 let dj = j as f64 - cy;
1645 let dk = k as f64 - cz;
1646 field[idx] = 0.01 * (dk * dk - di * di - dj * dj)
1648 / (di * di + dj * dj + dk * dk + 1.0);
1649 }
1650 }
1651 }
1652 }
1653
1654 let tol = 0.1;
1655 let maxit = 5; let grid = Grid::new(nx, ny, nz, vsx, vsy, vsz);
1658 let params = IlsqrParams { tol, max_iter: maxit };
1659 let (chi, _, _, _) = ilsqr(&field, &mask, &grid, bdir, ¶ms, |_, _| {});
1660
1661 assert_eq!(chi.len(), n_total, "output size mismatch");
1663
1664 for (i, &v) in chi.iter().enumerate() {
1666 assert!(v.is_finite(), "chi[{}] = {} is not finite", i, v);
1667 }
1668
1669 for i in 0..n_total {
1671 if mask[i] == 0 {
1672 assert_eq!(chi[i], 0.0, "chi outside mask should be 0 at index {}", i);
1673 }
1674 }
1675
1676 let inside_sum: f64 = chi.iter().zip(mask.iter())
1678 .filter(|(_, &m)| m > 0)
1679 .map(|(&v, _)| v.abs())
1680 .sum();
1681 assert!(inside_sum > 0.0, "chi should not be all zeros inside the mask");
1682 }
1683}