The Quantum Engineer

L. Python Numerical-Computing Reference

Quantum-specific numpy habits (Ch. 17's lessons compressed). dtype: complex128 everywhere, declared (np.zeros(2**n, dtype=complex)); silent upcasting is bug #1. Tensor products: np.kron(A, B) — decide endianness once, test it, document it (Ch. 17.6). Fast gate application: reshape state to (2,)*n, np.tensordot(gate, state, axes=([1],[k])) + np.moveaxis (Ch. 17.7). Measurement: probs = np.abs(state)2; np.random.default_rng(seed).choice(2n, p=probs). Linear algebra: np.linalg.eigh (Hermitian — sorted real eigenvalues; use for observables), eigvalsh (values only), norm, vdot (conjugates first arg — np.vdot(state, O @ state) is ⟨ψ|O|ψ⟩). Sparse: scipy.sparse for Pauli-string Hamiltonians (n ≤ ~16 dense, sparse beyond); SparsePauliOp in Qiskit for the same. Performance: profile first (cProfile); the bottleneck is never where intuition says (Ch. 17.11); vectorize over shots, batch over circuits. Reproducibility: rng = np.random.default_rng(seed) objects passed explicitly, never global np.random. Testing: np.allclose(a, b) (and rtol/atol set consciously); property tests with fixed seeds.