qedops.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. ######################################################
  2. #
  3. # Fock space Hamiltonian truncation for phi^4 theory in 2 dimensions
  4. # Authors: Slava Rychkov (slava.rychkov@lpt.ens.fr) and Lorenzo Vitale (lorenzo.vitale@epfl.ch)
  5. # December 2014
  6. #
  7. ######################################################
  8. import scipy
  9. import numpy as np
  10. from numpy import pi, sqrt, product
  11. from operator import attrgetter
  12. from statefuncs import omega, State, NotInBasis
  13. from qedstatefuncs import FermionState
  14. from dressedstatefuncs import DressedFermionState
  15. tol = 0.0001
  16. def uspinor(n,L,m,normed=False):
  17. if m == 0 and n == 0:
  18. return np.array([[1/sqrt(2)],[1/sqrt(2)]])
  19. k = (2.*pi/L)*n
  20. energy = omega(n,L,m)
  21. if normed:
  22. return np.array([[sqrt(energy-k)/sqrt(2*energy)],
  23. [sqrt(energy+k)/sqrt(2*energy)]])
  24. return np.array([[sqrt(energy-k)],[sqrt(energy+k)]])
  25. def vspinor(n,L,m,normed=False):
  26. if m == 0 and n == 0:
  27. return np.array([[1/sqrt(2)],[-1/sqrt(2)]])
  28. k = (2.*pi/L)*n
  29. energy = omega(n,L,m)
  30. if normed:
  31. return np.array([[sqrt(energy-k)/sqrt(2*energy)],
  32. [-sqrt(energy+k)/sqrt(2*energy)]])
  33. return np.array([[sqrt(energy-k)],[-sqrt(energy+k)]])
  34. class FermionOperator():
  35. """ abstract class for normal ordered fermionic operator
  36. A normal-ordered operator is given by two lists of mode indices specifying
  37. which creation operators are present (clist) and which annihilation operators
  38. are present (dlist) and an overall multiplicative coefficient.
  39. For fermionic operators, we also have anticlist and antidlist for the
  40. antiparticles. To keep the sign conventions straight, we apply particle
  41. operators first and then antiparticle operators, but each of these is
  42. separately normal-ordered.
  43. Note also that the order matters in clist and dlist due to the signs
  44. associated with anticommutation. Operators are provided in the order one
  45. would write them (from left to right) and stored in reverse order
  46. (the order they act on states).
  47. Attributes:
  48. clist (list of ints): a list of ns (Fourier indices) corresponding
  49. to creation ops
  50. dlist (list of ints): another list of ns corresponding to
  51. destruction ops
  52. anticlist (list of ints)
  53. antidlist (list of ints)
  54. L (float): circumference of the circle (the spatial dimension)
  55. m (float): mass of the field
  56. coeff (float): the overall multiplicative prefactor of this operator
  57. deltaE (float): the net energy difference produced by acting on a state
  58. with this operator
  59. """
  60. def __init__(self,clist,dlist,anticlist,antidlist,
  61. L,m,extracoeff=1,normed=False):
  62. """
  63. Args:
  64. clist, dlist, L, m: as above
  65. extracoeff (float): an overall multiplicative prefactor for the
  66. operator, *written as a power of the field operator phi*
  67. normed (bool): indicates whether factor of 1/sqrt(2*omega) has
  68. been absorbed into the definition of the spinor wavefunctions
  69. """
  70. # Check if there are multiple operators acting on the same mode.
  71. # Since fermionic operators anticommute, operators which have e.g.
  72. # 2 annihilation operators acting on the same mode are just 0.
  73. self.uniqueOps = (self.checkValidList(clist)
  74. and self.checkValidList(dlist)
  75. and self.checkValidList(anticlist)
  76. and self.checkValidList(antidlist))
  77. self.clist = clist[::-1]
  78. self.dlist = dlist[::-1]
  79. self.anticlist = anticlist[::-1]
  80. self.antidlist = antidlist[::-1]
  81. self.L=L
  82. self.m=m
  83. # coeff converts the overall prefactor of phi (extracoeff) to a prefactor
  84. # for the string of creation and annihilation operators in the final operator
  85. # see the normalization in Eq. 2.6
  86. if normed:
  87. self.coeff = extracoeff
  88. else:
  89. #note: have to be careful with this for massless zero modes
  90. self.coeff = extracoeff/product([sqrt(2.*L*omega(n,L,m))
  91. for n in clist+dlist])
  92. self.deltaE = sum([omega(n,L,m) for n in clist]) - sum([omega(n,L,m) for n in dlist])
  93. #can perform this as a vector op but the speedup is small
  94. #self.deltaE = sum(omega(array(clist),L,m))-sum(omega(array(dlist),L,m))
  95. def checkValidList(self, opsList):
  96. """
  97. Given a list of creation or annihilation operators, checks if
  98. the modes they act on are all unique.
  99. """
  100. return len(np.unique(opsList)) == len(opsList)
  101. def __repr__(self):
  102. return (str(self.clist) + " " + str(self.dlist) + " " + str(self.anticlist)
  103. + " " + str(self.antidlist) + " " + str(self.coeff))
  104. def _transformState(self, state0, returnCoeff=False, dressed=False):
  105. """
  106. Applies the normal ordered operator to a given state.
  107. Args:
  108. state0 (State): an input FermionState for this operator
  109. returncoeff (bool): boolean representing whether or not to
  110. include the factor self.coeff with the returned state
  111. Returns:
  112. A tuple representing the input state after being acted on by
  113. the normal-ordered operator and any multiplicative factors
  114. from performing the commutations.
  115. Example:
  116. For a state with nmax=1 and state0.occs = [0,0,2]
  117. (2 excitations in the n=+1 mode, since counting starts at
  118. -nmax) if the operator is a_{k=1}, corresponding to
  119. clist = []
  120. dlist = [1]
  121. coeff = 1
  122. then this will return a state with occs [0,0,1] and a prefactor of
  123. 2 (for the two commutations).
  124. """
  125. if not self.uniqueOps:
  126. return (0,None)
  127. #make a copy of this state up to occupation numbers and nmax
  128. #use DressedFermionState if the original state is dressed
  129. #otherwise use FermionState
  130. if state0.isDressed:
  131. state = DressedFermionState(particleOccs=state0.occs[:,0],
  132. antiparticleOccs=state0.occs[:,1],
  133. zeromode=state0.getAZeroMode(),
  134. nmax=state0.nmax,
  135. fast=True)
  136. else:
  137. state = FermionState(particleOccs=state0.occs[:,0],
  138. antiparticleOccs=state0.occs[:,1],
  139. nmax=state0.nmax,
  140. fast=True)
  141. # note: there may be an easier way for fermionic states
  142. # however, these loops are short and fast, so NumPy shortcuts probably
  143. # will not provide too much speed-up at this level.
  144. norm = 1
  145. for i in self.dlist:
  146. if state[i][0] == 0:
  147. return(0,None)
  148. state[i][0] -= 1
  149. # we have to anticommute past all the antiparticle creation ops
  150. # and the particle creation ops up to i
  151. norm *= (-1)**(np.sum(state.occs[:,1])+
  152. np.sum(state.occs[:int(i-state.nmin),0]))
  153. for i in self.antidlist:
  154. if state[i][1] == 0:
  155. return(0,None)
  156. state[i][1] -= 1
  157. # anticommute past the antiparticle creation ops up to i
  158. norm *= (-1)**(np.sum(state.occs[:int(i-state.nmin),1]))
  159. for i in self.clist:
  160. # by Pauli exclusion, states can have at most one excitation
  161. # in a mode
  162. if state[i][0] == 1:
  163. return (0,None)
  164. state[i][0] += 1
  165. # anticommute past all the antiparticle creation ops and the
  166. # particle creation ops through i
  167. norm *= (-1)**(np.sum(state.occs[:,1])+
  168. np.sum(state.occs[:int(i-state.nmin),0]))
  169. for i in self.anticlist:
  170. if state[i][1] == 1:
  171. return (0,None)
  172. state[i][1] += 1
  173. # anticommute past the antiparticle creation ops
  174. norm *= (-1)**(np.sum(state.occs[:int(i-state.nmin),1]))
  175. # We never pick up a nontrivial normalization factor for fermionic
  176. # states since the occupation numbers are either one or zero.
  177. # The only option is if we wish to return the overall coefficient
  178. # of this operator or not.
  179. if returnCoeff:
  180. return (norm*self.coeff, state)
  181. return (norm, state)
  182. def apply(self, basis, i, lookupbasis=None):
  183. """ Takes a state index in basis, returns another state index (if it
  184. belongs to the lookupbasis) and a proportionality coefficient. Otherwise raises NotInBasis.
  185. lookupbasis can be different from basis, but it's assumed that they have the same nmax"""
  186. if lookupbasis == None:
  187. lookupbasis = basis
  188. if self.deltaE+basis[i].energy < 0.-tol or self.deltaE+basis[i].energy > lookupbasis.Emax+tol:
  189. # The transformed element surely does not belong to the basis if E>Emax or E<0
  190. raise NotInBasis()
  191. # apply the normal-order operator to this basis state
  192. n, newstate = self._transformState(basis[i])
  193. #if the state was annihilated by the operator, return (0, None)
  194. if n==0:
  195. return (0, None)
  196. # otherwise, look up this state in the lookup basis
  197. norm, j = lookupbasis.lookup(newstate)
  198. c = 1.
  199. #if basis[i].isParityEigenstate():
  200. # c = 1/sqrt(2.)
  201. # Required for state normalization
  202. # return the overall proportionality and the state index
  203. return (norm*c*sqrt(n)*self.coeff, j)
  204. def apply2(self, basis, state, lookupbasis=None):
  205. """
  206. Like apply but with a state as input rather than an index in the
  207. original basis.
  208. """
  209. # TO-DO: add the energy shortcut from the original apply
  210. # we need the energy of the state-- is it expensive to recompute it?
  211. if lookupbasis == None:
  212. lookupbasis = basis
  213. n, newstate = self._transformState(state)
  214. if n==0:
  215. return (0, None)
  216. norm, j = lookupbasis.lookup(newstate)
  217. c = 1.
  218. #no sqrt n since n is either 1 or -1
  219. return (norm*c*n*self.coeff,j)