3. Mathematical Language
3.1Notation
Mathematics in this book is written as a programmer would write it: every symbol denotes a concrete object with a type. Scalars (float, complex) are lowercase Greek letters α, β, θ; vectors are lowercase Latin letters with arrows or kets, u, |ψ⟩; matrices are uppercase letters, U, H. The ket |ψ⟩ is a column vector of complex amplitudes; ⟨ψ| is its conjugate-transposed row vector. Throughout the book, one symbol means one thing per context, and cross-references like (37.6) point to where an idea returns. The fastest way to read quantum papers is to translate every expression into "what numpy object is this?" — a ket is a shape-(2ⁿ,) array, a gate is a shape-(2ⁿ, 2ⁿ) array, and a density matrix is a shape-(2ⁿ, 2ⁿ) Hermitian array. Notation is an API; learn its signature once and stop re-deriving it.
3.2Scalars
A scalar is a single number — the simplest mathematical object, and in quantum computing never quite as simple as float. Amplitudes are complex; probabilities are real and nonnegative; angles are real, usually in radians; counts of qubits and shots are integers. Each has different arithmetic rules and different failure modes, so the engineer's habit is to ask "scalar of what type?" before multiplying. Probabilities must sum to 1 (over all outcomes); amplitudes must instead satisfy a normalization constraint on the whole state vector. In code, keep probability-like quantities in floating point but track exact integers (shots, qubits) as Python ints. Watch out for the classic numerical trap: probabilities computed as |amplitude|² are always nonnegative in exact arithmetic, but floating-point rounding can produce values like −1e−17 that downstream code (a np.random.choice call, for instance) will reject.
3.3Real numbers
Real numbers ℝ form a continuum: between any two, infinitely many others. Physically measurable quantities — probabilities, energies, times — are real. But computers store only a finite subset: IEEE 754 floating-point numbers, roughly 2⁶⁴ distinct values per double. The gap between the mathematical continuum and the stored subset is where much numerical trouble lives (7.1). Two habits to build now. First, never test floating-point results for exact equality; compare with a tolerance like abs(a - b) < 1e-12. Second, remember that "real" claims in physics — a probability is nonnegative, a Hamiltonian is Hermitian — are exactly the properties your numerics should preserve or verify, not assume. In quantum computing, measurement outcomes are real numbers, but the machinery producing them runs on complex arithmetic; the real numbers are the interface, the complex ones the engine.
3.4Complex numbers
A complex number z = a + bi has a real part a and imaginary part b, with i² = −1. In Python this is a native type: complex(3, 4), with .real, .imag, and abs(z) built in. Complex numbers add componentwise and multiply by the distributive rule; division is defined by multiplying by the conjugate. Geometrically, multiplication rotates and scales — this single fact explains why quantum amplitudes are complex: phases must be able to rotate continuously so that amplitudes can cancel (interference). Real amplitudes could only reinforce or attenuate, never cancel completely. Every amplitude in this book is a complex number; every probability is |z|², real by construction. There is nothing mystical here — numpy handles complex128 natively, and you will manipulate thousands of them per simulation without ever seeing i "in nature".
import numpy as np
z1, z2 = 1 + 2j, 3 - 1j
print(z1 * z2) # (5+5j): multiply like polynomials in i
print(np.abs(z1), np.angle(z1)) # magnitude sqrt(5), phase atan2(2,1)3.5Complex conjugation
The conjugate of z = a + bi is z* = a − bi: flip the sign of the imaginary part, reflect across the real axis. Conjugation is the bridge between complex amplitudes and real probabilities: |z|² = z·z* = a² + b². In numpy, np.conj(z) or z.conj(). Three rules you will use constantly: the conjugate of a product is the product of conjugates, (zw)* = z*w*; the conjugate of a sum is the sum of conjugates; and a number equals its own conjugate exactly when it is real. Conjugation appears in quantum computing in two load-bearing places: the bra ⟨ψ| is the conjugate transpose of the ket |ψ⟩, and Hermitian matrices (4.9) — the quantum analogue of real symmetric matrices, used for observables and Hamiltonians — are defined by the condition H = H†, where † means conjugate transpose. Forgetting to conjugate is among the most common simulation bugs.
3.6Magnitude and phase
Every nonzero complex number has a magnitude r = |z| = √(a² + b²) and a phase θ = atan2(b, a), the angle counterclockwise from the positive real axis. Magnitude is "how much", phase is "in which rotational direction". Quantum mechanics cares about this split precisely: the Born rule depends only on magnitudes (probability = |amplitude|²), yet interference depends entirely on relative phases — two amplitudes of equal magnitude can reinforce (phase difference 0) or cancel completely (phase difference π). This is why a global phase — multiplying the whole state by e^{iφ} — is unobservable, while relative phases between basis components are the actual payload of gates like S and T. In code, np.abs and np.angle extract the two parts; phases wrap at ±π, so compare phases modulo 2π, never directly.
3.7Euler's formula
Euler's formula, e^{iθ} = cos θ + i·sin θ, states that the exponential of an imaginary number travels the unit circle. It is the single most useful identity in this book because it unifies the two previous sections: e^{iθ} has magnitude exactly 1 and phase exactly θ, so any complex number factors as z = r·e^{iθ} — a scaling times a rotation. Consequences used throughout: multiplying by e^{iθ} rotates a vector's phase without changing its length (this is what phase gates do physically); cos θ and sin θ are the even and odd parts of e^{iθ}; and e^{iπ} = −1, the famous identity, is just the point antipodal on the circle. When you later see U = e^{iHt} (time evolution under a Hamiltonian, 4.20), read it as "rotate phase continuously at a rate set by H". Euler's formula is the dictionary between algebra and geometry.
3.8Polar representation
Combining magnitude and phase: z = r·e^{iθ}, with r ≥ 0 the radius and θ the angle. This is the complex plane's polar coordinates, and it is the natural representation for amplitudes because quantum operations act on the two parts separately — unitary gates preserve r (norm) and manipulate θ (phase). numpy exposes the round trip directly. Practical warnings from engineering: the phase θ is only defined modulo 2π, so "the" phase of an amplitude is a representative, not a unique value; np.angle(0) is undefined, matching the physical fact that a zero amplitude carries no phase; and accumulating many rotations numerically can let the magnitude drift off 1, which is why simulators re-normalize states periodically. When debugging a circuit, print amplitudes in polar form — a phase pattern is far more readable than raw real-and-imaginary pairs.
3.9Vectors
A vector is an ordered list of numbers that can be added together and scaled: u + v adds componentwise; c·u scales every component. Geometrically an arrow; computationally a 1-D numpy array. In quantum computing, a state of n qubits is a vector of 2ⁿ complex amplitudes, so "the state" and "the vector" are the same object throughout this book. The operations that matter are few: linear combination (3.13), inner product (3.16), and matrix multiplication (4.4). Python gives you all three with numpy. Build the habit now of checking shapes before every operation — psi.shape should be (2**n,) for a state vector, and most "quantum" bugs are ordinary shape bugs underneath. A vector is normalized when the sum of squared magnitudes of its components equals 1; simulators must maintain this invariant explicitly, since floating-point drift erodes it slowly.
3.10Vector spaces
A vector space is a set of vectors closed under addition and scalar multiplication: sums and scaled versions of members stay inside. This closure is what makes the abstraction useful — you can do algebra without leaving the space. The complex vector space ℂⁿ is n-tuples of complex numbers; a single qubit's states live in ℂ², two qubits in ℂ⁴ (via tensor products, chapter 5), and n qubits in ℂ^{2ⁿ}. Two subspaces matter constantly in quantum computing: the set of valid states (normalized vectors, the unit sphere) and the set spanned by a code's logical states in error correction (chapter 37). Note what "space" buys you: dimension (3.15) tells you the degrees of freedom, a basis (3.11) tells you the coordinate system, and the whole geometric vocabulary — lengths, angles, projections — transfers from ℝ² to ℂ^{2ⁿ} unchanged.
3.11Bases
A basis is a minimal set of vectors from which every other vector in the space can be built as a linear combination. The standard basis of ℂ² is |0⟩ = (1, 0) and |1⟩ = (0, 1); any qubit state is α|0⟩ + β|1⟩. Bases are choices, not facts: the same state vector has different coordinates in different bases, and changing basis (4.14) is a central operation — the Hadamard gate is exactly a change of basis between the computational basis and the ± basis. In quantum computing, "measuring in a basis" means choosing which decomposition the measurement reads out, and algorithms work largely by choosing bases strategically. Rule to internalize: the vector is the reality; the basis is the description. Two engineers describing the same qubit in different bases are not disagreeing — they are printing the same array against different axes.
3.12Coordinates
Given a basis, any vector v equals a unique linear combination c₁b₁ + c₂b₂ + … + cₙbₙ, and the coefficients (c₁, …, cₙ) are v's coordinates in that basis — the actual array you store and compute with. This is the deepest "just an array" statement in the chapter: psi in numpy is nothing but the coordinate list of an abstract vector in the computational basis. Coordinates are basis-dependent — the same qubit |+⟩ has coordinates (1/√2, 1/√2) in the computational basis and (1, 0) in the ± basis. Every time you extract coefficients with an inner product ⟨bᵢ|v⟩ (3.16), you are doing exactly what np.vdot(basis_vector, psi) does. The engineering discipline: every state array in your code should have a documented basis. Undocumented basis assumptions are the coordinate-system bugs of quantum software — silent, and found only when statistics come out wrong.
3.13Linear combinations
A linear combination of vectors v₁, …, vₖ is c₁v₁ + … + cₖvₖ for scalar coefficients cᵢ. This is the only way quantum states are ever built from other states, so it is worth internalizing beyond the formula. A superposition (1.2) is a linear combination with complex coefficients, where |cᵢ|² is the probability of outcome i at measurement. The set of all linear combinations of some vectors is their span — a subspace — and "the state space is spanned by the computational basis" is the sentence every quantum operation ultimately manipulates. In numpy, one line: c1*v1 + c2*v2. For states, coefficients satisfy Σ|cᵢ|² = 1; gates update them all simultaneously by matrix multiplication. Everything visual — Bloch spheres, interference patterns, circuit diagrams — is a picture of coefficients moving under linear combinations.
3.14Linear independence
Vectors are linearly independent when no one of them can be written as a linear combination of the others — equivalently, c₁v₁ + … + cₖvₖ = 0 forces all cᵢ = 0. Independence is the precise meaning of a basis being "minimal": a basis for an n-dimensional space is exactly a set of n independent vectors that spans it. Why an engineer cares: independent states carry independent information; redundant (dependent) states do not. In error correction, the logical codewords of a code must be linearly independent or the code cannot distinguish them. In machine learning on quantum data, a feature set of states that is linearly dependent has fewer degrees of freedom than it appears. Test numerically by checking that the matrix with the vectors as columns has full rank: np.linalg.matrix_rank(A) == k. Rank is the workhorse answer to "how many genuinely independent things do I have?"
3.15Dimension
The dimension of a vector space is the number of vectors in any basis — a fixed property of the space, independent of which basis you pick. This count is the book's central exponential: one qubit lives in dimension 2, n qubits in dimension 2ⁿ (chapter 5), and every classical simulation of a quantum computer pays memory and time proportional to that dimension. Dimension counting is also a design tool. A single qubit's normalized state has dimension 2 but only 2 real degrees of freedom after normalization and global-phase removal (the Bloch sphere is 2-dimensional — a sphere's surface). Two qubits: dimension 4. Three hundred: dimension 2³⁰⁰, more than atoms in the observable universe — the sentence that explains why classical simulation (7.12) fails and why quantum hardware might matter. When you meet any new quantum object, the first question is always: what is its dimension?
3.16Inner products
The inner product generalizes the dot product to complex vectors: for u and v it is ⟨u|v⟩ = Σᵢ uᵢ*·vᵢ — note the conjugate on the first vector, which makes ⟨v|v⟩ = Σ|vᵢ|² a nonnegative real number. This single number is the most-used operation in quantum computing: ⟨ψ|ψ⟩ tests normalization; ⟨φ|ψ⟩ is the amplitude of finding ψ's measurement outcome in φ's direction, and |⟨φ|ψ⟩|² is that outcome's probability; ⟨φ|ψ⟩ = 0 (orthogonality, 3.18) means perfectly distinguishable states. In numpy, np.vdot(u, v) implements the conjugating convention correctly — plain u @ v does not conjugate, a classic bug. Geometrically the inner product measures alignment: how much of v lies along u. Every fidelity computation, every overlap check, every Born-rule probability in this book is an inner product wearing different notation.
import numpy as np
psi = np.array([1, 1j]) / np.sqrt(2)
phi = np.array([1, -1j]) / np.sqrt(2)
print(np.vdot(psi, psi)) # (1+0j): normalized
print(np.vdot(psi, phi)) # (0+1j): orthogonal, |overlap|^2 = 03.17Norms
A norm measures a vector's length: ‖v‖ = √⟨v|v⟩ for the standard (Euclidean, or L2) norm — the square root of the sum of squared magnitudes. Norms obey the triangle inequality (‖u + v‖ ≤ ‖u‖ + ‖v‖) and scale linearly (‖c·v‖ = |c|·‖v‖). The state-normalization condition of quantum mechanics is exactly ‖ψ‖ = 1, so the entire state space is the unit sphere of ℂ^{2ⁿ}. Other norms appear in engineering contexts: the L1 norm (sum of absolute values) bounds total variation, and matrix norms (operator norm, Frobenius norm) measure gate errors — "this pulse implements U with error ≤ ε" means ‖U_actual − U_target‖ ≤ ε for a specific norm the paper must name. In code: np.linalg.norm(psi) for states, np.linalg.norm(A, 2) for the operator norm. Whenever a fidelity or error is quoted without a norm, the number is undefined.
3.18Orthogonality
Vectors u and v are orthogonal when ⟨u|v⟩ = 0 — geometrically perpendicular, probabilistically decisive. In quantum mechanics, orthogonality is the mathematical form of distinguishability: two orthogonal states can be told apart by a measurement with certainty (⟨0|1⟩ = 0), while non-orthogonal states cannot, ever, by any measurement — the no-cloning theorem and quantum cryptography both rest on this. The computational basis states are mutually orthogonal, which is why they are classical, reliably readable outcomes; superposed states lean between them. Orthogonality also defines independence of measurement outcomes and underlies projection: the projector |φ⟩⟨φ| extracts the component of a state along φ, and annihilates everything orthogonal to it. Quick numerical check: abs(np.vdot(u, v)) < 1e-12. When an algorithm needs outcomes to be reliably separable, it is arranging orthogonality.
3.19Orthonormal bases
An orthonormal basis is a basis whose vectors are mutually orthogonal and individually unit-length: ⟨bᵢ|bⱼ⟩ = δᵢⱼ (1 if i = j, else 0). The computational basis |0⟩, |1⟩, …, |2ⁿ−1⟩ is orthonormal, and orthonormality makes the formulas of this chapter collapse to their simplest forms: coordinates are just inner products, cᵢ = ⟨bᵢ|ψ⟩; norms are the sum of squared coordinates; and unitary matrices (4.10) are exactly the transformations that carry one orthonormal basis to another. This is why quantum formalism is written in orthonormal bases by default — every measurement in a basis, every gate, every code's logical states assumes it. Practical note: numerically, orthonormality drifts. Simulators periodically re-orthonormalize (e.g. via QR decomposition, np.linalg.qr) or re-normalize, because roundoff slowly rotates basis vectors off exact orthogonality.
3.20Bra-ket notation
Dirac's bra-ket notation packages everything above into quantum computing's native syntax. A ket |ψ⟩ is a column vector; a bra ⟨φ| is the conjugate-transposed row vector; juxtaposition ⟨φ|ψ⟩ is the inner product, ⟨φ|ψ⟩ = Σᵢ φᵢ*·ψᵢ. The outer product |φ⟩⟨ψ| is a matrix — the projector |φ⟩⟨φ| being the special case that extracts the component along φ. The notation is deliberately self-documenting: ⟨bᵢ|ψ⟩ reads as "the i-th coordinate of ψ", and Σᵢ |bᵢ⟩⟨bᵢ| = I reads as "the basis tiles the identity". You can translate mechanically to numpy: ket → 1-D array, bra → np.conj, inner product → np.vdot, outer product → np.outer(conj(b), a). Once bra-ket becomes "rows and columns with conjugation where the bar is", quantum papers stop being notation-heavy and start being readable.