-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
factorization.jl
116 lines (92 loc) · 3.81 KB
/
factorization.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# This file is a part of Julia. License is MIT: https://julialang.org/license
## Matrix factorizations and decompositions
abstract type Factorization{T} end
eltype(::Type{Factorization{T}}) where {T} = T
transpose(F::Factorization) = error("transpose not implemented for $(typeof(F))")
ctranspose(F::Factorization) = error("ctranspose not implemented for $(typeof(F))")
macro assertposdef(A, info)
:($(esc(info)) == 0 ? $(esc(A)) : throw(PosDefException($(esc(info)))))
end
macro assertnonsingular(A, info)
:($(esc(info)) == 0 ? $(esc(A)) : throw(SingularException($(esc(info)))))
end
"""
issuccess(F::Factorization)
Test that a factorization of a matrix succeeded.
```jldoctest
julia> cholfact([1 0; 0 1])
Base.LinAlg.Cholesky{Float64,Array{Float64,2}} with factor:
[1.0 0.0; 0.0 1.0]
successful: true
julia> cholfact([1 0; 0 -1])
Base.LinAlg.Cholesky{Float64,Array{Float64,2}} with factor:
[1.0 0.0; 0.0 -1.0]
successful: false
```
"""
issuccess(F::Factorization)
function logdet(F::Factorization)
d, s = logabsdet(F)
return d + log(s)
end
function det(F::Factorization)
d, s = logabsdet(F)
return exp(d)*s
end
### General promotion rules
convert(::Type{Factorization{T}}, F::Factorization{T}) where {T} = F
inv(F::Factorization{T}) where {T} = A_ldiv_B!(F, eye(T, size(F,1)))
Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, h, 1:nfields(F))
Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F))
Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))
# With a real lhs and complex rhs with the same precision, we can reinterpret
# the complex rhs as a real rhs with twice the number of columns
function (\)(F::Factorization{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal
c2r = reshape(transpose(reinterpret(T, B, (2, length(B)))), size(B, 1), 2*size(B, 2))
x = A_ldiv_B!(F, c2r)
return reinterpret(Complex{T}, transpose(reshape(x, div(length(x), 2), 2)), _ret_size(F, B))
end
for (f1, f2) in ((:\, :A_ldiv_B!),
(:Ac_ldiv_B, :Ac_ldiv_B!))
@eval begin
function $f1(F::Factorization, B::AbstractVecOrMat)
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
BB = similar(B, TFB, size(B))
copy!(BB, B)
$f2(F, BB)
end
end
end
# support the same 3-arg idiom as in our other in-place A_*_B functions:
for f in (:A_ldiv_B!, :Ac_ldiv_B!, :At_ldiv_B!)
@eval $f(Y::AbstractVecOrMat, A::Factorization, B::AbstractVecOrMat) =
$f(A, copy!(Y, B))
end
# fallback methods for transposed solves
At_ldiv_B(F::Factorization{<:Real}, B::AbstractVecOrMat) = Ac_ldiv_B(F, B)
At_ldiv_B(F::Factorization, B) = conj.(Ac_ldiv_B(F, conj.(B)))
"""
A_ldiv_B!([Y,] A, B) -> Y
Compute `A \\ B` in-place and store the result in `Y`, returning the result.
If only two arguments are passed, then `A_ldiv_B!(A, B)` overwrites `B` with
the result.
The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a
factorization object (e.g. produced by [`factorize`](@ref) or [`cholfact`](@ref)).
The reason for this is that factorization itself is both expensive and typically allocates memory
(although it can also be done in-place via, e.g., [`lufact!`](@ref)),
and performance-critical situations requiring `A_ldiv_B!` usually also require fine-grained
control over the factorization of `A`.
"""
A_ldiv_B!
"""
Ac_ldiv_B!([Y,] A, B) -> Y
Similar to [`A_ldiv_B!`](@ref), but return ``Aᴴ`` \\ ``B``,
computing the result in-place in `Y` (or overwriting `B` if `Y` is not supplied).
"""
Ac_ldiv_B!
"""
At_ldiv_B!([Y,] A, B) -> Y
Similar to [`A_ldiv_B!`](@ref), but return ``Aᵀ`` \\ ``B``,
computing the result in-place in `Y` (or overwriting `B` if `Y` is not supplied).
"""
At_ldiv_B!