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
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)