AOT-biomaps 2.9.215__tar.gz → 2.9.281__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of AOT-biomaps might be problematic. Click here for more details.

Files changed (56) hide show
  1. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Experiment/Tomography.py +70 -0
  2. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Experiment/_mainExperiment.py +31 -18
  3. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_Optimizers/DEPIERRO.py +48 -11
  4. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +499 -0
  5. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_Optimizers/MAPEM.py +118 -38
  6. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +464 -0
  7. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +555 -0
  8. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/RelativeDifferences.py +10 -14
  9. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_CSR.py +281 -0
  10. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_SELL.py +328 -0
  11. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/__init__.py +2 -0
  12. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/AOT_biomaps_kernels.cubin +0 -0
  13. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AlgebraicRecon.py +276 -191
  14. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AnalyticRecon.py +26 -41
  15. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/BayesianRecon.py +33 -96
  16. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/PrimalDualRecon.py +14 -18
  17. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/ReconEnums.py +27 -2
  18. aot_biomaps-2.9.281/AOT_biomaps/AOT_Recon/ReconTools.py +489 -0
  19. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/__init__.py +1 -0
  20. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/_mainRecon.py +4 -4
  21. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/__init__.py +4 -111
  22. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps.egg-info/PKG-INFO +2 -1
  23. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps.egg-info/SOURCES.txt +5 -1
  24. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps.egg-info/requires.txt +1 -0
  25. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/PKG-INFO +2 -1
  26. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/setup.py +77 -1
  27. aot_biomaps-2.9.215/AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +0 -103
  28. aot_biomaps-2.9.215/AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +0 -267
  29. aot_biomaps-2.9.215/AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +0 -221
  30. aot_biomaps-2.9.215/AOT_biomaps/AOT_Recon/ReconTools.py +0 -272
  31. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/AcousticEnums.py +0 -0
  32. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/AcousticTools.py +0 -0
  33. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/FocusedWave.py +0 -0
  34. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/IrregularWave.py +0 -0
  35. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/PlaneWave.py +0 -0
  36. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/StructuredWave.py +0 -0
  37. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/__init__.py +0 -0
  38. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Acoustic/_mainAcoustic.py +0 -0
  39. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Experiment/Focus.py +0 -0
  40. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Experiment/__init__.py +0 -0
  41. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Optic/Absorber.py +0 -0
  42. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Optic/Laser.py +0 -0
  43. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Optic/OpticEnums.py +0 -0
  44. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Optic/__init__.py +0 -0
  45. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Optic/_mainOptic.py +0 -0
  46. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_Optimizers/__init__.py +0 -0
  47. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/Huber.py +0 -0
  48. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/Quadratic.py +0 -0
  49. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/__init__.py +0 -0
  50. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/AOT_Recon/DeepLearningRecon.py +0 -0
  51. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/Config.py +0 -0
  52. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps/Settings.py +0 -0
  53. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps.egg-info/dependency_links.txt +0 -0
  54. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/AOT_biomaps.egg-info/top_level.txt +0 -0
  55. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/README.md +0 -0
  56. {aot_biomaps-2.9.215 → aot_biomaps-2.9.281}/setup.cfg +0 -0
@@ -7,6 +7,8 @@ import psutil
7
7
  import numpy as np
8
8
  import matplotlib.pyplot as plt
9
9
  from tqdm import trange
10
+ import h5py
11
+ from scipy.io import loadmat
10
12
 
11
13
  class Tomography(Experiment):
12
14
  def __init__(self, **kwargs):
@@ -433,3 +435,71 @@ class Tomography(Experiment):
433
435
  listAcousticFields.append(AcousticField)
434
436
  progress_bar.set_postfix_str("")
435
437
  return listAcousticFields
438
+
439
+ def load_experimentalAO(self, pathAO, withTumor = True, h5name='AOsignal'):
440
+ """
441
+ Load experimental AO signals from specified file paths.
442
+ Args:
443
+ path_withTumor: Path to the AO signal with tumor.
444
+ path_withoutTumor: Path to the AO signal without tumor.
445
+ """
446
+ if not os.path.exists(pathAO):
447
+ raise FileNotFoundError(f"File {pathAO} not found.")
448
+
449
+ if pathAO.endswith('.npy'):
450
+ ao_signal = np.load(pathAO)
451
+ elif pathAO.endswith('.h5'):
452
+ with h5py.File(pathAO, 'r') as f:
453
+ if h5name not in f:
454
+ raise KeyError(f"Dataset '{h5name}' not found in the HDF5 file.")
455
+ ao_signal = f[h5name][:]
456
+ elif pathAO.endswith('.mat'):
457
+ mat_data = loadmat(pathAO)
458
+ if h5name not in mat_data:
459
+ raise KeyError(f"Dataset '{h5name}' not found in the .mat file.")
460
+ ao_signal = mat_data[h5name]
461
+ elif pathAO.endswith('.hdr'):
462
+ ao_signal = self._loadAOSignal(pathAO)
463
+ else:
464
+ raise ValueError("Unsupported file format. Supported formats are: .npy, .h5, .mat, .hdr")
465
+
466
+ if withTumor:
467
+ self.AOsignal_withTumor = ao_signal
468
+ else:
469
+ self.AOsignal_withoutTumor = ao_signal
470
+
471
+ def check_experimentalAO(self, activeListPath, withTumor=True):
472
+ """
473
+ Check if the experimental AO signals are correctly initialized.
474
+ """
475
+ if withTumor:
476
+ if self.AOsignal_withTumor is None:
477
+ raise ValueError("Experimental AOsignal with tumor is not initialized. Please load the experimental AO signal with tumor first.")
478
+ else:
479
+ if self.AOsignal_withoutTumor is None:
480
+ raise ValueError("Experimental AOsignal without tumor is not initialized. Please load the experimental AO signal without tumor first.")
481
+ if self.AcousticFields is not None:
482
+ # get min time shape between all AO signals
483
+ print()
484
+
485
+ if self.AcousticFields[0].field.shape[0] > self.AOsignal_withTumor.shape[0]:
486
+ self.cutAcousticFields(max_t=self.AOsignal_withTumor.shape[0]/float(self.params.acoustic['f_saving']))
487
+ else:
488
+ for i in range(len(self.AcousticFields)):
489
+ min_time_shape = min(self.AcousticFields[i].field.shape[0])
490
+ if withTumor:
491
+ self.AOsignal_withTumor = self.AOsignal_withTumor[:min_time_shape, :]
492
+ else:
493
+ self.AOsignal_withoutTumor = self.AOsignal_withoutTumor[:min_time_shape, :]
494
+
495
+ for field in self.AcousticFields:
496
+ if activeListPath is not None:
497
+ with open(activeListPath, 'r') as file:
498
+ lines = file.readlines()
499
+ expected_name = lines[self.AcousticFields.index(field)].strip()
500
+ nameField = field.getName_field()
501
+ if nameField.startswith("field_"):
502
+ nameField = nameField[len("field_"):]
503
+ if nameField != expected_name:
504
+ raise ValueError(f"Field name {nameField} does not match the expected name {expected_name} from the active list.")
505
+ print("Experimental AO signals are correctly initialized.")
@@ -77,7 +77,6 @@ class Experiment(ABC):
77
77
  raise ValueError(f"Field {field.getName_field()} has an invalid shape: {field.field.shape}. Expected shape to be at least ({max_sample},).")
78
78
  self.AcousticFields[i].field = field.field[min_sample:max_sample, :, :]
79
79
 
80
-
81
80
  def addNoise(self, noiseType='gaussian', noiseLvl=0.1, withTumor=True):
82
81
  """
83
82
  Ajoute du bruit (gaussien ou poisson) au signal AO sélectionné.
@@ -293,32 +292,46 @@ class Experiment(ABC):
293
292
  if AOsignalPath.endswith(".cdh"):
294
293
  with open(AOsignalPath, "r") as file:
295
294
  cdh_content = file.readlines()
295
+
296
+ cdf_path = AOsignalPath.replace(".cdh", ".cdf")
296
297
 
297
- # Extraction des paramètres
298
- n_events = int([line.split(":")[1].strip() for line in cdh_content if "Number of events" in line][0])
299
- n_acquisitions = int([line.split(":")[1].strip() for line in cdh_content if "Number of acquisitions per event" in line][0])
298
+ # Extraire les paramètres depuis le fichier .cdh
299
+ n_scans = int([line.split(":")[1].strip() for line in cdh_content if "Number of events" in line][0])
300
+ n_acquisitions_per_event = int([line.split(":")[1].strip() for line in cdh_content if "Number of acquisitions per event" in line][0])
300
301
  num_elements = int([line.split(":")[1].strip() for line in cdh_content if "Number of US transducers" in line][0])
301
302
 
302
- # Initialisation avec la bonne forme : (n_acquisitions, n_events)
303
- AOsignal_matrix = np.zeros((n_acquisitions, n_events), dtype=np.float32)
303
+ # Initialisation des structures
304
+ AO_signal = np.zeros((n_acquisitions_per_event, n_scans), dtype=np.float32)
305
+ active_lists = []
306
+ angles = []
307
+
308
+ # Lecture du fichier binaire
309
+ with open(cdf_path, "rb") as file:
310
+ for j in trange(n_scans, desc="Lecture des événements"):
311
+ # Lire l'activeList : 48 caractères hex = 24 bytes
312
+ active_list_bytes = file.read(24)
313
+ active_list_hex = active_list_bytes.hex()
314
+ active_lists.append(active_list_hex)
304
315
 
305
- with open(AOsignalPath.replace(".cdh", ".cdf"), "rb") as file:
306
- hex_length = (num_elements + 3) // 4
307
- for event in range(n_events):
308
- file.read(hex_length // 2) # Sauter l'en-tête de l'événement
309
- signal = np.frombuffer(file.read(n_acquisitions * 4), dtype=np.float32)
310
- AOsignal_matrix[:, event] = signal # Remplir par colonne
311
-
312
- AOsignal_matrix = AOsignal_matrix
316
+ # Lire l'angle (1 byte signé)
317
+ angle_byte = file.read(1)
318
+ angle = np.frombuffer(angle_byte, dtype=np.int8)[0]
319
+ angles.append(angle)
320
+
321
+ # Lire le signal AO (float32)
322
+ data = np.frombuffer(file.read(n_acquisitions_per_event * 4), dtype=np.float32)
323
+ if len(data) != n_acquisitions_per_event:
324
+ raise ValueError(f"Erreur à l'événement {j} : attendu {n_acquisitions_per_event}, obtenu {len(data)}")
325
+ AO_signal[:, j] = data
326
+
327
+ return AO_signal
313
328
 
314
- return AOsignal_matrix
315
329
 
316
330
  elif AOsignalPath.endswith(".npy"):
317
331
  return np.load(AOsignalPath) # Supposé déjà au bon format
318
332
  else:
319
333
  raise ValueError("Format de fichier non supporté. Utilisez .cdh/.cdf ou .npy.")
320
334
 
321
-
322
335
  def saveAOsignals_Castor(self, save_directory, withTumor=True):
323
336
  if withTumor:
324
337
  AO_signal = self.AOsignal_withTumor
@@ -345,10 +358,10 @@ class Experiment(ABC):
345
358
  header_content = (
346
359
  f"Data filename: {'AOSignals_withTumor.cdf' if withTumor else 'AOSignals_withoutTumor.cdf'}\n"
347
360
  f"Number of events: {nScan}\n"
348
- f"Number of acquisitions per event: {AO_signal.shape[1]}\n"
361
+ f"Number of acquisitions per event: {AO_signal.shape[0]}\n"
349
362
  f"Start time (s): 0\n"
350
363
  f"Duration (s): 1\n"
351
- f"Acquisition frequency (Hz): {1/self.AcousticFields[0].kgrid.dt}\n"
364
+ f"Acquisition frequency (Hz): {self.params.acoustic['f_saving']}\n"
352
365
  f"Data mode: histogram\n"
353
366
  f"Data type: AOT\n"
354
367
  f"Number of US transducers: {self.params.acoustic['num_elements']}"
@@ -1,6 +1,8 @@
1
1
  from AOT_biomaps.AOT_Recon.ReconEnums import PotentialType
2
- from AOT_biomaps.AOT_Recon.ReconTools import _build_adjacency_sparse
2
+ from AOT_biomaps.AOT_Recon.ReconTools import _build_adjacency_sparse, calculate_memory_requirement, check_gpu_memory
3
3
  from AOT_biomaps.Config import config
4
+
5
+ import warnings
4
6
  import numpy as np
5
7
  import torch
6
8
  from tqdm import trange
@@ -11,9 +13,42 @@ if config.get_process() == 'gpu':
11
13
  except ImportError:
12
14
  raise ImportError("torch_scatter and torch_sparse are required for GPU processing. Please install them using 'pip install torch-scatter torch-sparse' with correct link (follow instructions https://github.com/LucasDuclos/AcoustoOpticTomography/edit/main/README.md).")
13
15
 
14
- def _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, withTumor, max_saves=5000):
15
- # Initialisation du device
16
- device = torch.device(f"cuda:{config.select_best_gpu()}")
16
+ def DEPIERRO(
17
+ SMatrix,
18
+ y,
19
+ numIterations,
20
+ beta,
21
+ sigma,
22
+ isSavingEachIteration,
23
+ withTumor,
24
+ max_saves,
25
+ show_logs):
26
+ """
27
+ This method implements the DEPIERRO algorithm using either CPU or single-GPU PyTorch acceleration.
28
+ Multi-GPU and Multi-CPU modes are not implemented for this algorithm.
29
+ """
30
+ try:
31
+ tumor_str = "WITH" if withTumor else "WITHOUT"
32
+ # Auto-select device and method
33
+ if device is None:
34
+ if torch.cuda.is_available() and check_gpu_memory(config.select_best_gpu(), calculate_memory_requirement(SMatrix, y), show_logs=show_logs):
35
+ device = torch.device(f"cuda:{config.select_best_gpu()}")
36
+ use_gpu = True
37
+ else:
38
+ device = torch.device("cpu")
39
+ use_gpu = False
40
+ else:
41
+ use_gpu = device.type == "cuda"
42
+ # Dispatch to the appropriate implementation
43
+ if use_gpu:
44
+ return _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, tumor_str, device, max_saves, show_logs)
45
+ else:
46
+ return _DEPIERRO_CPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, tumor_str, device, max_saves, show_logs)
47
+ except Exception as e:
48
+ print(f"Error in MLEM: {type(e).__name__}: {e}")
49
+ return None, None
50
+
51
+ def _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, tumor_str, device, max_saves, show_logs=True):
17
52
  # Conversion des données en tenseurs PyTorch (float64)
18
53
  A_matrix_torch = torch.tensor(SMatrix, dtype=torch.float64, device=device)
19
54
  y_torch = torch.tensor(y, dtype=torch.float64, device=device)
@@ -33,7 +68,7 @@ def _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration,
33
68
  # Construction de la matrice d'adjacence
34
69
  adj_index, adj_values = _build_adjacency_sparse(Z, X, device=device, dtype=torch.float64)
35
70
  # Description pour la barre de progression
36
- description = f"AOT-BioMaps -- Bayesian Reconstruction Tomography: DE PIERRO (Sparse QUADRATIC β:{beta:.4f}, σ:{sigma:.4f}) ---- {'WITH' if withTumor else 'WITHOUT'} TUMOR ---- processing on single GPU no.{torch.cuda.current_device()}"
71
+ description = f"AOT-BioMaps -- Bayesian Reconstruction Tomography: DE PIERRO (Sparse QUADRATIC β:{beta:.4f}, σ:{sigma:.4f}) ---- {tumor_str} TUMOR ---- processing on single GPU no.{torch.cuda.current_device()}"
37
72
  # Configuration pour la sauvegarde des itérations
38
73
  saved_indices = [0]
39
74
 
@@ -47,7 +82,8 @@ def _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration,
47
82
  save_indices.append(numIterations - 1)
48
83
 
49
84
  # Boucle principale MAP-EM
50
- for it in trange(numIterations, desc=description):
85
+ iterator = trange(numIterations, desc=description) if show_logs else range(numIterations)
86
+ for it in iterator:
51
87
  theta_p = matrix_theta_torch[-1]
52
88
  theta_p_flat = theta_p.reshape(-1)
53
89
  # Étape 1 : Projection avant
@@ -92,7 +128,7 @@ def _DEPIERRO_GPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration,
92
128
  else:
93
129
  return matrix_theta_torch[-1].cpu().numpy(), None
94
130
 
95
- def _DEPIERRO_CPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, withTumor, max_saves=5000):
131
+ def _DEPIERRO_CPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration, tumor_str, device, max_saves, show_logs=True):
96
132
  try:
97
133
  if beta is None or sigma is None:
98
134
  raise ValueError("Depierro95 optimizer requires beta and sigma parameters.")
@@ -120,9 +156,10 @@ def _DEPIERRO_CPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration,
120
156
  if save_indices[-1] != numIterations - 1:
121
157
  save_indices.append(numIterations - 1)
122
158
 
123
- description = f"AOT-BioMaps -- Bayesian Reconstruction Tomography: DE PIERRO (Sparse QUADRATIC β:{beta:.4f}, σ:{sigma:.4f}) ---- {'WITH' if withTumor else 'WITHOUT'} TUMOR ---- processing on single CPU ----"
159
+ description = f"AOT-BioMaps -- Bayesian Reconstruction Tomography: DE PIERRO (Sparse QUADRATIC β:{beta:.4f}, σ:{sigma:.4f}) ---- {tumor_str} TUMOR ---- processing on single CPU ----"
124
160
 
125
- for p in trange(numIterations, desc=description):
161
+ iterator = trange(numIterations, desc=description) if show_logs else range(numIterations)
162
+ for it in iterator:
126
163
  theta_p = matrix_theta[-1]
127
164
  theta_p_flat = theta_p.reshape(-1)
128
165
  q_flat = np.dot(A_flat, theta_p_flat)
@@ -141,9 +178,9 @@ def _DEPIERRO_CPU(SMatrix, y, numIterations, beta, sigma, isSavingEachIteration,
141
178
  theta_p_plus_1_flat = np.clip(theta_p_plus_1_flat, a_min=0, a_max=None)
142
179
  theta_next = theta_p_plus_1_flat.reshape(Z, X)
143
180
  matrix_theta[-1] = theta_next
144
- if isSavingEachIteration and p in save_indices:
181
+ if isSavingEachIteration and it in save_indices:
145
182
  I_reconMatrix.append(theta_next.copy())
146
- saved_indices.append(p)
183
+ saved_indices.append(it)
147
184
 
148
185
  if isSavingEachIteration:
149
186
  return I_reconMatrix, saved_indices