1use num_complex::Complex64;
14use crate::Grid;
15use crate::fft::{fft3d, ifft3d};
16use crate::kernels::dipole::dipole_kernel;
17use crate::utils::vec_norm;
18
19#[cfg_attr(feature = "introspection", derive(serde::Serialize))]
21#[derive(Clone, Debug)]
22pub struct PdfParams {
23 pub tol: f64,
25 pub max_iter: Option<usize>,
28}
29
30impl Default for PdfParams {
31 fn default() -> Self {
32 Self { tol: 1e-5, max_iter: None }
33 }
34}
35
36pub fn pdf(
49 field: &[f64],
50 mask: &[u8],
51 grid: &Grid,
52 bdir: (f64, f64, f64),
53 params: &PdfParams,
54 progress: impl FnMut(usize, usize),
55) -> Vec<f64> {
56 let max_iter = params.max_iter.unwrap_or_else(|| (grid.n_total() as f64).sqrt() as usize);
58 pdf_core(field, mask, grid, bdir, params.tol, max_iter, progress)
59}
60
61pub(crate) fn pdf_core(
66 field: &[f64],
67 mask: &[u8],
68 grid: &Grid,
69 bdir: (f64, f64, f64),
70 tol: f64,
71 max_iter: usize,
72 mut progress: impl FnMut(usize, usize),
73) -> Vec<f64> {
74 let (nx, ny, nz) = grid.dims;
75 let n_total = nx * ny * nz;
76
77 let d_kernel = dipole_kernel(grid, bdir);
79
80 let bg_mask: Vec<f64> = mask.iter()
82 .map(|&m| if m == 0 { 1.0 } else { 0.0 })
83 .collect();
84
85 let brain_mask: Vec<f64> = mask.iter()
87 .map(|&m| if m != 0 { 1.0 } else { 0.0 })
88 .collect();
89
90 let b: Vec<f64> = field.iter()
92 .zip(brain_mask.iter())
93 .map(|(&f, &w)| f * w)
94 .collect();
95
96 let mut x = vec![0.0; n_total];
98
99 let mut u = b.clone();
100 let mut beta = vec_norm(&u);
101
102 if beta < 1e-20 {
103 return vec![0.0; n_total];
104 }
105
106 for i in 0..n_total {
107 u[i] /= beta;
108 }
109
110 let mut v = apply_at(&u, &brain_mask, &d_kernel, &bg_mask, nx, ny, nz);
111 let mut alpha = vec_norm(&v);
112
113 if alpha < 1e-20 {
114 return vec![0.0; n_total];
115 }
116
117 for i in 0..n_total {
118 v[i] /= alpha;
119 }
120
121 let norm_b = beta;
123 let mut w = v.clone();
124 let mut phi_bar = beta;
125 let mut rho_bar = alpha;
126
127 let mut beta_dd = beta;
129 let mut beta_d = 0.0;
130 let mut rho_d_old = 1.0;
131 let mut tau_tilde_old = 0.0;
132 let mut theta_tilde = 0.0;
133 let mut zeta = 0.0;
134 let d_accum = 0.0; let mut norm_a2 = alpha * alpha;
138
139 let mut zeta_bar = alpha * beta;
141 let mut alpha_bar = alpha;
142 let mut c_bar = 1.0;
143 let mut s_bar = 0.0;
144 let mut rho_val = 1.0;
145 let mut rho_bar_lsmr = 1.0;
146
147 for iter in 0..max_iter {
148 progress(iter + 1, max_iter);
150
151 let av = apply_a(&v, &bg_mask, &d_kernel, &brain_mask, nx, ny, nz);
153 for i in 0..n_total {
154 u[i] = av[i] - alpha * u[i];
155 }
156 beta = vec_norm(&u);
157
158 if beta < 1e-20 {
159 progress(iter + 1, iter + 1);
160 break;
161 }
162
163 for i in 0..n_total {
164 u[i] /= beta;
165 }
166
167 let atu = apply_at(&u, &brain_mask, &d_kernel, &bg_mask, nx, ny, nz);
168 for i in 0..n_total {
169 v[i] = atu[i] - beta * v[i];
170 }
171 alpha = vec_norm(&v);
172
173 if alpha < 1e-20 {
174 progress(iter + 1, iter + 1);
175 break;
176 }
177
178 for i in 0..n_total {
179 v[i] /= alpha;
180 }
181
182 let rho = (rho_bar * rho_bar + beta * beta).sqrt();
184 let c = rho_bar / rho;
185 let s = beta / rho;
186 let theta = s * alpha;
187 rho_bar = -c * alpha;
188 let phi = c * phi_bar;
189 phi_bar *= s;
190
191 let phi_rho = phi / rho;
193 let theta_rho = theta / rho;
194
195 for i in 0..n_total {
196 x[i] += phi_rho * w[i];
197 w[i] = v[i] - theta_rho * w[i];
198 }
199
200 let _rho_old = rho_val;
202 rho_val = (alpha_bar * alpha_bar + beta * beta).sqrt();
203 let c_lsmr = alpha_bar / rho_val;
204 let s_lsmr = beta / rho_val;
205 let theta_new = s_lsmr * alpha;
206 alpha_bar = c_lsmr * alpha;
207
208 let _rho_bar_old = rho_bar_lsmr;
209 let zeta_old = zeta;
210 let theta_bar = s_bar * rho_val;
211 let rho_tmp = c_bar * rho_val;
212 rho_bar_lsmr = (rho_tmp * rho_tmp + theta_new * theta_new).sqrt();
213 c_bar = rho_tmp / rho_bar_lsmr;
214 s_bar = theta_new / rho_bar_lsmr;
215 zeta = c_bar * zeta_bar;
216 zeta_bar *= -s_bar;
217
218 let beta_hat = c_lsmr * beta_dd;
220 beta_dd *= -s_lsmr;
221
222 let theta_tilde_old = theta_tilde;
223 let rho_tilde_old = (rho_d_old * rho_d_old + theta_bar * theta_bar).sqrt();
224 let c_tilde_old = rho_d_old / rho_tilde_old;
225 let s_tilde_old = theta_bar / rho_tilde_old;
226 theta_tilde = s_tilde_old * rho_bar_lsmr;
227 rho_d_old = c_tilde_old * rho_bar_lsmr;
228 beta_d = -s_tilde_old * beta_d + c_tilde_old * beta_hat;
229
230 tau_tilde_old = (zeta_old - theta_tilde_old * tau_tilde_old) / rho_tilde_old;
231 let tau_d = (zeta - theta_tilde * tau_tilde_old) / rho_d_old;
232
233 let norm_r = (d_accum + (beta_d - tau_d).powi(2) + beta_dd.powi(2)).sqrt();
234
235 norm_a2 += beta * beta;
237 let norm_a = norm_a2.sqrt();
238 norm_a2 += alpha * alpha;
239
240 let norm_ar = zeta_bar.abs();
242
243 let norm_x = vec_norm(&x);
245 let test1 = norm_r / norm_b;
246 let test2 = if norm_a * norm_r > 0.0 { norm_ar / (norm_a * norm_r) } else { 0.0 };
247 let eps_r = tol + tol * norm_a * norm_x / norm_b;
248
249 if test1 <= eps_r || test2 <= tol {
250 progress(iter + 1, iter + 1);
251 break;
252 }
253 }
254
255 let mut bg_source: Vec<Complex64> = x.iter()
257 .zip(bg_mask.iter())
258 .map(|(&xi, &m)| Complex64::new(xi * m, 0.0))
259 .collect();
260
261 fft3d(&mut bg_source, nx, ny, nz);
262
263 for i in 0..n_total {
264 bg_source[i] *= d_kernel[i];
265 }
266
267 ifft3d(&mut bg_source, nx, ny, nz);
268
269 let mut local_field = vec![0.0; n_total];
271 for i in 0..n_total {
272 if mask[i] != 0 {
273 local_field[i] = field[i] - bg_source[i].re;
274 }
275 }
276
277 local_field
278}
279
280fn apply_a(
282 x: &[f64],
283 bg_mask: &[f64],
284 d_kernel: &[f64],
285 brain_mask: &[f64],
286 nx: usize, ny: usize, nz: usize,
287) -> Vec<f64> {
288 let n_total = nx * ny * nz;
289
290 let mut temp: Vec<Complex64> = x.iter()
292 .zip(bg_mask.iter())
293 .map(|(&xi, &m)| Complex64::new(xi * m, 0.0))
294 .collect();
295
296 fft3d(&mut temp, nx, ny, nz);
298
299 for i in 0..n_total {
301 temp[i] *= d_kernel[i];
302 }
303
304 ifft3d(&mut temp, nx, ny, nz);
306
307 temp.iter()
309 .zip(brain_mask.iter())
310 .map(|(t, &w)| t.re * w)
311 .collect()
312}
313
314fn apply_at(
316 u: &[f64],
317 brain_mask: &[f64],
318 d_kernel: &[f64],
319 bg_mask: &[f64],
320 nx: usize, ny: usize, nz: usize,
321) -> Vec<f64> {
322 let n_total = nx * ny * nz;
323
324 let mut temp: Vec<Complex64> = u.iter()
326 .zip(brain_mask.iter())
327 .map(|(&ui, &w)| Complex64::new(ui * w, 0.0))
328 .collect();
329
330 fft3d(&mut temp, nx, ny, nz);
332
333 for i in 0..n_total {
335 temp[i] *= d_kernel[i];
336 }
337
338 ifft3d(&mut temp, nx, ny, nz);
340
341 temp.iter()
343 .zip(bg_mask.iter())
344 .map(|(t, &m)| t.re * m)
345 .collect()
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn test_pdf_zero_field() {
354 let n = 8;
355 let field = vec![0.0; n * n * n];
356 let mask = vec![1u8; n * n * n];
357 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
358
359 let local = pdf_core(
360 &field, &mask, &grid,
361 (0.0, 0.0, 1.0), 1e-5, 10, |_, _| {}
362 );
363
364 for &val in local.iter() {
365 assert!(val.abs() < 1e-10, "Zero field should give zero local field");
366 }
367 }
368
369 #[test]
370 fn test_pdf_finite() {
371 let n = 8;
372 let field: Vec<f64> = (0..n*n*n).map(|i| (i as f64) * 0.001).collect();
373
374 let mut mask = vec![0u8; n * n * n];
376 let center = n / 2;
377 let radius = n / 4;
378
379 for i in 0..n {
380 for j in 0..n {
381 for k in 0..n {
382 let di = (i as i32) - (center as i32);
383 let dj = (j as i32) - (center as i32);
384 let dk = (k as i32) - (center as i32);
385 if di*di + dj*dj + dk*dk <= (radius * radius) as i32 {
386 mask[i * n * n + j * n + k] = 1;
387 }
388 }
389 }
390 }
391
392 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
393 let local = pdf_core(
394 &field, &mask, &grid,
395 (0.0, 0.0, 1.0), 1e-5, 20, |_, _| {}
396 );
397
398 for (i, &val) in local.iter().enumerate() {
399 assert!(val.is_finite(), "Local field should be finite at index {}", i);
400 }
401 }
402
403 #[test]
404 fn test_pdf_mask() {
405 let n = 8;
406 let field: Vec<f64> = (0..n*n*n).map(|i| (i as f64) * 0.001).collect();
407 let mut mask = vec![1u8; n * n * n];
408 mask[0] = 0;
409 mask[10] = 0;
410 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
411
412 let local = pdf_core(
413 &field, &mask, &grid,
414 (0.0, 0.0, 1.0), 1e-5, 10, |_, _| {}
415 );
416
417 assert_eq!(local[0], 0.0, "Masked voxel should be zero");
418 assert_eq!(local[10], 0.0, "Masked voxel should be zero");
419 }
420
421 #[test]
422 fn test_pdf_nonuniform_voxels() {
423 let n = 8;
424 let field: Vec<f64> = (0..n*n*n).map(|i| (i as f64) * 0.001).collect();
425
426 let mut mask = vec![0u8; n * n * n];
428 let center = n / 2;
429 let radius = n / 4;
430
431 for z in 0..n {
432 for y in 0..n {
433 for x in 0..n {
434 let dx = (x as i32) - (center as i32);
435 let dy = (y as i32) - (center as i32);
436 let dz = (z as i32) - (center as i32);
437 if dx*dx + dy*dy + dz*dz <= (radius * radius) as i32 {
438 mask[x + y * n + z * n * n] = 1;
439 }
440 }
441 }
442 }
443
444 let grid = Grid::new(n, n, n, 0.5, 1.0, 2.0);
446 let local = pdf_core(
447 &field, &mask, &grid,
448 (0.0, 0.0, 1.0), 1e-5, 20, |_, _| {}
449 );
450
451 for (i, &val) in local.iter().enumerate() {
452 assert!(val.is_finite(), "PDF with nonuniform voxels should be finite at index {}", i);
453 }
454
455 for i in 0..n*n*n {
457 if mask[i] == 0 {
458 assert_eq!(local[i], 0.0, "Outside mask should be zero");
459 }
460 }
461 }
462
463 #[test]
464 fn test_pdf_varying_field() {
465 let n = 8;
466
467 let mut field = vec![0.0; n * n * n];
469 for z in 0..n {
470 for y in 0..n {
471 for x in 0..n {
472 let idx = x + y * n + z * n * n;
473 let zf = (z as f64) / (n as f64);
474 field[idx] = zf * zf * 0.5;
475 }
476 }
477 }
478
479 let mut mask = vec![0u8; n * n * n];
481 let center = n / 2;
482 let radius = n / 4;
483 for z in 0..n {
484 for y in 0..n {
485 for x in 0..n {
486 let dx = (x as i32) - (center as i32);
487 let dy = (y as i32) - (center as i32);
488 let dz = (z as i32) - (center as i32);
489 if dx*dx + dy*dy + dz*dz <= (radius * radius) as i32 {
490 mask[x + y * n + z * n * n] = 1;
491 }
492 }
493 }
494 }
495
496 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
497 let local = pdf_core(
498 &field, &mask, &grid,
499 (0.0, 0.0, 1.0), 1e-5, 30, |_, _| {}
500 );
501
502 for (i, &val) in local.iter().enumerate() {
504 assert!(val.is_finite(), "Varying field should produce finite results at index {}", i);
505 }
506
507 let mut any_changed = false;
510 for i in 0..n*n*n {
511 if mask[i] != 0 && (local[i] - field[i]).abs() > 1e-10 {
512 any_changed = true;
513 break;
514 }
515 }
516 assert!(any_changed, "PDF should modify the field inside the mask for a varying field");
517 }
518
519 #[test]
520 fn test_pdf_larger_volume() {
521 let n = 16;
523
524 let mut field = vec![0.0; n * n * n];
526 let center = n / 2;
527 for z in 0..n {
528 for y in 0..n {
529 for x in 0..n {
530 let dx = (x as f64) - (center as f64);
531 let dy = (y as f64) - (center as f64);
532 let dz = (z as f64) - (center as f64);
533 let r2 = dx * dx + dy * dy + dz * dz;
534 if r2 > 0.5 {
535 let r = r2.sqrt();
537 let cos_theta = dz / r;
538 field[x + y * n + z * n * n] = (3.0 * cos_theta * cos_theta - 1.0) / (r * r * r) * 0.01;
539 }
540 }
541 }
542 }
543
544 let mut mask = vec![0u8; n * n * n];
546 let radius = n / 3;
547 for z in 0..n {
548 for y in 0..n {
549 for x in 0..n {
550 let dx = (x as i32) - (center as i32);
551 let dy = (y as i32) - (center as i32);
552 let dz = (z as i32) - (center as i32);
553 if dx * dx + dy * dy + dz * dz <= (radius * radius) as i32 {
554 mask[x + y * n + z * n * n] = 1;
555 }
556 }
557 }
558 }
559
560 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
561 let local = pdf_core(
562 &field, &mask, &grid,
563 (0.0, 0.0, 1.0), 1e-4, 50, |_, _| {}
564 );
565
566 assert_eq!(local.len(), n * n * n);
567 for (i, &val) in local.iter().enumerate() {
568 assert!(val.is_finite(), "PDF larger volume: finite at index {}", i);
569 }
570
571 for i in 0..n * n * n {
573 if mask[i] == 0 {
574 assert_eq!(local[i], 0.0);
575 }
576 }
577 }
578
579 #[test]
580 fn test_pdf_more_iterations() {
581 let n = 8;
583 let field: Vec<f64> = (0..n * n * n).map(|i| (i as f64) * 0.001).collect();
584
585 let mut mask = vec![0u8; n * n * n];
586 let center = n / 2;
587 let radius = n / 4;
588 for z in 0..n {
589 for y in 0..n {
590 for x in 0..n {
591 let dx = (x as i32) - (center as i32);
592 let dy = (y as i32) - (center as i32);
593 let dz = (z as i32) - (center as i32);
594 if dx * dx + dy * dy + dz * dz <= (radius * radius) as i32 {
595 mask[x + y * n + z * n * n] = 1;
596 }
597 }
598 }
599 }
600
601 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
602
603 let local_many = pdf_core(
605 &field, &mask, &grid,
606 (0.0, 0.0, 1.0), 1e-8, 100, |_, _| {}
607 );
608
609 let local_few = pdf_core(
610 &field, &mask, &grid,
611 (0.0, 0.0, 1.0), 1e-8, 5, |_, _| {}
612 );
613
614 for &val in local_many.iter().chain(local_few.iter()) {
616 assert!(val.is_finite());
617 }
618 }
619
620 #[test]
621 fn test_pdf_different_bdir() {
622 let n = 8;
624 let field: Vec<f64> = (0..n * n * n).map(|i| (i as f64) * 0.001).collect();
625
626 let mut mask = vec![0u8; n * n * n];
627 let center = n / 2;
628 let radius = n / 4;
629 for z in 0..n {
630 for y in 0..n {
631 for x in 0..n {
632 let dx = (x as i32) - (center as i32);
633 let dy = (y as i32) - (center as i32);
634 let dz = (z as i32) - (center as i32);
635 if dx * dx + dy * dy + dz * dz <= (radius * radius) as i32 {
636 mask[x + y * n + z * n * n] = 1;
637 }
638 }
639 }
640 }
641
642 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
644 let local = pdf_core(
645 &field, &mask, &grid,
646 (0.1, 0.2, 0.97), 1e-5, 20, |_, _| {}
647 );
648
649 for &val in &local {
650 assert!(val.is_finite());
651 }
652 }
653
654 #[test]
655 fn test_pdf_with_progress() {
656 let n = 8;
657 let field: Vec<f64> = (0..n * n * n).map(|i| (i as f64) * 0.001).collect();
658
659 let mut mask = vec![0u8; n * n * n];
660 let center = n / 2;
661 let radius = n / 4;
662 for z in 0..n {
663 for y in 0..n {
664 for x in 0..n {
665 let dx = (x as i32) - (center as i32);
666 let dy = (y as i32) - (center as i32);
667 let dz = (z as i32) - (center as i32);
668 if dx * dx + dy * dy + dz * dz <= (radius * radius) as i32 {
669 mask[x + y * n + z * n * n] = 1;
670 }
671 }
672 }
673 }
674
675 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
676 let mut progress_calls = Vec::new();
677 let local = pdf_core(
678 &field, &mask, &grid,
679 (0.0, 0.0, 1.0), 1e-5, 20,
680 |iter, max| { progress_calls.push((iter, max)); }
681 );
682
683 assert_eq!(local.len(), n * n * n);
684 assert!(!progress_calls.is_empty(), "Progress should be called at least once");
685 for &val in &local {
686 assert!(val.is_finite());
687 }
688 }
689
690 #[test]
691 fn test_pdf_all_mask() {
692 let n = 8;
694 let field: Vec<f64> = (0..n * n * n).map(|i| (i as f64) * 0.001).collect();
695 let mask = vec![1u8; n * n * n];
696 let grid = Grid::new(n, n, n, 1.0, 1.0, 1.0);
697
698 let local = pdf_core(
699 &field, &mask, &grid,
700 (0.0, 0.0, 1.0), 1e-5, 10, |_, _| {}
701 );
702
703 for &val in &local {
706 assert!(val.is_finite());
707 }
708 }
709}