Chapter 4 — Key Takeaways (Multi-Qubit Programming)

The two-qubit reference. Part IV is built from this page plus Chapter 3's gate table.

Tensor product and ordering

$$|\psi\rangle = |q_{n-1}\rangle \otimes \cdots \otimes |q_1\rangle \otimes |q_0\rangle$$

Qubit 0 is LAST in the tensor product and RIGHTMOST in the bitstring. Reverse of most textbooks.

np.kron(zero, one) == Statevector(QuantumCircuit(2).x(0)).data     # both [0,1,0,0]
Circuit Amplitude on indices Labels
h(0) 0, 1 '00', '01'
h(1) 0, 2 '00', '10'

Use probabilities_dict() whenever the index needs a meaning. Reserve raw arrays for numerical work. Test with asymmetric states — symmetric ones hide endianness bugs.

Qubits General state Product state
$n$ $2^n$ amplitudes $2n$ numbers

CNOT

qc.cx(control, target)      # flips target iff control is 1
In (q1 q0) Out, after cx(0, 1)
00 00
01 11
10 10
11 01
cx(0,1)                    cx(1,0)  ← the textbook matrix
[[1 0 0 0]                 [[1 0 0 0]
 [0 0 0 1]                  [0 1 0 0]
 [0 0 1 0]                  [0 0 0 1]
 [0 1 0 0]]                 [0 0 1 0]]

Both are correct CNOTs; the difference is endianness.

On a superposition CNOT acts on both branches, producing a state that does not factorize. That non-factorizability is entanglement.

The four Bell states

qc.x(1)   if Psi        # then:
qc.h(0)
qc.z(0)   if minus
qc.cx(0, 1)
State Counts
$\lvert\Phi^+\rangle$ $\tfrac1{\sqrt2}(\lvert00\rangle+\lvert11\rangle)$ 00, 11
$\lvert\Phi^-\rangle$ $\tfrac1{\sqrt2}(\lvert00\rangle-\lvert11\rangle)$ 00, 11
$\lvert\Psi^+\rangle$ $\tfrac1{\sqrt2}(\lvert01\rangle+\lvert10\rangle)$ 01, 10
$\lvert\Psi^-\rangle$ $\tfrac1{\sqrt2}(\lvert01\rangle-\lvert10\rangle)$ 01, 10

$\Phi$ vs $\Psi$ shows in the counts. The $\pm$ does not.

Bell measurement = run the preparation backward (cx(0,1) then h(0)) before measuring → the four states map to 00, 01, 10, 11 deterministically. The primitive behind teleportation and superdense coding (Ch. 9).

★ Testing for entanglement

from qiskit.quantum_info import Statevector, partial_trace, entropy
rho0 = partial_trace(Statevector(qc), [1])       # keep qubit 0
ent = float(entropy(rho0))                        # 0 = product, 1 = maximal
Product state Bell state
purity $\mathrm{Tr}(\rho^2)$ 1.0 0.5 (the floor)
entanglement entropy 0.0 1.0 (maximal)
Bloch vector length 1.0 0.0

A qubit of a Bell state sits at the CENTER of the Bloch sphere — completely undetermined on its own, while the pair is in a completely definite, zero-entropy state. All the information is in the correlation and none is in the parts. It is not "in superposition."

⚠️ Counts never prove entanglement

An impostor with zero entanglement gives identical Z-basis counts:

qc.h(0); qc.measure(0, 0)
with qc.if_test((qc.clbits[0], 1)):
    qc.x(1)
Bell Impostor
Z basis {'00': 2074, '11': 2022} {'00': 2031, '11': 2065}
X basis (add h to both) {'00': 2074, '11': 2022} uniform over all four

More shots cannot help — same distribution, measured more precisely.

The witness (the real standard):

$$W = \langle Z\otimes Z\rangle + \langle X\otimes X\rangle, \qquad W \le 1 \text{ for every separable state}, \quad W = 2 \text{ for } |\Phi^+\rangle$$

$W$
Bell, ideal 2.0000
Impostor 0.9966
Bell on device-derived noise (opt 3) 1.9307
best over 20,000 random product states 0.9980

Gate costs — memorize these

Construct CNOTs Depth
CNOT, CZ 1 1
SWAP 3 3
Toffoli (CCX) 6 11
C3X 14 28
C4X 36 65

Add 3 CNOTs per SWAP the router inserts for non-adjacent qubits. You rarely write swap — the transpiler inserts it, invisibly, and it is the largest hidden cost in quantum programming.

CZ is symmetric (no control/target). $H_{(1)}\,\mathrm{CX}(0,1)\,H_{(1)} = \mathrm{CZ}$, because $HXH = Z$.

GHZ vs W

ghz: h(0); cx(0,1); cx(1,2); ...          # (|00…0⟩ + |11…1⟩)/√2
Lose one qubit → the survivors are
GHZ a classical mixture, no entanglement (eigenvalues 0.5, 0.5, 0, 0) — fragile
W still entangled, just less so (eigenvalues 0.667, 0.333, 0, 0) — robust

Not interconvertible by local operations: genuinely different kinds of tripartite entanglement. GHZ's fragility makes it a good benchmark (Ch. 30).

The exponential wall, refined

Product states cost $2n$; general states cost $2^n$. The cost is entanglement, in proportion to how much there is — which is exactly what matrix product state simulation exploits (Ch. 11).

An algorithm that does not generate substantial entanglement is classically simulable and cannot give a quantum advantage.

📉 Hardware: layout decides correctness

GHZ correct-fraction on a device-derived model, 4096 shots:

$n$ opt level 1 opt level 3
2 0.9561 0.9827
4 0.9084 0.9585
6 0.8127 0.8850
7 0.2148 0.8906
8 0.1091 0.8569

The cliff is a broken physical qubit (readout error 0.257, ECR error 1.0), not scaling. Levels 0–1 pick qubits by position; levels 2–3 pick them by measured error rate.

Preflight, always:

isa.layout.final_index_layout()          # RECORD THIS with every result
backend.target["measure"][(q,)].error    # > 0.1 → unusable
backend.target["ecr"][(a, b)].error      # > 0.05 → suspect; 1.0 → dead

Common pitfalls

  • Reading sv.data[2] and assuming you know which state that is.
  • Testing with symmetric states, which hide every endianness bug.
  • Inferring entanglement from computational-basis counts.
  • Taking more shots to fix a wrong measurement setting.
  • Trusting optimization level 1 for a result you will report.
  • Reporting a hardware result without the layout.
  • Reading "correct fraction" as evidence of coherence — a classical mixture scores 100%.

Project piece added this chapter

vqelab/circuits.py v1two_qubit_ansatz() (rotation layer → entangling layer → rotation layer), plus entanglement_entropy(), is_entangled(), and reachable_entanglement().

Without the CNOT the qubits stay in a product state forever, and a product-state ansatz can only represent product-state solutions — which for a molecular ground state is exactly wrong. reachable_entanglement() is the first diagnostic to run when a VQE will not converge.