Skip to content

Primitives

States

Qudit makes a state an almost first class citizen which allows us to easily create and manipulate quantum states. We can start by defining a basis, which for now is just the computational basis in some dimension. Consider a qutrit (d=3) system:

python
from qudit import Basis

Ket = Basis(3)

And then declare a computational basis pure state via the basis. The following are all equivalent in creating the state |012:

python
Psi = Ket("012")

Psi = Ket(0, 1, 2)

Psi = Ket(0) ^ Ket(1) ^ Ket(2)

We also have a State class which can be used to create more complex states with automatically normalized vectors. A result of this is that states are about as hard to declare in python as they are to write on paper. Consider

|ψ=ω|0000+ω2|1010+3i|2010+|2200+(9i+16)|1210+(ωω2)|0022+(ω1)2|2020+(eiπ18+6)|2221+|0112+(5+9i)|1200+0.67|1111+9eiπ5|2222
python
SV = State(
    w * Ket("0000")
    + w**2 * Ket("1010")
    + rt(3) * 1j * Ket("2010")
    + Ket("2200")
    + (9j + 16) * Ket("1210")
    + (w - w**2) * Ket("0022")
    + (w - 1) ** 2 * Ket("2020")
    + (e(1j * pi / 18) + 6) * Ket("2221")
    + Ket("0112")
    + (5 + 9j) * Ket("1200")
    + 0.67 * Ket("1111")
    + (9 * e(1j * pi / 16)) * Ket("2222")
)
python
from qudit import State, Basis
import numpy as np

Unity = lambda d: np.exp(2j * np.pi / d)
e, rt = np.exp, np.sqrt
pi = np.pi

w, Ket = Unity(3), Basis(4)

Properties of states

States contain several properties, and methods for information and manipulation

PropertyDescription
ddimension of state (e.g. 2 for qubits, 3 for qutrits)
HHermitian conjugate via .conj().T
traceReturn the trace of the state (if density matrix) else return norm squared (if pure state)
isDensityif state is a density matrix (True) else pure state (False)
isPure()if state is pure (True) else density matrix (False)
norm()Return the state normalized to unit norm (if not)
density()Return the density matrix of the state
proj()projector |ψψ| if pure state else ρ
oproj()perpendicular projector I|ψψ| if pure state else Iρ

Gates

Gates in qudit are operators (matrices) that act on states and states. With the same preliminaries as above;

Single-qudit examples

We use the operations

  • @ to apply matmult of an operator on a state, and similarly
  • ^ to build tensor products of states and gates.

For example, for a qubit system:

python
Ket = Basis(2)
G = Gategen(2)

psi = Ket("0")

# Apply a Hadamard
psi = G.H @ psi
print(psi)

# Apply a rotation
psi = G.RY(np.pi/2) @ psi
print(psi) # results in |1>
python
from qudit import Basis
from qudit.circuit import Gategen

The above system creates RY(π/2)|+=|1.

Three-qudit GHZ

Similarly we can start with |000 and apply a Hadamard on the first qubit followed by two CNOTs to create a three-qubit GHZ state:

|GHZ=|000+|1112.

python
Ket = Basis(2)
G = Gategen(2)

psi = Ket("000")

HII = G.H ^ G.I ^ G.I
CX1 = G.CX ^ G.I
CX2 = G.I ^ G.CX

psi = CX2 @ (CX1 @ (HII @ psi))
print(psi)
python
from qudit import Basis
from qudit.circuit import Gategen

Gategen Generics

Gates are made up of two interoperable classes Unitary and Gate, both of which interop with each other, states, and tensors. The following gates are available in Gategen for a given dimension d in the circuit-less form:

NameSymbolDescription
IdentityIIdentity gate
HadamardHGeneralized Hadamard gate
XXGeneralized Pauli X gate
YYGeneralized Pauli Y gate
ZZGeneralized Pauli Z gate
RX(θ)RX(θ)X rotation axis by θ
RY(θ)RY(θ)Y rotation axis by θ
RZ(θ)RZ(θ)Z rotation axis by θ
CXCXCX gate
CSUMCSUMGeneralized CX gate, or the CSUM gate such that CSUM|i|j=|i|i+jmodd
SWAPSWAPGeneralized SWAP gate such that SWAP|i|j=|j|i

Gategen Special Gates

In addition to these gates there are two special gates called CU or Controlled-U and GMR or GellMann Rotation used as

  • CU is a generalization of the CX gate where the target operation can be any unitary, and the control can be on any state. For example, CU(control=1, target=G.H) applies a Hadamard on the target qudit if the control qudit is in state |1.
  • GMR is a generalization of the RX, RY, RZ gates where the rotation can be on any axis defined by two states. For example, GMR(0, 2, np.pi/2) applies a π/2 rotation on the axis defined by |0 and |2.

Common States

qudit.tools provides standard multi-partite states. All return normalized State objects.

FunctionDescription
GHZ(n, d)n-partite qudit GHZ: 1di=0d1|in
W(n, d=2)n-qudit W state: 1ni|01i0 for local dimension d
NOON(n, theta)Two-mode NOON state: |n,0+einθ|0,n in dimension n+1
Dicke(n, k, d=2)n-qudit Dicke state with k zeros and nk ones for local dimension d
Coherent(N, alpha)Truncated coherent state in (N+1)-dimensional Fock space
python
print(GHZ(3, 2))       # (|000> + |111>) / sqrt(2)
print(W(3))            # (|100> + |010> + |001>) / sqrt(3)  qubit
print(W(3, d=3))       # qutrit W state, 27-dimensional space
print(NOON(3, 0))      # (|3,0> + |0,3>) / sqrt(2) in d=4
print(Dicke(4, 1))     # uniform superposition of weight-3 states (qubit)
print(Dicke(4, 1, d=3))# qutrit Dicke state, 81-dimensional space
print(Coherent(5, alpha=1.0))
python
from qudit.tools.states import GHZ, W, NOON, Dicke, Coherent

Random

qudit.random samples from the Haar measure on U(n):

FunctionDescription
random_unitary(n)Haar-random n×n unitary via complex Ginibre QR
random_state(n)Haar-random pure state |ψCn
python
from qudit import random_unitary, random_state

U = random_unitary(4)   # 4x4 unitary matrix (np.ndarray)
psi = random_state(4)   # random 4-component statevector

Spaces and Separability

qudit.tools provides tools for analyzing bipartite states:

MethodDescription
Space.PPT(rho, sub)Peres–Horodecky partial-transpose test: True if ρTB0
Space.gramSchmidt(vectors)Gram-Schmidt orthonormalization
Space.schmidtDecompose(state)Schmidt decomposition; returns list of (λk,|uk,|vk)
Space.schmidtRank(mat)Schmidt rank (matrix rank) of a bipartite coefficient matrix
python
# Bell state: maximally entangled, Schmidt rank 2
psi = (np.kron([1, 0], [1, 0]) + np.kron([0, 1], [0, 1])) / np.sqrt(2)
mat = psi.reshape(2, 2)  # bipartite coefficient matrix

decomp = Space.schmidtDecompose(mat)
print(Space.schmidtRank(mat))  # 2

rho = np.outer(psi, psi.conj())
print(Space.PPT(rho, 2))  # False  (entangled states fail PPT)
python
from qudit.tools.tests import Space
import numpy as np

NOTE

Space.PPT currently overrides the sub argument to 3 internally; pass the actual block size you intend and verify against the matrix dimensions.