def __init__(self, particleOccs, antiparticleOccs, zeromode, nmax, L=None, m=None, fast=False, checkAtRest=True, checkChargeNeutral=True, e_var=1): """ Args: antiparticleOccs: occupation number list particleOccs: occupation number list zeromode (int): int specifying eigenstate of the A1 zero mode nmax (int): wave number of the last element in occs fast (bool): a flag for when occs and nmax are all that are needed (see transformState in oscillators.py) checkAtRest (bool): a flag to check if the total momentum is zero e_var (float): charge (default 1), used in calculating zero mode energy """ #assert m >= 0, "Negative or zero mass" #assert L > 0, "Circumference must be positive" assert zeromode >= 0, "zeromode eigenstate label should be >=0" self.zeromode = zeromode self.omega0 = e_var/sqrt(pi) FermionState.__init__(self, particleOccs, antiparticleOccs, nmax, L, m, fast, checkAtRest, checkChargeNeutral) self.isDressed = True if fast: return self.energy += self.zeromode * self.omega0
def testOperator(self): #test that adding a particle to a filled state annihilates it for c1 in (-0.5, 0.5): operator = FermionOperator([c1], [], [], [], self.L, self.m) self.assertEqual(operator._transformState(self.state), (0, None)) for c2 in (-0.5, 0.5): operator = FermionOperator([], [], [c2], [], self.L, self.m) self.assertEqual(operator._transformState(self.state), (0, None)) op1 = FermionOperator([], [-0.5], [], [], self.L, self.m, normed=True) outState1 = FermionState([0, 1], [1, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = op1._transformState(self.state, returnCoeff=True) self.assertEqual(n, 1) self.assertEqual(newState, outState1) op2 = FermionOperator([], [0.5], [], [], self.L, self.m, normed=True) outState2 = FermionState([1, 0], [1, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = op2._transformState(self.state, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState2) op3 = FermionOperator([], [], [], [-0.5], self.L, self.m, normed=True) outState3 = FermionState([1, 1], [0, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = op3._transformState(self.state, returnCoeff=True) self.assertEqual(n, 1) self.assertEqual(newState, outState3) op4 = FermionOperator([], [], [], [0.5], self.L, self.m, normed=True) outState4 = FermionState([1, 1], [1, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = op4._transformState(self.state, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState4)
def testCreationOperatorOrdering(self): # these operators have the creation operators in a different order # so there should be a relative sign in applying these operators op1 = FermionOperator([-1, 1], [], [], [], self.L, self.m, normed=True) op2 = FermionOperator([1, -1], [], [], [], self.L, self.m, normed=True) # do this also for antiparticle creation operators op3 = FermionOperator([], [], [-1, 1], [], self.L, self.m, normed=True) op4 = FermionOperator([], [], [1, -1], [], self.L, self.m, normed=True) state = FermionState([0, 0, 0], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) outState = FermionState([1, 0, 1], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) outState2 = FermionState([0, 0, 0], [1, 0, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = op1._transformState(state, returnCoeff=True) self.assertEqual(n, 1) self.assertEqual(newState, outState) n, newState = op2._transformState(state, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState) n, newState = op3._transformState(state, returnCoeff=True) self.assertEqual(n, 1) self.assertEqual(newState, outState2) n, newState = op4._transformState(state, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState2)
def testOrderingParticleAntiparticles(self): state1 = FermionState([0, 1, 0], [0, 0, 0], 2, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) state2 = FermionState([1, 1, 0], [0, 0, 0], 2, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) state3 = FermionState([1, 1, 0], [1, 0, 0], 2, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) state4 = FermionState([1, 1, 0], [1, 0, 1], 2, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) op1 = FermionOperator([2], [], [], [], self.L, self.m, normed=True) n, newState = op1._transformState(state1, returnCoeff=True) #one anticommutation to get to the right spot self.assertEqual(n, -1) #two anticommutations n, newState = op1._transformState(state2, returnCoeff=True) self.assertEqual(n, 1) #three anticommutations n, newState = op1._transformState(state3, returnCoeff=True) self.assertEqual(n, -1) #four anticommutations ah ah ah n, newState = op1._transformState(state4, returnCoeff=True) self.assertEqual(n, 1)
def setUp(self): self.L = 2 * pi self.nmax = 0.5 self.m = 0. #initialize a FermionState with half-integer nmax=0.5 self.state = FermionState([1, 1], [1, 1], self.nmax, self.L, self.m) self.operator = FermionOperator(clist=[], dlist=[], anticlist=[], antidlist=[0.5], L=self.L, m=self.m, normed=True)
def setUp(self): self.L = 2 * pi self.nmax = 1 self.m = 0. self.Emax = 5. #self.fermionBasis = FermionBasis(L=2*pi, Emax=self.Emax, m=0.) self.state = FermionState([1, 0, 1], [1, 0, 1], self.nmax, self.L, self.m) #create an operator that annihilates a particle of momentum 1 self.operator = FermionOperator(clist=[], dlist=[1], anticlist=[], antidlist=[], L=self.L, m=self.m, normed=True, extracoeff=1)
def testDestructionOperatorSigns(self): # test that destruction operators correctly anticommute when applied # to states with our convention dOperator = FermionOperator(clist=[], dlist=[1], anticlist=[], antidlist=[], L=self.L, m=self.m, normed=True, extracoeff=1) # this state has excitations in the n=+1 and n=-1 modes # so there is one trivial anticommutation state1 = FermionState([1, 0, 1], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) # this state only has an excitation in the +1 mode state2 = FermionState([0, 0, 1], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) # this state has a particle and an antiparticle # so there is one trivial anticommutation with the antiparticle op state3 = FermionState([0, 0, 1], [0, 0, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) outState1 = FermionState([1, 0, 0], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) outState2 = FermionState([0, 0, 0], [0, 0, 0], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) outState3 = FermionState([0, 0, 0], [0, 0, 1], self.nmax, self.L, self.m, checkAtRest=False, checkChargeNeutral=False) n, newState = dOperator._transformState(state1, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState1) n, newState = dOperator._transformState(state2, returnCoeff=True) self.assertEqual(n, 1) self.assertEqual(newState, outState2) n, newState = dOperator._transformState(state3, returnCoeff=True) self.assertEqual(n, -1) self.assertEqual(newState, outState3)
def __buildRMlist(self): """ sets list of all right-moving states with particles of individual wave number <= nmax, total momentum <= Emax/2 and total energy <= Emax This function works by first filling in n=1 mode in all possible ways, then n=2 mode in all possible ways assuming the occupation of n=1 mode, etc This is modified for fermionic states. In the fermionic case, occupation numbers are zero or one due to Pauli exclusion. """ if self.nmax == 0: self.__RMlist = [FermionState([],[],nmax=0,L=self.L,m=self.m, checkAtRest=False, checkChargeNeutral=False)] return # for zero-momentum states, the maximum value of k is as follows. kmax = max(0., np.sqrt((self.Emax/2.)**2.-self.m**2.)) # the max occupation number of the n=1 mode is either kmax divided # by the momentum at n=1 or Emax/omega, whichever is less # the 2 here accounts for that we can have a single particle and an # antiparticle in n=1 if self.bcs == "periodic": seedN = 1 elif self.bcs == "antiperiodic": seedN = 0.5 maxN1 = min([math.floor(kmax/k(seedN,self.L)), math.floor(self.Emax/omega(seedN,self.L,self.m)), 2]) if maxN1 <= 0: nextOccs = [[0,0]] elif maxN1 == 1: nextOccs = [[0,0],[0,1],[1,0]] else: nextOccs = [[0,0],[0,1],[1,0],[1,1]] RMlist0 = [FermionState([occs[0]],[occs[1]],seedN,L=self.L,m=self.m,checkAtRest=False, checkChargeNeutral=False) for occs in nextOccs] # seed list of RM states,all possible n=1 mode occupation numbers for n in np.arange(seedN+1,self.nmax+1): #go over all other modes RMlist1=[] #we will take states out of RMlist0, augment them and add to RMlist1 for RMstate in RMlist0: # cycle over all RMstates p0 = RMstate.momentum e0 = RMstate.energy # maximal occupation number of mode n given the momentum/energy # in all previous modes. The sqrt term accounts for the # ground state energy of the overall state, while e0 gives # the energy in each of the mode excitations. maxNn = min([math.floor((kmax-p0)/k(n,self.L)), math.floor((self.Emax-np.sqrt(self.m**2+p0**2)-e0)/omega(n,self.L,self.m)), 2]) if maxNn <= 0: nextOccsList = [[0,0]] elif maxNn == 1: nextOccsList = [[0,0],[0,1],[1,0]] else: nextOccsList = [[0,0],[0,1],[1,0],[1,1]] assert maxNn <= 2, f"maxNn was {maxNn}" # got to here in edits. # should we maybe just write a numpy function to calculate # energy and momentum from the occs list? # update: i did this. But it would take an extra # function call instead of accessing state properties. #print(f"RMstate occs are {RMstate.occs}") for nextOccs in nextOccsList: longerstate = np.append(RMstate.occs,[nextOccs],axis=0) RMlist1.append(FermionState(longerstate[:,0], longerstate[:,1], nmax=n,L=self.L,m=self.m, checkAtRest=False, checkChargeNeutral=False)) #RMlist1 created, copy it back to RMlist0 RMlist0 = RMlist1 self.__RMlist = RMlist0 #save list of RMstates in an internal variable
def _transformState(self, state0, returnCoeff=False, dressed=False): """ Applies the normal ordered operator to a given state. Args: state0 (State): an input FermionState for this operator returncoeff (bool): boolean representing whether or not to include the factor self.coeff with the returned state Returns: A tuple representing the input state after being acted on by the normal-ordered operator and any multiplicative factors from performing the commutations. Example: For a state with nmax=1 and state0.occs = [0,0,2] (2 excitations in the n=+1 mode, since counting starts at -nmax) if the operator is a_{k=1}, corresponding to clist = [] dlist = [1] coeff = 1 then this will return a state with occs [0,0,1] and a prefactor of 2 (for the two commutations). """ if not self.uniqueOps: return (0, None) #make a copy of this state up to occupation numbers and nmax #use DressedFermionState if the original state is dressed #otherwise use FermionState if state0.isDressed: state = DressedFermionState(particleOccs=state0.occs[:, 0], antiparticleOccs=state0.occs[:, 1], zeromode=state0.getAZeroMode(), nmax=state0.nmax, fast=True) else: state = FermionState(particleOccs=state0.occs[:, 0], antiparticleOccs=state0.occs[:, 1], nmax=state0.nmax, fast=True) # note: there may be an easier way for fermionic states # however, these loops are short and fast, so NumPy shortcuts probably # will not provide too much speed-up at this level. norm = 1 for i in self.dlist: if state[i][0] == 0: return (0, None) state[i][0] -= 1 # we have to anticommute past all the antiparticle creation ops # and the particle creation ops up to i norm *= (-1)**(np.sum(state.occs[:, 1]) + np.sum(state.occs[:int(i - state.nmin), 0])) for i in self.antidlist: if state[i][1] == 0: return (0, None) state[i][1] -= 1 # anticommute past the antiparticle creation ops up to i norm *= (-1)**(np.sum(state.occs[:int(i - state.nmin), 1])) for i in self.clist: # by Pauli exclusion, states can have at most one excitation # in a mode if state[i][0] == 1: return (0, None) state[i][0] += 1 # anticommute past all the antiparticle creation ops and the # particle creation ops through i norm *= (-1)**(np.sum(state.occs[:, 1]) + np.sum(state.occs[:int(i - state.nmin), 0])) for i in self.anticlist: if state[i][1] == 1: return (0, None) state[i][1] += 1 # anticommute past the antiparticle creation ops norm *= (-1)**(np.sum(state.occs[:int(i - state.nmin), 1])) # We never pick up a nontrivial normalization factor for fermionic # states since the occupation numbers are either one or zero. # The only option is if we wish to return the overall coefficient # of this operator or not. if returnCoeff: return (norm * self.coeff, state) return (norm, state)