Qudit Circuit Basics¶
A gentle intro to ``tensorcircuit.quditcircuit.QuditCircuit``
Overview¶
This tutorial shows how to build and simulate qudit circuits (d‑level systems, where d ≥ 3) using tensorcircuit’s QuditCircuit API. Highlights
Create a
QuditCircuit(nqudits, dim)with dimensiondim ∈ [3, 36].Single-qudit gates:
X,Z,H, rotationsRX/RY/RZon selected levels(j, k).Two‑qudit gates:
RXX,RZZ, and the generalized controlled‑sumCSUMand controlled-phaseCPHASE.Obtain wavefunctions, probabilities, samples, expectations, and sub‑system projections.
Samples and bitstrings use base‑36 digits (
0–9A–Z) whereA = 10, ..., Z = 35.
Setup¶
[1]:
import tensorcircuit as tc
from tensorcircuit.quditcircuit import QuditCircuit
tc.set_backend("numpy") # or "jax", "tensorflow", "pytorch"
print("tensorcircuit version:", tc.__version__)
tensorcircuit version: 1.3.0
Hello, Qutrit! (dim = 13)¶
We’ll prepare a single qudit (nqudits=1, dim=13), apply a generalized Hadamard H to put it into an equal superposition, and inspect the resulting state and probabilities.
[2]:
c = QuditCircuit(nqudits=1, dim=13)
c.h(0) # generalized Hadamard on the only qudit
psi = c.wavefunction() # state vector of length 13^1 = 13
probs = c.probability() # probability vector (length 3)
print(r"\psi:", psi)
print("P:", probs)
\psi: [0.2773501+0.j 0.2773501+0.j 0.2773501+0.j 0.2773501+0.j 0.2773501+0.j
0.2773501+0.j 0.2773501+0.j 0.2773501+0.j 0.2773501+0.j 0.2773501+0.j
0.2773501+0.j 0.2773501+0.j 0.2773501+0.j]
P: [0.07692308 0.07692308 0.07692308 0.07692308 0.07692308 0.07692308
0.07692308 0.07692308 0.07692308 0.07692308 0.07692308 0.07692308
0.07692308]
Multi‑Qudit Basics¶
Let’s move to two qutrits and create a maximally entangled state using H and the qudit controlled‑sum CSUM.
The operator CSUM(control, target, cv=None) adds the control’s value to the target modulo dim. It’s a natural generalization of CNOT. If you pass cv, the gate activates only when the control equals that value (default is None).
[3]:
cq = QuditCircuit(nqudits=2, dim=3) # two qutrits
cq.h(0) # superpose control
cq.csum(0, 1) # qudit CNOT analog (control=0, target=1)
psi = cq.wavefunction()
probs = cq.probability()
print(r"|\psi|^2 (length 3^2=9):", probs)
|\psi|^2 (length 3^2=9): [0.3333333 0. 0. 0. 0.3333333 0. 0.
0. 0.3333333]
Sampling and Base‑36 Readout¶
Sampling returns strings in base‑dim using ``0-9A-Z``. For dim=3, the alphabet is 0,1,2:
[4]:
samples = cq.sample(batch=512, format="count_dict_bin") # e.g., '00', '11', '22'
samples
[4]:
{'00': 160, '11': 171, '22': 181}
Single‑Qudit Rotations on Selected Levels¶
For a qudit, rotations target a two‑level subspace inside the d levels.
rx(index, theta, j=0, k=1)rotates between levelsjandkabout the X‑axis of that embedded SU(2).ry(index, theta, j=0, k=1)similarly for Y.rz(index, theta, j=0)applies a Z‑phase to a single levelj.
Tip:
(j, k)must be distinct integers in[0, dim-1].
[5]:
import numpy as np
c = QuditCircuit(nqudits=1, dim=5) # a ququint
c.h(0) # start in equal superposition
c.rx(0, theta=np.pi / 3, j=1, k=3) # rotate levels 1 and 3
c.rz(0, theta=np.pi / 5, j=4) # add a phase to level 4
psi = c.wavefunction()
probs = c.probability()
psi, probs
[5]:
(array([0.4472136 +0.j , 0.38729832-0.2236068j ,
0.4472136 +0.j , 0.38729832-0.2236068j ,
0.3618034 +0.26286554j], dtype=complex64),
array([0.19999999, 0.19999997, 0.19999999, 0.19999997, 0.20000002],
dtype=float32))
Two‑Qudit Interactions: RXX, RZZ¶
You can couple two qudits by acting on chosen subspaces of each:
rxx(q1, q2, theta, j1=0, k1=1, j2=0, k2=1)rzz(q1, q2, theta, j1=0, k1=1, j2=0, k2=1)
Both gates are the natural generalizations of qubit XX/ZZ rotations but restricted to the (j, k) subspaces.
[6]:
c2 = QuditCircuit(nqudits=2, dim=4) # two ququarts
c2.h(0)
c2.h(1)
c2.rxx(0, 1, theta=np.pi / 4, j1=0, k1=2, j2=1, k2=3)
c2.rzz(0, 1, theta=np.pi / 7, j1=0, k1=1, j2=0, k2=1)
c2.probability()
[6]:
array([0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625,
0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625],
dtype=float32)
Expectation Values of Local Operators¶
expectation(*ops) computes the expectation for one or more local observables. Each observable is a pair (op, [site_indices]) where op is a tensor (matrix) with appropriate dimension.
[7]:
# Example: build a diagonal operator on a single qutrit (dim=3)
import numpy as np
c = QuditCircuit(1, dim=3)
c.h(0)
op = np.diag([0.0, 0.5, 1.0]) # acts on subspace levels 0,1,2
expval = c.expectation((op, [0]))
expval
[7]:
array(0.49999997+0.j, dtype=complex64)
Apply Arbitrary Gate¶
Just directly using any API by feeding the corresponding unitary
[8]:
d = 36
c = tc.QuditCircuit(2, dim=d)
h_matrix = tc.quditgates.h_matrix_func(d)
c.any(0, unitary=h_matrix)
csum_matrix = tc.quditgates.csum_matrix_func(d)
c.any(0, 1, unitary=csum_matrix)
c.sample(1024, format="count_dict_bin")
[8]:
{'00': 29,
'11': 35,
'22': 29,
'33': 41,
'44': 25,
'55': 28,
'66': 28,
'77': 35,
'88': 32,
'99': 27,
'AA': 38,
'BB': 35,
'CC': 29,
'DD': 31,
'EE': 30,
'FF': 22,
'GG': 26,
'HH': 19,
'II': 26,
'JJ': 24,
'KK': 37,
'LL': 27,
'MM': 34,
'NN': 27,
'OO': 31,
'PP': 31,
'QQ': 28,
'RR': 26,
'SS': 23,
'TT': 27,
'UU': 32,
'VV': 27,
'WW': 19,
'XX': 27,
'YY': 22,
'ZZ': 17}
Notes & Tips¶
Dimensions:
QuditCircuitvalidatesdimand keeps it consistent across the circuit.Wavefunction & Probability:
wavefunction()returns the state;probability()returns a length‑dim^nvector.Sampling:
sample(batch, format="str")returns base‑36 strings for readability; useformat=Nonefor raw integers.Controlled Operations:
csum(control, target, cv=None)generalizes CNOT;cvpicks the active control value.Backend: Switch via
tc.set_backend("numpy" | "jax" | "tensorflow" | "pytorch")as needed.Interoperability: You can still obtain
matrix()for the full unitary orquoperator()MPO‑like forms for advanced workflows.
All the functions are similar to the tc.Circuit