-
Notifications
You must be signed in to change notification settings - Fork 0
/
ensembles.py
227 lines (206 loc) · 10.4 KB
/
ensembles.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
from itertools import product
from numpy import trace, exp, sum as nsum, identity, where, array, ndarray
from scipy.optimize import bisect
from time import time
from observables import StaticObservable, DynamicObservable
from operators import AnnihilationOperator
from util import scatter_list, sumScatteredLists, report, allgather_list, equals
class CanonicalEnsemble(object):
def __init__(self, hamiltonian, beta, verbose = False):
self.hamiltonian = hamiltonian
self.singleParticleBasis = hamiltonian.singleParticleBasis
self.orderedSingleParticleStates = hamiltonian.orderedSingleParticleStates
self.fockspaceSize = hamiltonian.fockspaceSize
self.partitionFunction = None
self.beta = beta
self.energyEigenstates = None
self.energyEigenvalues = None
self.verbose = verbose
self.occupation = StaticObservable(verbose = self.verbose)
self.g1 = DynamicObservable(verbose = self.verbose)
def setLehmannSumStatic(self, staticObservable):
assert type(staticObservable) == StaticObservable, 'StaticObservable expected.'
e = self.getEnergyEigenvalues()
ve = self.getEnergyEigenstates().toarray()
z = self.getPartitionFunction()
fockstates = range(self.fockspaceSize)
for index, matrix in staticObservable.operators.items():
if type(matrix) != ndarray:
m = matrix.toarray()
else:
m = matrix
n_inds_p = array(scatter_list(fockstates))
terms_p = list()
for n in n_inds_p:
el = ve[:,n].dot(m.dot(ve[:,n]))
expn = exp(-self.beta*e[n])
if not equals(el, 0) and not equals(expn, 0):
terms_p.append(expn * el)
staticObservable.expectationValue.update({index: sumScatteredLists(terms_p)/z})
def calcOccupation(self, singleParticleState = None):
c = AnnihilationOperator(self.singleParticleBasis)
if singleParticleState == None:
states = self.orderedSingleParticleStates
else:
states = [singleParticleState]
report('Calculating Occupation...', self.verbose)
t0 = time()
for state in states:
n_state = c[state].H.dot(c[state])
self.occupation.addOperator(state, n_state)
self.setLehmannSumStatic(self.occupation)
report('took '+str(time()-t0)[:4]+' seconds', self.verbose)
def getTotalOccupation(self):
return nsum(self.occupation.expectationValue.values())
def calcPartitionFunction(self):
report('Calculating Partition Function...', self.verbose)
t0 = time()
self.partitionFunction = nsum([exp(-self.beta*eva) for eva in self.getEnergyEigenvalues()])
report('took '+str(time()-t0)[:4]+' seconds', self.verbose)
def getPartitionFunction(self):
if self.partitionFunction is not None:
return self.partitionFunction
else:
self.calcPartitionFunction()
return self.partitionFunction
def getEnergyEigenstates(self):
if self.energyEigenstates is not None:
return self.energyEigenstates
else:
self.calcEigensystem()
return self.energyEigenstates
def getEnergyEigenvalues(self):
if self.energyEigenvalues is not None:
return self.energyEigenvalues
else:
self.calcEigensystem()
return self.energyEigenvalues
def calcEigensystem(self, *args, **kwargs):
self.hamiltonian.solve(*args, **kwargs)
self.energyEigenstates = self.hamiltonian.eigenStates
self.energyEigenvalues = self.hamiltonian.eigenEnergies
def setLehmannTermsDynamic(self, dynamicObservable):
assert type(dynamicObservable) == DynamicObservable, 'Type DynamicObservable expected.'
if dynamicObservable.species == 'fermionic':
self.setLehmannTermsDynamicFermionic(dynamicObservable)
elif dynamicObservable.species == 'bosonic':
self.setLehmannTermsDynamicBosonic(dynamicObservable)
def setLehmannTermsDynamicFermionic(self, dynamicObservable):
for statePair, operatorPair in dynamicObservable.operatorPairs.items():
operator1 = operatorPair[0]
operator2 = operatorPair[1]
if type(operator1) != ndarray:
operator1 = operator1.toarray()
operator2 = operator2.toarray()
e = self.getEnergyEigenvalues()
ve = self.getEnergyEigenstates().toarray()
fockstates = range(self.fockspaceSize)
n_inds_p = array(scatter_list(fockstates))
nominators_p = []
denominators_p = []
for n in n_inds_p:
n_op1 = ve[:,n].dot(operator1)
op2_n = operator2.dot(ve[:,n])
expn = exp(-self.beta*e[n])
for m in fockstates:
if dynamicObservable.species == 'bosonic':
if equals(e[n], e[m]):
continue
el1 = n_op1.dot(ve[:,m])
if not equals(el1, 0):
el2 = ve[:,m].dot(op2_n)
if not equals(el2, 0):
expm = exp(-self.beta*e[m])
el = el1*el2
nominators_p.append([])
nominators_p[-1].append(expn*el)
nominators_p[-1].append(expm*el)
denominators_p.append(e[n]-e[m])
dynamicObservable.lehmannNominators.update({statePair: allgather_list(nominators_p)})
dynamicObservable.lehmannDenominators.update({statePair: allgather_list(denominators_p)})
dynamicObservable.partitionFunction = self.getPartitionFunction()
def setZeroFrequencyTerms(self, dynamicObservable):
for statePair, operatorPair in dynamicObservable.operatorPairs.items():
operator1 = operatorPair[0]
operator2 = operatorPair[1]
if type(operator1) != ndarray:
operator1 = operator1.toarray()
operator2 = operator2.toarray()
e = self.getEnergyEigenvalues()
ve = self.getEnergyEigenstates().toarray()
fockstates = range(self.fockspaceSize)
n_inds_p = array(scatter_list(fockstates))
zeroFrequencyTerms_p = []
for n in n_inds_p:
n_op1 = ve[:,n].dot(operator1)
op2_n = operator2.dot(ve[:,n])
expn = exp(-self.beta*e[n])
for m in fockstates:
if equals(e[n], e[m]):
el1 = n_op1.dot(ve[:,m])
if not equals(el1, 0):
el2 = ve[:,m].dot(op2_n)
if not equals(el2, 0):
el = el1*el2
zeroFrequencyTerms_p.append(-self.beta*expn*el)
dynamicObservable.zeroFrequencyTerms.update({statePair: allgather_list(zeroFrequencyTerms_p)})
def setLehmannTermsDynamicBosonic(self, dynamicObservable):
self.setLehmannTermsDynamicFermionic(dynamicObservable)
self.setZeroFrequencyTerms(dynamicObservable)
def calcG1(self, singleParticleStatePairs = None):
c = AnnihilationOperator(self.singleParticleBasis)
if singleParticleStatePairs == None:
statePairs = [(state1, state2) for state1, state2 in product(self.orderedSingleParticleStates, self.orderedSingleParticleStates)]
else:
statePairs = singleParticleStatePairs
report('Calculating one-particle Green\'s function transition elements and energies...', self.verbose)
t0 = time()
for statePair in statePairs:
c_state = c[statePair[0]]
c_state_dag = c[statePair[1]].H
self.g1.addOperatorPair(statePair, (c_state, c_state_dag))
self.setLehmannTermsDynamic(self.g1)
report('took '+str(time()-t0)[:4]+' seconds', self.verbose)
class GrandcanonicalEnsemble(CanonicalEnsemble):
"""use canonical with mu in H"""
def __init__(self, hamiltonian, beta, mu, verbose = False):
self.mu = mu
CanonicalEnsemble.__init__(self, hamiltonian, beta, verbose)
c = AnnihilationOperator(self.singleParticleBasis)
muMatrix = mu * nsum([c[orb].H.dot(c[orb]) for orb in self.orderedSingleParticleStates], axis = 0)
self.hamiltonian.matrix = self.hamiltonian.matrix - muMatrix
self.filling = None
def setMu(self, mu):
c = AnnihilationOperator(self.singleParticleBasis)
nMatrix = nsum([c[orb].H.dot(c[orb]) for orb in self.orderedSingleParticleStates], axis = 0)
self.hamiltonian.matrix = self.hamiltonian.matrix + self.mu * nMatrix
self.mu = mu
self.hamiltonian.matrix = self.hamiltonian.matrix - mu * nMatrix
self.energyEigenvalues = None
self.energyEigenstates = None
self.partitionFunction = None
self.occupation = dict()
report('Chemical potential set to '+str(mu), self.verbose)
def setMuByFilling(self, filling, muMin, muMax, muTol = .001, maxiter = 100):
c = AnnihilationOperator(self.singleParticleBasis)
nMatrix = nsum([c[orb].H.dot(c[orb]) for orb in self.orderedSingleParticleStates], axis = 0)
self.hamiltonian.matrix = self.hamiltonian.matrix + self.mu * nMatrix
def fillingFunction(muTrial):
self.hamiltonian.matrix = self.hamiltonian.matrix - muTrial * nMatrix
self.calcEigensystem()
self.calcPartitionFunction()
self.calcOccupation()
fillingTrial = self.getTotalOccupation()
self.hamiltonian.matrix = self.hamiltonian.matrix + muTrial * nMatrix
report('Filling(mu='+str(muTrial)+') = '+str(fillingTrial), self.verbose)
self.filling = fillingTrial
return fillingTrial - filling
mu = bisect(fillingFunction, muMin, muMax, xtol = muTol, maxiter = maxiter)
self.mu = mu
self.hamiltonian.matrix = self.hamiltonian.matrix - mu * nMatrix
report('Chemical potential set to '+str(mu), self.verbose)
class MicrocanonicalEnsemble(CanonicalEnsemble):
def __init__(self, hamiltonian):
CanonicalEnsemble.__init__(self, hamiltonian, 0)
def calcPartitionFunction(self):
self.partitionFunction = self.fockspaceSize