Quantum entanglement for developers, shown through the CNOT gate

Entanglement is the most misunderstood concept in quantum computing. Pop science calls it "spooky action at a distance," as if measuring one particle magically changes another across the universe. That framing is poetic but useless if you're trying to use entanglement in code.

Here's the developer-friendly version: entanglement is a correlation between measurement outcomes that can't be explained by classical shared randomness. And creating it is trivially easy — a Hadamard gate followed by a CNOT. Two lines of Qiskit. In this post I'll strip away the mysticism, show you the circuits, and explain how we actually used entanglement to generate better randomness for the Quantum Genesis art.

Quantum Genesis NFT #30 — entangled via a CNOT gate

What entanglement really is

Two qubits are entangled when their measurement outcomes are correlated in a way that can't be reproduced by flipping two independent coins — even if those coins are somehow pre-programmed to match.

Try this analogy. You flip two coins. Normally each is independent — 50/50 each, and knowing one tells you nothing about the other. Now imagine perfectly correlated coins: every time one lands heads, the other does too, 100% of the time. That sounds like entanglement, but it isn't — you could get that classically by gluing both coins to the same mechanism.

Entanglement is stranger. Entangled qubits produce correlations that depend on the measurement basis. Measure both in the same basis and they're perfectly correlated; measure in different bases and the correlations change. No classical system reproduces this for all possible measurement choices at once — that's what Bell's theorem proves.

For developers, the takeaway: entanglement creates correlations between qubits that persist regardless of how far apart they are or when you measure them. These correlations underpin quantum teleportation, quantum key distribution, and — in our case — better random number generation.

Bell states: the simplest entanglement

The simplest entangled state involves two qubits. It's called a Bell state, and there are four of them:

NameStateMeasurement correlation
\Φ+⟩(\00⟩ + \11⟩) / √2Always agree (both 0 or both 1)
\Φ-⟩(\00⟩ - \11⟩) / √2Always agree (phase differs)
\Ψ+⟩(\01⟩ + \10⟩) / √2Always disagree (one 0, one 1)
\Ψ-⟩(\01⟩ - \10⟩) / √2Always disagree (phase differs)

The most common, the Bell pair |Φ+⟩, measures out as:

  • 50% chance of |00⟩ (both qubits 0)
  • 50% chance of |11⟩ (both qubits 1)
  • 0% chance of |01⟩ or |10⟩

Each individual qubit looks perfectly random (50/50), yet they're perfectly correlated with each other. That's entanglement.

The CNOT gate

The CNOT (Controlled-NOT) gate is the workhorse of entanglement. It takes a control and a target qubit, and flips the target if and only if the control is |1⟩.

Truth table:

Control (in)Target (in)Control (out)Target (out)
\0⟩\0⟩\0⟩\0⟩
\0⟩\1⟩\0⟩\1⟩
\1⟩\0⟩\1⟩\1⟩ (flipped!)
\1⟩\1⟩\1⟩\0⟩ (flipped!)

When the control is in a definite state, CNOT is boring — a conditional flip. But when the control is in superposition, CNOT creates entanglement. Here's the step-by-step:

  • Control: H|0⟩ = (|0⟩ + |1⟩)/√2 (superposition from Hadamard)
  • Target: |0⟩
  • Combined state: (|0⟩ + |1⟩)/√2 ⊗ |0⟩ = (|00⟩ + |10⟩)/√2

Apply CNOT:

  • |00⟩ → |00⟩ (control is 0, target unchanged)
  • |10⟩ → |11⟩ (control is 1, target flipped)

Result: (|00⟩ + |11⟩)/√2 — the Bell state |Φ+⟩! The qubits are now entangled.

> Developer intuition: think of CNOT as an "if-then" that operates on superpositions. When the control is a superposition of "yes" and "no," CNOT produces a superposition of "did flip" and "didn't flip" — linking the two qubits' fates together.

Creating entanglement in Qiskit

Two lines of circuit code:

from qiskit import QuantumCircuit

# Create a 2-qubit circuit
qc = QuantumCircuit(2, 2)

# Step 1: Put qubit 0 in superposition
qc.h(0)

# Step 2: Entangle qubit 0 and qubit 1
qc.cx(0, 1)  # CNOT: control=0, target=1

# Measure both
qc.measure([0, 1], [0, 1])

print(qc.draw())

Output:

     ┌───┐     ┌─┐
q_0: ┤ H ├──■──┤M├───
     └───┘┌─┴─┐└╥┘┌─┐
q_1: ─────┤ X ├─╫─┤M├
          └───┘ ║ └╥┘
c: 2/═══════════╩══╩═
                0  1

The H creates superposition, the cx (CNOT) creates entanglement. Measure, and you'll see only 00 and 11 — never 01 or 10.

Measuring entangled qubits

On a real quantum computer, noise leaks in. Running on IBM Quantum:

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

# Connect to IBM Quantum
service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.backend("ibm_fez")

# Create Bell circuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

# Transpile for the specific backend
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
transpiled = pm.run(qc)

# Run on real hardware
sampler = SamplerV2(backend)
job = sampler.run([transpiled], shots=4096)
result = job.result()

# Get counts
counts = result[0].data.c.get_counts()
print(counts)
# Typical output: {'00': 2010, '11': 2038, '01': 24, '10': 24}

On a perfect machine you'd see exactly {'00': 2048, '11': 2048}. On real hardware, noise introduces a small number of 01 and 10 results — typically under 2%. The overwhelming correlation (both 0 or both 1) is the entanglement signal.

GHZ states: three-qubit entanglement

A GHZ state (Greenberger-Horne-Zeilinger) extends entanglement to three or more qubits. All three are correlated:

|GHZ⟩ = (|000⟩ + |111⟩) / √2

When measured, all three qubits are 0 or all three are 1 — never a mix.

# GHZ-3: Three-qubit entanglement
qc = QuantumCircuit(3, 3)
qc.h(0)          # Superposition on qubit 0
qc.cx(0, 1)      # Entangle 0-1
qc.cx(0, 2)      # Entangle 0-2
qc.measure([0, 1, 2], [0, 1, 2])

print(qc.draw())
     ┌───┐          ┌─┐
q_0: ┤ H ├──■────■──┤M├──────
     └───┘┌─┴─┐  │  └╥┘┌─┐
q_1: ─────┤ X ├──┼───╫─┤M├───
          └───┘┌─┴─┐ ║ └╥┘┌─┐
q_2: ──────────┤ X ├─╫──╫─┤M├
               └───┘ ║  ║ └╥┘
c: 3/═════════════════╩══╩══╩═
                     0  1  2

GHZ-3 is one of the metadata attributes in Quantum Genesis. Pieces carrying that attribute were generated with circuits including this three-qubit pattern, producing correlations that shape the art's symmetry and structure.

How we used entanglement

Each Quantum Genesis piece is generated from a circuit running on real hardware. The measurement outcomes become the seed for the artwork — determining colors, shapes, positions, and symmetry. We used entanglement three ways:

a) Seed generation. The raw measurement bitstring (e.g. 01101011) gets hashed into a seed value. Entangled qubits produce correlated bits, so the seed isn't just random — it has internal structure that shows up as subtle symmetries and patterns.

# Simplified version of our seed generation
import hashlib

def quantum_seed(measurement_result):
    """Convert quantum measurement to art seed."""
    bitstring = measurement_result  # e.g., "01101011"
    hash_hex = hashlib.sha256(bitstring.encode()).hexdigest()
    return hash_hex  # 64-char hex seed for art generation

b) Correlated color selection. Entangled qubit pairs pick color palettes. Because entangled qubits always agree (or always disagree), the colors carry inherent harmony — not just random RGB values.

# Entangled pair → correlated color channels
# If qubits 0,1 are entangled (Bell pair):
#   Both 0 → cool palette (blues, purples)
#   Both 1 → warm palette (reds, oranges)
# The entanglement ensures palette consistency

c) Structural symmetry. GHZ-3 creates three-way correlations that map to three-fold visual symmetry. When all three qubits agree, the artwork develops triangular or hexagonal patterns. That's why GHZ-3 pieces look distinct from non-entangled ones.

Why entanglement produces better randomness

This sounds contradictory: if entangled qubits are correlated, doesn't that reduce randomness? Not exactly. Entanglement creates correlations between qubits, but each individual qubit's measurement is still perfectly random (50/50). The structure sits on top of the randomness.

For art, that's ideal. Pure random noise looks like static — no structure, no pattern. But correlated randomness produces emergent patterns: symmetries, color harmonies, structural motifs. It's the difference between white noise and music — both contain randomness, but music has correlations that make it interesting.

MethodRandomnessStructureArt quality
Classical PRNGDeterministic (fake)None (or algorithmic)Repetitive
Hadamard-only (no entanglement)True quantum randomNoneNoisy, unstructured
Hadamard + EntanglementTrue quantum randomQuantum correlationsStructured, organic

The entangled circuits produce art that's genuinely random — unpredictable, non-reproducible — but also visually coherent. That's the unique aesthetic of quantum-generated art.

Complete code examples

Ready-to-run examples you can try on IBM Quantum's free tier:

Example 1: Bell state statistics

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

# Bell state
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

# Run on simulator
sim = AerSimulator()
result = sim.run(qc, shots=10000).result()
counts = result.get_counts()

print("Bell state results:")
for outcome, count in sorted(counts.items()):
    pct = count / 100
    bar = "#" * int(pct / 2)
    print(f"  |{outcome}>: {count:5d} ({pct:.1f}%) {bar}")

# Expected: ~50% |00>, ~50% |11>, ~0% |01> and |10>

Example 2: GHZ-3 on real hardware

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.backend("ibm_fez")

# GHZ-3
qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.measure([0, 1, 2], [0, 1, 2])

# Transpile and run
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
transpiled = pm.run(qc)

sampler = SamplerV2(backend)
job = sampler.run([transpiled], shots=4096)
result = job.result()
counts = result[0].data.c.get_counts()

print("GHZ-3 results on ibm_fez:")
for outcome, count in sorted(counts.items(), key=lambda x: -x[1]):
    print(f"  |{outcome}>: {count}")

Example 3: entanglement-based seed (our NFT method)

import hashlib
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

def generate_quantum_seed(num_qubits=8, shots=4096):
    """Generate an art seed using entangled quantum circuits."""
    qc = QuantumCircuit(num_qubits, num_qubits)

    # Hadamard on all qubits
    for i in range(num_qubits):
        qc.h(i)

    # Entangle pairs (Bell pairs)
    for i in range(0, num_qubits - 1, 2):
        qc.cx(i, i + 1)

    # Additional GHZ-like entanglement across triplets
    for i in range(0, num_qubits - 2, 3):
        qc.cx(i, i + 2)

    # Measure
    qc.measure(range(num_qubits), range(num_qubits))

    # Run
    sim = AerSimulator()
    result = sim.run(qc, shots=shots).result()
    counts = result.get_counts()

    # Concatenate all measurement outcomes into one big string
    seed_input = ""
    for bitstring, count in sorted(counts.items()):
        seed_input += f"{bitstring}:{count},"

    # Hash to fixed-length seed
    seed = hashlib.sha256(seed_input.encode()).hexdigest()
    return seed

seed = generate_quantum_seed()
print(f"Quantum seed: {seed}")
# This seed drives the art generation algorithm

Quantum Genesis NFT #58 — Correlations built from a single CNOT gate

Entanglement stops being mysterious once you see it as code. It's a Hadamard, a CNOT, and a measurement. The magic isn't in the gates — it's in the correlations they create: correlations no classical computer can fake, which when fed through a generative algorithm produce genuinely unique art. That's what makes each Quantum Genesis piece one of a kind — not just randomness, but quantum randomness, with structure built in at the physics level.

Comments

Popular posts from this blog

Getting Your Collection Visible on OpenSea, Step by Step

Polygon versus Ethereum for an NFT contract, from the gas bills up

Quantum Error Correction, or Why Your Qubits Forget What They Were Doing