-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unit testing tolerances + more consistent definition of SNR #79
base: main
Are you sure you want to change the base?
Changes from all commits
25b29d9
eea774c
8d1a418
1bcb389
1d26bad
9beef6d
889a2c1
66cb0db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this algorithm wrapped? I just want to make sure these changes are also made there, if necessary. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,29 +124,30 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu | |
:return Dp: Fitted Dp | ||
:return S0: Fitted S0 | ||
""" | ||
p0 = [p0[0] * 1000, p0[1] * 10, p0[2] * 10, p0[3]] | ||
try: | ||
# determine high b-values and data for D | ||
dw_data=dw_data/np.mean(dw_data[bvalues==0]) | ||
high_b = bvalues[bvalues >= cutoff] | ||
high_dw_data = dw_data[bvalues >= cutoff] | ||
# correct the bounds. Note that S0 bounds determine the max and min of f | ||
bounds1 = ([bounds[0][0] * 1000., 0.7 - bounds[1][1]], [bounds[1][0] * 1000., 1.3 - bounds[0][ | ||
1]]) # By bounding S0 like this, we effectively insert the boundaries of f | ||
# fit for S0' and D | ||
params, _ = curve_fit(lambda b, Dt, int: int * np.exp(-b * Dt / 1000), high_b, high_dw_data, | ||
p0=(p0[0], p0[3]-p0[1]/10), | ||
bounds1 = ([bounds[0][0], 0], [bounds[1][0], 10000000000]) | ||
params, _ = curve_fit(lambda b, Dt, int: int * np.exp(-b * Dt ), high_b, high_dw_data, | ||
p0=(p0[0], p0[3]-p0[1]), | ||
bounds=bounds1) | ||
Dt, Fp = params[0] / 1000, 1 - params[1] | ||
Dt, Fp = 0+params[0], 1 - params[1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if Fp < bounds[0][1] : Fp = bounds[0][1] | ||
if Fp > bounds[1][1] : Fp = bounds[1][1] | ||
|
||
# remove the diffusion part to only keep the pseudo-diffusion | ||
dw_data_remaining = dw_data - (1 - Fp) * np.exp(-bvalues * Dt) | ||
bounds2 = (bounds[0][2]*10, bounds[1][2]*10) | ||
bounds2 = (bounds[0][2], bounds[1][2]) | ||
# fit for D* | ||
params, _ = curve_fit(lambda b, Dp: Fp * np.exp(-b * Dp), bvalues, dw_data_remaining, p0=(p0[2]), bounds=bounds2) | ||
Dp = params[0] | ||
return Dt, Fp, Dp | ||
Dp = 0+params[0] | ||
return Dt, np.float64(Fp), Dp | ||
except: | ||
# if fit fails, return zeros | ||
# print('segnetned fit failed') | ||
# print('segmented fit failed') | ||
return 0., 0., 0. | ||
|
||
|
||
|
@@ -235,17 +236,17 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True, | |
try: | ||
if not fitS0: | ||
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting. | ||
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10], | ||
bounds2 = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10], | ||
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10]) | ||
p0=[p0[0]*1000,p0[1]*10,p0[2]*10] | ||
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=p0, bounds=bounds) | ||
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=p0, bounds=bounds2) | ||
S0 = 1 | ||
else: | ||
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting. | ||
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]], | ||
bounds2 = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]], | ||
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10, bounds[1][3]]) | ||
p0=[p0[0]*1000,p0[1]*10,p0[2]*10,p0[3]] | ||
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds) | ||
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds2) | ||
S0 = params[3] | ||
# correct for the rescaling of parameters | ||
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, | |
Dt, Fp, Dp = fit_segmented(bvalues, dw_data, bounds=bounds) | ||
return Dt, Fp, Dp, 1 | ||
else: | ||
return fit_segmented(bvalues, dw_data) | ||
return fit_segmented(bvalues, dw_data, bounds=bounds) | ||
|
||
|
||
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): | |
Dt, Fp, Dp = p[0], p[1], p[2] | ||
# make D*<D very unlikely | ||
if (Dp < Dt): | ||
return 1e3 | ||
return 1e10 | ||
else: | ||
# determine and return the prior for D, f and D* (and S0) | ||
if len(p) == 4: | ||
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 | ||
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 | ||
return 0 | ||
else: | ||
return 1e3 | ||
return 1e10 | ||
else: | ||
if Dt_range[0] < Dt < Dt_range[1] and Fp_range[0] < Fp < Fp_range[1] and Dp_range[0] < Dp < Dp_range[1]: | ||
return 0 | ||
else: | ||
return 1e3 | ||
return 1e10 | ||
|
||
return neg_log_prior | ||
|
||
|
@@ -638,7 +639,7 @@ def parfun(i): | |
return Dt_pred, Fp_pred, Dp_pred, S0_pred | ||
|
||
|
||
def fit_bayesian(bvalues, dw_data, neg_log_prior, x0=[0.001, 0.2, 0.05, 1], fitS0=True): | ||
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])): | ||
''' | ||
This is an implementation of the Bayesian IVIM fit. It returns the Maximum a posterior probability. | ||
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 | |
''' | ||
try: | ||
# define fit bounds | ||
bounds = [(0, 0.005), (0, 1.5), (0, 2), (0, 2.5)] | ||
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])] | ||
# Find the Maximum a posterior probability (MAP) by minimising the negative log of the posterior | ||
if fitS0: | ||
params = minimize(neg_log_posterior, x0=x0, args=(bvalues, dw_data, neg_log_prior), bounds=bounds) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Median now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, somehow there was a weird rounding happening in the np.mean that made values become 0.0000000000000001 off or so. So all round values become D= 0.0029999999999 instead of 0.003 etc.