Cholesky Decomposition (sksparse.cholmod)¶
The sksparse.cholmod module provides an interface to the SuiteSparse
CHOLMOD package, which computes basic linear algebra
operations for sparse, symmetric, positive-definite matrices.
The main function of this module is to compute the Cholesky factor \(L\) of a sparse, symmetric (Hermitian if complex), positive-definite matrix \(A\) with a fill-reducing permutation \(P\), such that:
For matrices that are symmetric but may be numerically close to semi-definite, the module can compute the LDL factorization:
Either of these factors can then be used to solve linear systems of the form \(Ax = b\).
The cholmod module exposes most of the capabilities of the CHOLMOD
package including:
Computation of the Cholesky factor with fill-reducing permutation for both real and complex sparse matrices \(A\), in any format supported by
scipy.sparse.An interface for using this decomposition to solve problems of the form \(Ax = b\).
Functions to compute a rank-\(k\) “update” or “downdate” of the Cholesky factor.
The ability to perform the fill-reduction analysis once, and then re-use it to efficiently decompose many matrices with the same pattern of non-zero entries.
This wrapper handles both 32-bit and 64-bit integer types, depending on the input matrix format.
Quickstart¶
If \(A\) is a sparse, symmetric, positive-definite matrix, then the following code computes the Cholesky decomposition of \(A\):
from sksparse.cholmod import cholesky
L = cholesky(A)
or, with a fill-reducing permutation:
L, p = cholesky(A, order="default")
See the example below for a demonstration of the effect of the fill-reducing permutation.
Once the factorization has been computed, it can be used to solve a linear system:
from sksparse.cholmod import ldl_factor
A = ... # a symmetric, positive-definite sparse matrix
b = ... # right-hand side
f = ldl_factor(A) # compute LDL^T factorization
x = f.solve(b) # solve Ax = b
Examples¶
Cholesky Example¶
To see how to use the Cholesky factorization, we can load a sparse matrix from the SuiteSparse Matrix Collection and compute its ordering.
# Part of the scikit-sparse project.
# Copyright (C) 2025 Bernard Roesler. All rights reserved.
# See pyproject.toml for full author list and LICENSE.txt for license details.
# SPDX-License-Identifier: BSD-2-Clause
#
# =============================================================================
# File: cholmod_example.py
# Created: 2025-08-12 20:12
# =============================================================================
"""An example of using CHOLMOD to compute the Cholesky factorization of a
sparse matrix.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from numpy.testing import assert_allclose
from scipy.io import mmread
from sksparse.cholmod import cholesky
# Load the west0479 matrix (downloaded from the SuiteSparse Matrix Collection:
# <https://sparse.tamu.edu/HB/west0479>)
filepath = Path("data") / "west0479.mtx"
A = mmread(filepath, spmatrix=False) # read the matrix
A = (A.T @ A).tocsc() # make it symmetric positive definite
# compute the Cholesky factorization
R = cholesky(A)
Rp, p = cholesky(A, order="amd")
PAPT = A[p][:, p] # apply the ordering to the matrix
# Make sure the factorization is correct
assert_allclose((R.T.conj() @ R).toarray(), A.toarray(), atol=1e-9)
assert_allclose((Rp.T.conj() @ Rp).toarray(), PAPT.toarray(), atol=1e-9)
# Plot the original and permuted matrices
plt.rcParams.update({"font.size": 10})
fig, axs = plt.subplots(num=1, nrows=2, ncols=2, clear=True)
fig.set_size_inches((6, 6), forward=True)
fig.set_constrained_layout(True)
fig.suptitle("CHOLMOD Example: Cholesky Factor of west0479")
ax = axs[0, 0]
ax.spy(A, markersize=1)
ax.set_title(r"Original Matrix $A$")
ax = axs[0, 1]
ax.spy(PAPT, markersize=1)
ax.set_title(r"Permuted Matrix $PAP^{\top}$")
ax = axs[1, 0]
ax.spy(R, markersize=1)
ax.set_title(r"Original Cholesky Factor $R$")
ax.set_xlabel(f"{R.nnz:,} non-zeros")
ax = axs[1, 1]
ax.spy(Rp, markersize=1)
ax.set_title(r"Permuted R Factor $R_p$")
ax.set_xlabel(f"{Rp.nnz:,} non-zeros")
for ax in axs.flat:
ax.set_aspect("equal")
ax.set_xticks([])
ax.set_yticks([])
plt.show()
fig.savefig("cholesky_example.svg")
This figure shows the effect of AMD ordering that reduces the fill-in of the Cholesky factorization of a sparse matrix.
The number of non-zeros in the Cholesky factorization of the original matrix (left) and the permuted matrix (right) using AMD ordering.¶
Nested Dissection Example¶
To see the effects of nested dissection ordering, we can load a sparse matrix and compute its ordering.
# Part of the scikit-sparse project.
# Copyright (C) 2025 Bernard Roesler. All rights reserved.
# See pyproject.toml for full author list and LICENSE.txt for license details.
# SPDX-License-Identifier: BSD-2-Clause
#
# =============================================================================
# File: nesdis_example.py
# Created: 2025-08-22 11:04
# =============================================================================
"""An example of using CHOLMOD to compute the Cholesky factorization of a
sparse matrix with various orderings.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from scipy.io import mmread
from scipy.sparse.linalg import splu
from sksparse.amd import amd
from sksparse.cholmod import nesdis
# Load the west0479 matrix (downloaded from the SuiteSparse Matrix Collection:
# <https://sparse.tamu.edu/HB/west0479>)
filepath = Path("data") / "west0479.mtx"
A = mmread(filepath, spmatrix=False).tocsc() # read the matrix
# Compute the AMD permutation
p = amd(A)
PAPT = A[p][:, p]
# Compute the nested dissection A.T @ A permutation
q = nesdis(A)
QAQT = A[q][:, q]
# Compute the LU decompositions
lu = splu(A)
lup = splu(PAPT)
luq = splu(QAQT)
LU = lu.L + lu.U
LUp = lup.L + lup.U
LUq = luq.L + luq.U
# Plot the original and permuted matrices
plt.rcParams.update({"font.size": 10})
fig, axs = plt.subplots(num=1, nrows=3, ncols=2, clear=True)
fig.set_size_inches((6, 10), forward=True)
fig.set_constrained_layout(True)
fig.suptitle("CHOLMOD Example: Cholesky Factor of west0479")
ax = axs[0, 0]
ax.spy(A, markersize=1)
ax.set_title(r"Original Matrix $A$")
ax = axs[1, 0]
ax.spy(PAPT, markersize=1)
ax.set_title(r"AMD-Ordered Matrix $PAP^{\top}$")
ax = axs[0, 1]
ax.spy(LU, markersize=1)
ax.set_title(r"LU Factors $LU = A$")
ax.set_xlabel(f"{LU.nnz:,} non-zeros")
ax = axs[1, 1]
ax.spy(LUp, markersize=1)
ax.set_title(r"AMD Factors $LU = PAP^{\top}$")
ax.set_xlabel(f"{LUp.nnz:,} non-zeros")
ax = axs[2, 0]
ax.spy(QAQT, markersize=1)
ax.set_title(r"Nested Dissection Matrix $QAQ^{\top}$")
ax = axs[2, 1]
ax.spy(LUq, markersize=1)
ax.set_title(r"Nesdis Factors $LU = QAQ^{\top}$")
ax.set_xlabel(f"{LUq.nnz:,} non-zeros")
for ax in axs.flat:
ax.set_aspect("equal")
ax.set_xticks([])
ax.set_yticks([])
plt.show()
fig.savefig("nesdis_example.svg")
This figure shows the effect of nested dissection ordering that reduces the fill-in of the LU factorization of a sparse matrix in a case where the AMD order does not help.
The number of non-zeros in the LU factorization of the original matrix and the permuted matrix using AMD and nested dissection ordering.¶
Function Interface¶
For users who want to directly compute the factorization without needing to
manipulate the CholeskyFactor object, the cholmod module
provides the cholesky() and ldl() functions that perform both the
symbolic analysis and the numerical factorization in one step, and return the
matrices directly.
Object Interface¶
For more advanced usage, users can instantiate the CholeskyFactor
class. This class can be instantiated directly using its constructor, or more
conveniently using the cho_factor() or ldl_factor() functions.
When instantiated directly, the constructor performs a symbolic analysis of the
matrix, but does not compute the numerical factorization. The numerical
factorization is then performed by calling the CholeskyFactor.factorize() method.
The cho_factor() and ldl_factor() functions perform both the
symbolic analysis and the numerical factorization in one step, and return an
instance of the CholeskyFactor class.
The resulting CholeskyFactor object can then be used to solve linear
systems using its CholeskyFactor.solve() method, or to update the
factorization in-place using the update(), rowadd(),
rowdel(), and resymbol() methods.
The CholeskyFactor.factorize() method can be called again to factor a new
matrix with the same sparsity pattern.
Symbolic Analysis¶
In addition to numerical factorization, cholmod provides symbolic
operations symbfact(), and etree() that can be used to analyze the
structure of the Cholesky factor and to compute fill-reducing permutations.
Graph Partitioning¶
The cholmod module also includes functions for graph partitioning and
node reordering, which can be used like the amd and
colamd (and their constrained counterparts) modules to reduce
fill-in during factorization.
These functions provide a direct interface to the corresponding CHOLMOD
functions that are used internally by cholesky() and ldl() when the
order argument is specified. The functions bisect(), metis(),
and nesdis() can be used to compute fill-reducing orderings, and the
SeparatorTree class represents the resulting separator tree.
Exceptions and Warnings¶
Warnings issued by CHOLMOD are converted into Python warnings of
type CholmodWarning. The module will also issue
a SparseEfficiencyWarning if the input matrix is not
a csc_array (note that the
csc_matrix class is not supported, as it will be
deprecated and is not recommended for use in new code).
Errors detected by CHOLMOD or by our wrapper code are converted into exceptions
of type CholmodError or an appropriate subclass. See the
Exceptions and Warnings for details.