Skip to content

Commit 7e8f0cc

Browse files
authored
Merge pull request #126 from OSIPI/wrapper_dev
Dictionary bounds + standard OSIPI bounds
2 parents 3cd6b50 + ba924e8 commit 7e8f0cc

37 files changed

Lines changed: 487 additions & 354 deletions

conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ def bound_input(datafile, algorithms):
282282
for algorithm in algorithms["algorithms"]:
283283
algorithm_dict = algorithms.get(algorithm, {})
284284
if not algorithm_dict.get('deep_learning',False):
285-
xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}),
286-
"strict": algorithm_dict.get("xfail_names", {}).get(name, True)}
285+
xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}) or "bounds" in algorithm_dict.get("xfail_names", {}),
286+
"strict": algorithm_dict.get("xfail_names", {}).get("bounds", algorithm_dict.get("xfail_names", {}).get(name,True))}
287287
kwargs = algorithm_dict.get("options", {})
288288
tolerances = algorithm_dict.get("tolerances", {})
289289
requires_matlab = algorithm_dict.get("requires_matlab", False)

src/original/IAR_LundUniversity/ivim_fit_method_modified_mix.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ def __init__(self, gtab, bounds=None, maxiter=10, xtol=1e-8, rescale_units=False
8686
(bounds[0][1]*1000, bounds[1][1]*1000), \
8787
(bounds[0][2]*1000, bounds[1][2]*1000)])
8888
else: # Finally, if units if µm2/ms are already used
89-
self.bounds = np.array([(bounds[0][0], bounds[1][0], \
89+
self.bounds = np.array([(bounds[0][0], bounds[1][0]), \
9090
(bounds[0][1], bounds[1][1]), \
91-
(bounds[0][2], bounds[1][2]))])
91+
(bounds[0][2], bounds[1][2])])
9292

9393
@multi_voxel_fit
9494
def fit(self, data, bounds_de=None):

src/original/IAR_LundUniversity/ivim_fit_method_modified_topopro.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ def __init__(self, gtab, bounds=[[0, 0.005, 1e-5], [1, 0.1, 0.004]], \
8484
(bounds[0][1]*1000, bounds[1][1]*1000), \
8585
(bounds[0][2]*1000, bounds[1][2]*1000)])
8686
else: # Finally, if units if µm2/ms are already used
87-
self.bounds = np.array([(bounds[0][0], bounds[1][0], \
87+
self.bounds = np.array([(bounds[0][0], bounds[1][0]), \
8888
(bounds[0][1], bounds[1][1]), \
89-
(bounds[0][2], bounds[1][2]))])
89+
(bounds[0][2], bounds[1][2])])
9090

9191
@multi_voxel_fit
9292
def fit(self, data):

src/original/IAR_LundUniversity/ivim_fit_method_segmented_2step.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def fit(self, data):
5050
data = data / data_max
5151

5252
### Fit the diffusion signal to bvals >= diff_b_threshold_lower
53-
diff_bounds = [(self.bounds[0][0], self.bounds[0][3]), \
53+
diff_bounds = [(0, self.bounds[0][3]), \
5454
(self.bounds[1][0], self.bounds[1][3])] # Bounds for S0 and D
5555

5656
diff_bval_indices = np.where(self.bvals >= self.diff_b_threshold_lower)[0]

src/original/IAR_LundUniversity/ivim_fit_method_segmented_3step.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def fit(self, data):
5454
data = data / data_max
5555

5656
### Fit the diffusion signal to bvals >= diff_b_threshold_lower
57-
diff_bounds = [(self.bounds[0][0], self.bounds[0][3]), \
57+
diff_bounds = [(0, self.bounds[0][3]), \
5858
(self.bounds[1][0], self.bounds[1][3])] # Bounds for S0 and D
5959

6060
diff_bval_indices = np.where(self.bvals >= self.diff_b_threshold_lower)[0]
@@ -80,7 +80,8 @@ def fit(self, data):
8080
f_est = S0_perf_est/(S0_perf_est + S0_diff_est)
8181

8282
# Fit to the full bi-exponential, f estimate as initial guess, D fixed
83-
full_initial_guess = np.array([self.initial_guess[0], f_est, self.initial_guess[2]])
83+
f_intial_guess = np.min((f_est, self.bounds[0][1])) if f_est > self.bounds[0][1] else np.max((f_est, self.bounds[1][1]))
84+
full_initial_guess = np.array([self.initial_guess[0], f_intial_guess, self.initial_guess[2]])
8485

8586
full_bounds_lower = self.bounds[0][:-1]
8687
full_bounds_upper = self.bounds[1][:-1]

src/original/IAR_LundUniversity/ivim_fit_method_subtracted.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def fit(self, data):
5252
data = data / data_max
5353

5454
### Fit the diffusion signal to bvals >= diff_b_threshold_lower
55-
diff_bounds = [(self.bounds[0][0], self.bounds[0][3]), \
55+
diff_bounds = [(0, self.bounds[0][3]), \
5656
(self.bounds[1][0], self.bounds[1][3])] # Bounds for S0 and D
5757

5858
diff_bval_indices = np.where(self.bvals >= self.diff_b_threshold_lower)[0]
@@ -64,7 +64,7 @@ def fit(self, data):
6464

6565

6666
### Fit the perfusion signal to bvals <= perf_b_threshold_upper
67-
perf_bounds = [(self.bounds[0][0], self.bounds[0][2]), \
67+
perf_bounds = [(0, self.bounds[0][2]), \
6868
(self.bounds[1][0], self.bounds[1][2])] # Bounds for S0 and D*
6969

7070
perf_bvals = self.bvals[self.bvals <= self.perf_b_threshold_upper]

src/standardized/ASD_MemorialSloanKettering_QAMPER_IVIM.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
4141
Our OsipiBase object could contain functions that compare the inputs with
4242
the requirements.
4343
"""
44-
#super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds, initial_guess, fitS0)
4544
super(ASD_MemorialSloanKettering_QAMPER_IVIM, self).__init__(bvalues=bvalues, bounds=bounds, initial_guess=initial_guess)
46-
self.initialize(bounds, initial_guess)
45+
46+
self.use_bounds = {"f" : True, "D" : True, "Dp" : True, "S0" : True}
47+
self.use_initial_guess = {"f" : True, "D" : True, "Dp" : True, "S0" : True}
48+
4749
if eng is None:
4850
print('initiating matlab; this may take some time. For repeated testing one could use the optional input eng as an already initiated matlab engine')
4951
self.eng=matlab.engine.start_matlab()
@@ -63,21 +65,6 @@ def algorithm(self,dwi_arr, bval_arr, LB0, UB0, x0in):
6365
(f_arr, D_arr, Dx_arr, s0_arr, fitted_dwi_arr, RSS, rms_val, chi, AIC, BIC, R_sq) = results
6466
return D_arr/1000, f_arr, Dx_arr/1000, s0_arr
6567

66-
def initialize(self, bounds, initial_guess):
67-
if bounds is None:
68-
print('warning, no bounds were defined, so algorithm-specific default bounds are used')
69-
self.bounds=([1e-6, 0, 0.004, 0],[0.003, 1.0, 0.2, 5])
70-
else:
71-
self.bounds=bounds
72-
if initial_guess is None:
73-
print('warning, no initial guesses were defined, so algorithm-specific default initial guess is used')
74-
self.initial_guess = [0.001, 0.2, 0.01, 1]
75-
else:
76-
self.initial_guess = initial_guess
77-
self.use_initial_guess = True
78-
self.use_initial_guess = True
79-
self.use_bounds = True
80-
8168
def ivim_fit(self, signals, bvalues, **kwargs):
8269
"""Perform the IVIM fit
8370
@@ -88,12 +75,16 @@ def ivim_fit(self, signals, bvalues, **kwargs):
8875
Returns:
8976
_type_: _description_
9077
"""
78+
bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]],
79+
[self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]])
80+
81+
initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]]
9182

9283
bvalues=np.array(bvalues)
93-
LB = np.array(self.bounds[0])[[1,0,2,3]]
94-
UB = np.array(self.bounds[1])[[1,0,2,3]]
84+
LB = np.array(bounds[0])[[1,0,2,3]]
85+
UB = np.array(bounds[1])[[1,0,2,3]]
9586

96-
fit_results = self.algorithm(np.array(signals)[:,np.newaxis], bvalues, LB, UB, np.array(self.initial_guess)[[1,0,2,3]])
87+
fit_results = self.algorithm(np.array(signals)[:,np.newaxis], bvalues, LB, UB, np.array(initial_guess)[[1,0,2,3]])
9788

9889
results = {}
9990
results["D"] = fit_results[0]

src/standardized/ETP_SRI_LinearFitting.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
4848
super(ETP_SRI_LinearFitting, self).__init__(bvalues, thresholds, bounds, initial_guess)
4949
if bounds is not None:
5050
print('warning, bounds from wrapper are not (yet) used in this algorithm')
51-
self.use_bounds = False
52-
self.use_initial_guess = False
51+
self.use_bounds = {"f": False, "Dp": False, "D": False, "S0": False}
52+
self.use_initial_guess = {"f": False, "Dp": False, "D": False, "S0": False}
5353

5454
# Could be a good idea to have all the submission-specfic variable be
5555
# defined with initials?

src/standardized/IAR_LU_biexp.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,18 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
4343
the requirements.
4444
"""
4545
super(IAR_LU_biexp, self).__init__(bvalues, thresholds, bounds, initial_guess)
46-
if bounds is not None:
47-
print('warning, bounds from wrapper are not (yet) used in this algorithm')
48-
self.use_bounds = False
49-
self.use_initial_guess = False
46+
if bounds is None:
47+
self.use_bounds = {"f": False, "Dp": False, "D": False}
48+
else:
49+
self.use_bounds = {"f": True, "Dp": True, "D": True}
50+
51+
if initial_guess is None:
52+
self.use_initial_guess = {"f": False, "Dp": False, "D": False}
53+
else:
54+
self.use_initial_guess = {"f": True, "Dp": True, "D": True}
55+
5056
# Check the inputs
51-
57+
5258
# Initialize the algorithm
5359
if self.bvalues is not None:
5460
bvec = np.zeros((self.bvalues.size, 3))
@@ -70,6 +76,11 @@ def ivim_fit(self, signals, bvalues, **kwargs):
7076
Returns:
7177
_type_: _description_
7278
"""
79+
80+
# Make sure bounds and initial guess conform to the algorithm requirements
81+
bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]],
82+
[self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]]
83+
initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]]
7384

7485
if self.IAR_algorithm is None:
7586
if bvalues is None:
@@ -81,7 +92,7 @@ def ivim_fit(self, signals, bvalues, **kwargs):
8192
bvec[:,2] = 1
8293
gtab = gradient_table(bvalues, bvecs=bvec, b0_threshold=0)
8394

84-
self.IAR_algorithm = IvimModelBiExp(gtab, bounds=self.bounds, initial_guess=self.initial_guess)
95+
self.IAR_algorithm = IvimModelBiExp(gtab, bounds=bounds, initial_guess=initial_guess)
8596

8697
fit_results = self.IAR_algorithm.fit(signals)
8798

@@ -103,7 +114,10 @@ def ivim_fit_full_volume(self, signals, bvalues, **kwargs):
103114
Returns:
104115
_type_: _description_
105116
"""
106-
117+
# Make sure bounds and initial guess conform to the algorithm requirements
118+
bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]],
119+
[self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]]
120+
initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]]
107121
if self.IAR_algorithm is None:
108122
if bvalues is None:
109123
bvalues = self.bvalues
@@ -114,7 +128,7 @@ def ivim_fit_full_volume(self, signals, bvalues, **kwargs):
114128
bvec[:,2] = 1
115129
gtab = gradient_table(bvalues, bvecs=bvec, b0_threshold=0)
116130

117-
self.IAR_algorithm = IvimModelBiExp(gtab, bounds=self.bounds, initial_guess=self.initial_guess)
131+
self.IAR_algorithm = IvimModelBiExp(gtab, bounds=bounds, initial_guess=initial_guess)
118132
b0_index = np.where(bvalues == 0)[0][0]
119133
mask = signals[...,b0_index]>0
120134
fit_results = self.IAR_algorithm.fit(signals, mask=mask)

src/standardized/IAR_LU_modified_mix.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,25 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
4545
the requirements.
4646
"""
4747
super(IAR_LU_modified_mix, self).__init__(bvalues, thresholds, bounds, initial_guess)
48-
if bounds is not None:
49-
print('warning, bounds from wrapper are not (yet) used in this algorithm')
50-
self.use_bounds = False
51-
self.use_initial_guess = False
48+
49+
self.use_bounds = {"f": False, "Dp": False, "D": False} # This algorithm performs intermediate steps that generates initial guesses outside very constrainted bounds
50+
self.use_initial_guess = {"f": False, "Dp": False, "D": False} # This algorithm does not use initial guesses
5251

5352
# Additional options
5453
self.stochastic = True
5554

5655
# Check the inputs
57-
56+
5857
# Initialize the algorithm
5958
if self.bvalues is not None:
6059
bvec = np.zeros((self.bvalues.size, 3))
6160
bvec[:,2] = 1
6261
gtab = gradient_table(self.bvalues, bvec, b0_threshold=0)
6362

64-
self.IAR_algorithm = IvimModelVP(gtab, bounds=self.bounds, rescale_results_to_mm2_s=True)
63+
bounds = [[self.bounds["f"][0], self.bounds["Dp"][0]*1000, self.bounds["D"][0]*1000],
64+
[self.bounds["f"][1], self.bounds["Dp"][1]*1000, self.bounds["D"][1]*1000]]
65+
66+
self.IAR_algorithm = IvimModelVP(gtab, bounds=bounds, rescale_units=False, rescale_results_to_mm2_s=True)
6567
else:
6668
self.IAR_algorithm = None
6769

@@ -76,6 +78,9 @@ def ivim_fit(self, signals, bvalues, **kwargs):
7678
Returns:
7779
_type_: _description_
7880
"""
81+
82+
bounds = [[self.bounds["f"][0], self.bounds["Dp"][0]*1000, self.bounds["D"][0]*1000],
83+
[self.bounds["f"][1], self.bounds["Dp"][1]*1000, self.bounds["D"][1]*1000]]
7984

8085
if self.IAR_algorithm is None:
8186
if bvalues is None:
@@ -87,7 +92,7 @@ def ivim_fit(self, signals, bvalues, **kwargs):
8792
bvec[:,2] = 1
8893
gtab = gradient_table(bvalues, bvec, b0_threshold=0)
8994

90-
self.IAR_algorithm = IvimModelVP(gtab, bounds=self.bounds, rescale_results_to_mm2_s=True)
95+
self.IAR_algorithm = IvimModelVP(gtab, bounds=bounds, rescale_results_to_mm2_s=True)
9196

9297
fit_results = self.IAR_algorithm.fit(signals)
9398

0 commit comments

Comments
 (0)