1. Basics

The quimb.operator module provides a unified way of ‘symbolically’ defining Hamiltonians or operators, including in symmetry sub-sectors, which can then be built into multiple different concrete representations for consumption by either the matrix or tensor network routines in quimb.

Matrix representations:

  • Dense matrix

  • Sparse matrix

  • Linear operator (action on a dense vector)

Tensor network representations:

  • Matrix product operator (MPO)

  • Dict of local dense terms (for PEPS algorithms)

  • Projected entangled pair operator (PEPO)

VMC routines:

  • Coupled configurations

There is also support for certain transformations of the terms, such as the Jordan-Wigner transformation and pauli decomposition.

Here we’ll illustrate with a 2D Heisenberg model.

%config InlineBackend.figure_formats = ['svg']
import matplotlib as mpl
import matplotlib.pyplot as plt

import quimb as qu
import quimb.operator as qop
import quimb.tensor as qtn

mpl.style.use(qu.NEUTRAL_STYLE)

1.1. The HilbertSpace

For full control over the sites in our system and how to order them we first explicitly declare a HilbertSpace object, here for a 2D square lattice.

Lx = 4
Ly = 5
sites = [(i, j) for i in range(Lx) for j in range(Ly)]
nsites = len(sites)

# define a default symmetry sector for efficiency
symmetry = "U1"
sector = nsites // 2

hilbert_space = qop.HilbertSpace(
    sites=sites,
    symmetry=symmetry,
    sector=sector,
)
hilbert_space
HilbertSpace(nsites=20, total_size=184_756, symmetry=U1, sector=10)

On its own this just has a few helpful tools for enumerating the valid configurations. The terminology used here is:

  • site: the labels of the individual sites in the system, these can be arbtirary hashable and sortable objects, e.g. our 2D coordinates above.

  • reg: in a linear ordering of the sites, the reg is the integer index or ‘register’ of a site. This can be controlled using the order argument to the HilbertSpace constructor.

hilbert_space.site_to_reg((2, 3))
13
hilbert_space.reg_to_site(nsites - 1)
(3, 4)

Individual configurations can be referred to in three ways:

  • config: a dict of site to local state.

  • flatconfig: a numpy.ndarray[uint8] of local states in register order.

  • rank: an integer between 0 and hilbert_space.size - 1 indexing the configurations in lexicographically sorted order.

# the first configuration (00..011..1) as a dict:
print(hilbert_space.rank_to_config(0))
{(0, 0): np.uint8(0), (0, 1): np.uint8(0), (0, 2): np.uint8(0), (0, 3): np.uint8(0), (0, 4): np.uint8(0), (1, 0): np.uint8(0), (1, 1): np.uint8(0), (1, 2): np.uint8(0), (1, 3): np.uint8(0), (1, 4): np.uint8(0), (2, 0): np.uint8(1), (2, 1): np.uint8(1), (2, 2): np.uint8(1), (2, 3): np.uint8(1), (2, 4): np.uint8(1), (3, 0): np.uint8(1), (3, 1): np.uint8(1), (3, 2): np.uint8(1), (3, 3): np.uint8(1), (3, 4): np.uint8(1)}
# get a random configuration
config = hilbert_space.rand_config(seed=42)
print(config)
{(0, 0): np.uint8(0), (0, 1): np.uint8(0), (0, 2): np.uint8(0), (0, 3): np.uint8(1), (0, 4): np.uint8(1), (1, 0): np.uint8(1), (1, 1): np.uint8(0), (1, 2): np.uint8(0), (1, 3): np.uint8(1), (1, 4): np.uint8(0), (2, 0): np.uint8(1), (2, 1): np.uint8(1), (2, 2): np.uint8(0), (2, 3): np.uint8(0), (2, 4): np.uint8(0), (3, 0): np.uint8(1), (3, 1): np.uint8(1), (3, 2): np.uint8(1), (3, 3): np.uint8(0), (3, 4): np.uint8(1)}
# find its rank
hilbert_space.config_to_rank(config)
16489
# the last configuration (11..100..0) as a flat vector
hilbert_space.rank_to_flatconfig(hilbert_space.size - 1)
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
      dtype=uint8)

1.2. The SparseOperatorBuilder

Now we can ‘symbolically’ define operators for this Hilbert space using the SparseOperatorBuilder object. Here we’ll define a 2D Heisenberg Hamiltonian:

H = qop.SparseOperatorBuilder(hilbert_space=hilbert_space)

# add nearest neighbor Heisenberg interactions
for i in range(Lx):
    for j in range(Ly):
        if i < Lx - 1:
            H += 0.5, ("+", (i, j)), ("-", (i + 1, j))
            H += 0.5, ("-", (i, j)), ("+", (i + 1, j))
            H += 1.0, ("z", (i, j)), ("z", (i + 1, j))
        if j < Ly - 1:
            H += 0.5, ("+", (i, j)), ("-", (i, j + 1))
            H += 0.5, ("-", (i, j)), ("+", (i, j + 1))
            H += 1.0, ("z", (i, j)), ("z", (i, j + 1))

# and a single site magnetic field to break symmetry
H += -0.1, ("z", (0, 0))

Note

If you don’t supply a HilbertSpace then the operator builder will build the default one for the minimal set of sites in has seen so far.

We can see all the terms visually with the show method (again the ordering is given by the register order):

H.show()
SparseOperatorBuilder(nsites=20, nterms=94, locality=2)
+ . . . . - . . . . . . . . . . . . . .  +0.5
- . . . . + . . . . . . . . . . . . . .  +0.5
z . . . . z . . . . . . . . . . . . . .  +1.0
+ - . . . . . . . . . . . . . . . . . .  +0.5
- + . . . . . . . . . . . . . . . . . .  +0.5
z z . . . . . . . . . . . . . . . . . .  +1.0
. + . . . . - . . . . . . . . . . . . .  +0.5
. - . . . . + . . . . . . . . . . . . .  +0.5
. z . . . . z . . . . . . . . . . . . .  +1.0
. + - . . . . . . . . . . . . . . . . .  +0.5
. - + . . . . . . . . . . . . . . . . .  +0.5
. z z . . . . . . . . . . . . . . . . .  +1.0
. . + . . . . - . . . . . . . . . . . .  +0.5
. . - . . . . + . . . . . . . . . . . .  +0.5
. . z . . . . z . . . . . . . . . . . .  +1.0
. . + - . . . . . . . . . . . . . . . .  +0.5
. . - + . . . . . . . . . . . . . . . .  +0.5
. . z z . . . . . . . . . . . . . . . .  +1.0
. . . + . . . . - . . . . . . . . . . .  +0.5
. . . - . . . . + . . . . . . . . . . .  +0.5
. . . z . . . . z . . . . . . . . . . .  +1.0
. . . + - . . . . . . . . . . . . . . .  +0.5
. . . - + . . . . . . . . . . . . . . .  +0.5
. . . z z . . . . . . . . . . . . . . .  +1.0
. . . . + . . . . - . . . . . . . . . .  +0.5
. . . . - . . . . + . . . . . . . . . .  +0.5
. . . . z . . . . z . . . . . . . . . .  +1.0
. . . . . + . . . . - . . . . . . . . .  +0.5
. . . . . - . . . . + . . . . . . . . .  +0.5
. . . . . z . . . . z . . . . . . . . .  +1.0
. . . . . + - . . . . . . . . . . . . .  +0.5
. . . . . - + . . . . . . . . . . . . .  +0.5
. . . . . z z . . . . . . . . . . . . .  +1.0
. . . . . . + . . . . - . . . . . . . .  +0.5
. . . . . . - . . . . + . . . . . . . .  +0.5
. . . . . . z . . . . z . . . . . . . .  +1.0
. . . . . . + - . . . . . . . . . . . .  +0.5
. . . . . . - + . . . . . . . . . . . .  +0.5
. . . . . . z z . . . . . . . . . . . .  +1.0
. . . . . . . + . . . . - . . . . . . .  +0.5
. . . . . . . - . . . . + . . . . . . .  +0.5
. . . . . . . z . . . . z . . . . . . .  +1.0
. . . . . . . + - . . . . . . . . . . .  +0.5
. . . . . . . - + . . . . . . . . . . .  +0.5
. . . . . . . z z . . . . . . . . . . .  +1.0
. . . . . . . . + . . . . - . . . . . .  +0.5
. . . . . . . . - . . . . + . . . . . .  +0.5
. . . . . . . . z . . . . z . . . . . .  +1.0
. . . . . . . . + - . . . . . . . . . .  +0.5
. . . . . . . . - + . . . . . . . . . .  +0.5
. . . . . . . . z z . . . . . . . . . .  +1.0
. . . . . . . . . + . . . . - . . . . .  +0.5
. . . . . . . . . - . . . . + . . . . .  +0.5
. . . . . . . . . z . . . . z . . . . .  +1.0
. . . . . . . . . . + . . . . - . . . .  +0.5
. . . . . . . . . . - . . . . + . . . .  +0.5
. . . . . . . . . . z . . . . z . . . .  +1.0
. . . . . . . . . . + - . . . . . . . .  +0.5
. . . . . . . . . . - + . . . . . . . .  +0.5
. . . . . . . . . . z z . . . . . . . .  +1.0
. . . . . . . . . . . + . . . . - . . .  +0.5
. . . . . . . . . . . - . . . . + . . .  +0.5
. . . . . . . . . . . z . . . . z . . .  +1.0
. . . . . . . . . . . + - . . . . . . .  +0.5
. . . . . . . . . . . - + . . . . . . .  +0.5
. . . . . . . . . . . z z . . . . . . .  +1.0
. . . . . . . . . . . . + . . . . - . .  +0.5
. . . . . . . . . . . . - . . . . + . .  +0.5
. . . . . . . . . . . . z . . . . z . .  +1.0
. . . . . . . . . . . . + - . . . . . .  +0.5
. . . . . . . . . . . . - + . . . . . .  +0.5
. . . . . . . . . . . . z z . . . . . .  +1.0
. . . . . . . . . . . . . + . . . . - .  +0.5
. . . . . . . . . . . . . - . . . . + .  +0.5
. . . . . . . . . . . . . z . . . . z .  +1.0
. . . . . . . . . . . . . + - . . . . .  +0.5
. . . . . . . . . . . . . - + . . . . .  +0.5
. . . . . . . . . . . . . z z . . . . .  +1.0
. . . . . . . . . . . . . . + . . . . -  +0.5
. . . . . . . . . . . . . . - . . . . +  +0.5
. . . . . . . . . . . . . . z . . . . z  +1.0
. . . . . . . . . . . . . . . + - . . .  +0.5
. . . . . . . . . . . . . . . - + . . .  +0.5
. . . . . . . . . . . . . . . z z . . .  +1.0
. . . . . . . . . . . . . . . . + - . .  +0.5
. . . . . . . . . . . . . . . . - + . .  +0.5
. . . . . . . . . . . . . . . . z z . .  +1.0
. . . . . . . . . . . . . . . . . + - .  +0.5
. . . . . . . . . . . . . . . . . - + .  +0.5
. . . . . . . . . . . . . . . . . z z .  +1.0
. . . . . . . . . . . . . . . . . . + -  +0.5
. . . . . . . . . . . . . . . . . . - +  +0.5
. . . . . . . . . . . . . . . . . . z z  +1.0
z . . . . . . . . . . . . . . . . . . .  -0.1

Currently the supported local operators are:

{
    "I": {0: (0, 1.0), 1: (1, 1.0)},
    # pauli matrices
    "x": {0: (1, 1.0), 1: (0, 1.0)},
    "y": {0: (1, 1.0j), 1: (0, -1.0j)},
    "z": {0: (0, 1.0), 1: (1, -1.0)},
    # ZX=iY: 'real Y'
    "⧖": {0: (1, -1.0), 1: (0, 1.0)},
    # spin 1/2 matrices (scaled paulis)
    "sx": {0: (1, 0.5), 1: (0, 0.5)},
    "sy": {0: (1, 0.5j), 1: (0, -0.5j)},
    "sz": {0: (0, 0.5), 1: (1, -0.5)},
    # creation / annihilation operators
    "+": {0: (1, 1.0)},
    "-": {1: (0, 1.0)},
    # number, symmetric number, and hole operators
    "n": {1: (1, 1.0)},
    "sn": {0: (0, -0.5), 1: (1, 0.5)},
    "h": {0: (0, 1.0)},
}

1.2.1. Building matrices

Now we can build out concrete realizations of the operator. For example the sparse matrix representation:

Hint

By default this will pick up the default sector and symmetry from the hilbert space object, or you can explicitly override it here.

%%time
H_sparse = H.build_sparse_matrix()
H_sparse
CPU times: user 992 ms, sys: 180 ms, total: 1.17 s
Wall time: 1.18 s
<Compressed Sparse Row sparse matrix of dtype 'float64'
	with 3199196 stored elements and shape (184756, 184756)>

This allows us to find the full exact groundstate and energy:

%%time
energy, psi = qu.eigh(H_sparse, k=1)
CPU times: user 5.01 s, sys: 82.5 ms, total: 5.09 s
Wall time: 694 ms
energy
array([-32.01392954])

Now we can define some other operators that act on the same hilbert space, and measure their expectation, here the Z magnetization acting on every site:

zijs = [[None for j in range(Ly)] for i in range(Lx)]

for i in range(Lx):
    for j in range(Ly):
        zop_ij = qop.SparseOperatorBuilder(
            # we can supply all the terms at construction
            terms=[(1.0, ("z", (i, j)))],
            # because we only act on 1 site of many, we do
            # need to explicitly provide the hilbert space
            hilbert_space=hilbert_space,
        )

        # build the sparse matrix
        zsparse_ij = zop_ij.build_sparse_matrix()

        # evaluate <psi|Z_ij|psi>
        zijs[i][j] = qu.expec(psi, zsparse_ij)
plt.imshow(zijs)
<matplotlib.image.AxesImage at 0x731f57a8dfd0>
../_images/e28016f8ac6e1db65f20b1fa198bf49fe2c2c5345ec4eed3b3287655a03ab22e.svg

If this was a smaller space we could also build the full dense matrix with build_dense_matrix.

The SparseOperatorBuilder also has a matvec method which computes the action on a dense vector without forming the explicit matrix. You can use this directly or call .aslinearoperator to get a scipy.sparse.linalg.LinearOperator object which can be used with scipy.sparse.linalg routines.

Warning

This is not a very efficient implementation yet, you may want to consider for example quspin if performance is critical.

1.2.2. Building tensor network representations

1.2.2.1. Building MPOs for MPS algorithms

We can also build out concrete representations of the operator for consumption by various tensor network algorithms in quimb.tensor.

For example as a matrix product operator (MPO) using build_mpo:

H_mpo = H.build_mpo()
H_mpo
MatrixProductOperator(tensors=20, indices=59, L=20, max_bond=17)
Tensor(shape=(4, 2, 2), inds=[_23d24bAAAAB, k0, b0], tags={I0}),backend=numpy, dtype=float64, data=array([[[ 0., 0.], [ 1., 0.]], [[ 0., 1.], [ 0., 0.]], [[ 1., 0.], [ 0., -1.]], [[ 1., 0.], [ 0., 1.]]])
Tensor(shape=(4, 8, 2, 2), inds=[_23d24bAAAAB, _23d24bAAAAC, k1, b1], tags={I1}),backend=numpy, dtype=float64, data=...
Tensor(shape=(8, 11, 2, 2), inds=[_23d24bAAAAC, _23d24bAAAAD, k2, b2], tags={I2}),backend=numpy, dtype=float64, data=...
Tensor(shape=(11, 14, 2, 2), inds=[_23d24bAAAAD, _23d24bAAAAE, k3, b3], tags={I3}),backend=numpy, dtype=float64, data=...
Tensor(shape=(14, 17, 2, 2), inds=[_23d24bAAAAE, _23d24bAAAAF, k4, b4], tags={I4}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAF, _23d24bAAAAG, k5, b5], tags={I5}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAG, _23d24bAAAAH, k6, b6], tags={I6}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAH, _23d24bAAAAI, k7, b7], tags={I7}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAI, _23d24bAAAAJ, k8, b8], tags={I8}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAJ, _23d24bAAAAK, k9, b9], tags={I9}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAK, _23d24bAAAAL, k10, b10], tags={I10}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAL, _23d24bAAAAM, k11, b11], tags={I11}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAM, _23d24bAAAAN, k12, b12], tags={I12}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAN, _23d24bAAAAO, k13, b13], tags={I13}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 17, 2, 2), inds=[_23d24bAAAAO, _23d24bAAAAP, k14, b14], tags={I14}),backend=numpy, dtype=float64, data=...
Tensor(shape=(17, 14, 2, 2), inds=[_23d24bAAAAP, _23d24bAAAAQ, k15, b15], tags={I15}),backend=numpy, dtype=float64, data=...
Tensor(shape=(14, 11, 2, 2), inds=[_23d24bAAAAQ, _23d24bAAAAR, k16, b16], tags={I16}),backend=numpy, dtype=float64, data=...
Tensor(shape=(11, 8, 2, 2), inds=[_23d24bAAAAR, _23d24bAAAAS, k17, b17], tags={I17}),backend=numpy, dtype=float64, data=...
Tensor(shape=(8, 4, 2, 2), inds=[_23d24bAAAAS, _23d24bAAAAT, k18, b18], tags={I18}),backend=numpy, dtype=float64, data=...
Tensor(shape=(4, 2, 2), inds=[_23d24bAAAAT, k19, b19], tags={I19}),backend=numpy, dtype=float64, data=array([[[ 1., 0.], [ 0., 1.]], [[ 0., 1.], [ 0., 0.]], [[ 0., 0.], [ 1., 0.]], [[ 1., 0.], [ 0., -1.]]])

The algorithm to build the MPO is a greedy algorithm that works well for local Hamiltonians, but will not be optimal for complicated long range models. You can check the underyling finite state machine representatino with draw_state_machine:

H.draw_state_machine()
../_images/4bd395cc545af87f8d4cc7b0e4fbbf9b407a8fc333f1d76b6c11b3ed24fa6db7.svg

We can use the MPO to find the groundstate with DMRG:

dmrg = qtn.DMRG2(H_mpo)
dmrg.solve(verbosity=1)
1, R, max_bond=(8/8), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 306.08it/s]
Energy: -31.656832428772546 ... not converged.
2, R, max_bond=(8/16), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 362.23it/s]
Energy: -31.813407135272556 ... not converged.
3, R, max_bond=(16/32), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 279.30it/s]
Energy: -31.818448856710024 ... not converged.
4, R, max_bond=(29/64), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 192.93it/s]
Energy: -31.81932769634487 ... not converged.
5, R, max_bond=(39/128), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 152.65it/s]
Energy: -31.81949636476171 ... not converged.
6, R, max_bond=(42/256), cutoff:1e-08
100%|##########################################| 19/19 [00:00<00:00, 210.71it/s]
Energy: -31.819509858329468 ... converged!

np.True_

Then we can check the relative error in the groundstate energy:

1 - dmrg.energy / energy.item()
np.float64(0.006072971495561852)

1.2.2.2. Building local terms for PEPS

For projected entangled pair states (PEPS) algorithms we often need the local terms of the Hamiltonian as dense matrices. We can build these with build_local_ham:

H_local = H.build_local_ham()
H_local.draw()
../_images/4f404b93a3a50c9ad3ee65b932b8115999870eca0542dbae8d55f81f78b3cf3d.svg
(<Figure size 547.214x547.214 with 1 Axes>, <Axes: >)

We can then feed this into algorithms such as simple update:

peps = qtn.PEPS.rand(Lx, Ly, 4, seed=42)
su = qtn.SimpleUpdate(peps, H_local)
su.evolve(100, tau=0.1)
n=100, D=4, tau=0.1, energy≈-31.9678: 100%|##########| 100/100 [00:02<00:00, 35.70it/s]
1 - su.energy / energy.item()
0.0014423055073261581

1.2.3. Monte carlo usage

For variational monte carlo (VMC) or other monte carlo routines one needs to the compute the ‘local energy’. This is encapsulated in the config_coupling and flatconfig_coupling methods. These return a tuple with A) the coupled configurations and B) the corresponding coefficients.

Mathematically, for a given configuration \(| x \rangle\), this returns the non-zero coupled configurations: \(\{| y \rangle\} \), as well as the coefficients \(\{ \langle x | H | y \rangle \}\) such that the ‘local energy’ is given by:

\[ E_{\text{loc}}(x) = \sum_j \dfrac{ \langle x | H | y \rangle \langle y | \psi \rangle } { \langle x | \psi \rangle } \]
flatconfig = hilbert_space.rand_flatconfig(seed=42)
flatconfig
array([0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
      dtype=uint8)
coupled_flatconfigs, coupled_coeffs = H.flatconfig_coupling(flatconfig)
coupled_flatconfigs, coupled_coeffs
(array([[1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1],
        [0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0]],
       dtype=uint8),
 array([0.5, 4.9, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
        0.5]))
plt.imshow(coupled_flatconfigs)
<matplotlib.image.AxesImage at 0x731f54488050>
../_images/2fe35f243de4940ce1970793ed7db6e3175a65738a0dd3ae48ebb41b938c26c7.svg

1.2.4. Transforming operators

The SparseOperatorBuilder also has some methods for transforming the terms.

1.2.4.1. Jordan-Wigner transformation

For example the Jordan-Wigner transformation which maps spin-1/2 operators to fermionic ones:

Lx = 4
Ly = 3
sites = [(s, i, j) for s in ("u", "d") for i in range(Lx) for j in range(Ly)]
nsites = len(sites)

# define half filling in each species
symmetry = "U1U1"
sector = (nsites // 2, nsites // 4), (nsites // 2, nsites // 4)

hilbert_space = qop.HilbertSpace(
    sites=sites,
    symmetry=symmetry,
    sector=sector,
)
hilbert_space
HilbertSpace(nsites=24, total_size=853_776, symmetry=U1U1, sector=((12, 6), (12, 6)))
H = qop.SparseOperatorBuilder(hilbert_space=hilbert_space)

t = 1.0
U = 8.0

# add nearest neighbor Heisenberg interactions
for i in range(Lx):
    for j in range(Ly):
        for s in ("u", "d"):
            # hopping
            if i < Lx - 1:
                # the relative ordering within the term now matters too
                H.add_term(-t, ("+", (s, i, j)), ("-", (s, i + 1, j)))
                H.add_term(-t, ("+", (s, i + 1, j)), ("-", (s, i, j)))
            if j < Ly - 1:
                H.add_term(-t, ("+", (s, i, j)), ("-", (s, i, j + 1)))
                H.add_term(-t, ("+", (s, i, j + 1)), ("-", (s, i, j)))

        # interaction
        H.add_term(U, ("n", ("u", i, j)), ("n", ("d", i, j)))

Note

You can either call the method jordan_wigner_transform to toggle the transformation, or supply jordan_wigner=True to the SparseOperatorBuilder constructor.

H.jordan_wigner_transform()
H.show()
SparseOperatorBuilder(nsites=24, nterms=80, locality=4, jordan_wigner=True)
+ z z - . . . . . . . . . . . . . . . . . . . .  -1.0
- z z + . . . . . . . . . . . . . . . . . . . .  -1.0
+ - . . . . . . . . . . . . . . . . . . . . . .  -1.0
- + . . . . . . . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . + z z - . . . . . . . .  -1.0
. . . . . . . . . . . . - z z + . . . . . . . .  -1.0
. . . . . . . . . . . . + - . . . . . . . . . .  -1.0
. . . . . . . . . . . . - + . . . . . . . . . .  -1.0
n . . . . . . . . . . . n . . . . . . . . . . .  +8.0
. + z z - . . . . . . . . . . . . . . . . . . .  -1.0
. - z z + . . . . . . . . . . . . . . . . . . .  -1.0
. + - . . . . . . . . . . . . . . . . . . . . .  -1.0
. - + . . . . . . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . + z z - . . . . . . .  -1.0
. . . . . . . . . . . . . - z z + . . . . . . .  -1.0
. . . . . . . . . . . . . + - . . . . . . . . .  -1.0
. . . . . . . . . . . . . - + . . . . . . . . .  -1.0
. n . . . . . . . . . . . n . . . . . . . . . .  +8.0
. . + z z - . . . . . . . . . . . . . . . . . .  -1.0
. . - z z + . . . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . + z z - . . . . . .  -1.0
. . . . . . . . . . . . . . - z z + . . . . . .  -1.0
. . n . . . . . . . . . . . n . . . . . . . . .  +8.0
. . . + z z - . . . . . . . . . . . . . . . . .  -1.0
. . . - z z + . . . . . . . . . . . . . . . . .  -1.0
. . . + - . . . . . . . . . . . . . . . . . . .  -1.0
. . . - + . . . . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . + z z - . . . . .  -1.0
. . . . . . . . . . . . . . . - z z + . . . . .  -1.0
. . . . . . . . . . . . . . . + - . . . . . . .  -1.0
. . . . . . . . . . . . . . . - + . . . . . . .  -1.0
. . . n . . . . . . . . . . . n . . . . . . . .  +8.0
. . . . + z z - . . . . . . . . . . . . . . . .  -1.0
. . . . - z z + . . . . . . . . . . . . . . . .  -1.0
. . . . + - . . . . . . . . . . . . . . . . . .  -1.0
. . . . - + . . . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . + z z - . . . .  -1.0
. . . . . . . . . . . . . . . . - z z + . . . .  -1.0
. . . . . . . . . . . . . . . . + - . . . . . .  -1.0
. . . . . . . . . . . . . . . . - + . . . . . .  -1.0
. . . . n . . . . . . . . . . . n . . . . . . .  +8.0
. . . . . + z z - . . . . . . . . . . . . . . .  -1.0
. . . . . - z z + . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . + z z - . . .  -1.0
. . . . . . . . . . . . . . . . . - z z + . . .  -1.0
. . . . . n . . . . . . . . . . . n . . . . . .  +8.0
. . . . . . + z z - . . . . . . . . . . . . . .  -1.0
. . . . . . - z z + . . . . . . . . . . . . . .  -1.0
. . . . . . + - . . . . . . . . . . . . . . . .  -1.0
. . . . . . - + . . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . . + z z - . .  -1.0
. . . . . . . . . . . . . . . . . . - z z + . .  -1.0
. . . . . . . . . . . . . . . . . . + - . . . .  -1.0
. . . . . . . . . . . . . . . . . . - + . . . .  -1.0
. . . . . . n . . . . . . . . . . . n . . . . .  +8.0
. . . . . . . + z z - . . . . . . . . . . . . .  -1.0
. . . . . . . - z z + . . . . . . . . . . . . .  -1.0
. . . . . . . + - . . . . . . . . . . . . . . .  -1.0
. . . . . . . - + . . . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . . . + z z - .  -1.0
. . . . . . . . . . . . . . . . . . . - z z + .  -1.0
. . . . . . . . . . . . . . . . . . . + - . . .  -1.0
. . . . . . . . . . . . . . . . . . . - + . . .  -1.0
. . . . . . . n . . . . . . . . . . . n . . . .  +8.0
. . . . . . . . + z z - . . . . . . . . . . . .  -1.0
. . . . . . . . - z z + . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . . . . + z z -  -1.0
. . . . . . . . . . . . . . . . . . . . - z z +  -1.0
. . . . . . . . n . . . . . . . . . . . n . . .  +8.0
. . . . . . . . . + - . . . . . . . . . . . . .  -1.0
. . . . . . . . . - + . . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . . . . . + - .  -1.0
. . . . . . . . . . . . . . . . . . . . . - + .  -1.0
. . . . . . . . . n . . . . . . . . . . . n . .  +8.0
. . . . . . . . . . + - . . . . . . . . . . . .  -1.0
. . . . . . . . . . - + . . . . . . . . . . . .  -1.0
. . . . . . . . . . . . . . . . . . . . . . + -  -1.0
. . . . . . . . . . . . . . . . . . . . . . - +  -1.0
. . . . . . . . . . n . . . . . . . . . . . n .  +8.0
. . . . . . . . . . . n . . . . . . . . . . . n  +8.0
H_sparse = H.build_sparse_matrix()
H_sparse
<Compressed Sparse Row sparse matrix of dtype 'float64'
	with 16686516 stored elements and shape (853776, 853776)>
energy, psi = qu.eigh(H_sparse, k=1)
energy / (Lx * Ly)
array([-0.40943827])

1.2.4.2. Pauli Decomposition

You can also decompose an operator into strings of Pauli operators using the pauli_decompose method, or by supplying pauli_decompose=True to the SparseOperatorBuilder constructor. The use_zx argument enables decomposing Y operators into the real combination of Z and X (i.e. ⴵ = ZX = iY) to avoid complex coefficients:

H.pauli_decompose(use_zx=True)
H.show()
SparseOperatorBuilder(nsites=24, nterms=105, locality=4, jordan_wigner=True, pauli_decompose=zx)
x z z x . . . . . . . . . . . . . . . . . . . .  -0.5
ⴵ z z ⴵ . . . . . . . . . . . . . . . . . . . .  +0.5
x x . . . . . . . . . . . . . . . . . . . . . .  -0.5
ⴵ ⴵ . . . . . . . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . x z z x . . . . . . . .  -0.5
. . . . . . . . . . . . ⴵ z z ⴵ . . . . . . . .  +0.5
. . . . . . . . . . . . x x . . . . . . . . . .  -0.5
. . . . . . . . . . . . ⴵ ⴵ . . . . . . . . . .  +0.5
z . . . . . . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . z . . . . . . . . . . .  -2.0
z . . . . . . . . . . . z . . . . . . . . . . .  +2.0
. x z z x . . . . . . . . . . . . . . . . . . .  -0.5
. ⴵ z z ⴵ . . . . . . . . . . . . . . . . . . .  +0.5
. x x . . . . . . . . . . . . . . . . . . . . .  -0.5
. ⴵ ⴵ . . . . . . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . x z z x . . . . . . .  -0.5
. . . . . . . . . . . . . ⴵ z z ⴵ . . . . . . .  +0.5
. . . . . . . . . . . . . x x . . . . . . . . .  -0.5
. . . . . . . . . . . . . ⴵ ⴵ . . . . . . . . .  +0.5
. z . . . . . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . z . . . . . . . . . .  -2.0
. z . . . . . . . . . . . z . . . . . . . . . .  +2.0
. . x z z x . . . . . . . . . . . . . . . . . .  -0.5
. . ⴵ z z ⴵ . . . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . x z z x . . . . . .  -0.5
. . . . . . . . . . . . . . ⴵ z z ⴵ . . . . . .  +0.5
. . z . . . . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . z . . . . . . . . .  -2.0
. . z . . . . . . . . . . . z . . . . . . . . .  +2.0
. . . x z z x . . . . . . . . . . . . . . . . .  -0.5
. . . ⴵ z z ⴵ . . . . . . . . . . . . . . . . .  +0.5
. . . x x . . . . . . . . . . . . . . . . . . .  -0.5
. . . ⴵ ⴵ . . . . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . x z z x . . . . .  -0.5
. . . . . . . . . . . . . . . ⴵ z z ⴵ . . . . .  +0.5
. . . . . . . . . . . . . . . x x . . . . . . .  -0.5
. . . . . . . . . . . . . . . ⴵ ⴵ . . . . . . .  +0.5
. . . z . . . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . z . . . . . . . .  -2.0
. . . z . . . . . . . . . . . z . . . . . . . .  +2.0
. . . . x z z x . . . . . . . . . . . . . . . .  -0.5
. . . . ⴵ z z ⴵ . . . . . . . . . . . . . . . .  +0.5
. . . . x x . . . . . . . . . . . . . . . . . .  -0.5
. . . . ⴵ ⴵ . . . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . x z z x . . . .  -0.5
. . . . . . . . . . . . . . . . ⴵ z z ⴵ . . . .  +0.5
. . . . . . . . . . . . . . . . x x . . . . . .  -0.5
. . . . . . . . . . . . . . . . ⴵ ⴵ . . . . . .  +0.5
. . . . z . . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . z . . . . . . .  -2.0
. . . . z . . . . . . . . . . . z . . . . . . .  +2.0
. . . . . x z z x . . . . . . . . . . . . . . .  -0.5
. . . . . ⴵ z z ⴵ . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . x z z x . . .  -0.5
. . . . . . . . . . . . . . . . . ⴵ z z ⴵ . . .  +0.5
. . . . . z . . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . z . . . . . .  -2.0
. . . . . z . . . . . . . . . . . z . . . . . .  +2.0
. . . . . . x z z x . . . . . . . . . . . . . .  -0.5
. . . . . . ⴵ z z ⴵ . . . . . . . . . . . . . .  +0.5
. . . . . . x x . . . . . . . . . . . . . . . .  -0.5
. . . . . . ⴵ ⴵ . . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . . x z z x . .  -0.5
. . . . . . . . . . . . . . . . . . ⴵ z z ⴵ . .  +0.5
. . . . . . . . . . . . . . . . . . x x . . . .  -0.5
. . . . . . . . . . . . . . . . . . ⴵ ⴵ . . . .  +0.5
. . . . . . z . . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . z . . . . .  -2.0
. . . . . . z . . . . . . . . . . . z . . . . .  +2.0
. . . . . . . x z z x . . . . . . . . . . . . .  -0.5
. . . . . . . ⴵ z z ⴵ . . . . . . . . . . . . .  +0.5
. . . . . . . x x . . . . . . . . . . . . . . .  -0.5
. . . . . . . ⴵ ⴵ . . . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . . . x z z x .  -0.5
. . . . . . . . . . . . . . . . . . . ⴵ z z ⴵ .  +0.5
. . . . . . . . . . . . . . . . . . . x x . . .  -0.5
. . . . . . . . . . . . . . . . . . . ⴵ ⴵ . . .  +0.5
. . . . . . . z . . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . . z . . . .  -2.0
. . . . . . . z . . . . . . . . . . . z . . . .  +2.0
. . . . . . . . x z z x . . . . . . . . . . . .  -0.5
. . . . . . . . ⴵ z z ⴵ . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . . . . x z z x  -0.5
. . . . . . . . . . . . . . . . . . . . ⴵ z z ⴵ  +0.5
. . . . . . . . z . . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . . . z . . .  -2.0
. . . . . . . . z . . . . . . . . . . . z . . .  +2.0
. . . . . . . . . x x . . . . . . . . . . . . .  -0.5
. . . . . . . . . ⴵ ⴵ . . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . . . . . x x .  -0.5
. . . . . . . . . . . . . . . . . . . . . ⴵ ⴵ .  +0.5
. . . . . . . . . z . . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . . . . z . .  -2.0
. . . . . . . . . z . . . . . . . . . . . z . .  +2.0
. . . . . . . . . . x x . . . . . . . . . . . .  -0.5
. . . . . . . . . . ⴵ ⴵ . . . . . . . . . . . .  +0.5
. . . . . . . . . . . . . . . . . . . . . . x x  -0.5
. . . . . . . . . . . . . . . . . . . . . . ⴵ ⴵ  +0.5
. . . . . . . . . . z . . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . . . . . z .  -2.0
. . . . . . . . . . z . . . . . . . . . . . z .  +2.0
. . . . . . . . . . . . . . . . . . . . . . . .  +24.0
. . . . . . . . . . . z . . . . . . . . . . . .  -2.0
. . . . . . . . . . . . . . . . . . . . . . . z  -2.0
. . . . . . . . . . . z . . . . . . . . . . . z  +2.0

1.2.5. Builtin operators

The following hamiltonians are built in:

  • heisenberg_from_edges: the Heisenberg model on an arbitrary graph defined by edges, includes the transverse field ising model as a special case of the coupling and magnetic field terms.

  • fermi_hubbard_from_edges: the Fermi-Hubbard model on an arbitrary graph defined by edges.

  • fermi_hubbard_spinless_from_edges: the spinless Fermi-Hubbard model (aka t-V model) on an arbitrary graph defined by edges.