20
$\begingroup$

I am working with square matrices with a special form, which for large rank ($> 100,000$) would be best stored and manipulated as a SparseArray. I believe that the Cholesky decomposition of these matrices itself could also be sparse. The question I have is

How do I compute the sparse Cholesky decomposition of a sparse matrix without resorting to dense storage of the intermediates and result?

For purposes of illustration:

n = 5;
s = SparseArray[{{i_, i_} -> 2., {i_, j_} /; Abs[i - j] == 1-> -1.}, {n, n}];
s // MatrixForm

s

The CholeskyDecomposition function returns a dense matrix:

CholeskyDecomposition[s] // MatrixForm

Cholesky triangle

The CholeskyDecomposition documentation gives a lead: "Using LinearSolve will give a LinearSolveFunction that has a sparse Cholesky factorization".

ls = LinearSolve[s,"Method" -> "Cholesky"];
ls // InputForm

However, I'm stuck with what to do with this object to bring it in for the win.

$\endgroup$
2
  • $\begingroup$ OTOH, if the matrix in your application is actually SPD and tridiagonal, then no permutation is actually needed, and you can derive a bidiagonal Cholesky triangle (as you've shown in your example). But, you'll have to write the algorithm for producing it yourself… $\endgroup$ Nov 6, 2015 at 0:06
  • $\begingroup$ With the help of ls["Properties"], then you can achieve many useful results. $\endgroup$
    – xyz
    Nov 7, 2015 at 15:13

1 Answer 1

21
$\begingroup$

LinearSolve[] actually computes a permuted Cholesky decomposition; that is, it performs the decomposition $\mathbf P^\top\mathbf A\mathbf P=\mathbf G^\top\mathbf G$. To extract $\mathbf P$ and $\mathbf G$, we need to use some undocumented properties. Here's a demo:

mat = SparseArray[{Band[{2, 1}] -> -1., Band[{1, 1}] -> 2.,
                   Band[{1, 2}] -> -1.}, {5, 5}];

ls = LinearSolve[mat, Method -> "Cholesky"];
g = ls["getU"]; (* upper triangular factor *)
perm = ls["getPermutations"][[1]]; (* permutation vector *)
p = SparseArray[MapIndexed[Append[#2, #1] -> 1 &, perm]]; (* permutation matrix *)

p.Transpose[g].g.Transpose[p] == mat (* check! *)
   True

Here's a classical example of why permutation matrices are a must in sparse Cholesky decompositions.

Consider the following upper arrowhead matrix:

arr = SparseArray[{{1, j_} | {j_, 1} /; j != 1 -> -1., Band[{1, 1}] -> 3.}, {5, 5}];

ArrayPlot[arr]

upper arrowhead

Watch what happens after performing a Cholesky decomposition:

ArrayPlot[CholeskyDecomposition[arr]]

Cholesky triangle of upper arrowhead

Boom, fill-in. Imagine if this had been a $100\,000\times 100\,000$ upper arrowhead matrix!

If, however, we permute arr to a lower arrowhead matrix, like so:

exc = Reverse[IdentityMatrix[5]];
la = exc.arr.Transpose[exc];

ArrayPlot[CholeskyDecomposition[la]]

Cholesky triangle of lower arrowhead

What a difference a permutation makes!

For matrices with even more complicated sparsity patterns, it is doubtful if you can predict in advance that you won't get any disastrous fill-in if you insist on an unpermuted Cholesky triangle. Thus, all standard sparse Cholesky routines always perform some sort of permutation; though, as with any automatic routine of this sort, the permutation chosen might not be the most optimal, and yet yield something still good enough to work.

For reference, here's how LinearSolve[] does on an upper arrowhead:

lsar = LinearSolve[arr, Method -> "Cholesky"];
g = lsar["getU"];
ArrayPlot[g]

Cholesky triangle from LinearSolve

$\endgroup$
7
  • $\begingroup$ (As a further check that LinearSolve[] knows that it was fed an SPD matrix: ls["getL"] == Transpose[ls["getU"]].) $\endgroup$ Nov 5, 2015 at 14:08
  • $\begingroup$ Apologies for being "dense" but how would I get the upper form as shown by CholeskyDecomposition? $\endgroup$
    – Eric Brown
    Nov 5, 2015 at 14:31
  • 1
    $\begingroup$ @Eric, I don't think there's a way to turn off the permutation in this case. Recall that direct linear solvers will often permute in an attempt to keep the triangular factors sparse (e.g. Cuthill-McKee). $\endgroup$ Nov 5, 2015 at 14:37
  • $\begingroup$ A marvelous, helpful answer. Thanks a million! $\endgroup$
    – Eric Brown
    Nov 5, 2015 at 22:07
  • $\begingroup$ J.M. Why ls = LinearSolve[{{1, 2}, {3, 4}}] ls["getPermutations"] gives the error info? $\endgroup$
    – xyz
    Nov 7, 2015 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.