Quantum Computing Foundations: The Theory Behind QRL

A comprehensive guide to the quantum mechanics you need to understand relations-first quantum programming. From superposition to entanglement entropy.
quantum computing
theory
physics
tutorial
Author

David Coldeira

Published

December 13, 2025

Why This Post Exists

This is the prerequisite for understanding Stage Zero: Programming Quantum Reality Through Relations, Not Gates.

If you’re coming from classical programming and want to understand quantum computing—not just run circuits, but actually understand what’s happening—this guide is for you.

We’ll cover the physics that makes quantum computing possible:

  1. Quantum States - What |0⟩, |1⟩, and superposition actually mean
  2. Measurement & Basis - Why “measuring” depends on what question you ask
  3. Entanglement - The fundamental resource of quantum computing
  4. Correlations - Why entangled systems show different correlations in different bases
  5. Entanglement Entropy - How to quantify quantum correlation
  6. Basis Transformations - The mathematics behind multi-basis measurements

By the end, you’ll understand: - Why quantum computing is different from probabilistic classical computing - Why entanglement is a relation between systems, not a property of systems - Why measurement basis matters (the foundation of QRL’s QuantumQuestion abstraction) - The physics that QRL Stage Zero implements

Let’s start from first principles.


Prerequisites

pip install qiskit qiskit-aer matplotlib numpy
# Import libraries
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import Aer
from qiskit.visualization import plot_histogram, circuit_drawer
from qiskit.quantum_info import Statevector, partial_trace, entropy
import matplotlib.pyplot as plt
import numpy as np

%matplotlib inline
print("✅ Qiskit imported successfully!")

Part 1: Quantum States - The Foundation

Classical Bits vs Qubits

A classical bit is either 0 or 1. Period.

A qubit is a quantum system that, when measured, gives 0 or 1—but before measurement, it exists in a superposition of both states.

The Mathematical Representation

We write quantum states using Dirac notation (bra-ket notation):

\[|0\rangle = \begin{pmatrix} 1 \\ 0 \end{pmatrix}, \quad |1\rangle = \begin{pmatrix} 0 \\ 1 \end{pmatrix}\]

These are basis states (like unit vectors in 2D space).

A general qubit state is:

\[|\psi\rangle = \alpha|0\rangle + \beta|1\rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}\]

where \(\alpha, \beta\) are complex numbers satisfying \(|\alpha|^2 + |\beta|^2 = 1\) (normalization).

Why Complex Numbers?

Classical probability uses real numbers: P(heads) = 0.5, P(tails) = 0.5.

Quantum mechanics uses probability amplitudes (complex numbers). The probability of measuring a state is:

\[P(0) = |\alpha|^2, \quad P(1) = |\beta|^2\]

The complex phase matters for interference—the key to quantum algorithms.

Let’s see this in code:

# Create a qubit in state |0⟩
state_0 = Statevector([1, 0])
print("State |0⟩:")
print(state_0)
print(f"Probabilities: P(0) = {abs(state_0[0])**2}, P(1) = {abs(state_0[1])**2}")

print("\n" + "="*50 + "\n")

# Create a qubit in state |1⟩
state_1 = Statevector([0, 1])
print("State |1⟩:")
print(state_1)
print(f"Probabilities: P(0) = {abs(state_1[0])**2}, P(1) = {abs(state_1[1])**2}")

print("\n" + "="*50 + "\n")

# Create a superposition: (|0⟩ + |1⟩)/√2
state_plus = Statevector([1/np.sqrt(2), 1/np.sqrt(2)])
print("State |+⟩ = (|0⟩ + |1⟩)/√2:")
print(state_plus)
print(f"Probabilities: P(0) = {abs(state_plus[0])**2:.3f}, P(1) = {abs(state_plus[1])**2:.3f}")

Key insight: The state \(|+\rangle\) is NOT “50% chance of being 0, 50% chance of being 1.” It’s both simultaneously until measured. The probabilities only emerge upon measurement.


Part 2: Superposition - Both At Once

The Hadamard Gate: Creating Superposition

The Hadamard gate (H) transforms basis states into superposition:

\[H|0\rangle = \frac{|0\rangle + |1\rangle}{\sqrt{2}} = |+\rangle\] \[H|1\rangle = \frac{|0\rangle - |1\rangle}{\sqrt{2}} = |-\rangle\]

Mathematically, H is a matrix:

\[H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\]

Applying H to \(|0\rangle\):

\[H|0\rangle = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 \\ 1 \end{pmatrix} = |+\rangle\]

# Create a circuit with Hadamard gate
qc = QuantumCircuit(1, 1)
qc.h(0)  # Apply Hadamard to qubit 0
qc.measure(0, 0)

# Draw the circuit
print("Circuit:")
display(qc.draw('mpl'))

# Simulate
simulator = Aer.get_backend('qasm_simulator')
job = simulator.run(qc, shots=1000)
counts = job.result().get_counts()

print("\nMeasurement results after H gate:")
print(counts)
plot_histogram(counts)

Key insight: The state doesn’t change, but the measurement basis does. This is contextuality—a fundamental feature of quantum mechanics that QRL makes explicit.


Part 5: Entanglement - Relations Between Systems

What is Entanglement?

Entanglement is when the quantum state of a multi-particle system cannot be factored into independent single-particle states.

Separable (not entangled): \[|\psi\rangle = |0\rangle_A \otimes |0\rangle_B = |00\rangle\] Alice has \(|0\rangle\), Bob has \(|0\rangle\). These are independent.

Entangled (Bell state): \[|\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}\] You cannot write this as \(|\psi\rangle_A \otimes |\phi\rangle_B\). Alice and Bob’s states are fundamentally correlated.

Creating a Bell State

The standard recipe: 1. Start with \(|00\rangle\) 2. Apply Hadamard to first qubit: \(\frac{|0\rangle + |1\rangle}{\sqrt{2}} \otimes |0\rangle = \frac{|00\rangle + |10\rangle}{\sqrt{2}}\) 3. Apply CNOT (controlled-NOT): \(\frac{|00\rangle + |11\rangle}{\sqrt{2}} = |\Phi^+\rangle\)

# Measure |0⟩ in Z basis (default)
qc_z = QuantumCircuit(1, 1)
# Qubit starts in |0⟩
qc_z.measure(0, 0)

print("Measuring |0⟩ in Z basis:")
job_z = simulator.run(qc_z, shots=1000)
counts_z = job_z.result().get_counts()
print(counts_z, "← 100% outcome 0")

print("\n" + "="*50 + "\n")

# Measure |0⟩ in X basis (apply H before measurement to rotate basis)
qc_x = QuantumCircuit(1, 1)
# Qubit starts in |0⟩
qc_x.h(0)  # Rotate from Z basis to X basis
qc_x.measure(0, 0)

print("Measuring |0⟩ in X basis:")
job_x = simulator.run(qc_x, shots=1000)
counts_x = job_x.result().get_counts()
print(counts_x, "← ~50% outcome 0, ~50% outcome 1")

# Visualize both
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
plot_histogram(counts_z, ax=ax1, title="Z Basis Measurement of |0⟩")
plot_histogram(counts_x, ax=ax2, title="X Basis Measurement of |0⟩")
plt.tight_layout()
plt.show()

Result: ~50% |00⟩ and ~50% |11⟩. Never |01⟩ or |10⟩.

Why This is Weird

Before measurement: - Alice’s qubit is not “definitely 0” or “definitely 1” - Bob’s qubit is not “definitely 0” or “definitely 1” - But they are perfectly correlated

When Alice measures her qubit: - If she gets 0, Bob will definitely get 0 - If she gets 1, Bob will definitely get 1

This correlation exists regardless of how far apart they are. This is quantum entanglement.

In QRL, this is why entanglement is a QuantumRelation—it’s fundamentally about the relationship between systems, not properties of individual systems.


Part 6: Measuring Entangled Systems - When Basis Matters

Same Basis: Perfect Correlation

If Alice and Bob both measure in Z basis on a Bell state: - 100% correlation (always get same result)

We just saw this above.

# Create Bell state circuit
qc_bell = QuantumCircuit(2, 2)
qc_bell.h(0)           # Hadamard on qubit 0
qc_bell.cx(0, 1)       # CNOT from qubit 0 to 1
qc_bell.measure([0, 1], [0, 1])

print("Bell State Circuit:")
display(qc_bell.draw('mpl'))

# Simulate
job_bell = simulator.run(qc_bell, shots=1000)
counts_bell = job_bell.result().get_counts()

print("\nMeasurement results:")
print(counts_bell)
plot_histogram(counts_bell)

Different Basis: Random Correlation

If Alice measures in Z basis and Bob measures in X basis: - ~50% correlation (random, uncorrelated)

This is critical for understanding QRL Stage Zero! When measuring entangled systems in different bases, the correlation should be approximately 50%, not 100%. This is a key distinction between same-basis and cross-basis measurements.

# Create Bell state and measure both in Z basis
qc_zz = QuantumCircuit(2, 2)
qc_zz.h(0)
qc_zz.cx(0, 1)
# Measure both in Z basis (default)
qc_zz.measure([0, 1], [0, 1])

job_zz = simulator.run(qc_zz, shots=1000)
counts_zz = job_zz.result().get_counts()

print("Bell state, both measured in Z basis:")
print(counts_zz)

# Compute correlation
same_results = counts_zz.get('00', 0) + counts_zz.get('11', 0)
correlation_zz = same_results / 1000
print(f"\nCorrelation (same outcome rate): {correlation_zz*100:.1f}%")

This is the physics that QRL Stage Zero implements correctly.

The measurement basis changes the physics. This is why QuantumQuestion in QRL has an explicit basis parameter—because quantum mechanics is contextual.


Part 7: Entanglement Entropy - Quantifying Correlation

How Do We Measure “How Entangled” Two Systems Are?

For a pure bipartite state (2 subsystems), we use entanglement entropy (also called von Neumann entropy).

The Math: Schmidt Decomposition

Any 2-qubit pure state can be written as:

\[|\psi\rangle = \sum_i \lambda_i |i\rangle_A \otimes |i\rangle_B\]

where \(\lambda_i \geq 0\) are the Schmidt coefficients (singular values).

The entanglement entropy is:

\[S = -\sum_i \lambda_i^2 \log_2(\lambda_i^2)\]

Interpretation: - \(S = 0\): No entanglement (separable state) - \(S = 1\): Maximal entanglement (Bell state) - \(0 < S < 1\): Partial entanglement

Examples

Separable state: \(|00\rangle\) - Schmidt decomposition: \(\lambda_1 = 1, \lambda_2 = 0\) - \(S = -(1^2 \log_2(1^2) + 0) = 0\)

Bell state: \(\frac{|00\rangle + |11\rangle}{\sqrt{2}}\) - Schmidt decomposition: \(\lambda_1 = \lambda_2 = \frac{1}{\sqrt{2}}\) - \(S = -(\frac{1}{2}\log_2(\frac{1}{2}) + \frac{1}{2}\log_2(\frac{1}{2})) = 1\)

Let’s compute this in Qiskit:

# Create Bell state, Alice measures Z, Bob measures X
qc_zx = QuantumCircuit(2, 2)
qc_zx.h(0)
qc_zx.cx(0, 1)

# Alice measures in Z basis (default)
qc_zx.measure(0, 0)

# Bob measures in X basis (apply H first to rotate basis)
qc_zx.h(1)
qc_zx.measure(1, 1)

job_zx = simulator.run(qc_zx, shots=1000)
counts_zx = job_zx.result().get_counts()

print("Bell state, Alice: Z basis, Bob: X basis:")
print(counts_zx)

# Compute correlation
same_results = counts_zx.get('00', 0) + counts_zx.get('11', 0)
correlation_zx = same_results / 1000
print(f"\nCorrelation (same outcome rate): {correlation_zx*100:.1f}%")

# Compare both
print("\n" + "="*50)
print(f"Z-Z correlation: {correlation_zz*100:.1f}% (perfect correlation)")
print(f"Z-X correlation: {correlation_zx*100:.1f}% (random, uncorrelated)")
print("="*50)

This is the math that QRL Stage Zero computes automatically.

Every QuantumRelation object in QRL has an entanglement_entropy field that’s computed via Schmidt decomposition. This lets you see the entanglement structure of your quantum program.


Part 8: Basis Transformations - The Key to Multi-Basis Measurement

The Problem

Quantum circuits naturally measure in the Z (computational) basis. But what if we want to measure in the X basis? Or an arbitrary basis at angle \(\theta\)?

The Solution: Unitary Rotation

To measure in basis defined by unitary \(U\), we: 1. Apply \(U^\dagger\) (conjugate transpose) to rotate state to computational basis 2. Measure in computational basis 3. Interpret result as measurement in original basis

Example: X basis measurement

The X basis has eigenstates \(|+\rangle\) and \(|-\rangle\). To measure in X basis: - Apply \(H\) (Hadamard) before measurement - \(H\) is its own inverse: \(H^\dagger = H\) - \(H\) rotates X basis to Z basis

The Mathematics

For a state \(|\psi\rangle\) and measurement basis \(U\):

\[|\psi'\rangle = U^\dagger |\psi\rangle\]

Then measure \(|\psi'\rangle\) in computational basis.

This is exactly what QRL Stage Zero implements in measurement.py:

def compute_subsystem_probabilities(state, basis, subsystem_idx, num_qubits):
    state_matrix = state.reshape(2, 2)
    
    # Apply U† to transform to measurement basis
    if not np.allclose(basis, np.eye(2)):
        if subsystem_idx == 0:
            state_matrix = basis.T.conj() @ state_matrix
        else:
            state_matrix = state_matrix @ basis.T.conj()
    
    # Compute probabilities in transformed basis
    # ...

Let’s verify this works:

# Separable state: |00⟩
separable_state = Statevector([1, 0, 0, 0])  # |00⟩
rho_A_sep = partial_trace(separable_state, [1])  # Trace out qubit 1
S_sep = entropy(rho_A_sep, base=2)

print("Separable state |00⟩:")
print(f"Entanglement entropy: {S_sep:.6f}")

print("\n" + "="*50 + "\n")

# Bell state: (|00⟩ + |11⟩)/√2
bell_state = Statevector([1/np.sqrt(2), 0, 0, 1/np.sqrt(2)])  # |Φ+⟩
rho_A_bell = partial_trace(bell_state, [1])  # Trace out qubit 1
S_bell = entropy(rho_A_bell, base=2)

print("Bell state (|00⟩ + |11⟩)/√2:")
print(f"Entanglement entropy: {S_bell:.6f}")

print("\n" + "="*50 + "\n")

# Partially entangled state: 0.9|00⟩ + 0.436|11⟩
partial_state = Statevector([0.9, 0, 0, 0.436])
partial_state = partial_state / np.linalg.norm(partial_state.data)  # Normalize
rho_A_partial = partial_trace(partial_state, [1])
S_partial = entropy(rho_A_partial, base=2)

print("Partially entangled state:")
print(f"Entanglement entropy: {S_partial:.6f}")

print("\n" + "="*50)
print("Summary:")
print(f"  Separable:  S = {S_sep:.3f} (no entanglement)")
print(f"  Partial:    S = {S_partial:.3f} (some entanglement)")
print(f"  Maximal:    S = {S_bell:.3f} (Bell state)")
print("="*50)

This is textbook quantum mechanics (Nielsen & Chuang, Chapter 2), but it’s the foundation of QRL’s multi-basis measurement system.


Part 9: Why This Matters for QRL

Everything we’ve covered is implemented in QRL Stage Zero:

1. Quantum States → QuantumRelation

bell_pair = program.entangle(alice, bob)
# bell_pair.state is the 4D state vector

2. Superposition → First-Class

# Superposition is natural in QRL
system = program.create_system()  # |0⟩
# In Stage 1+: apply_hadamard(system)  # |+⟩

3. Measurement Basis → QuantumQuestion

question_z = create_question(QuestionType.SPIN_Z)  # Z basis
question_x = create_question(QuestionType.SPIN_X)  # X basis

4. Contextual Measurement → ask(relation, question, perspective)

result = program.ask(bell_pair, question_z, perspective="alice")
# Explicitly: who asks, what they ask, what they ask it of

5. Entanglement Tracking → Automatic

print(bell_pair.entanglement_entropy)  # → 1.0 (maximal)

6. Basis Transformation → Built-In

# QRL automatically applies U† when you specify basis in QuantumQuestion
# You don't write the matrix multiplication—it's in the semantics

7. Cross-Basis Correlations → Correct Physics

# QRL correctly implements cross-basis measurement correlations:
alice_z = ask(bell_pair, SPIN_Z, subsystem=0, perspective="alice")
bob_x = ask(remaining, SPIN_X, subsystem=1, perspective="bob")
# Correlation: ~50% (as expected from quantum mechanics)

Summary: The Theory You Need

To understand QRL Stage Zero, you need to understand:

Quantum states are superpositions of basis states, not classical probabilities

Measurement collapses superposition and is contextual (basis-dependent)

Entanglement is a relation between systems, not a property of systems

Same-basis measurements of entangled systems show 100% correlation

Different-basis measurements of entangled systems show ~50% correlation

Entanglement entropy quantifies correlation via Schmidt decomposition

Basis transformations (\(U^\dagger\)) enable measurement in arbitrary bases

With this foundation, you can now understand why QRL’s abstractions—QuantumRelation, QuantumQuestion, Perspective—match the physics of quantum mechanics better than gate-based frameworks.


Next: Read Stage Zero

Now you’re ready: Stage Zero: Programming Quantum Reality Through Relations, Not Gates

You’ll see how all this theory translates into a working quantum programming language where: - Relations are first-class citizens - Questions (measurements) are explicit and contextual - Entanglement entropy is tracked automatically - The code reads like physics, not circuit design

Welcome to relations-first quantum programming.

# Define X basis matrix
H_matrix = np.array([[1, 1], [1, -1]]) / np.sqrt(2)

# State |0⟩
state_0 = np.array([1, 0])

# Transform to X basis: H† |0⟩
state_in_x_basis = H_matrix.T.conj() @ state_0

print("State |0⟩ transformed to X basis:")
print(state_in_x_basis)
print(f"\nThis is |+⟩ = (|0⟩ + |1⟩)/√2")
print(f"\nProbabilities when measured in computational basis:")
print(f"P(0) = {abs(state_in_x_basis[0])**2:.3f}")
print(f"P(1) = {abs(state_in_x_basis[1])**2:.3f}")
print("\n→ This means 50% + outcome, 50% - outcome in X basis")

This is textbook quantum mechanics (Nielsen & Chuang, Chapter 2), but it’s the foundation of QRL’s multi-basis measurement system.


Part 9: Why This Matters for QRL

Everything we’ve covered is implemented in QRL Stage Zero:

1. Quantum States → QuantumRelation

bell_pair = program.entangle(alice, bob)
# bell_pair.state is the 4D state vector

2. Superposition → First-Class

# Superposition is natural in QRL
system = program.create_system()  # |0⟩
# In Stage 1+: apply_hadamard(system)  # |+⟩

3. Measurement Basis → QuantumQuestion

question_z = create_question(QuestionType.SPIN_Z)  # Z basis
question_x = create_question(QuestionType.SPIN_X)  # X basis

4. Contextual Measurement → ask(relation, question, perspective)

result = program.ask(bell_pair, question_z, perspective="alice")
# Explicitly: who asks, what they ask, what they ask it of

5. Entanglement Tracking → Automatic

print(bell_pair.entanglement_entropy)  # → 1.0 (maximal)

6. Basis Transformation → Built-In

# QRL automatically applies U† when you specify basis in QuantumQuestion
# You don't write the matrix multiplication—it's in the semantics

7. Cross-Basis Correlations → Correct Physics

# QRL correctly implements cross-basis measurement correlations:
alice_z = ask(bell_pair, SPIN_Z, subsystem=0, perspective="alice")
bob_x = ask(remaining, SPIN_X, subsystem=1, perspective="bob")
# Correlation: ~50% (as expected from quantum mechanics)

Summary: The Theory You Need

To understand QRL Stage Zero, you need to understand:

Quantum states are superpositions of basis states, not classical probabilities

Measurement collapses superposition and is contextual (basis-dependent)

Entanglement is a relation between systems, not a property of systems

Same-basis measurements of entangled systems show 100% correlation

Different-basis measurements of entangled systems show ~50% correlation

Entanglement entropy quantifies correlation via Schmidt decomposition

Basis transformations (\(U^\dagger\)) enable measurement in arbitrary bases

With this foundation, you can now understand why QRL’s abstractions—QuantumRelation, QuantumQuestion, Perspective—match the physics of quantum mechanics better than gate-based frameworks.


Next: Read Stage Zero

Now you’re ready: Stage Zero: Programming Quantum Reality Through Relations, Not Gates

You’ll see how all this theory translates into a working quantum programming language where: - Relations are first-class citizens - Questions (measurements) are explicit and contextual - Entanglement entropy is tracked automatically - The code reads like physics, not circuit design

Welcome to relations-first quantum programming.