-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibLN.py
326 lines (254 loc) · 11.3 KB
/
libLN.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# Define a log_normal field class
class LogNormalField:
@staticmethod
def compute_rsquared(nside):
"""
Compute the correlation function of the underlying gaussian field
Parameters:
nside : int
Image is nside x nside pixels
"""
import numpy as np
from scipy.linalg import toeplitz
_Di = np.tile(toeplitz(np.arange(nside)),(nside,nside))
_Dj = np.concatenate(
[np.concatenate(
[np.tile(np.abs(i-j),(nside,nside)) for i in range(nside)],
axis=0)
for j in range(nside)],axis=1)
_distance_squared = _Di*_Di+_Dj*_Dj
return _distance_squared
# The lognormal correlation function where the gaussian field has a gaussian power spectrum,
# and the gaussian correlation function xi_G.
@staticmethod
def xi_G(rsq, beta):
"""
Calculates the two-point correlation function of a gaussian field with gaussian power spectrum
Parameters:
rsq : float
separation^2
beta : float
Gaussian smoothing width of gaussian field
"""
import numpy as np
xi = np.exp(-0.25*rsq/(beta**2))
return xi
@staticmethod
def xi_LN(r, beta, alpha, PixelNoise):
"""
Calculates the lognormal two-point correlation function
Parameters:
r : float
Pair separation
beta : float
Gaussian smoothing width of underlying gaussian field
alpha : float
Nongaussianity parameter in lognormal transformation
PixelNoise : float
Standard deviation of added noise per pixel
"""
import numpy as np
xi = 1/(np.power(alpha+1e-12,2)) * (np.exp(np.power(alpha,2)*np.exp(-0.25*np.power(r/beta,2))) - 1)
# Add pixel noise at zero separation:
xi[np.where(r<1e-5)] += PixelNoise**2
return xi
@staticmethod
def dxi_LN_dalpha(r, beta, alpha, PixelNoise):
import numpy as np
return 2/(alpha+1e-12) * np.exp(-0.25*np.power(r/beta,2)) * np.exp(np.power(alpha,2)*np.exp(-0.25*np.power(r/beta,2))) - 2/np.power(alpha+1e-12,3) * (np.exp(np.power(alpha,2)*np.exp(-0.25*np.power(r/beta,2))) - 1)
@staticmethod
def dxi_LN_dbeta(r, beta, alpha, PixelNoise):
import numpy as np
return (0.5*np.power(r,2)/np.power(beta,3)) * np.exp(-0.25*np.power(r/beta,2)) * np.exp(np.power(alpha,2)*np.exp(-0.25*np.power(r/beta,2)))
def __init__(self,Lside,rmax,nbin):
"""
Parameters:
rmax : float
Maximum pair separation considered
nbin : int
Number of bins for shell-averaged correlation function
"""
import numpy as np
self.rmax = rmax
self.nbin = nbin
self.Lside = Lside
# compute the separations and indices on a grid
self.rsq = self.compute_rsquared(Lside)
self.r = np.sqrt(self.rsq)
self.bins = np.arange(nbin)*rmax/nbin
self.index = np.digitize(self.r,self.bins)
self.average_r = np.array([self.r[self.index == n].mean() for n in range(nbin) if np.sum(self.index == n)>0])
@staticmethod
def G_to_LN(gaussian, alpha):
import numpy as np
# Make lognormal (variance of gaussian field is unity by construction)
# Divide by 1/alpha so that the signal-to-noise ratio is independent of alpha
return 1/alpha * (np.exp(alpha * gaussian-0.5*alpha**2)-1)
def run_simulation(self, alpha, beta, PixelNoise):
"""
Create a lognormal field from a gaussian field with a Gaussian correlation function
"""
import numpy as np
Lside = self.Lside
rsq = self.rsq
# Compute the Gaussian correlation function
xiG = self.xi_G(rsq,beta)
# Compute the Gaussian random field
field = (np.random.multivariate_normal(np.zeros(Lside*Lside),xiG)).reshape(Lside,Lside)
# Make lognormal (variance of gaussian field is unity by construction)
field = self.G_to_LN(field, alpha)
# Add noise
field += np.random.normal(loc=0.0,scale=PixelNoise,size=(Lside,Lside))
return field
def pymc3_model(self, field_data, alphamin, alphamax, betamin, betamax, PixelNoise):
import numpy as np
import pymc3 as pm
LN_model = pm.Model()
Lside = self.Lside
rsq = self.rsq
zero = np.zeros(Lside*Lside)
PixelNoiseVector = PixelNoise*np.ones(Lside*Lside)
InvNoiseCovariance = np.diag(1/(PixelNoiseVector**2))
field_data = field_data.reshape(Lside*Lside)
with LN_model:
# Uniform priors for unknown model parameters (alpha,beta):
alpha_p = pm.Uniform("alpha", lower=alphamin, upper=alphamax)
beta_p = pm.Uniform("beta", lower=betamin, upper=betamax)
# Compute (beta-dependent) gaussian field correlation function:
xi = pm.math.exp(-0.25*rsq/(beta_p*beta_p))
# Gaussian field values are latent variables:
gaussian = pm.MvNormal("gaussian",mu=zero,cov=xi,shape=Lside*Lside)
# Expected value of lognormal field, for given (alpha, beta, gaussian):
muLN = 1/alpha_p * (pm.math.exp(alpha_p * gaussian-0.5*alpha_p*alpha_p)-1)
# Likelihood (sampling distribution) of observations, given the mean lognormal field:
Y_obs = pm.MvNormal("Y_obs", mu=muLN, tau=InvNoiseCovariance, observed=field_data)
return LN_model
def run_diff_simulation(self, alpha, beta, PixelNoise, step, seed):
"""
Run simulations for finite differencing
"""
import numpy as np
from scipy.stats import multivariate_normal
Lside = self.Lside
rsq = self.rsq
alphap = alpha*(1+step)
alpham = alpha*(1-step)
betap = beta*(1+step)
betam = beta*(1-step)
# Compute the gaussian correlation function
xiG = self.xi_G(rsq,beta)
xiG_betap = self.xi_G(rsq,betap)
xiG_betam = self.xi_G(rsq,betam)
# Compute Gaussian random fields with the same phases
Gfield = multivariate_normal(mean=np.zeros(Lside*Lside), cov=xiG).rvs(random_state=seed).reshape(Lside,Lside)
Gfield_betap = multivariate_normal(mean=np.zeros(Lside*Lside), cov=xiG_betap).rvs(random_state=seed).reshape(Lside,Lside)
Gfield_betam = multivariate_normal(mean=np.zeros(Lside*Lside), cov=xiG_betam).rvs(random_state=seed).reshape(Lside,Lside)
# Make lognormal (variance of gaussian field is unity by construction)
field = self.G_to_LN(Gfield, alpha)
field_betap = self.G_to_LN(Gfield_betap, alpha)
field_betam = self.G_to_LN(Gfield_betam, alpha)
field_alphap = self.G_to_LN(Gfield, alphap)
field_alpham = self.G_to_LN(Gfield, alpham)
# Add noise
noise = np.random.normal(loc=0.0,scale=PixelNoise,size=(Lside,Lside))
field += noise
field_betap += noise
field_betam += noise
field_alphap += noise
field_alpham += noise
return field, field_alphap, field_alpham, field_betap, field_betam
def compute_corrfn(self,field):
"""
Compute two-point correlation function
"""
import numpy as np
index = self.index
nbin = self.nbin
# compute the correlations
correlations = np.outer(field,field)
corrfn = np.array([correlations[index==n].mean() for n in range(nbin) if len(correlations[index==n])>0])
return corrfn
def compute_corrfn_derivatives(self, field, field_alphap, field_alpham, field_betap, field_betam, step):
"""
Compute derivatives of the two-point correlation function
"""
# Compute correlation functions
corrfn = self.compute_corrfn(field)
corrfn_dalphap = self.compute_corrfn(field_alphap)
corrfn_dalpham = self.compute_corrfn(field_alpham)
corrfn_dbetap = self.compute_corrfn(field_betap)
corrfn_dbetam = self.compute_corrfn(field_betam)
# Compute derivatives by second-order central finite differences
dcorrfn_dalpha = (corrfn_dalpham - 2*corrfn + corrfn_dalphap)/(step**2)
dcorrfn_dbeta = (corrfn_dbetam - 2*corrfn + corrfn_dbetap )/(step**2)
return dcorrfn_dalpha, dcorrfn_dbeta
def covariance(self,fields):
"""
Compute covariance from a number of fields
Parameter:
fields : int
lognormal field objects contributing to the covariance matrix
"""
import numpy as np
nsims = len(fields)
nbins = self.nonzerobins
print('Number of simulations',nsims)
print('Number of non-zero pair bins',nbins)
corrfns = np.array([fields[i]['corrfn'] for i in range(nsims)])
meanxi = np.mean(corrfns,axis=0)
covxi = np.cov(corrfns.T)
return meanxi, covxi
# Utility properties
@staticmethod
def var_th(alpha, PixelNoise):
import numpy as np
return 1/np.power(alpha+1e-12,2)*(np.exp(alpha**2)-1)+PixelNoise**2
@staticmethod
def skew_th(alpha):
import numpy as np
return (np.exp(alpha**2)+2)*np.sqrt(np.exp(alpha**2)-1)
@staticmethod
def dskew_dalpha(alpha):
import numpy as np
return 2*alpha*np.exp(alpha**2) * ( np.sqrt(np.exp(alpha**2)-1) - 0.5*(np.exp(alpha**2)+2)/(np.sqrt(np.exp(alpha**2)-1)) )
@staticmethod
def kurtosis_th(alpha):
import numpy as np
return np.exp(4*alpha**2)+2*np.exp(3*alpha**2)+3*np.exp(2*alpha**2)-6
@staticmethod
def dkurtosis_dalpha(alpha):
import numpy as np
return 8*alpha*np.exp(4*alpha**2)+6*alpha*np.exp(3*alpha**2)+6*alpha*np.exp(2*alpha**2)
@staticmethod
def max(field):
import numpy as np
return np.max(field)
@staticmethod
def min(field):
import numpy as np
return np.min(field)
@staticmethod
def var(field):
import numpy as np
return np.var(field)
@staticmethod
def mean(field):
import numpy as np
return np.mean(field)
@staticmethod
def skew(field):
from scipy.stats import skew
return skew(field.flatten())
@staticmethod
def kurtosis(field):
from scipy.stats import kurtosis
return kurtosis(field.flatten())
# xi has empty bins removed. Note the number of non-empty elements
@property
def nonzerobins(self):
return len(self.average_r)
@property
def dt(self):
import numpy as np
return np.dtype([('field', np.float, (self.Lside,self.Lside)), ('corrfn', np.float, (self.nonzerobins))])
# end class LogNormalField