Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SparseCSCInterface sparspaklu! check sparsity pattern #27

Closed
wants to merge 6 commits into from

Conversation

sjdaines
Copy link
Contributor

@sjdaines sjdaines commented Oct 14, 2023

(update: closed in favour of cleaned up version in PR #28)

Robustness fix to the existing implementation of sparsepaklu!, adding a check for a change in the sparsity pattern, and adding allow_pattern_change argument to optionally error if sparsity pattern has changed.

sparspaklu!(lu::SparseSolver, m::SparseMatrixCSC; allow_pattern_change=true) -> lu::SparseSolver

Updated docstring now reads:

Calculate LU factorization of a sparse matrix m, reusing ordering and symbolic factorization lu, if that was previously calculated.

If allow_pattern_change = true (the default) the sparse matrix m may have a nonzero pattern different to that of the matrix used to create the LU factorization lu, in which case the ordering and symbolic factorization are updated.

If allow_pattern_change = false an error is thrown if the nonzero pattern of m is different to that of the matrix used to create the LU factorization lu.

If lu has not been factorized (ie it has just been created with option factorize = false) then lu is always updated from m and allow_pattern_change is ignored.

NB: this check is efficient as possible I think, but does need to compare colptr and rowval arrays, however this should be inexpensive compared to the actual factorization.

Also some bug fixes:

  • sparspaklu! always mutate and update 'lu'
  • ErrorExceptions were created, but not actually thrown ! Fix by
    replacing ErrorException() with error()

NB: there is a wrong turn in the first commit (attempting to compare sparsity pattern against the stored graph, which doesn't work as the graph may be modified with an asymmetric matrix), so suggest squash when merging...

sparsepaklu! now has a 'reuse_symbolic' option:
    sparspaklu!(lu::SparseSolver, m::SparseMatricCSC; reuse_symbolic=true)

If 'reuse_symbolic=true' (the default) then the sparsity pattern of 'm' is checked
and an error generated if it does not match that of the matrix used to create 'lu'

This matches the interface for the Julia SparseArrays stdlib UMFPACK lu!

Also some bug fixes:
    - sparspaklu! always mutate and update 'lu'
    - ErrorExceptions were created, but not actually thrown ! Fix by
    replacing ErrorException() with error()
Change ErrorException(...) to error(...) so that an exception is thrown in

SpkSparseSolver.solve!
SpkSparseSpdSolver.solve!
@sjdaines sjdaines marked this pull request as draft October 14, 2023 11:02
@sjdaines sjdaines marked this pull request as ready for review October 14, 2023 11:18
sparspaklu!(lu::SparseSolver, m::SparseMatrixCSC; allow_pattern_change=true)

If `allow_pattern_change = true` (the default) the sparse matrix `m` may have a nonzero pattern
different to that of the matrix used to create the LU factorization `lu`, in which case the ordering
and symbolic factorization are updated.

If `allow_pattern_change = false` an error is thrown if the nonzero pattern of `m` is different to that
of the matrix used to create the LU factorization `lu`.

If `lu` was created with option `factorize = false` then `lu` is always updated from `m` and `allow_pattern_change` is ignored.
@@ -1,5 +1,5 @@
module SparseCSCInterface
using SparseArrays, LinearAlgebra
import SparseArrays, LinearAlgebra
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is an import necessary? Are we adding methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at this the other way around:
import is safer than an unqualified using, with the only difference being that it avoids importing many unknown and unused methods into the namespace with possibility of name clashes etc?

In this case, the only effect of this change was to enforce SparseArrays.nnz which looks like what was intended, and caught a couple of places where an unqualified nnz was inadvertently used which then was potentially confusing (well confused me anyway!).

Many thanks for fantastic work making this package available btw!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the only effect of this change was to enforce SparseArrays.nnz

Good point.

inmatrix!(s) || ErrorException("Matrix input.")
factor!(s) || ErrorException("Numerical Factorization.")
triangularsolve!(s,rhs) || ErrorException("Triangular Solve.")
findorder!(s) || error("Finding Order.")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch with the exceptions!

@PetrKryslUCSD
Copy link
Owner

Does this represent a substantial performance gain? If I recall, symbolic factorization was relatively inexpensive...

@sjdaines
Copy link
Contributor Author

sjdaines commented Oct 14, 2023

This PR is just a robustness fix to the existing implementation of sparsepaklu!, adding a check for a change in the sparsity pattern (I'll edit the PR description to make this clear).

Yup, as far as I know the performance gains from reusing symbolic factorisation are small (10 - 20% from eyeballing profview output?).

But this method is already used in eg LinearSolve.jl, so at least from my user's perspective, I think if it exists at all it has to be bulletproof wrt inadvertent changes in sparsity pattern.

@PetrKryslUCSD
Copy link
Owner

But this method is already used in eg LinearSolve.jl, so at least from my user's perspective, I think if it exists at all it has to be bulletproof wrt inadvertent changes in sparsity pattern.

Most definitely.

@PetrKryslUCSD
Copy link
Owner

Is this what you meant to squash: 6c84869 ?

@sjdaines
Copy link
Contributor Author

Yup, and I realise there was another wrong turn in the sparspaklu! API (first attempt used a reuse_symbolic argument in an attempt to be consistent with the Julia SparseArrays UMFPACK lu!, but that was just confusing).

So maybe clearer to just squash-and-merge the whole PR?

Or happy to make a new PR with a clean version if you prefer?

@PetrKryslUCSD
Copy link
Owner

New PR might be cleaner, if you don't mind terribly?

1 similar comment
@PetrKryslUCSD
Copy link
Owner

New PR might be cleaner, if you don't mind terribly?

@sjdaines
Copy link
Contributor Author

OK done.

Closed in favour of cleaned up version in PR #28

Sorry about the mess!

@sjdaines sjdaines closed this Oct 14, 2023
@sjdaines sjdaines deleted the csc_sparsity_check branch October 14, 2023 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants