-
Notifications
You must be signed in to change notification settings - Fork 3
/
helpers.py
304 lines (193 loc) · 8.36 KB
/
helpers.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
import numpy as np
from scipy import spatial
from scipy.optimize import nnls
from math import acos, degrees
def get_q(lambda0,refractiveIndex,scatteringAngle):
# Calculate the Bragg wave vector
# scatteringAngle in radians (2pi rad equals 360 degrees).
# labmda in nanometers
lambda0 = lambda0 / 1e9 # To meters
# Output units are 1/m
try:
refractiveIndex = complex(refractiveIndex).real
except:
return None
return 4*np.pi*refractiveIndex/lambda0 * np.sin(scatteringAngle/2)
def hydrodynamic_radius(D,temp,viscosity):
# D is the single-particle diffusion coefficient
# D units are m**2 / seconds
# viscosity units are pascal-second (Newton-second per square meter)
# Returns the Stokes–Einstein radius
kb = 1.380649e-23 # Joul / Kelvin
return kb*temp / (6*np.pi*viscosity*D) # Units are meters
def diffusion_from_hydrodynamic_radius(hr,temp,viscosity):
# Compute the single-particle diffusion coefficient (units are m**2 / seconds)
# viscosity units are pascal-second (Newton-second per square meter)
# temp in kelvin
kb = 1.380649e-23 # Joul / Kelvin
return kb*temp / (6*np.pi*viscosity*hr) # Units are meters
def diffusion_from_inverse_decay_rate(s,q):
# s is the inverse of the decay rate (gamma)
# q is derived from get_q()
return 1 / (s*(q**2))
def s_inverse_decay_rate(D,q):
# D is the single-particle diffusion coefficient
# D units are m**2 / seconds
return 1 / (D*(q**2)) # units are seconds
def g1_first_order_corr(s,t):
# s is the inverse of the decay rate (gamma)
gamma = 1/s
return np.exp(-gamma*t) # unitless
def g2_second_order_corr(g1,beta):
# g1 is the first order autocorrelation
return 1 + beta * (g1)**2 # unitless
def g1_from_g2(g2,beta):
# g2 is the second order autocorrelation
return np.sqrt( (g2-1) / beta) # unitless
def get_beta_prior(g2,time):
"""
Requires -
g2 matrix n*m
Time vector of length n
Returns the intercept estimate
"""
npFit = np.polyfit( time[time < 5*1e-6],np.log(g2[time < 5*1e-6,:] - 1), 2 )
betaPrior = np.exp(npFit[-1])
return betaPrior
def tikhonov_Phillips_reg(kernel,alpha,data,W):
"""
Solve x / minimize ||w(Ax - b)|| + alpha||Mx||
A is the kernel
b is the vector with the measurements
x is the unknown vector we want to estimate
W is the weights vectors
M is the second order derivative matrix
Moreover, we add a last equation to the system such that sum(x) equals 1!
and we force the initial and last values to be equal to 0!
This function is was based on a
Quora answer (https://scicomp.stackexchange.com/questions/10671/tikhonov-regularization-in-the-non-negative-least-square-nnls-pythonscipy)
given by Dr. Brian Borchers (https://scicomp.stackexchange.com/users/2150/brian-borchers)
"""
W = np.append(W,np.array([1e3,1e3,1e3])) # weight to force the initial and last values equal to 0, and the sum of contributions equal to 1
rowToForceInitialValue = np.zeros(kernel.shape[1])
rowToForceInitialValue[0] = 1
rowToForceLastValue = np.flip(rowToForceInitialValue)
data = np.sqrt(W)*np.append(data,np.array([1,0,0]))
data = data.reshape(-1, 1)
kernel = np.vstack([kernel,np.ones(kernel.shape[1]),rowToForceInitialValue,rowToForceLastValue])
kernel = np.sqrt(W)[:, None] * kernel # Fidelity term
cols = kernel.shape[1]
M = np.zeros((cols,cols))
for i in range(1,M.shape[1]-1):
M[i,i-1] = -1
M[i,i] = 2
M[i,i+1] = -1
L = np.sqrt(alpha) * M # Penalty term
C = np.concatenate([kernel, L], axis=0)
d = np.concatenate([data, np.zeros(cols).reshape(-1, 1)])
# residual from || Ax-b ||_2
x, residualNorm = nnls(C, d.flatten())
# Get the norm of the penalty term
penaltyNorm = np.linalg.norm(M.dot(x),2)
return x, residualNorm, penaltyNorm
def get_contributios_prior(g1_autocorrelation,time,s_space,betaPrior,alpha,weights=None):
"""
Input -
g1 autocorrelation matrix n-points m-datasets
time vector of length n
s_space to create the kernel for the Thinkohonov regularization function
betaPrior vector of length m
Returns -
The estimated contribution of each decay rate (length defined by the s_space vector)
"""
nDatasets = g1_autocorrelation.shape[1]
# Convert to list if required - we need one reg term (aka alpha) per curve
if type(alpha) is not list:
alphaList = [alpha for _ in range(nDatasets)]
else:
# No need to convert to list
alphaList = alpha
s = s_space.reshape((-1, 1))
sM, tM = np.meshgrid(s, time, indexing='xy')
A = np.exp(-tM/sM)
contributions = []
residuals = []
penaltyNorms = []
for i in range(nDatasets):
g1temp = g1_autocorrelation[:,i]
try:
maxID = np.min(np.argwhere(np.isnan(g1temp)))
except:
maxID = len(g1temp)
try:
if weights is None:
weightsIdx = np.arange(maxID)*0 + 1 # Equal weights
else:
weightsIdx = weights[:maxID,i] # Custom weights
g1Filtered = g1temp[:maxID]
Afiltered = A[:maxID,:]
cont, residual, penaltyNorm = tikhonov_Phillips_reg(Afiltered,alphaList[i],g1Filtered,weightsIdx)
# If the fitting didn't work!
except:
cont = [0]
residuals = [0]
penaltyNorm = [0]
contributions.append(np.array(cont))
residuals.append(residual)
penaltyNorms.append(penaltyNorm)
return contributions, residuals, penaltyNorms
def g2_finite_aproximation(decay_rates,times,beta,contributions):
"""
Obtain the autocorrelation function based on decay rates and their
relatives contributions
beta is the intercept (g2 at time 0)
"""
assert len(decay_rates) == len(contributions)
g1 = np.array([np.sum(contributions*np.exp(-decay_rates*t)) for t in times])
return 1 + beta * (g1)**2
def cosLawAngle(d1,d2,d3):
"""
Use the three distances between vertices to get the angle
d1 = AB segment
d2 = AC segment
d3 = BC segment
"""
return (degrees(acos((d1**2 + d2**2 - d3**2)/(2.0 * d1 * d2))))
def find_Lcurve_corner(residualsNorm,contributionsNorm):
"""
Use the triangle method to find the corner of the L-curve
If you use this function please cite the original manuscript from which I took the idea:
Castellanos, J. Longina, Susana Gómez, and Valia Guerra.
"The triangle method for finding the corner of the L-curve."
Applied Numerical Mathematics 43.4 (2002): 359-373.
Input - the norm vector of the residuals and the norm vector of the contributions
i.e., the norm of the fidelity term and the norm of the penalty term
Returns the position of the corner of the curve log(contributionsNorm) vs log(residualsNorm)
"""
# Convert to log
x = np.log(residualsNorm)
y = np.log(contributionsNorm)
# Normalise to avoid floating point errors - This doesn't change the shape of the curve
x = (x - np.min(x)) / (np.max(x)-np.min(x)) * 100
y = (y - np.min(y)) / (np.max(y)-np.min(y)) * 100
nPoints = len(x)
angles = []
poi2 = []
C = (x[nPoints-1],y[nPoints-1]) # Last point of the curve
for i in range(nPoints-4):
B = (x[i],y[i])
d3 = spatial.distance.cdist([B],[C])[0]
for ii in range(i+2,nPoints-2):
A = (x[ii],y[ii])
d1 = spatial.distance.cdist([B],[A])[0]
d2 = spatial.distance.cdist([A],[C])[0]
area = (B[0] - A[0])*(A[1]-C[1]) - (A[0] - C[0])*(B[1]-A[1])
angle = cosLawAngle(d1,d2,d3)
if angle < 160 and area > 0:
poi2.append(ii)
angles.append(angle)
try:
selIdx2 = poi2[np.argmin(angles)]
return selIdx2
except:
return None