Create a new spin Hilbert space with a discrete symmetry

From diagham
Revision as of 17:06, 23 August 2010 by Regnault (talk | contribs) (→‎Implementing operators)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This example will help you to write a new Hilbert space for spin 1/2 including translation in the k=0 sector and the discrete symmetry that maps k to -k (k inversion symmetry). The code will be based on the existing class Spin1_2ChainWithTranslations. Let's call this new class Spin1_2ChainWithTranslationsKInversionSymmetry.


The inversion method

The first method we need to implement is, given a configuration to get the corresponding one under the k inversion symmetry. Such a code actually exists in the case of the FQHE implementing the inversion symmetry (from Lz to -Lz). A quick look at FermionOnSphereSymmetricBasis.h shows the inline method GetCanonicalState


   // get canonical expression of a given state
   //
   // initialState = state that has to be converted to its canonical expression
   // return value = corresponding canonical state
   
   inline unsigned long FermionOnSphereSymmetricBasis::GetCanonicalState (unsigned long initialState)
   {
     initialState <<= this->InvertShift;
   #ifdef __64_BITS__
     unsigned long TmpState = FermionOnSphereInvertTable[initialState & 0xff] << 56;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 8) & 0xff] << 48;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 16) & 0xff] << 40;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 24) & 0xff] << 32;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 32) & 0xff] << 24;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 40) & 0xff] << 16;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 48) & 0xff] << 8;
     TmpState |= FermionOnSphereInvertTable[initialState >> 56]; 
   #else
     unsigned long TmpState = FermionOnSphereInvertTable[initialState & 0xff] << 24;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 8) & 0xff] << 16;
     TmpState |= FermionOnSphereInvertTable[(initialState >> 16) & 0xff] << 8;
     TmpState |= FermionOnSphereInvertTable[initialState >> 24];
   #endif  
     initialState >>= this->InvertShift;
     TmpState >>= this->InvertUnshift;
     if (TmpState < initialState)
       return TmpState;
     else
       return initialState;
   }


To apply the inversion to a given state description, you first need to shift the configuration so that it is centered around the bit 16 in 32 bit mode or 32 in 64 bit mode. Then the inversion is applied to each of 8 bits. Each possible case for a 8 bits configuration has been precalculated in the FermionOnSphereInvertTable table. The result being added at the right place. The resulting is unshifted to place back the configuration at the beginning of the word. An example for the configuration 010011 stored in a 16 bit word is depicted below

Invertbits.png

We will thus implement a similar method in our new class. Let's name it GetKInversionCanonicalState

Generating the Hilbert space

Let's have a look at we we need to implement. We will inherit from Spin1_2ChainWithTranslations. The only methods we need to rewrite are the constructors, assignment and all basic operators such as SmiSpj.

In Spin1_2ChainWithTranslations, the generation of the Hilbert space is done int the constructor Spin1_2ChainWithTranslations(int chainLength, int momentum, int translationStep, int sz, int memorySize, int memorySlice) and more specificaly this part :


   this->StateDescription = new unsigned long [this->EvaluateHilbertSpaceDimension(this->ChainLength, this->Sz)];
   long TmpHilbertSpaceDimension = this->GenerateStates(0l, this->ChainLength - 1, (this->ChainLength + this->Sz) >> 1);
   this->LargeHilbertSpaceDimension = 0l;
   unsigned long TmpState;
   unsigned long TmpState2;
   int NbrTranslation;
   int CurrentNbrStateInOrbit;
   unsigned long DicardFlag = ~0x0ul;
   for (long i = 0l; i < TmpHilbertSpaceDimension; ++i)
     {
       TmpState = this->StateDescription[i];
       TmpState2 = this->FindCanonicalForm(TmpState, NbrTranslation);
       if (TmpState2 == TmpState)
         {
           CurrentNbrStateInOrbit = this->FindNumberTranslation(TmpState2);
           if (this->CompatibilityWithMomentum[CurrentNbrStateInOrbit] == true)
             {
               ++this->LargeHilbertSpaceDimension;
             }
           else
             {  
               this->StateDescription[i] = DicardFlag;
             }
         }
       else
         {
           this->StateDescription[i] = DicardFlag;
         }
     }

The two first lines generate the Hilbert space without the momentum constraint (only the total Sz constraint). Then for each state, we compute its associated canonical state which is defined as the translated state with the largest integer representation. The Hilbert space will only hold the canonical states. Thus the current state we are looking at is not a canonical one, we set it to DicardFlag. If we want to add a discrete symmetry, we should add a similar test before incrementing the Hilbert space dimension. We can do that using our GetKInversionCanonicalState method in similar way we check the state is canonical under translation. Beware that the way we create the Hilbert space defines how we should check a state is canonical. We first find the canonical state for translation, then for inversion. In particular, the canonical state is NOT defined as the one with the largest integer representation for both transformations.

The constructor should have one additional parameter the parity sector we are considering and thus we need an additional class element to store this information. For a reason that will become obvious later, we should store it as a double with value +1.0 or -1.0 .

Implementing operators

The operators are a bit more tricky to implement. We will focus on SmiSpj. In Spin1_2ChainWithTranslations, the method is

   unsigned long tmpState = this->StateDescription[state];
   unsigned long State = tmpState;
   unsigned long tmpState2 = tmpState;
   tmpState >>= i;
   tmpState &= 0x1ul;
   if (i != j)
     {
       tmpState2 >>= j;
       tmpState2 &= 0x1ul;
       tmpState2 <<= 1;
       tmpState2 |= tmpState;
       if (tmpState2 == 0x1ul)
         {
           State = this->FindCanonicalForm((State | (0x1ul << j)) & ~(0x1ul << i), nbrTranslation, i);
           if (this->CompatibilityWithMomentum[i] == false)
             return this->HilbertSpaceDimension;
           j = this->FindStateIndex(State);
           coefficient = this->RescalingFactors[this->NbrStateInOrbit[state]][i];
           return j;
         }
       else
         {
           coefficient = 0.0;
           return this->HilbertSpaceDimension;
         }
     }
   if (tmpState == 0)
     {
       coefficient = -0.25;
       return state;
     }
   return this->HilbertSpaceDimension;

If the operator acting on the input state state does not give zero, we need to check if it a canonical state with respect to the inversion symmetry. If State is not a canonical state, we should multiply coefficient with the sector parity we are working on. The difficult part is that such an operator might connect a canonical state which is invariant under the inversion to a state which is not, or vice versa. Thus we need to check if the input state is invariant under the inversion, do the same for the output state and find the corresponding scaling factor which is sqrt(2) (from invariant to non-invariant) or 1/sqrt(2) (from non-invariant to invariant). Of course, we need to do such calculations and check only if we are sure the result is non-zero.