Schur complement / Sparsity

Bundle adjustment looks intractable at first glance: a modest map with mm keyframes and nn points has 6m+3n6m + 3n unknowns, and nn is easily in the hundreds of thousands. What makes it practical is that the problem is sparse, and the Schur complement is the trick that exploits that sparsity.

Where the sparsity comes from. Each reprojection-error term involves exactly one pose and one point. So in the Gauss-Newton normal equations HΔx=bH \Delta\mathbf{x} = -\mathbf{b} with H=JTJH = J^T J, the Hessian has the arrow-like block structure

H=[BEETC]H = \begin{bmatrix} B & E \\ E^T & C \end{bmatrix}

where BB (6m×6m6m \times 6m) couples only poses, CC (3n×3n3n \times 3n) couples only points, and EE holds the pose-point coupling. Crucially, two points never appear in the same residual, so CC is block-diagonal — one independent 3×33 \times 3 block per point.

The Schur complement step (also called marginalizing out the points) eliminates the point variables from the linear system, leaving the reduced camera system:

(BEC1ET)Δxcam=bcam+EC1bpts\left(B - E C^{-1} E^T\right) \Delta\mathbf{x}_{\text{cam}} = -\mathbf{b}_{\text{cam}} + E C^{-1} \mathbf{b}_{\text{pts}}

Because CC is block-diagonal, C1C^{-1} costs almost nothing (invert each 3×33 \times 3 block). One solves the 6m×6m6m \times 6m reduced system for the pose update, then back-substitutes to recover each point update independently. A (6m+3n)(6m + 3n)-dimensional solve becomes a 6m6m-dimensional one — orders of magnitude faster when nmn \gg m, which is always.

Two further layers of sparsity matter in practice. First, the reduced camera matrix BEC1ETB - EC^{-1}E^T is itself sparse: entry (i,j)(i, j) is non-zero only if keyframes ii and jj observe a common point (the covisibility structure), so sparse Cholesky factorization with good variable ordering (COLAMD) applies. Second, the same elimination viewpoint generalises: in factor-graph terms, the Schur complement is just variable elimination, the operation underlying marginalization in sliding-window VIO and incremental smoothers like iSAM2.

Where the formula comes from

The Schur complement is nothing more exotic than block Gaussian elimination. Write out the two block rows of the normal equations:

BΔxcam+EΔxpts=bcamB\,\Delta\mathbf{x}_{\text{cam}} + E\,\Delta\mathbf{x}_{\text{pts}} = -\mathbf{b}_{\text{cam}} ETΔxcam+CΔxpts=bptsE^T \Delta\mathbf{x}_{\text{cam}} + C\,\Delta\mathbf{x}_{\text{pts}} = -\mathbf{b}_{\text{pts}}

Solve the second row for the point update, Δxpts=C1(bpts+ETΔxcam)\Delta\mathbf{x}_{\text{pts}} = -C^{-1}\left(\mathbf{b}_{\text{pts}} + E^T \Delta\mathbf{x}_{\text{cam}}\right), and substitute into the first — the reduced camera system above appears immediately, and the same expression is the back-substitution rule once Δxcam\Delta\mathbf{x}_{\text{cam}} is known. Statistically, the reduced system is the information-matrix form of the marginal over the cameras: eliminating the points does not approximate anything, it re-expresses the same Gaussian exactly.

Counting the win. A dense solve of the full system costs O((6m+3n)3)O((6m + 3n)^3). With the Schur trick: inverting CC is nn independent 3×33\times 3 inversions (O(n)O(n)), forming the reduced system is bounded by the number of observations, and the remaining solve is O((6m)3)O((6m)^3) worst-case — usually far less thanks to covisibility sparsity. With mm in the hundreds and nn in the hundreds of thousands, the point elimination is the difference between milliseconds and minutes. When mm itself grows large (city-scale SfM), even the reduced system stops being cheap to factor, and solvers switch to iterative methods (preconditioned conjugate gradient on the Schur complement — Ceres’ ITERATIVE_SCHUR).

In Ceres, all of this is one configuration choice:

ceres::Solver::Options options;
options.linear_solver_type = ceres::SPARSE_SCHUR;  // or DENSE_SCHUR, ITERATIVE_SCHUR

with the solver automatically detecting the pose/point elimination ordering (or taking an explicit ParameterBlockOrdering). Every serious solver — Ceres, g2o, GTSAM — implements the same trick; knowing it explains why BA scales, why marginalizing old states creates fill-in (dense blocks) in the remaining Hessian, and why solver choice and ordering can change runtime by orders of magnitude.

Common pitfalls

Why it matters for SLAM

Real-time SLAM exists because of structure-exploiting linear algebra: without the Schur complement, bundle adjustment over even a few hundred keyframes would be hopeless. The same idea — eliminate variables, keep the problem sparse — reappears in marginalization, sliding-window estimators, and incremental smoothing, so this is one of the highest-leverage pieces of math on the whole roadmap.