|
10 | 10 |
|
11 | 11 | module CHOLMOD
|
12 | 12 |
|
13 |
| -import Base: (*), convert, copy, eltype, getindex, getproperty, show, size, |
14 |
| - IndexStyle, IndexLinear, IndexCartesian, adjoint, axes, |
| 13 | +import Base: (*), convert, copy, eltype, getindex, getproperty, propertynames, |
| 14 | + show, size, IndexStyle, IndexLinear, IndexCartesian, adjoint, axes, |
15 | 15 | Matrix, Vector
|
16 | 16 | using Base: require_one_based_indexing
|
17 | 17 |
|
@@ -380,24 +380,25 @@ Base.pointer(x::Dense{Tv}) where {Tv} = Base.unsafe_convert(Ptr{cholmod_dense},
|
380 | 380 | Base.pointer(x::Sparse{Tv}) where {Tv} = Base.unsafe_convert(Ptr{cholmod_sparse}, x)
|
381 | 381 | Base.pointer(x::Factor{Tv}) where {Tv} = Base.unsafe_convert(Ptr{cholmod_factor}, x)
|
382 | 382 |
|
| 383 | +function _factor_components(is_ll) |
| 384 | + is_ll ? (:L, :U, :PtL, :UP) : (:L, :U, :PtL, :UP, :D, :LD, :DU, :PtLD, :DUP) |
| 385 | +end |
| 386 | + |
383 | 387 | # FactorComponent, for encoding particular factors from a factorization
|
384 | 388 | mutable struct FactorComponent{Tv, S, Ti} <: AbstractMatrix{Tv}
|
385 | 389 | F::Factor{Tv, Ti}
|
386 | 390 |
|
387 |
| - function FactorComponent{Tv, S, Ti}(F::Factor{Tv, Ti}) where {Tv, S, Ti} |
388 |
| - s = unsafe_load(pointer(F)) |
389 |
| - if s.is_ll != 0 |
390 |
| - if !(S === :L || S === :U || S === :PtL || S === :UP) |
391 |
| - throw(CHOLMODException(string(S, " not supported for sparse ", |
392 |
| - "LLt matrices; try :L, :U, :PtL, or :UP"))) |
393 |
| - end |
394 |
| - elseif !(S === :L || S === :U || S === :PtL || S === :UP || |
395 |
| - S === :D || S === :LD || S === :DU || S === :PtLD || S === :DUP) |
396 |
| - throw(CHOLMODException(string(S, " not supported for sparse LDLt ", |
397 |
| - "matrices; try :L, :U, :PtL, :UP, :D, :LD, :DU, :PtLD, or :DUP"))) |
398 |
| - end |
399 |
| - new(F) |
400 |
| - end |
| 391 | + function FactorComponent{Tv,S,Ti}(F::Factor{Tv,Ti}) where {Tv,S,Ti} |
| 392 | + s = unsafe_load(pointer(F)) |
| 393 | + is_ll = (s.is_ll != 0) |
| 394 | + components = _factor_components(is_ll) |
| 395 | + if !(S in components) |
| 396 | + type = is_ll ? "LLt" : "LDLt" |
| 397 | + component_list = join(repr.(components), ", ") |
| 398 | + throw(CHOLMODException("$(repr(S)) not supported for sparse $type factorizations; try $component_list, or :p")) |
| 399 | + end |
| 400 | + new(F) |
| 401 | + end |
401 | 402 | end
|
402 | 403 | function FactorComponent{Tv, S}(F::Factor{Tv, Ti}) where {Tv, S, Ti}
|
403 | 404 | return FactorComponent{Tv, S, Ti}(F)
|
@@ -1285,11 +1286,17 @@ function sparse(FC::FactorComponent{Tv,:L}) where Tv
|
1285 | 1286 | F = Factor(FC)
|
1286 | 1287 | s = unsafe_load(pointer(F))
|
1287 | 1288 | if s.is_ll == 0
|
1288 |
| - throw(CHOLMODException("sparse: supported only for :LD on LDLt factorizations")) |
| 1289 | + _sparse_exception(F) |
1289 | 1290 | end
|
1290 | 1291 | sparse(Sparse(F))
|
1291 | 1292 | end
|
1292 | 1293 | sparse(FC::FactorComponent{Tv,:LD}) where {Tv} = sparse(Sparse(Factor(FC)))
|
| 1294 | +sparse(FC::FactorComponent{Tv}) where {Tv} = _sparse_exception(Factor(FC)) |
| 1295 | +function _sparse_exception(F::Factor) |
| 1296 | + s = unsafe_load(pointer(F)) |
| 1297 | + details = (s.is_ll == 0) ? ":LD on LDLt" : ":L on LLt" |
| 1298 | + throw(CHOLMODException("sparse: supported only for $details factorizations")) |
| 1299 | +end |
1293 | 1300 |
|
1294 | 1301 | # Calculate the offset into the stype field of the cholmod_sparse_struct and
|
1295 | 1302 | # change the value
|
@@ -1396,6 +1403,11 @@ end
|
1396 | 1403 | end
|
1397 | 1404 | end
|
1398 | 1405 |
|
| 1406 | +function propertynames(F::Factor) |
| 1407 | + s = unsafe_load(pointer(F)) |
| 1408 | + (_factor_components(s.is_ll != 0)..., :p, :ptr) |
| 1409 | +end |
| 1410 | + |
1399 | 1411 | function getLd!(S::SparseMatrixCSC)
|
1400 | 1412 | d = Vector{eltype(S)}(undef, size(S, 1))
|
1401 | 1413 | fill!(d, 0)
|
@@ -1555,6 +1567,12 @@ using just `L` without accounting for `P` will give incorrect answers.
|
1555 | 1567 | To include the effects of permutation,
|
1556 | 1568 | it's typically preferable to extract "combined" factors like `PtL = F.PtL`
|
1557 | 1569 | (the equivalent of `P'*L`) and `LtP = F.UP` (the equivalent of `L'*P`).
|
| 1570 | +The complete list of supported factors is `:L, :PtL, :UP, :U`. |
| 1571 | +The permutation vector is available as `F.p`, defined such that `L*L' == A[p, p]`, |
| 1572 | +
|
| 1573 | +The `L` component can be materialized as a sparse matrix using `sparse(F.L)`. |
| 1574 | +Other components cannot be materialized directly, but can be reconstructed |
| 1575 | +from `sparse(F.L)` and `F.p` if needed. |
1558 | 1576 |
|
1559 | 1577 | When `check = true`, an error is thrown if the decomposition fails.
|
1560 | 1578 | When `check = false`, responsibility for checking the decomposition's
|
@@ -1732,6 +1750,11 @@ To include the effects of permutation, it is typically preferable to extract
|
1732 | 1750 | "combined" factors like `PtL = F.PtL` (the equivalent of
|
1733 | 1751 | `P'*L`) and `LtP = F.UP` (the equivalent of `L'*P`).
|
1734 | 1752 | The complete list of supported factors is `:L, :PtL, :D, :UP, :U, :LD, :DU, :PtLD, :DUP`.
|
| 1753 | +The permutation vector is available as `F.p`, defined such that `L*D*L' == A[p, p]`, |
| 1754 | +
|
| 1755 | +The `LD` component can be materialized as a sparse matrix using `sparse(F.LD)`, |
| 1756 | +Other components cannot be materialized directly, but can be reconstructed from |
| 1757 | +`sparse(F.LD)` and `F.p` if needed. |
1735 | 1758 |
|
1736 | 1759 | Unlike the related Cholesky factorization, the ``LDL'`` factorization does not
|
1737 | 1760 | require `A` to be positive definite. However, it still requires all leading
|
|
0 commit comments