The Machinery · Topic 24 of 41 · The Workbench: before the tools

Vectors, matrices and Kronecker products

How does a computer hold one qubit, two qubits, thirty, and why does each extra qubit double the bill?

  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
About this section: The Workbench: before the tools

Everything above is mathematics you can do on paper. This part is the other half: what a researcher does on a computer to see how a superconducting qubit behaves. Two free programs do most of it, QuTiP for how a system changes in time and scqubits for the levels of a circuit, and both are taught here from the beginning. This first group builds what they are made of: a state as a list of numbers, the one equation that moves it, and the oscillator that a resonator is. Every number in the part was produced by QuTiP 5.2.0 or scqubits 4.3.1, every example prints what the page says it prints, and each topic has an explorer you can move; all of them are on one page, the Workbench. If you only want to install the programs, start at topic 33.

See it

a b 1 qubit 2 numbers ⊗ c d 1 qubit = ac ad bc bd 2 qubits 4 numbers 1 2 3 4 qubits (numbers: 2, 4, 8, 16) 30 qubits 2³⁰ numbers = 16 GiB of memory Each qubit you add multiplies the length of the column by two. That doubling is the whole cost of simulating a quantum computer.
The state of n qubits is a column of 2n numbers. Joining two systems means taking the Kronecker product of their columns, and the product has as many entries as the two lengths multiplied together.

Try it: build a three-qubit state

Click gates to apply them, left to right, to three qubits that start in 000. The bars are the eight amplitudes; the line under them is the exact matrix product being computed. Watch what happens to the purity of qubit 1 when you add a CNOT: it drops below 1, which is entanglement showing up as a number.

Honest limits: the explorer is a classical state-vector simulator, the very thing this topic describes, so it is exact. Its cost is the doubling above, which is why it stops at three qubits on this page and why the memory slider below it goes up to fifty.

The intuition

A single qubit is a short list of two complex numbers, the amplitudes of |0⟩ and |1⟩. A gate is a 2 × 2 table of numbers, and applying a gate is multiplying the table by the list. Nothing in the quantum world is needed to hold that: it is arithmetic, and any computer can do it. This is how every simulation in the rest of this part works.

Two qubits make a list of four, not of two plus two. The state of a pair is described by the amplitude of 00, of 01, of 10 and of 11. When the two qubits have nothing to do with each other, those four amplitudes are products of the single-qubit ones (a·c, a·d, b·c, b·d in the figure), and the operation that builds the long list from the two short ones is the Kronecker product, written ⊗. When the qubits are entangled the four numbers cannot be split back into two short lists, and that failure is what entanglement means in this language.

The doubling. Three qubits give eight numbers, ten give 1,024, thirty give 1,073,741,824. At 16 bytes per complex number (two 64-bit floats, the usual choice) thirty qubits need 16 gibibytes, which a large workstation holds. Forty need 16 tebibytes and fifty need 16 pebibytes, which no machine holds. This is the honest reason people build quantum computers, and also the reason that the simulations in this part are of small pieces of hardware: one qubit and a resonator, a pair of coupled qubits. They are not simulations of a whole processor. In 2017 a supercomputer simulated a 45-qubit circuit exactly, on 8,192 nodes with half a petabyte of memory (Häner and Steiger, arXiv:1704.01127). Each extra qubit asks for double again, so a bigger machine buys a few more qubits and no more.

Which factor is which qubit. When you write 010 as one long index, one qubit has to come first. In physics and in QuTiP the leftmost factor is the first qubit and the most significant bit of the index. Qiskit, IBM’s toolkit, numbers its qubits the other way round: its bit-ordering guide puts qubit 0 on the right of a bit string. Neither is wrong, but a program that mixes the two silently acts on the wrong qubit, so this is the first thing to check when a result looks like a mirror image of what you expected.

Why simulations of hardware are not made of qubits at all. A real superconducting chip has a transmon that has more than two levels, and a resonator that has many. The state space of the combined system is the product of the sizes of its parts: a transmon cut down to 3 levels beside a resonator cut down to 10 gives 3 × 10 = 30 numbers, nowhere near 2n in the number of qubits. The Kronecker product is the same; only the list lengths change. This is the main thing to carry into the next topics.

The mathematics

Pauli matrices and the computational basis. The states |0⟩ and |1⟩ are the columns (1, 0) and (0, 1). The three Pauli matrices and the identity are

X = [[0, 1], [1, 0]] Y = [[0, −i], [i, 0]] Z = [[1, 0], [0, −1]] I = [[1, 0], [0, 1]]

and they obey X² = Y² = Z² = I and XY = iZ, YZ = iX, ZX = iY. In QuTiP, Z|0⟩ = +|0⟩: the “ground state” basis(2, 0) has σz = +1. A Hamiltonian that puts |0⟩ lower than |1⟩ is therefore −(ω/2)σz or equally ω·num(2), a sign convention that catches many people once.

The Kronecker product. For an m × m matrix A and an n × n matrix B,

A ⊗ B = [[a11B, a12B, …], [a21B, a22B, …], …] (size mn × mn)

and for columns it is the same rule with one column of blocks: (a, b) ⊗ (c, d) = (ac, ad, bc, bd). Its working property is the mixed product rule, (A ⊗ B)(u ⊗ v) = (Au) ⊗ (Bv): to act on one factor of a product state, act on that factor alone. It follows that Tr(A ⊗ B) = Tr(A)·Tr(B) and that the eigenvalues of A ⊗ B are all the products λiμj.

A gate on one qubit of many. To apply a one-qubit gate G to qubit k of n, with qubit 0 leftmost, build the 2n × 2n matrix

Gk = I⊗k ⊗ G ⊗ I⊗(n−k−1) and the basis state |q0q1…qn−1⟩ has index Σ qj 2n−1−j

A two-qubit gate is built the same way. CNOT with the first qubit as control is the projector sum |0⟩⟨0| ⊗ I + |1⟩⟨1| ⊗ X, which is the 4 × 4 matrix with rows (1,0,0,0), (0,1,0,0), (0,0,0,1), (0,0,1,0). Then the Bell pair comes out as

CNOT · (H ⊗ I) |00⟩ = CNOT · (|00⟩ + |10⟩)/√2 = (|00⟩ + |11⟩)/√2 = (0.7071, 0, 0, 0.7071)

Product or entangled? Write the four amplitudes of a two-qubit state as a 2 × 2 matrix M with Mij = ψ2i+j. The state is a product exactly when M has rank 1, that is when det M = 0. For the Bell pair M = diag(0.7071, 0.7071) has determinant 0.5, so it is not a product. Equivalently, the reduced state of qubit 1 has purity Tr(ρ1²) = 1 for a product and less than 1 otherwise; for the Bell pair it is 1/2. (The same fact, called the Schmidt rank, is developed in topic 7.)

The cost, exactly. A state of n qubits stored as complex numbers of 16 bytes takes 16·2n bytes. A density matrix (topic 2) takes 16·4n, so 15 qubits held as a density matrix already cost 16·230 bytes, the same 16 GiB as thirty qubits held as a state. The table is 16 KiB at 10 qubits, 16 MiB at 20, 16 GiB at 30, 16 TiB at 40 and 16 PiB at 50, for complex numbers of 16 bytes.

Try it in Python. Everything above is a few lines of QuTiP. tensor is the Kronecker product, ptrace keeps one factor and discards the rest, and fock_dm(2, k) is the projector |k⟩⟨k|. QuTiP’s core has no named Hadamard or CNOT gate (the optional qutip-qip package has them as snot() and cnot() in qutip_qip.operations, checked on qutip-qip 0.4.2), so the example writes them out. Run on QuTiP 5.2.0 with NumPy 2.2.6, 25 October 2026:

import numpy as np
import qutip as qt

X, Z, I = qt.sigmax(), qt.sigmaz(), qt.qeye(2)

# two qubits, |0>|1>, as one vector of four numbers
psi = qt.tensor(qt.basis(2, 0), qt.basis(2, 1))
print(psi.full().ravel().real)               # [0. 1. 0. 0.]   index 1 is binary 01

# Z on the FIRST qubit (the left-hand factor), identity on the second
Z_first = qt.tensor(Z, I)
print(Z_first.dims, Z_first.shape)           # [[2, 2], [2, 2]] (4, 4)

# no named gates in the core: write the Hadamard, build CNOT from projectors
H = qt.Qobj([[1, 1], [1, -1]]) / np.sqrt(2)
CNOT = qt.tensor(qt.fock_dm(2, 0), I) + qt.tensor(qt.fock_dm(2, 1), X)
bell = CNOT * qt.tensor(H, I) * qt.tensor(qt.basis(2, 0), qt.basis(2, 0))
print(bell.full().ravel().real.round(4))     # [0.7071 0.     0.     0.7071]
print(bell.ptrace(0).full().real)            # one half of a Bell pair is maximally mixed
[0. 1. 0. 0.]
[[2, 2], [2, 2]] (4, 4)
[0.7071 0.     0.     0.7071]
[[0.5 0. ]
 [0.  0.5]]

Where it actually matters

The shape of every simulation in this part Every tool in the next topics is built on this one operation. QuTiP’s Qobj carries a dims list such as [[2, 2], [2, 2]] precisely so that tensor and ptrace know where one subsystem ends and the next begins; scqubits’ HilbertSpace is a bookkeeper that does the same for a qubit and a resonator. The doubling is also why research groups simulate the part of a processor they need: a pair of coupled qubits and their readout resonators, never all of the chip at once. Exact state-vector simulation of general circuits stops where memory does (Häner and Steiger’s 45-qubit run took half a petabyte); special structure such as Clifford circuits (topic 21) is the way past it.