KBKnowledge Base
Linear Algebra for ML · 1.12

Determinants

Signed area, invertibility, and why det = product of eigenvalues.

On this page
In plain English — beginner to advanced

Beginner: the determinant is a single number computed from a square matrix that tells you how much the matrix's transformation scales area (in 2D) or volume (in 3D and beyond). A determinant of 2 means "everything doubles in area." A determinant of 0 means "everything gets squashed flat" — the transformation destroys a dimension.

Intermediate: the sign matters too, not just the size. A negative determinant means the transformation flips orientation — like a reflection in a mirror. This is why the determinant is "signed area/volume," not just plain area/volume.

Advanced: a matrix has an inverse if and only if its determinant is non-zero — this is the precise, computable version of the "singular vs. non-singular" language from section 1.6, and it's mathematically identical to saying the matrix has full rank (section 1.9). All three ideas — nonzero determinant, full rank, invertibility — are exactly the same fact viewed three different ways.

Formula
2×2 case:
det⁡[abcd]=ad−bc\det\begin{bmatrix}a&b\\c&d\end{bmatrix} = ad - bc

For larger matrices the formula generalizes recursively (cofactor expansion), but in practice no one computes determinants this way by hand past 3×3 — libraries use LU decomposition (section 1.13) instead, since the determinant of a triangular matrix is just the product of its diagonal.

Derivation: the 2×2 determinant as signed area

Place two vectors a=(a₁,a₂) and b=(b₁,b₂) as the columns of a matrix — exactly the parallelogram in the diagram below. Decompose the parallelogram's area using the "base times height" formula, resolved into coordinates: the area equals the area of the bounding rectangle (a₁+b₁)(a₂+b₂) minus the four triangles/rectangles around it that aren't part of the parallelogram. Carrying out that bookkeeping (the classic "shoelace formula" derivation) collapses to exactly:

Area=∣a1b2−a2b1∣\text{Area} = |a_1b_2 - a_2b_1|

which is precisely |det[a\ b]| — the absolute value of the determinant of the matrix whose columns are a and b. Dropping the absolute value recovers the signed area: positive when b is counterclockwise from a, negative when clockwise — exactly the orientation flip shown in the diagram.

Where this is used: this signed-area interpretation generalizes directly to signed volume in 3D (and hypervolume in higher dimensions), which is exactly what makes the determinant the correct scaling factor in the change-of-variables formula used by normalizing flows (section 1.35).

Determinant as signed area

Drag either vector — the shaded parallelogram's area is exactly |det|.

Practical example — determinants in NumPy

det(AB) = det(A)·det(B) always holds — composing two transformations multiplies their scaling factors, exactly as you'd expect.

cpp
#include <cstdio>
#include <vector>

using Matrix = std::vector<std::vector<double>>;

// Recursive cofactor expansion along the first row -- no linear-algebra library.
double determinant(const Matrix& M) {
    int n = static_cast<int>(M.size());
    if (n == 1) return M[0][0];
    if (n == 2) return M[0][0] * M[1][1] - M[0][1] * M[1][0];
    double total = 0.0;
    for (int col = 0; col < n; ++col) {
        Matrix minor;
        for (int r = 1; r < n; ++r) {
            std::vector<double> row;
            for (int c = 0; c < n; ++c) {
                if (c == col) continue;
                row.push_back(M[r][c]);
            }
            minor.push_back(row);
        }
        double sign = (col % 2 == 0) ? 1.0 : -1.0;
        total += sign * M[0][col] * determinant(minor);
    }
    return total;
}

Matrix matmul(const Matrix& A, const Matrix& B) {
    int n = static_cast<int>(A.size());
    int k = static_cast<int>(A[0].size());
    int m = static_cast<int>(B[0].size());
    Matrix C(n, std::vector<double>(m, 0.0));
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            for (int x = 0; x < k; ++x) C[i][j] += A[i][x] * B[x][j];
    return C;
}

int main() {
    Matrix A = {{3, 1}, {2, 4}};
    std::printf("%.1f\n", determinant(A));  // 10.0

    Matrix singular = {{1, 2}, {2, 4}};
    std::printf("%.1f\n", determinant(singular));  // 0.0

    Matrix B = {{0, -1}, {1, 0}};  // 90-degree rotation, det = 1
    std::printf("%.4f %.4f\n", determinant(matmul(A, B)), determinant(A) * determinant(B));
    return 0;
}
Real-world examples
  • Change of variables in probability and statistics — transforming a probability distribution to a new coordinate system requires dividing by the absolute value of the transformation's Jacobian determinant (section 1.17), to keep total probability equal to 1.
  • Fast invertibility checks — before attempting to invert a matrix or solve a system, checking det ≈ 0 flags a numerically dangerous problem early.
  • Computer graphics — the determinant of a 3×3 transformation matrix tells a renderer whether a triangle's winding order (and therefore which face is "front-facing") has flipped.
Common mistakes
  • Determinants are only defined for square matrices — there's no such thing as "the determinant" of a rectangular data matrix.
  • Trusting det(A) == 0 exactly in floating point — real computations produce tiny non-zero values like 1e-16 for genuinely singular matrices. Compare against a small tolerance, or better, check the condition number (section 1.10) instead.
Going deeper

The determinant equals the product of a matrix's eigenvalues — this is why det(A − λI) = 0 (the characteristic equation from section 1.7) works at all: it's asking "for what λ does A − λI become singular (determinant zero)?"

At the master level: for large matrices, computing the raw determinant is numerically dangerous — it can overflow or underflow to zero even when the matrix is perfectly healthy, because it's a product of many numbers. Production code almost always works with the log-determinant instead (summing log|eigenvalues| or log of the diagonal of a Cholesky/LU factor), which is exactly how multivariate Gaussian log-likelihoods are computed in every serious statistics and ML library.

Check yourself
If det(A) = 0, what does that tell you about solving Ax = b?

A has no inverse, so the system either has no solution or infinitely many — you cannot solve it uniquely with A⁻¹.

Newsletter

Stay in the loop

Subscribe to get new docs, diagrams, and engineering write-ups by Dharaneesh Boobalan delivered to your inbox.

  • Deep-dive write-ups on ML, inference, and systems.
  • New Draw.io diagrams & interactive canvases.
  • Agentic patterns and rocket-science notes.
  • No spam. One tasteful email when there's something new.

Crafted by Dharaneesh Boobalan

Newsletter

Get new docs, diagrams, and write-ups in your inbox.

We never share your details. Unsubscribe anytime.