1
"""Contains simple algorithms.
3
Copyright (C) 2013, Joshua More and Michele Ceriotti
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http.//www.gnu.org/licenses/>.
20
matrix_exp: Computes the exponential of a square matrix via a Taylor series.
21
stab_cholesky: A numerically stable version of the Cholesky decomposition.
22
h2abc: Takes the representation of the system box in terms of an upper
23
triangular matrix of column vectors, and returns the representation in
24
terms of the lattice vector lengths and the angles between them
26
h2abc_deg: Takes the representation of the system box in terms of an upper
27
triangular matrix of column vectors, and returns the representation in
28
terms of the lattice vector lengths and the angles between them in
30
abc2h: Takes the representation of the system box in terms of the lattice
31
vector lengths and the angles between them, and returns the
32
representation in terms of an upper triangular lattice vector matrix.
33
invert_ut3x3: Inverts a 3*3 upper triangular matrix.
34
det_ut3x3(h): Finds the determinant of a 3*3 upper triangular matrix.
35
eigensystem_ut3x3: Finds the eigenvector matrix and eigenvalues of a 3*3
36
upper triangular matrix
37
exp_ut3x3: Computes the exponential of a 3*3 upper triangular matrix.
38
root_herm: Computes the square root of a positive-definite hermitian
40
logsumlog: Routine to accumulate the logarithm of a sum
43
__all__ = ['matrix_exp', 'stab_cholesky', 'h2abc', 'h2abc_deg', 'abc2h',
44
'invert_ut3x3', 'det_ut3x3', 'eigensystem_ut3x3', 'exp_ut3x3',
45
'root_herm', 'logsumlog' ]
49
from ipi.utils.messages import verbosity, warning
51
def logsumlog(lasa, lbsb):
52
"""Computes log(|A+B|) and sign(A+B) given log(|A|), log(|B|),
56
lasa: (log(|A|), sign(A)) as a tuple
57
lbsb: (log(|B|), sign(B)) as a tuple
60
(log(|A+B|), sign(A+B)) as a tuple
68
lr = la + np.log(1.0 + sb*np.exp(lb-la))
71
lr = lb + np.log(1.0 + sa*np.exp(la-lb))
75
def matrix_exp(M, ntaylor=15, nsquare=15):
76
"""Computes the exponential of a square matrix via a Taylor series.
78
Calculates the matrix exponential by first calculating exp(M/(2**nsquare)),
79
then squaring the result the appropriate number of times.
82
M: Matrix to be exponentiated.
83
ntaylor: Optional integer giving the number of terms in the Taylor series.
85
nsquare: Optional integer giving how many times the original matrix will
86
be halved. Defaults to 15.
89
The matrix exponential of M.
93
tc = np.zeros(ntaylor+1)
95
for i in range(ntaylor):
98
SM = np.copy(M)/2.0**nsquare
100
EM = np.identity(n,float)*tc[ntaylor]
101
for i in range(ntaylor-1,-1,-1):
103
EM += np.identity(n)*tc[i]
105
for i in range(nsquare):
109
def stab_cholesky(M):
110
""" A numerically stable version of the Cholesky decomposition.
112
Used in the GLE implementation. Since many of the matrices used in this
113
algorithm have very large and very small numbers in at once, to handle a
114
wide range of frequencies, a naive algorithm can end up having to calculate
115
the square root of a negative number, which breaks the algorithm. This is
116
due to numerical precision errors turning a very tiny positive eigenvalue
117
into a tiny negative value.
119
Instead of this, an LDU decomposition is used, and any small negative numbers
120
in the diagonal D matrix are assumed to be due to numerical precision errors,
121
and so are replaced with zero.
124
M: The matrix to be decomposed.
128
D = np.zeros(n,float)
129
L = np.zeros(M.shape,float)
135
L[i,j] -= L[i,k]*L[j,k]*D[k]
136
if (not D[j] == 0.0):
140
D[i] -= L[i,k]*L[i,k]*D[k]
142
S = np.zeros(M.shape,float)
145
D[i] = math.sqrt(D[i])
147
warning("Zeroing negative element in stab-cholesky decomposition: " + str(D[i]), verbosity.low)
150
S[i,j] += L[i,j]*D[j]
154
"""Returns a description of the cell in terms of the length of the
155
lattice vectors and the angles between them in radians.
158
h: Cell matrix in upper triangular column vector form.
161
A list containing the lattice vector lengths and the angles between them.
165
b = math.sqrt(h[0,1]**2 + h[1,1]**2)
166
c = math.sqrt(h[0,2]**2 + h[1,2]**2 + h[2,2]**2)
167
gamma = math.acos(h[0,1]/b)
168
beta = math.acos(h[0,2]/c)
169
alpha = math.acos(np.dot(h[:,1], h[:,2])/(b*c))
171
return a, b, c, alpha, beta, gamma
174
"""Returns a description of the cell in terms of the length of the
175
lattice vectors and the angles between them in degrees.
178
h: Cell matrix in upper triangular column vector form.
181
A list containing the lattice vector lengths and the angles between them
185
(a, b, c, alpha, beta, gamma) = h2abc(h)
186
return a, b, c, alpha*180/math.pi, beta*180/math.pi, gamma*180/math.pi
188
def abc2h(a, b, c, alpha, beta, gamma):
189
"""Returns a lattice vector matrix given a description in terms of the
190
lattice vector lengths and the angles in between.
193
a: First cell vector length.
194
b: Second cell vector length.
195
c: Third cell vector length.
196
alpha: Angle between sides b and c in radians.
197
beta: Angle between sides a and c in radians.
198
gamma: Angle between sides b and a in radians.
201
An array giving the lattice vector matrix in upper triangular form.
204
h = np.zeros((3,3) ,float)
206
h[0,1] = b*math.cos(gamma)
207
h[0,2] = c*math.cos(beta)
208
h[1,1] = b*math.sin(gamma)
209
h[1,2] = (b*c*math.cos(alpha) - h[0,1]*h[0,2])/h[1,1]
210
h[2,2] = math.sqrt(c**2 - h[0,2]**2 - h[1,2]**2)
214
"""Inverts a 3*3 upper triangular matrix.
217
h: An upper triangular 3*3 matrix.
220
The inverse matrix of h.
223
ih = np.zeros((3,3), float)
226
ih[0,1] = -ih[0,0]*h[0,1]*ih[1,1]
227
ih[1,2] = -ih[1,1]*h[1,2]*ih[2,2]
228
ih[0,2] = -ih[1,2]*h[0,1]*ih[0,0] - ih[0,0]*h[0,2]*ih[2,2]
231
def eigensystem_ut3x3(p):
232
"""Finds the eigenvector matrix of a 3*3 upper-triangular matrix.
235
p: An upper triangular 3*3 matrix.
238
An array giving the 3 eigenvalues of p, and the eigenvector matrix of p.
241
eigp = np.zeros((3,3), float)
242
eigvals = np.zeros(3, float)
246
eigp[0,1] = -p[0,1]/(p[0,0] - p[1,1])
247
eigp[1,2] = -p[1,2]/(p[1,1] - p[2,2])
248
eigp[0,2] = -(p[0,1]*p[1,2] - p[0,2]*p[1,1] + p[0,2]*p[2,2])/((p[0,0] - p[2,2])*(p[2,2] - p[1,1]))
255
"""Calculates the determinant of a 3*3 upper triangular matrix.
257
Note that the volume of the system box when the lattice vector matrix is
258
expressed as a 3*3 upper triangular matrix is given by the determinant of
262
h: An upper triangular 3*3 matrix.
265
The determinant of h.
268
return h[0,0]*h[1,1]*h[2,2]
272
"""Computes the matrix exponential for a 3x3 upper-triangular matrix.
274
Note that for 3*3 upper triangular matrices this is the best method, as
275
it is stable. This is terms which become unstable as the
276
denominator tends to zero are calculated via a Taylor series in this limit.
279
h: An upper triangular 3*3 matrix.
282
The matrix exponential of h.
284
eh = np.zeros((3,3), float)
285
e00 = math.exp(h[0,0])
286
e11 = math.exp(h[1,1])
287
e22 = math.exp(h[2,2])
292
if (abs((h[0,0] - h[1,1])/h[0,0])>MINSERIES):
293
r01 = (e00 - e11)/(h[0,0] - h[1,1])
295
r01 = e00*(1 + (h[0,0] - h[1,1])*(0.5 + (h[0,0] - h[1,1])/6.0))
296
if (abs((h[1,1] - h[2,2])/h[1,1])>MINSERIES):
297
r12 = (e11 - e22)/(h[1,1] - h[2,2])
299
r12 = e11*(1 + (h[1,1] - h[2,2])*(0.5 + (h[1,1] - h[2,2])/6.0))
300
if (abs((h[2,2] - h[0,0])/h[2,2])>MINSERIES):
301
r02 = (e22 - e00)/(h[2,2] - h[0,0])
303
r02 = e22*(1 + (h[2,2] - h[0,0])*(0.5 + (h[2,2] - h[0,0])/6.0))
309
if (abs((h[2,2] - h[0,0])/h[2,2])>MINSERIES):
310
eh[0,2] += h[0,1]*h[0,2]*(r01 - r12)/(h[0,0] - h[2,2])
311
elif (abs((h[1,1] - h[0,0])/h[1,1])>MINSERIES):
312
eh[0,2] += h[0,1]*h[0,2]*(r12 - r02)/(h[1,1] - h[0,0])
313
elif (abs((h[1,1]-h[2,2])/h[1,1])>MINSERIES):
314
eh[0,2] += h[0,1]*h[0,2]*(r02 - r01)/(h[2,2] - h[1,1])
316
eh[0,2] += h[0,1]*h[0,2]*e00/24.0*(12.0 + 4*(h[1,1] + h[2,2] - 2*h[0,0]) + (h[1,1] - h[0,0])*(h[2,2] - h[0,0]))
321
"""Gives the square root of a hermitian matrix with real eigenvalues.
324
A: A Hermitian matrix.
327
A matrix such that itself matrix multiplied by its transpose gives the
331
if not (abs(A.T - A) < 1e-10).all():
332
raise ValueError("Non-Hermitian matrix passed to root_herm function")
333
eigvals, eigvecs = np.linalg.eigh(A)
335
diag = np.zeros((ndgrs,ndgrs))
336
for i in range(ndgrs):
338
diag[i,i] = math.sqrt(eigvals[i])
340
warning("Zeroing negative element in matrix square root: " + str(eigvals[i]), verbosity.low)
342
return np.dot(eigvecs, np.dot(diag, eigvecs.T))