In [1]:
from quimb import *
data = [1, 2j, -3]

Kets are column vectors, i.e. with shape (d, 1):


In [2]:
qu(data, qtype='ket')


Out[2]:
qarray([[ 1.+0.j],
        [ 0.+2.j],
        [-3.+0.j]])

The normalized=True option can be used to ensure a normalized output.

Bras are row vectors, i.e. with shape (1, d):


In [3]:
qu(data, qtype='bra')  # also conjugates the data


Out[3]:
qarray([[ 1.-0.j,  0.-2.j, -3.-0.j]])

And operators are square matrices, i.e. have shape (d, d):


In [4]:
qu(data, qtype='dop')


Out[4]:
qarray([[ 1.+0.j,  0.-2.j, -3.-0.j],
        [ 0.+2.j,  4.+0.j,  0.-6.j],
        [-3.+0.j,  0.+6.j,  9.+0.j]])

Which can also be sparse:


In [5]:
qu(data, qtype='dop', sparse=True)


Out[5]:
<3x3 sparse matrix of type '<class 'numpy.complex128'>'
	with 9 stored elements in Compressed Sparse Row format>

In [6]:
psi = 1.0j * bell_state('psi-')
psi


Out[6]:
qarray([[ 0.+0.j      ],
        [ 0.+0.707107j],
        [-0.-0.707107j],
        [ 0.+0.j      ]])

In [7]:
psi.H


Out[7]:
qarray([[ 0.-0.j      ,  0.-0.707107j, -0.+0.707107j,  0.-0.j      ]])

In [8]:
psi = up()
psi


Out[8]:
qarray([[1.+0.j],
        [0.+0.j]])

In [9]:
psi.H @ psi  # inner product


Out[9]:
qarray([[1.+0.j]])

In [10]:
X = pauli('X')
X @ psi  # act as gate


Out[10]:
qarray([[0.+0.j],
        [1.+0.j]])

In [11]:
psi.H @ X @ psi  # operator expectation


Out[11]:
qarray([[0.+0.j]])

In [12]:
expec(psi, psi)


Out[12]:
1.0

In [13]:
expec(psi, X)


Out[13]:
0j

Here's an example for a much larger (20 qubit), sparse operator expecation, which will be automatically parallelized:


In [14]:
psi = rand_ket(2**20)
A = rand_herm(2**20, sparse=True) + speye(2**20)
A


Out[14]:
<1048576x1048576 sparse matrix of type '<class 'numpy.complex128'>'
	with 11534284 stored elements in Compressed Sparse Row format>

In [15]:
expec(A, psi)  # should be ~ 1


Out[15]:
0.9999672709199712

In [16]:
%%timeit
expec(A, psi)


117 ms ± 10.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [17]:
dims = [2] * 10  # overall space of 10 qubits
X = pauli('X')
IIIXXIIIII = ikron(X, dims, inds=[3, 4])  # act on 4th and 5th spin only
IIIXXIIIII.shape


Out[17]:
(1024, 1024)

In [18]:
dims = [2] * 3
XZ = pauli('X') & pauli('Z')
ZIX = pkron(XZ, dims, inds=[2, 0])
ZIX.real.astype(int)


Out[18]:
qarray([[ 0,  1,  0,  0,  0,  0,  0,  0],
        [ 1,  0,  0,  0,  0,  0,  0,  0],
        [ 0,  0,  0,  1,  0,  0,  0,  0],
        [ 0,  0,  1,  0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0, -1,  0,  0],
        [ 0,  0,  0,  0, -1,  0,  0,  0],
        [ 0,  0,  0,  0,  0,  0,  0, -1],
        [ 0,  0,  0,  0,  0,  0, -1,  0]])

In [19]:
dims = [2] * 10
D = prod(dims)
psi = rand_ket(D)
rho_ab = ptr(psi, dims, [0, 9])
rho_ab.round(3)  # probably pretty close to identity


Out[19]:
qarray([[ 0.252+0.j   , -0.002+0.005j,  0.006+0.014j, -0.014+0.004j],
        [-0.002-0.005j,  0.246+0.j   ,  0.001+0.003j,  0.013+0.029j],
        [ 0.006-0.014j,  0.001-0.003j,  0.247+0.j   ,  0.008+0.01j ],
        [-0.014-0.004j,  0.013-0.029j,  0.008-0.01j ,  0.254+0.j   ]])