Basics of QuSpin basis objects

This tutorial shows how to define and interpret basis objects.

In particular, we discuss how to define and read off physical states from the basis in the presence and absence of symmetries.

Notes:
  • we advise the users whenever possible to work with the basis_general objects, since they have enhanced functionality; However, occasionally it might be more convenient to work with the basis_1d objects where creating the basis might be a little bit faster.

  • the general_basis objects have a much more pronounced functionality, including some useful methods like ent_entropy(), partial_trace(), Op_bra_ket(), Op_shift_sector(), get_amp(), representative(), normalization(), etc., see documentation.

Script

download script

  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
from __future__ import print_function, division
#
import sys,os
os.environ['KMP_DUPLICATE_LIB_OK']='True' # uncomment this line if omp error occurs on OSX for python 3
os.environ['OMP_NUM_THREADS']='1' # set number of OpenMP threads to run in parallel
os.environ['MKL_NUM_THREADS']='1' # set number of MKL threads to run in parallel
#
quspin_path = os.path.join(os.getcwd(),"../../")
sys.path.insert(0,quspin_path)
#
# line 12 and line 13 below are for development purposes and can be removed
qspin_path = os.path.join(os.getcwd(),"../../")
sys.path.insert(0,qspin_path)
#####################################################################
#                            example 00                             #
#    In this script we demonstrate how to use QuSpin's              #
#    `basis_general` routines to construct, interpret,              #
#    and use basis objects.                                         #
#####################################################################
from quspin.basis import spin_basis_general # Hilbert space spin basis
import numpy as np # generic math functions
#
L=2 # system size
sites=np.arange(L) #define sites
P=sites[::-1] # define action of parity/reflection on the lattice sites
#
#############################################
print("\n----------------------------")
print("---  NO  SYMMETRIES  -------")
print("----------------------------\n")
#############################################
#
##### basis objects without symmetries
#
basis=spin_basis_general(L,)
Ns = basis.Ns # number of states in the basis
#
print(basis)
#
# states in integer representation
states=basis.states # = third column when printing the basis object (not consecutive if there are symmetries --> see below)
array_inds=np.arange(basis.Ns) # = first column when printing the basis object (always consecutive)
#
print("\n'array index' and 'states' columns when printing the basis:")
print("array indices:", array_inds)
print("states in int rep:", states)
# find array index of a state from its integer representation; Note: the array index is crucial for reading-off matrix elements
s = basis.states[2]
array_ind_s = basis.index(s) # = array_inds[2] whenever there are no symmetries defined in the basis
print("\nprint array index of s, and s (in int rep); Note: the array index is crucial for reading-off matrix elements")
print(array_ind_s, s)
#--------------------------------------------
##### States: ket and integer representations
#--------------------------------------------
# find integer representation from Fock state string
fock_state_str_s = '|01>' # works also if the ket-forming strings | > are omitted  
int_rep_s=basis.state_to_int(fock_state_str_s)
print("\nprint Fock state string of s, and s (in int rep):")
print(fock_state_str_s, int_rep_s)
#
# find Fock state string from integer representation
fock_s = basis.int_to_state(int_rep_s,bracket_notation=True)
print("\nprint Fock state string of s, and s (in int rep):")
print(fock_s, int_rep_s)
# same as above but dropping the ket-forming strings | >
fock_s = basis.int_to_state(int_rep_s,bracket_notation=False)
print("print Fock state string (without | and >) of s, and s (in int rep):")
print(fock_s, int_rep_s)
#
# find Fock state from array index
array_ind_s = 2
int_rep_s = basis.states[array_ind_s]
fock_s = basis.int_to_state(int_rep_s,bracket_notation=True)
print("\nprint array index, int rep, and fock state rep of s:")
print(array_ind_s, int_rep_s, fock_s) # compare with print(basis) output
#--------------------------------------------
##### States: array/vector representation
#--------------------------------------------
# define a zero vector of size set by the basis dimenion 
psi_s=np.zeros(basis.Ns)
# obtain array index for the fock state |01>
array_ind_s=basis.index( basis.state_to_int('01') )
# construct the pure state |01>
psi_s[array_ind_s]=1.0
print("\nprint state psi_s in the basis:")
print(psi_s)
#
#############################################
print("\n\n\n----------------------------")
print("-------  SYMMETRIES  -------")
print("----------------------------\n")
#############################################
#
##### basis objects with symmetries
#
basis_triplet=spin_basis_general(L,pblock=(P,0))
basis_singlet=spin_basis_general(L,pblock=(P,1))
#
print('print full basis:')
print(basis)
#
print('\n\nprint pblock=+1 basis:\n')
print(basis_triplet)
#
print("\n  * integer rep column no longer consecutive! (|01> falls outside symmetry sector)")
print("  * array index column still consecutive! (but indices differ compared to full basis, e.g. for |00>)")
print("  * |11> and |00> invariant under parity, so they correspond to physical states |11> and |00>")
print("  * |10> not invariant under parity! It represents the physical symmetric superposition 1/sqrt(2)(|10> + |01>) [see bottom note when printing the symmetry-reduced basis]; quspin keeps track of the coefficient 1/sqrt(2) under the hood.")
print('\n\nprint pblock=-1 basis:\n')
#
print(basis_singlet)
print("  * |10> here represents the physical ANTI-symmetric superposition 1/sqrt(2)(|10> - |01>) [see bottom note when printing the symmetry-reduced basis]")
print("  *  NOTE: same state |01> is used to label both the symmetric and antisymmetric superposition because in this cases quspin uses the smallest integer from the integer representations of the states comprising the superposition states.\n")
#
#--------------------------------------------------
##### transform states from one basis to the other
#--------------------------------------------------
#
array_ind_s=basis_triplet.index( basis.state_to_int('10') )
psi_symm_s = np.zeros(basis_triplet.Ns)
psi_symm_s[array_ind_s]=1.0 # create the state |10> + |01> in basis_triplet
print("print state psi_symm_s in the symmetry-reduced basis_triplet:")
print(psi_symm_s)
# 
# compute corresponding state in the full basis
psi_s = basis_triplet.project_from(psi_symm_s,sparse=False)
print("\nprint state psi_s in the full basis: (note the factor 1/sqrt(2) which comes out correct.")
print(psi_s)
#
# one can also project a full-basis state to a symmetry-reduced basis
psi_s=np.zeros(basis.Ns)
array_ind_s=basis.index( basis.state_to_int('01') )
psi_s[array_ind_s]=1.0 # create the state |01> in the full basis
# 
psi_symm_s=basis_singlet.project_to(psi_s,sparse=False) # projects |01> to 1/sqrt(2) (|01> - |10>) in basis_singlet 
print("\nprint state psi_symm_s in the symmetry-reduced basis_singlet; NOTE: projection does not give a normalized state!")
print(psi_symm_s)
# normalize
psi_symm_s = psi_symm_s/np.linalg.norm(psi_symm_s)
# 
# lift the projected state back to full basis
psi_symm_s=np.expand_dims(psi_symm_s,0) # required only when the basis_singlet contains a single state
psi_lifted_s = basis_singlet.project_from(psi_symm_s,sparse=False) # corresponds to the projection 1/sqrt(2) (|01> - |10>) in the full basis
print("\nprint state psi_lifted_s = 1/sqrt(2) (|01> - |10>) in the full basis; NOTE: info lost by the first projection!")
print(psi_lifted_s)