Quant Memo
Core

Linear Solvers and the Condition Number

Solving a system of linear equations numerically can amplify tiny input errors into huge output errors — how much amplification depends on a single number, the condition number of the matrix.

Prerequisites: Catastrophic Cancellation

A portfolio optimizer solves Ax=bAx = b for optimal weights, where AA is a covariance matrix. The inputs — estimated covariances — are never known exactly; there's always some noise from finite sample size. For a well-behaved matrix, small noise in the inputs produces small changes in the solution. For an ill-conditioned matrix, the same tiny input noise can blow up into wildly different, useless output weights — and this happens even with perfect floating-point arithmetic, purely because of the matrix's own geometry.

The condition number of a matrix measures how much it can amplify relative error: a solver's output error is roughly bounded by the input error times the condition number. A condition number near 1 means the system is well-behaved; a condition number of 101010^{10} means a 10610^{-6} relative error in your inputs can become a 10410^4 relative error in your answer.

What the condition number measures

For a matrix AA, the condition number is κ(A)=AA1\kappa(A) = \|A\| \cdot \|A^{-1}\| — informally, the ratio of the matrix's largest to smallest "stretch factor" (for a symmetric matrix, the ratio of its largest to smallest eigenvalue). A matrix that stretches some directions enormously and barely touches others has a huge condition number: information about the barely-touched direction is nearly invisible in AxAx, so recovering it by inverting is extremely sensitive to any noise in bb or AA itself.

δxxκ(A)×δbb\frac{\|\delta x\|}{\|x\|} \lesssim \kappa(A) \times \frac{\|\delta b\|}{\|b\|}

In words: the relative error in your solution is bounded by the condition number times the relative error in your input. A condition number of 100 means input noise gets amplified up to 100x; a condition number of 101010^{10} means the solution can be almost entirely noise.

well-conditioned (kappa ~ 3) ill-conditioned (kappa ~ 200) roughly round — invertible reliably nearly flat — short-axis info is lost in noise
An ill-conditioned matrix squashes one direction almost to zero — inverting it means trying to recover that squashed direction from almost nothing, so any noise dominates the answer along that axis.

Worked example

Take a nearly-singular 2x2 matrix A=(1111.0001)A = \begin{pmatrix} 1 & 1 \\ 1 & 1.0001 \end{pmatrix}, with b=(22.0001)b = \begin{pmatrix} 2 \\ 2.0001 \end{pmatrix}. The exact solution is x=(1,1)x = (1, 1). Now perturb bb by a tiny amount, to b=(22.0002)b' = \begin{pmatrix} 2 \\ 2.0002 \end{pmatrix} — a relative change of about 5×1055 \times 10^{-5}. Solving Ax=bAx' = b' gives x(0,2)x' \approx (0, 2) — the solution moved by 100%, not 0.005%. This matrix's condition number is roughly 40,000, and the amplification in the example (relative output change divided by relative input change) is consistent with that order of magnitude: a tiny nudge in the input became a completely different answer.

What this means in practice

Ill-conditioned systems show up whenever a quant inverts a covariance matrix built from highly correlated assets (two ETFs tracking nearly the same index), fits a regression with near-collinear features, or calibrates a model where two parameters trade off against each other almost perfectly. np.linalg.cond(A) reports the condition number directly; a common rule of thumb is to be cautious above 10410^4-10610^6 and treat results as unreliable well before reaching the machine-precision limit (around 101610^{16} for double precision, where the matrix is numerically singular). Standard fixes include ridge regularization (adding a small multiple of the identity before inverting), dropping near-duplicate features, or using a solver method (like SVD-based pseudo-inverse) that degrades more gracefully than direct inversion.

A small residual Axb\|Ax - b\| does not mean xx is close to the true solution when AA is ill-conditioned — the solver can report a solution that fits the equations almost perfectly while being far from the answer you'd get with slightly different, equally plausible input data. Checking the condition number is necessary precisely because the usual sanity check (does it satisfy the equations) can mislead you.

Related concepts

Practice in interviews

Further reading

  • Trefethen & Bau, Numerical Linear Algebra (lecture 12)
  • Higham, Accuracy and Stability of Numerical Algorithms
ShareTwitterLinkedIn