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#
  2import sys, os
  3
  4os.environ["KMP_DUPLICATE_LIB_OK"] = (
  5    "True"  # uncomment this line if omp error occurs on OSX for python 3
  6)
  7os.environ["OMP_NUM_THREADS"] = "1"  # set number of OpenMP threads to run in parallel
  8os.environ["MKL_NUM_THREADS"] = "1"  # set number of MKL threads to run in parallel
  9#
 10
 11#
 12# line 12 and line 13 below are for development purposes and can be removed
 13qspin_path = os.path.join(os.getcwd(), "../../")
 14sys.path.insert(0, qspin_path)
 15#####################################################################
 16#                            example 00                             #
 17#    In this script we demonstrate how to use QuSpin's              #
 18#    `basis_general` routines to construct, interpret,              #
 19#    and use basis objects.                                         #
 20#####################################################################
 21from quspin.basis import spin_basis_general  # Hilbert space spin basis
 22import numpy as np  # generic math functions
 23
 24#
 25L = 2  # system size
 26sites = np.arange(L)  # define sites
 27P = sites[::-1]  # define action of parity/reflection on the lattice sites
 28#
 29#############################################
 30print("\n----------------------------")
 31print("---  NO  SYMMETRIES  -------")
 32print("----------------------------\n")
 33#############################################
 34#
 35##### basis objects without symmetries
 36#
 37basis = spin_basis_general(
 38    L,
 39)
 40Ns = basis.Ns  # number of states in the basis
 41#
 42print(basis)
 43#
 44# states in integer representation
 45states = (
 46    basis.states
 47)  # = third column when printing the basis object (not consecutive if there are symmetries --> see below)
 48array_inds = np.arange(
 49    basis.Ns
 50)  # = first column when printing the basis object (always consecutive)
 51#
 52print("\n'array index' and 'states' columns when printing the basis:")
 53print("array indices:", array_inds)
 54print("states in int rep:", states)
 55# find array index of a state from its integer representation; Note: the array index is crucial for reading-off matrix elements
 56s = basis.states[2]
 57array_ind_s = basis.index(
 58    s
 59)  # = array_inds[2] whenever there are no symmetries defined in the basis
 60print(
 61    "\nprint array index of s, and s (in int rep); Note: the array index is crucial for reading-off matrix elements"
 62)
 63print(array_ind_s, s)
 64# --------------------------------------------
 65##### States: ket and integer representations
 66# --------------------------------------------
 67# find integer representation from Fock state string
 68fock_state_str_s = "|01>"  # works also if the ket-forming strings | > are omitted
 69int_rep_s = basis.state_to_int(fock_state_str_s)
 70print("\nprint Fock state string of s, and s (in int rep):")
 71print(fock_state_str_s, int_rep_s)
 72#
 73# find Fock state string from integer representation
 74fock_s = basis.int_to_state(int_rep_s, bracket_notation=True)
 75print("\nprint Fock state string of s, and s (in int rep):")
 76print(fock_s, int_rep_s)
 77# same as above but dropping the ket-forming strings | >
 78fock_s = basis.int_to_state(int_rep_s, bracket_notation=False)
 79print("print Fock state string (without | and >) of s, and s (in int rep):")
 80print(fock_s, int_rep_s)
 81#
 82# find Fock state from array index
 83array_ind_s = 2
 84int_rep_s = basis.states[array_ind_s]
 85fock_s = basis.int_to_state(int_rep_s, bracket_notation=True)
 86print("\nprint array index, int rep, and fock state rep of s:")
 87print(array_ind_s, int_rep_s, fock_s)  # compare with print(basis) output
 88# --------------------------------------------
 89##### States: array/vector representation
 90# --------------------------------------------
 91# define a zero vector of size set by the basis dimenion
 92psi_s = np.zeros(basis.Ns)
 93# obtain array index for the fock state |01>
 94array_ind_s = basis.index(basis.state_to_int("01"))
 95# construct the pure state |01>
 96psi_s[array_ind_s] = 1.0
 97print("\nprint state psi_s in the basis:")
 98print(psi_s)
 99#
100#############################################
101print("\n\n\n----------------------------")
102print("-------  SYMMETRIES  -------")
103print("----------------------------\n")
104#############################################
105#
106##### basis objects with symmetries
107#
108basis_triplet = spin_basis_general(L, pblock=(P, 0))
109basis_singlet = spin_basis_general(L, pblock=(P, 1))
110#
111print("print full basis:")
112print(basis)
113#
114print("\n\nprint pblock=+1 basis:\n")
115print(basis_triplet)
116#
117print(
118    "\n  * integer rep column no longer consecutive! (|01> falls outside symmetry sector)"
119)
120print(
121    "  * array index column still consecutive! (but indices differ compared to full basis, e.g. for |00>)"
122)
123print(
124    "  * |11> and |00> invariant under parity, so they correspond to physical states |11> and |00>"
125)
126print(
127    "  * |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."
128)
129print("\n\nprint pblock=-1 basis:\n")
130#
131print(basis_singlet)
132print(
133    "  * |10> here represents the physical ANTI-symmetric superposition 1/sqrt(2)(|10> - |01>) [see bottom note when printing the symmetry-reduced basis]"
134)
135print(
136    "  *  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"
137)
138#
139# --------------------------------------------------
140##### transform states from one basis to the other
141# --------------------------------------------------
142#
143array_ind_s = basis_triplet.index(basis.state_to_int("10"))
144psi_symm_s = np.zeros(basis_triplet.Ns)
145psi_symm_s[array_ind_s] = 1.0  # create the state |10> + |01> in basis_triplet
146print("print state psi_symm_s in the symmetry-reduced basis_triplet:")
147print(psi_symm_s)
148#
149# compute corresponding state in the full basis
150psi_s = basis_triplet.project_from(psi_symm_s, sparse=False)
151print(
152    "\nprint state psi_s in the full basis: (note the factor 1/sqrt(2) which comes out correct."
153)
154print(psi_s)
155#
156# one can also project a full-basis state to a symmetry-reduced basis
157psi_s = np.zeros(basis.Ns)
158array_ind_s = basis.index(basis.state_to_int("01"))
159psi_s[array_ind_s] = 1.0  # create the state |01> in the full basis
160#
161psi_symm_s = basis_singlet.project_to(
162    psi_s, sparse=False
163)  # projects |01> to 1/sqrt(2) (|01> - |10>) in basis_singlet
164print(
165    "\nprint state psi_symm_s in the symmetry-reduced basis_singlet; NOTE: projection does not give a normalized state!"
166)
167print(psi_symm_s)
168# normalize
169psi_symm_s = psi_symm_s / np.linalg.norm(psi_symm_s)
170#
171# lift the projected state back to full basis
172psi_symm_s = np.expand_dims(
173    psi_symm_s, 0
174)  # required only when the basis_singlet contains a single state
175psi_lifted_s = basis_singlet.project_from(
176    psi_symm_s, sparse=False
177)  # corresponds to the projection 1/sqrt(2) (|01> - |10>) in the full basis
178print(
179    "\nprint state psi_lifted_s = 1/sqrt(2) (|01> - |10>) in the full basis; NOTE: info lost by the first projection!"
180)
181print(psi_lifted_s)