Skip to content

Commit b872d4d

Browse files
Added Bayesian fit
With flat prior, as hard to get emperical prior from single input
1 parent 69aec08 commit b872d4d

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/original/OGC_AmsterdamUMC/LSQ_fitting.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,37 @@ def neg_log_prior(p):
519519

520520
return neg_log_prior
521521

522+
def flat_neg_log_prior(Dt_range, Fp_range, Dp_range, S0_range=None):
523+
"""
524+
This function determines the negative of the log of the empirical prior probability of the IVIM parameters
525+
:param Dt0: 1D Array with the initial D estimates
526+
:param Dt0: 1D Array with the initial f estimates
527+
:param Dt0: 1D Array with the initial D* estimates
528+
:param Dt0: 1D Array with the initial S0 estimates (optional)
529+
"""
530+
def neg_log_prior(p):
531+
# depends on whether S0 is fitted or not
532+
if len(p) == 4:
533+
Dt, Fp, Dp, S0 = p[0], p[1], p[2], p[3]
534+
else:
535+
Dt, Fp, Dp = p[0], p[1], p[2]
536+
# make D*<D very unlikely
537+
if (Dp < Dt):
538+
return 1e8
539+
else:
540+
eps = 1e-8
541+
Dp_prior = stats.loguniform.pdf(Dp, Dp_range[0], scale=Dp_range[1]-Dp_range[0])
542+
Dt_prior = stats.loguniform.pdf(Dt, Dt_range[0], scale=Dt_range[1]-Dt_range[0])
543+
Fp_prior = stats.loguniform.pdf(Fp, Fp_range[0], scale=Fp_range[1]-Fp_range[0])
544+
# determine and return the prior for D, f and D* (and S0)
545+
if len(p) == 4:
546+
S0_prior = stats.loguniform.pdf(S0, S0_range[0], scale=S0_range[1] - S0_range[0])
547+
return -np.log(Dp_prior + eps) - np.log(Dt_prior + eps) - np.log(Fp_prior + eps) - np.log(
548+
S0_prior + eps)
549+
else:
550+
return -np.log(Dp_prior + eps) - np.log(Dt_prior + eps) - np.log(Fp_prior + eps)
522551

552+
return neg_log_prior
523553
def neg_log_likelihood(p, bvalues, dw_data):
524554
"""
525555
This function determines the negative of the log of the likelihood of parameters p, given the data dw_data for the Bayesian fit
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from src.wrappers.OsipiBase import OsipiBase
2+
from src.original.OGC_AmsterdamUMC.LSQ_fitting import flat_neg_log_prior, fit_bayesian
3+
import numpy as np
4+
5+
class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase):
6+
"""
7+
Bayesian Bi-exponential fitting algorithm by Oliver Gurney-Champion, Amsterdam UMC
8+
"""
9+
10+
# I'm thinking that we define default attributes for each submission like this
11+
# And in __init__, we can call the OsipiBase control functions to check whether
12+
# the user inputs fulfil the requirements
13+
14+
# Some basic stuff that identifies the algorithm
15+
id_author = "Oliver Gurney Champion, Amsterdam UMC"
16+
id_algorithm_type = "Bi-exponential fit"
17+
id_return_parameters = "f, D*, D, S0"
18+
id_units = "seconds per milli metre squared or milliseconds per micro metre squared"
19+
20+
# Algorithm requirements
21+
required_bvalues = 4
22+
required_thresholds = [0,
23+
0] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
24+
required_bounds = False
25+
required_bounds_optional = True # Bounds may not be required but are optional
26+
required_initial_guess = False
27+
required_initial_guess_optional = True
28+
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
29+
30+
def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, thresholds=None):
31+
"""
32+
Everything this algorithm requires should be implemented here.
33+
Number of segmentation thresholds, bounds, etc.
34+
35+
Our OsipiBase object could contain functions that compare the inputs with
36+
the requirements.
37+
"""
38+
super(OGC_AmsterdamUMC_Bayesian_biexp, self).__init__(bvalues, bounds,initial_guess,fitS0)
39+
if bounds is None:
40+
self.bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])
41+
else:
42+
self.bounds=bounds
43+
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][1],self.bounds[1][1]],[self.bounds[0][2],self.bounds[1][2]])
44+
self.OGC_algorithm = fit_bayesian
45+
self.bounds=bounds
46+
self.initial_guess=initial_guess
47+
self.fitS0=fitS0
48+
49+
def ivim_fit(self, signals, bvalues=None):
50+
"""Perform the IVIM fit
51+
52+
Args:
53+
signals (array-like)
54+
bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
55+
56+
Returns:
57+
_type_: _description_
58+
"""
59+
bvalues=np.array(bvalues)
60+
fit_results = self.OGC_algorithm(bvalues, signals, self.neg_log_prior, x0=self.initial_guess, fitS0=self.fitS0)
61+
62+
D = fit_results[0]
63+
f = fit_results[1]
64+
Dstar = fit_results[2]
65+
66+
return f, Dstar, D

0 commit comments

Comments
 (0)