Skip to content

Commit 66cb0db

Browse files
bounds test automatically check whether bounds are relevant
1 parent 889a2c1 commit 66cb0db

17 files changed

+118
-102
lines changed

src/original/OGC_AmsterdamUMC/LSQ_fitting.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,29 +124,30 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu
124124
:return Dp: Fitted Dp
125125
:return S0: Fitted S0
126126
"""
127-
p0 = [p0[0] * 1000, p0[1] * 10, p0[2] * 10, p0[3]]
128127
try:
129128
# determine high b-values and data for D
129+
dw_data=dw_data/np.mean(dw_data[bvalues==0])
130130
high_b = bvalues[bvalues >= cutoff]
131131
high_dw_data = dw_data[bvalues >= cutoff]
132132
# correct the bounds. Note that S0 bounds determine the max and min of f
133-
bounds1 = ([bounds[0][0] * 1000., 0.7 - bounds[1][1]], [bounds[1][0] * 1000., 1.3 - bounds[0][
134-
1]]) # By bounding S0 like this, we effectively insert the boundaries of f
135-
# fit for S0' and D
136-
params, _ = curve_fit(lambda b, Dt, int: int * np.exp(-b * Dt / 1000), high_b, high_dw_data,
137-
p0=(p0[0], p0[3]-p0[1]/10),
133+
bounds1 = ([bounds[0][0], 0], [bounds[1][0], 10000000000])
134+
params, _ = curve_fit(lambda b, Dt, int: int * np.exp(-b * Dt ), high_b, high_dw_data,
135+
p0=(p0[0], p0[3]-p0[1]),
138136
bounds=bounds1)
139-
Dt, Fp = params[0] / 1000, 1 - params[1]
137+
Dt, Fp = 0+params[0], 1 - params[1]
138+
if Fp < bounds[0][1] : Fp = bounds[0][1]
139+
if Fp > bounds[1][1] : Fp = bounds[1][1]
140+
140141
# remove the diffusion part to only keep the pseudo-diffusion
141142
dw_data_remaining = dw_data - (1 - Fp) * np.exp(-bvalues * Dt)
142-
bounds2 = (bounds[0][2]*10, bounds[1][2]*10)
143+
bounds2 = (bounds[0][2], bounds[1][2])
143144
# fit for D*
144145
params, _ = curve_fit(lambda b, Dp: Fp * np.exp(-b * Dp), bvalues, dw_data_remaining, p0=(p0[2]), bounds=bounds2)
145-
Dp = params[0]
146-
return Dt, Fp, Dp
146+
Dp = 0+params[0]
147+
return Dt, np.float64(Fp), Dp
147148
except:
148149
# if fit fails, return zeros
149-
# print('segnetned fit failed')
150+
# print('segmented fit failed')
150151
return 0., 0., 0.
151152

152153

@@ -235,17 +236,17 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
235236
try:
236237
if not fitS0:
237238
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
238-
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10],
239+
bounds2 = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10],
239240
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10])
240241
p0=[p0[0]*1000,p0[1]*10,p0[2]*10]
241-
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=p0, bounds=bounds)
242+
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=p0, bounds=bounds2)
242243
S0 = 1
243244
else:
244245
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
245-
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]],
246+
bounds2 = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]],
246247
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10, bounds[1][3]])
247248
p0=[p0[0]*1000,p0[1]*10,p0[2]*10,p0[3]]
248-
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds)
249+
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds2)
249250
S0 = params[3]
250251
# correct for the rescaling of parameters
251252
Dt, Fp, Dp = params[0] / 1000, params[1] / 10, params[2] / 10
@@ -261,7 +262,7 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
261262
Dt, Fp, Dp = fit_segmented(bvalues, dw_data, bounds=bounds)
262263
return Dt, Fp, Dp, 1
263264
else:
264-
return fit_segmented(bvalues, dw_data)
265+
return fit_segmented(bvalues, dw_data, bounds=bounds)
265266

266267

267268
def fit_least_squares_array_tri_exp(bvalues, dw_data, S0_output=True, fitS0=True, njobs=4,
@@ -561,19 +562,19 @@ def neg_log_prior(p):
561562
Dt, Fp, Dp = p[0], p[1], p[2]
562563
# make D*<D very unlikely
563564
if (Dp < Dt):
564-
return 1e3
565+
return 1e10
565566
else:
566567
# determine and return the prior for D, f and D* (and S0)
567568
if len(p) == 4:
568-
if Dt_range[0] < Dt < Dt_range[1] and Fp_range[0] < Fp < Fp_range[1] and Dp_range[0] < Dp < Dp_range[1]: # and S0_range[0] < S0 < S0_range[1]: << not sure whether this helps. Technically it should be here
569+
if Dt_range[0] < Dt < Dt_range[1] and Fp_range[0] < Fp < Fp_range[1] and Dp_range[0] < Dp < Dp_range[1] and S0_range[0] < S0 < S0_range[1]: #<< not sure whether this helps. Technically it should be here
569570
return 0
570571
else:
571-
return 1e3
572+
return 1e10
572573
else:
573574
if Dt_range[0] < Dt < Dt_range[1] and Fp_range[0] < Fp < Fp_range[1] and Dp_range[0] < Dp < Dp_range[1]:
574575
return 0
575576
else:
576-
return 1e3
577+
return 1e10
577578

578579
return neg_log_prior
579580

@@ -638,7 +639,7 @@ def parfun(i):
638639
return Dt_pred, Fp_pred, Dp_pred, S0_pred
639640

640641

641-
def fit_bayesian(bvalues, dw_data, neg_log_prior, x0=[0.001, 0.2, 0.05, 1], fitS0=True):
642+
def fit_bayesian(bvalues, dw_data, neg_log_prior, x0=[0.001, 0.2, 0.05, 1], fitS0=True, bounds=([0,0,0,0],[0.005,1.5,2,2.5])):
642643
'''
643644
This is an implementation of the Bayesian IVIM fit. It returns the Maximum a posterior probability.
644645
The fit is taken from Barbieri et al. which was initially introduced in http://arxiv.org/10.1002/mrm.25765 and
@@ -655,7 +656,7 @@ def fit_bayesian(bvalues, dw_data, neg_log_prior, x0=[0.001, 0.2, 0.05, 1], fitS
655656
'''
656657
try:
657658
# define fit bounds
658-
bounds = [(0, 0.005), (0, 1.5), (0, 2), (0, 2.5)]
659+
bounds = [(bounds[0][0], bounds[1][0]), (bounds[0][1], bounds[1][1]), (bounds[0][2], bounds[1][2]), (bounds[0][3], bounds[1][3])]
659660
# Find the Maximum a posterior probability (MAP) by minimising the negative log of the posterior
660661
if fitS0:
661662
params = minimize(neg_log_posterior, x0=x0, args=(bvalues, dw_data, neg_log_prior), bounds=bounds)

src/standardized/ETP_SRI_LinearFitting.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(ETP_SRI_LinearFitting, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Could be a good idea to have all the submission-specfic variable be
4245
# defined with initials?
4346
self.ETP_weighting = weighting

src/standardized/IAR_LU_biexp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_biexp, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/IAR_LU_modified_mix.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_modified_mix, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/IAR_LU_modified_topopro.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_modified_topopro, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/IAR_LU_segmented_2step.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_segmented_2step, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/IAR_LU_segmented_3step.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_segmented_3step, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/IAR_LU_subtracted.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3737
the requirements.
3838
"""
3939
super(IAR_LU_subtracted, self).__init__(bvalues, thresholds, bounds, initial_guess)
40-
40+
if bounds is not None:
41+
print('warning, bounds from wrapper are not (yet) used in this algorithm')
42+
self.use_bounds = False
43+
self.use_initial_guess = False
4144
# Check the inputs
4245

4346
# Initialize the algorithm

src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase):
2828
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
2929
accepts_priors = True
3030

31-
def __init__(self, bvalues=None, thresholds=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, prior_in=None):
31+
def __init__(self, bvalues=None, bounds=None, thresholds = None, initial_guess=None, fitS0=True, prior_in=None):
3232
"""
3333
Everything this algorithm requires should be implemented here.
3434
Number of segmentation thresholds, bounds, etc.
@@ -42,23 +42,28 @@ def __init__(self, bvalues=None, thresholds=None, bounds=([0, 0, 0.005, 0.7],[0.
4242
super(OGC_AmsterdamUMC_Bayesian_biexp, self).__init__(bvalues, bounds, initial_guess) #, fitS0, prior_in)
4343
self.OGC_algorithm = fit_bayesian
4444
self.initialize(bounds, initial_guess, fitS0, prior_in)
45+
self.fit_segmented=fit_segmented
4546

46-
def initialize(self, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, prior_in=None):
47+
def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None):
4748
if bounds is None:
48-
self.bounds=([0, 0, 0.005, 0.7],[0.005, 1, 0.2, 1.3])
49+
self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.1, 1.3])
4950
else:
5051
self.bounds=bounds
52+
self.use_bounds = True
53+
if initial_guess is None:
54+
self.initial_guess = [0.001, 0.1, 0.01, 1]
55+
else:
56+
self.initial_guess = initial_guess
57+
self.use_initial_guess = True
5158
if prior_in is None:
59+
print('using a flat prior between bounds')
5260
self.neg_log_prior=flat_neg_log_prior([self.bounds[0][0],self.bounds[1][0]],[self.bounds[0][1],self.bounds[1][1]],[self.bounds[0][2],self.bounds[1][2]],[self.bounds[0][3],self.bounds[1][3]])
5361
else:
62+
print('warning, bounds are not used, as a prior is used instead')
5463
if len(prior_in) is 4:
5564
self.neg_log_prior = empirical_neg_log_prior(prior_in[0], prior_in[1], prior_in[2],prior_in[3])
5665
else:
5766
self.neg_log_prior = empirical_neg_log_prior(prior_in[0], prior_in[1], prior_in[2])
58-
if initial_guess is None:
59-
self.initial_guess = [0.001, 0.5, 0.1, 1]
60-
else:
61-
self.initial_guess = initial_guess
6267
self.fitS0=fitS0
6368

6469
def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs):
@@ -71,12 +76,14 @@ def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs):
7176
Returns:
7277
_type_: _description_
7378
"""
74-
if initial_guess is not None and len(initial_guess) == 4:
75-
self.initial_guess = initial_guess
7679
bvalues=np.array(bvalues)
77-
fit_results = fit_segmented(bvalues, signals, bounds=self.bounds, cutoff=150, p0=self.initial_guess)
78-
fit_results=fit_results+(1,)
79-
fit_results = self.OGC_algorithm(bvalues, signals, self.neg_log_prior, x0=fit_results, fitS0=self.fitS0)
80+
epsilon = 0.000001
81+
fit_results = self.fit_segmented(bvalues, signals, bounds=self.bounds, cutoff=150, p0=self.initial_guess)
82+
fit_results=np.array(fit_results+(1,))
83+
for i in range(4):
84+
if fit_results[i] < self.bounds[0][i] : fit_results[0] = self.bounds[0][i]+epsilon
85+
if fit_results[i] > self.bounds[1][i] : fit_results[0] = self.bounds[1][i]-epsilon
86+
fit_results = self.OGC_algorithm(bvalues, signals, self.neg_log_prior, x0=fit_results, fitS0=self.fitS0, bounds=self.bounds)
8087

8188
results = {}
8289
results["D"] = fit_results[0]

src/standardized/OGC_AmsterdamUMC_biexp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,23 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
3838
#super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds, initial_guess, fitS0)
3939
super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues=bvalues, bounds=bounds, initial_guess=initial_guess)
4040
self.OGC_algorithm = fit_least_squares
41-
#self.initialize(bounds, initial_guess, fitS0)
4241
self.fitS0=fitS0
42+
self.initialize(bounds, initial_guess, fitS0)
4343

4444
def initialize(self, bounds, initial_guess, fitS0):
4545
if bounds is None:
4646
self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3])
4747
else:
4848
self.bounds=bounds
49+
self.use_bounds = True
4950
if initial_guess is None:
5051
self.initial_guess = [0.001, 0.1, 0.01, 1]
5152
else:
5253
self.initial_guess = initial_guess
54+
self.use_initial_guess = True
5355
self.fitS0=fitS0
5456

57+
5558
def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs):
5659
"""Perform the IVIM fit
5760

0 commit comments

Comments
 (0)