Skip to content

Create blockcholesky.jl #126

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

Merged
merged 21 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/BlockArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ using Base: ReshapedArray, dataids
import Base: (:), IteratorSize, iterate, axes1, strides, isempty
import Base.Broadcast: broadcasted, DefaultArrayStyle, AbstractArrayStyle, Broadcasted, broadcastable
import LinearAlgebra: lmul!, rmul!, AbstractTriangular, HermOrSym, AdjOrTrans,
StructuredMatrixStyle
StructuredMatrixStyle, cholesky, cholesky!, cholcopy
import ArrayLayouts: _fill_lmul!, MatMulVecAdd, MatMulMatAdd, MatLmulVec, MatLdivVec,
materialize!, MemoryLayout, sublayout, transposelayout, conjlayout,
triangularlayout, triangulardata, _inv, _copyto!, axes_print_matrix_row,
Expand All @@ -59,5 +59,6 @@ include("blockproduct.jl")
include("show.jl")
include("blockreduce.jl")
include("blockdeque.jl")
include("blockcholesky.jl")

end # module
83 changes: 83 additions & 0 deletions src/blockcholesky.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@


##########################
# Cholesky Factorization #
##########################

"""
Funtions to do
2.check square
3.check positive definite
"""

cholesky(A::Symmetric{<:Real,<:BlockArray},
::Val{false}=Val(false); check::Bool = true) = cholesky!(cholcopy(A); check = check)


function b_chol!(A::BlockArray{T}, ::Type{UpperTriangular}) where T<:Real
Copy link
Member

Choose a reason for hiding this comment

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

Convention in Julia is that internal routines start with _. So perhaps rename this function _block_chol!?

n = blocksize(A)[1]

@inbounds begin
# Initializing the first role of blocks
cholesky!(Symmetric(getblock(A,1,1)))
for j = 2:n
ldiv!(UpperTriangular(getblock(A,1,1))', getblock(A,1,j))
end

# For the left blocks
for i = 2:n
Pii = getblock(A,i,i)
for k = 1:i-1
muladd!(-1.0, getblock(A,k,i)', getblock(A,k,i), 1.0, Pii)
end
cholesky!(Symmetric(Pii))

for j = i+1:n
Pij = getblock(A,i,j)
for k = 1:i-1
muladd!(-1.0, getblock(A,k,i)', getblock(A,k,j), 1.0, Pij)
end
ldiv!(UpperTriangular(getblock(A,i,i))', Pij)
end
end
end

return UpperTriangular(A)
Copy link
Member

@dlfivefifty dlfivefifty Jul 23, 2020

Choose a reason for hiding this comment

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

Please have this return Cholesky(A, :U, 0). The 0 should actually be info which is determined from the cholesky! call, so probably what we want is

info = 0
for ...
   ...
    info = cholesky!(Symmetric(Pii))
   iszero(info) || break
   ...
end
Cholesky(A, :U, info)

though we should double check what info actually means: that is, do we break and info indicates a problem, or do we continue as if it were SPD?

Similarly for lower below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the document of PosDefException(info), it says that the info field indicates the location of (one of) the eigenvalue(s) which is (are) less than/equal to 0. And the info is determined by the function LAPACK.potrf!(uplo, A). So we can do the check of SPD in a similar way with LinearAlgebra.cholesky

Copy link
Member

Choose a reason for hiding this comment

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

I think you mean indicates that there exists a negative eigenvalue, not that it finds one. Here is the doc for LAPACK.potrf!: http://www.netlib.org/lapack/explore-html/d1/d7a/group__double_p_ocomputational_ga2f55f604a6003d03b5cd4a0adcfb74d6.html

INFO is INTEGER
          = 0:  successful exit
          < 0:  if INFO = -i, the i-th argument had an illegal value
          > 0:  if INFO = i, the leading minor of order i is not
                positive definite, and the factorization could not be
                completed.

So I think we want

if !iszero(info)
    @assert info > 0 # should never have illegal values
    info += sum(blockaxes(A,2)[Block.(1:i-1)]) # add columns before we got to block to indicate correct minor
    break
end

end
function b_chol!(A::BlockArray{T}, ::Type{LowerTriangular}) where T<:Real
n = blocksize(A)[1]

@inbounds begin
# Initializing the first role of blocks
cholesky!(Symmetric(getblock(A,1,1), :L))
for j = 2:n
rdiv!(getblock(A,j,1), LowerTriangular(getblock(A,1,1))')
end

# For the left blocks
for i = 2:n
Pii = getblock(A,i,i)
for k = 1:i-1
muladd!(-1.0, getblock(A,i,k), getblock(A,i,k)', 1.0, Pii)
Copy link
Member

Choose a reason for hiding this comment

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

1.0 should be one(T)

end
cholesky!(Symmetric(Pii, :L))

for j = i+1:n
Pij = getblock(A,j,i)
for k = 1:i-1
muladd!(-1.0, getblock(A,j,k), getblock(A,i,k)', 1.0, Pij)
end
rdiv!(Pij, LowerTriangular(getblock(A,i,i))')
end
end
end

return LowerTriangular(A)
end

function cholesky!(A::Symmetric{<:Real,<:BlockArray}, ::Val{false}=Val(false); check::Bool = true)
C = b_chol!(A.data, A.uplo == 'U' ? UpperTriangular : LowerTriangular)
#check && checkpositivedefinite(info)
return Cholesky(C.data, A.uplo, 0)
end

1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ using BlockArrays, LinearAlgebra, Test
include("test_blockproduct.jl")
include("test_blockreduce.jl")
include("test_blockdeque.jl")
include("test_cholesky.jl")
end
35 changes: 35 additions & 0 deletions test/test_cholesky.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BlockArrays, Test, LinearAlgebra



@testset "Block cholesky" begin

# Generating random positive definite and symmetric matrices
A = BlockArray{Float64}(randn(9,9)+100I, fill(3,3), fill(3,3)); A = Symmetric(A)
B = BlockArray{Float64}(randn(55,55)+100I, 1:10, 1:10); B = Symmetric(B)
Copy link
Member

Choose a reason for hiding this comment

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

Add tests with Float32

C = BlockArray{Float64}(randn(9,9)+100I, fill(3,3), fill(3,3)); C = Symmetric(C, :L)
D = BlockArray{Float64}(randn(55,55)+100I, 1:10, 1:10); D = Symmetric(D, :L)

A_T = Matrix(A)
B_T = Matrix(B)
C_T = Matrix(C)
D_T = Matrix(D)

#Tests on A
@test cholesky(A).U ≈ cholesky(A_T).U
@test cholesky(A).U'cholesky(A).U ≈ A

#Tests on B
@test cholesky(B).U ≈ cholesky(B_T).U
@test cholesky(B).U'cholesky(B).U ≈ B

#Tests on C
@test cholesky(C).L ≈ cholesky(C_T).L
@test cholesky(C).L*cholesky(C).L' ≈ C

#tests on D
@test cholesky(D).L ≈ cholesky(D_T).L
@test cholesky(D).L*cholesky(D).L' ≈ D

end