1+ import numpy as np
2+ import emcee
3+ import tqdm
4+ import corner
5+ from scipy .stats import rice , norm , uniform
6+ from matplotlib import pyplot as plt
7+
8+ from utilities .data_simulation .GenerateData import GenerateData
9+
10+ class MCMC :
11+ """
12+ Performs sampling of exponential data
13+ """
14+ def __init__ (self ,
15+ data ,
16+ b ,
17+ data_scale = 1e-5 ,
18+ parameter_scale = (1e-7 , 1e-11 , 1e-9 ),
19+ bounds = ((0 , 1 ), (0 , 1 ), (0 , 1 )),
20+ priors = None ,
21+ gaussian_noise = False ,
22+ nwalkers = 16 ,
23+ nsteps = 10000 ,
24+ burn_in = 2000 ,
25+ progress = True ):
26+ """
27+ Parameters
28+ ----------
29+
30+ """
31+ self .data = np .atleast_2d (np .asarray (data ))
32+ self .b = np .atleast_2d (np .asarray (b )).T
33+ self .data_scale = np .asarray (data_scale )
34+ self .parameter_scale = np .asarray (parameter_scale )
35+ self .bounds = np .asarray (bounds )
36+ self .priors = np .asarray (priors )
37+ self .nwalkers = nwalkers
38+ self .nsteps = nsteps
39+ self .burn_in = burn_in
40+ self .progress = progress
41+ if priors is None :
42+ self .prior = self .zero_prior
43+ else :
44+ self .prior = self .loglike_gauss_prior
45+ if gaussian_noise :
46+ self .likelihood = self .biexp_loglike_gauss
47+ else :
48+ self .likelihood = self .biexp_loglike_rice
49+ self .ndim = 3
50+ self .chain = None
51+ self .means = None
52+ self .stds = None
53+
54+ def accepted_dimensions (self ):
55+ return (1 , 1 )
56+
57+ def bounds_prior (self , params ):
58+ return np .sum (uniform .logpdf (params , loc = self .bounds [:, 0 ], scale = self .bounds [:, 1 ] - self .bounds [:, 0 ]), 1 )
59+
60+ def loglike_gauss_prior (self , params ):
61+ return np .sum (norm .logpdf (params , loc = self .priors [:, 0 ], scale = self .priors [:, 1 ]), 1 )
62+
63+ def zero_prior (self , params ):
64+ return 0
65+
66+ def signal (self , f , D , D_star ):
67+ return (f * np .exp (- self .b * D_star ) + (1 - f ) * np .exp (- self .b * D )).T
68+
69+ def biexp_loglike_gauss (self , f , D , D_star ):
70+ expected = self .signal (f , D , D_star )
71+ # check this!
72+ # print(f'likelihood {norm.logpdf(self.data, loc=expected/self.data_scale, scale=self.data_scale)}')
73+ return np .sum (norm .logpdf (self .data , loc = expected , scale = self .data_scale ), 1 )
74+
75+ # def biexp_loglike_gauss_full(self, f, D, D_star):
76+ # expected = self.signal(f, D, D_star)
77+ # print(f'expected {expected}')
78+ # print(f'data {self.data}')
79+ # return norm.logpdf(self.data, loc=expected, scale=self.data_scale)
80+
81+ def biexp_loglike_rice (self , f , D , D_star ):
82+ expected = self .signal (f , D , D_star )
83+ # print(f'expected {expected}')
84+ return np .sum (rice .logpdf (self .data , b = expected / self .data_scale , scale = self .data_scale ), 1 )
85+
86+ def posterior (self , params ):
87+ params = np .atleast_2d (params )
88+ total = self .bounds_prior (params )
89+ # print(f'bounds params {total}')
90+ neginf = np .isneginf (total )
91+ # print(f'neginf {neginf}')
92+ f = params [~ neginf , 0 ]
93+ D = params [~ neginf , 1 ]
94+ D_star = params [~ neginf , 2 ]
95+ prior = self .prior (params [~ neginf , :])
96+ # print(f'prior {prior}')
97+ likelihood = self .likelihood (f , D , D_star )
98+ # print(f'likelihood {likelihood}')
99+ total [~ neginf ] += prior + likelihood
100+ return total
101+
102+ def sample (self , initial_pos ):
103+ # f = initial_pos[0]
104+ # D = initial_pos[1]
105+ # D_star = initial_pos[2]
106+ # print(f'initial pos likelihood {self.biexp_loglike_gauss_full(f, D, D_star)}')
107+ print (f'initial pos likelihood { self .posterior (initial_pos )} ' )
108+ sampler = emcee .EnsembleSampler (self .nwalkers , 3 , self .posterior , vectorize = True )
109+ pos = initial_pos + self .parameter_scale * np .random .randn (self .nwalkers , self .ndim )
110+ # print(f'pos {pos}')
111+ # print(f'nsteps {self.nsteps}')
112+ sampler .run_mcmc (pos , self .nsteps , progress = True )
113+ self .chain = sampler .get_chain (discard = self .burn_in , flat = True )
114+ self .means = np .mean (self .chain , 0 )
115+ self .stds = np .std (self .chain , 0 )
116+ print (f'final pos likelihood { self .posterior (self .means )} ' )
117+ # print(f'final pos likelihood {self.biexp_loglike_gauss_full(self.means[0], self.means[1], self.means[2])}')
118+ # print(f'chain {self.chain}')
119+ return self .means , self .stds
120+
121+ def plot (self , truths = None , labels = ('f' , 'D' , 'D*' ), overplot = None ):
122+ if truths is None :
123+ truths = self .means
124+ # print(f'chain size {self.chain.shape}')
125+ fig = corner .corner (self .chain , labels = labels , truths = truths )
126+ fig .suptitle ("Sampling of the IVIM data" , fontsize = 16 )
127+ if overplot is not None :
128+ corner .overplot_lines (fig , overplot , color = 'r' )
129+ plt .show ()
130+
0 commit comments