Skip to content

Commit be11c3d

Browse files
author
mohamed82008
committed
Decoupling nev and block size and some minor cleanup
1 parent 40b2d03 commit be11c3d

File tree

2 files changed

+200
-37
lines changed

2 files changed

+200
-37
lines changed

src/lobpcg.jl

Lines changed: 164 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,42 @@ function Base.show(io::IO, tr::LOBPCGTrace)
5050
return
5151
end
5252

53-
struct LOBPCGResults{T, TL, TR, TX, TI, TTrace <: LOBPCGTrace}
53+
struct LOBPCGResults{TL, TX, T, TR, TI, TM, TB, TTrace <: Union{LOBPCGTrace, AbstractVector{<:LOBPCGTrace}}}
5454
λ::TL
5555
X::TX
56-
iterations::Int
57-
residual_norms::TR
5856
tolerance::T
59-
converged::Bool
60-
maxiter::TI
57+
residual_norms::TR
58+
iterations::TI
59+
maxiter::TM
60+
converged::TB
6161
trace::TTrace
6262
end
63+
function EmptyLOBPCGResults(X::TX, k::Integer, tolerance, maxiter) where {T, TX<:AbstractMatrix{T}}
64+
blocksize = size(X,2)
65+
λ = Vector{T}(k)
66+
X = TX(size(X, 1), k)
67+
68+
iterations = zeros(Int, ceil(Int, k/blocksize))
69+
residual_norms = copy(λ)
70+
converged = falses(k)
71+
trace = fill(LOBPCGTrace{Vector{real(T)},Vector{T}}(), k÷blocksize+1)
72+
73+
return LOBPCGResults(λ, X, tolerance, residual_norms, iterations, maxiter, converged, trace)
74+
end
75+
76+
function Base.append!(r1::LOBPCGResults, r2::LOBPCGResults, n1, n2=length(r2.λ))
77+
n = n1 + n2
78+
r1.λ[n1+1:n] .= @view r2.λ[end-n2+1:end]
79+
r1.residual_norms[n1+1:n] .= @view r2.residual_norms[end-n2+1:end]
80+
r1.X[:, n1+1:n] .= @view r2.X[:, end-n2+1:end]
81+
82+
ind = n1 ÷ length(r2.λ) + 1
83+
r1.iterations[ind] = r2.iterations
84+
r1.converged[n1+1:n] .= r2.converged
85+
r1.trace[ind] = r2.trace
86+
87+
return r1
88+
end
6389
function Base.show(io::IO, r::LOBPCGResults)
6490
first_two(fr) = [x for (i, x) in enumerate(fr)][1:2]
6591

@@ -79,7 +105,7 @@ function Base.show(io::IO, r::LOBPCGResults)
79105
end
80106
@printf io " * Convergence\n"
81107
@printf io " * Iterations: %s\n" r.iterations
82-
@printf io " * Converged: %s\n" r.converged
108+
@printf io " * Converged: %s\n" all(r.converged)
83109
@printf io " * Iterations limit: %s\n" r.maxiter
84110

85111
return
@@ -112,47 +138,85 @@ function B_mul_X!(b::Blocks{false}, B, n = 0)
112138
return
113139
end
114140

115-
struct Constraint{T, TVorM<:Union{AbstractVector{T}, AbstractMatrix{T}}, TM<:AbstractMatrix{T}, TC}
141+
mutable struct Constraint{T, TVorM<:Union{AbstractVector{T}, AbstractMatrix{T}}, TM<:AbstractMatrix{T}, TC}
116142
Y::TVorM
117143
BY::TVorM
118144
gram_chol::TC
119145
gramYBV::TM # to be used in view
120146
tmp::TM # to be used in view
121147
end
122-
function Constraint(::Void, B, X)
148+
struct BWrapper end
149+
struct NotBWrapper end
150+
151+
Constraint(::Void, B, X) = Constraint(nothing, B, X, BWrapper())
152+
Constraint(::Void, B, X, ::NotBWrapper) = Constraint(nothing, B, X, BWrapper())
153+
function Constraint(::Void, B, X, ::BWrapper)
123154
return Constraint{Void, Matrix{Void}, Matrix{Void}, Void}(Matrix{Void}(0,0), Matrix{Void}(0,0), nothing, Matrix{Void}(0,0), Matrix{Void}(0,0))
124155
end
125-
function Constraint(Y, B, X)
156+
157+
Constraint(Y, B, X) = Constraint(Y, B, X, BWrapper())
158+
function Constraint(Y, B, X, ::BWrapper)
126159
T = eltype(X)
127160
if B isa Void
128161
BY = Y
129162
else
130163
BY = similar(Y)
131164
A_mul_B!(BY, B, Y)
132165
end
133-
gramYBY = Ac_mul_B(Y, BY)
166+
return Constraint(Y, BY, X, NotBWrapper())
167+
end
168+
function Constraint(Y, BY, X, ::NotBWrapper)
169+
T = eltype(X)
170+
if Y isa SubArray
171+
gramYBY = @view eye(T, size(Y.parent, 2))[1:size(Y, 2), 1:size(Y, 2)]
172+
Ac_mul_B!(gramYBY, Y, BY)
173+
gramYBV = @view zeros(T, size(Y.parent, 2), size(X, 2))[1:size(Y, 2), :]
174+
else
175+
gramYBY = Ac_mul_B(Y, BY)
176+
gramYBV = zeros(T, size(Y, 2), size(X, 2))
177+
end
134178
realdiag!(gramYBY)
135179
gramYBY_chol = cholfact!(Hermitian(gramYBY))
136-
gramYBV = zeros(T, size(Y, 2), size(X, 2))
137-
tmp = similar(gramYBV)
180+
tmp = deepcopy(gramYBV)
138181

139182
return Constraint{eltype(Y), typeof(Y), typeof(gramYBV), typeof(gramYBY_chol)}(Y, BY, gramYBY_chol, gramYBV, tmp)
140183
end
141184

185+
function update!(c::Constraint, X, BX)
186+
sizeY = size(c.Y, 2)
187+
sizeX = size(X, 2)
188+
c.Y.parent[:, sizeY+1:sizeY+sizeX] .= X
189+
if X !== BX
190+
c.BY.parent[:, sizeY+1:sizeY+sizeX] .= BX
191+
end
192+
sizeY += sizeX
193+
Y = @view c.Y.parent[:, 1:sizeY]
194+
BY = @view c.BY.parent[:, 1:sizeY]
195+
c.Y = Y
196+
c.BY = BY
197+
gram_chol = c.gram_chol
198+
new_factors = @view gram_chol.factors.parent[1:sizeY, 1:sizeY]
199+
c.gram_chol = typeof(gram_chol)(new_factors, gram_chol.uplo)
200+
c.gramYBV = @view c.gramYBV.parent[1:sizeY, :]
201+
c.tmp = @view c.tmp.parent[1:sizeY, :]
202+
return c
203+
end
204+
142205
function (constr!::Constraint{Void})(X, X_temp)
143206
nothing
144207
end
145208

146209
function (constr!::Constraint)(X, X_temp)
147-
sizeX = size(X, 2)
148-
sizeY = size(constr!.Y, 2)
149-
gramYBV_view = view(constr!.gramYBV, 1:sizeY, 1:sizeX)
150-
Ac_mul_B!(gramYBV_view, constr!.BY, X)
151-
tmp_view = view(constr!.tmp, 1:sizeY, 1:sizeX)
152-
A_ldiv_B!(tmp_view, constr!.gram_chol, gramYBV_view)
153-
A_mul_B!(X_temp, constr!.Y, tmp_view)
154-
@inbounds X .= X .- X_temp
155-
210+
if size(constr!.Y, 2) > 0
211+
sizeX = size(X, 2)
212+
sizeY = size(constr!.Y, 2)
213+
gramYBV_view = view(constr!.gramYBV, 1:sizeY, 1:sizeX)
214+
Ac_mul_B!(gramYBV_view, constr!.BY, X)
215+
tmp_view = view(constr!.tmp, 1:sizeY, 1:sizeX)
216+
A_ldiv_B!(tmp_view, constr!.gram_chol, gramYBV_view)
217+
A_mul_B!(X_temp, constr!.Y, tmp_view)
218+
@inbounds X .= X .- X_temp
219+
end
156220
nothing
157221
end
158222

@@ -170,7 +234,7 @@ function (precond!::RPreconditioner)(X)
170234
bs = size(X, 2)
171235
A_ldiv_B!(view(precond!.buffer, :, 1:bs), precond!.M, X)
172236
# Just returning buffer would be cheaper but struct at call site must be mutable
173-
@inbounds X .= view(precond!.buffer, :, 1:bs)
237+
@inbounds X .= @view precond!.buffer[:, 1:bs]
174238
nothing
175239
end
176240

@@ -379,13 +443,14 @@ LOBPCGIterator(A, X, largest::Bool, P=nothing, C=nothing) = LOBPCGIterator(A, no
379443
- `P`: preconditioner of residual vectors, must overload `A_ldiv_B!`;
380444
- `C`: constraint to deflate the residual and solution vectors orthogonal
381445
to a subspace; must overload `A_mul_B!`;
382-
383446
"""
384447
function LOBPCGIterator(A, B, X, largest::Bool, P=nothing, C=nothing)
385-
T = eltype(X)
386-
constr! = Constraint(C, B, X)
448+
constr! = Constraint(C, B, X, BWrapper())
387449
precond! = RPreconditioner(P, X)
388-
450+
return LOBPCGIterator(A, B, X, largest, constr!, precond!)
451+
end
452+
function LOBPCGIterator(A, B, X, largest::Bool, constr!::Constraint, precond!::RPreconditioner)
453+
T = eltype(X)
389454
nev = size(X, 2)
390455
if B isa Void
391456
XBlocks = Blocks(X, similar(X))
@@ -423,6 +488,35 @@ function LOBPCGIterator(A, B, X, largest::Bool, P=nothing, C=nothing)
423488

424489
return LOBPCGIterator{generalized, T, typeof(A), typeof(B), typeof(λ), typeof(residuals), typeof(λperm), typeof(V), typeof(XBlocks), typeof(ortho!), typeof(precond!), typeof(constr!), typeof(gramABlock), typeof(activeMask), typeof(trace)}(A, B, ritz_values, λperm, λ, V, residuals, largest, XBlocks, tempXBlocks, PBlocks, activePBlocks, RBlocks, activeRBlocks, iteration, currentBlockSize, ortho!, precond!, constr!, gramABlock, gramBBlock, gramA, gramB, activeMask, trace)
425490
end
491+
function LOBPCGIterator(A, X, largest::Bool, nev::Int, P=nothing, C=nothing)
492+
LOBPCGIterator(A, nothing, X, largest, nev, P, C)
493+
end
494+
function LOBPCGIterator(A, B, X, largest::Bool, nev::Int, P=nothing, C=nothing)
495+
T = eltype(X)
496+
n = size(X, 1)
497+
sizeX = size(X, 2)
498+
if C isa Void
499+
sizeC = 0
500+
new_C = typeof(X)(n, (nev÷sizeX)*sizeX)
501+
else
502+
sizeC = size(C,2)
503+
new_C = typeof(C)(n, sizeC+(nev÷sizeX)*sizeX)
504+
new_C[:,1:sizeC] .= C
505+
end
506+
if B isa Void
507+
new_BC = new_C
508+
else
509+
new_BC = similar(new_C)
510+
end
511+
Y = @view new_C[:, 1:sizeC]
512+
BY = @view new_BC[:, 1:sizeC]
513+
if !(B isa Void)
514+
A_mul_B!(BY, B, Y)
515+
end
516+
constr! = Constraint(Y, BY, X, NotBWrapper())
517+
precond! = RPreconditioner(P, X)
518+
return LOBPCGIterator(A, B, X, largest, constr!, precond!)
519+
end
426520

427521
function ortho_AB_mul_X!(blocks::Blocks, ortho!, A, B, bs=-1)
428522
# Finds BX
@@ -683,10 +777,10 @@ Finds the `nev` extremal eigenvalues and their corresponding eigenvectors satisf
683777
684778
- `results`: a `LOBPCGResults` struct. `r.λ` and `r.X` store the eigenvalues and eigenvectors.
685779
"""
686-
function lobpcg(A, largest::Bool, nev::Int=1; kwargs...)
780+
function lobpcg(A, largest::Bool, nev::Int; kwargs...)
687781
lobpcg(A, nothing, largest, nev; kwargs...)
688782
end
689-
function lobpcg(A, B, largest::Bool, nev::Int=1; kwargs...)
783+
function lobpcg(A, B, largest::Bool, nev::Int; kwargs...)
690784
lobpcg(A, B, largest, rand(eltype(A), size(A, 1), nev); not_zeros=true, kwargs...)
691785
end
692786

@@ -720,13 +814,12 @@ end
720814
721815
- `results`: a `LOBPCGResults` struct. `r.λ` and `r.X` store the eigenvalues and eigenvectors.
722816
"""
723-
function lobpcg(A, largest::Bool, X0::Union{AbstractMatrix, AbstractVector}; kwargs...)
817+
function lobpcg(A, largest::Bool, X0; kwargs...)
724818
lobpcg(A, nothing, largest, X0; kwargs...)
725819
end
726820
function lobpcg(A, B, largest, X0;
727821
not_zeros=false, log=false, P=nothing,
728822
C=nothing, tol=nothing, maxiter=200)
729-
730823
X = copy(X0)
731824
T = eltype(X)
732825
n = size(X, 1)
@@ -776,7 +869,7 @@ function lobpcg!(iterator::LOBPCGIterator; log=false, tol=nothing, maxiter=200,
776869
end
777870
n = size(X, 1)
778871
sizeX = size(X, 2)
779-
residualTolerance = (tol isa Void) ? (eps(real(T)))^(real(T)(4)/10) : tol
872+
residualTolerance = (tol isa Void) ? (eps(real(T)))^(real(T)(4)/10) : real(tol)
780873
iterator.iteration[] = 1
781874
while iterator.iteration[] <= maxiter
782875
state = iterator(residualTolerance, log)
@@ -788,7 +881,46 @@ function lobpcg!(iterator::LOBPCGIterator; log=false, tol=nothing, maxiter=200,
788881
end
789882
@inbounds iterator.λ .= view(iterator.ritz_values, 1:sizeX)
790883

791-
results = LOBPCGResults(iterator.λ, X, iterator.iteration[], iterator.residuals[1:sizeX], residualTolerance, all((x)->(norm(x)<=residualTolerance), view(iterator.residuals, 1:sizeX)), maxiter, iterator.trace)
884+
results = LOBPCGResults(iterator.λ, X, residualTolerance, iterator.residuals, iterator.iteration[], maxiter, all((x)->(norm(x)<=residualTolerance), view(iterator.residuals, 1:sizeX)), iterator.trace)
792885

793886
return results
794887
end
888+
889+
function lobpcg(A, largest::Bool, X0, nev::Int; kwargs...)
890+
lobpcg(A, nothing, largest, X0, nev; kwargs...)
891+
end
892+
function lobpcg(A, B, largest::Bool, X0, nev::Int;
893+
not_zeros=false, log=false, P=nothing,
894+
C=nothing, tol=nothing, maxiter=200)
895+
T = eltype(X0)
896+
n = size(X0, 1)
897+
sizeX = size(X0, 2)
898+
nev > n && throw("Number of eigenvectors desired exceeds the row dimension.")
899+
900+
sizeX = min(nev, sizeX)
901+
X = X0[:, 1:sizeX]
902+
iterator = LOBPCGIterator(A, B, X, largest, nev, C, P)
903+
904+
r = EmptyLOBPCGResults(X, nev, tol, maxiter)
905+
rnext = lobpcg!(iterator, log=log, tol=tol, maxiter=maxiter, not_zeros=not_zeros)
906+
append!(r, rnext, 0)
907+
converged_x = sizeX
908+
while converged_x < nev
909+
if nev-converged_x < sizeX
910+
cutoff = sizeX-(nev-converged_x)
911+
update!(iterator.constr!, view(iterator.XBlocks.block, :, 1:cutoff), view(iterator.XBlocks.B_block, :, 1:cutoff))
912+
X[:, 1:sizeX-cutoff] .= @view X[:, cutoff+1:sizeX]
913+
rand!(view(X, :, cutoff+1:sizeX))
914+
rnext = lobpcg!(iterator, log=log, tol=tol, maxiter=maxiter, not_zeros=true)
915+
append!(r, rnext, converged_x, sizeX-cutoff)
916+
converged_x += sizeX-cutoff
917+
else
918+
update!(iterator.constr!, iterator.XBlocks.block, iterator.XBlocks.B_block)
919+
rand!(X)
920+
rnext = lobpcg!(iterator, log=log, tol=tol, maxiter=maxiter, not_zeros=true)
921+
append!(r, rnext, converged_x)
922+
converged_x += sizeX
923+
end
924+
end
925+
return r
926+
end

test/lobpcg.jl

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ using Base.Test
44

55
# Already defined in another file
66
#=
7-
import Base.A_ldiv_B!
8-
97
include("laplace_matrix.jl")
108
119
struct JacobiPrec{TD}
1210
diagonal::TD
1311
end
1412
15-
A_ldiv_B!(y, P::JacobiPrec, x) = y .= x ./ P.diagonal
13+
Base.A_ldiv_B!(y, P::JacobiPrec, x) = y .= x ./ P.diagonal
1614
=#
1715

1816
function max_err(R)
@@ -211,7 +209,6 @@ end
211209
end
212210
end
213211
end
214-
215212
@testset "Constraint" begin
216213
@testset "Simple eigenvalue problem" begin
217214
@testset "Matrix{$T}" for T in (Float32, Float64, Complex64, Complex128)
@@ -235,7 +232,7 @@ end
235232
A = A' * A + I
236233
B = rand(T, n, n)
237234
B = B' * B + I
238-
tol = eps(real(T))
235+
tol = eps(real(T))^0.4
239236
r = lobpcg(A, B, largest, 1; tol=tol, maxiter=Inf, log=false)
240237
λ1, X1 = r.λ, r.X
241238
r = lobpcg(A, B, largest, 1; C=copy(r.X), tol=tol, maxiter=Inf, log=false)
@@ -289,4 +286,38 @@ end
289286
end
290287
end
291288
end
289+
@testset "nev = 3, block size = 2" begin
290+
n = 10
291+
@testset "Simple eigenvalue problem" begin
292+
@testset "Matrix{$T}" for T in (Float32, Float64, Complex64, Complex128)
293+
@testset "largest = $largest" for largest in (true, false)
294+
A = rand(T, n, n)
295+
A = A' * A + I
296+
tol = eps(real(T))^0.4
297+
X0 = rand(T, n, 2)
298+
r = lobpcg(A, largest, X0, 3, tol=tol, maxiter=Inf, log=true)
299+
λ, X = r.λ, r.X
300+
@test max_err(A*X - X*diagm(λ)) tol
301+
@test all(isapprox.(Ac_mul_B(X, X), eye(3), atol=n*tol))
302+
end
303+
end
304+
end
305+
@testset "Generalized eigenvalue problem" begin
306+
@testset "Matrix{$T}" for T in (Float32, Float64, Complex64, Complex128)
307+
@testset "largest = $largest" for largest in (true, false)
308+
A = rand(T, n, n)
309+
A = A' * A + I
310+
B = rand(T, n, n)
311+
B = B' * B + I
312+
tol = eps(real(T))^0.4
313+
314+
X0 = rand(T, n, 2)
315+
r = lobpcg(A, B, largest, X0, 3, tol=tol, maxiter=Inf, log=true)
316+
λ, X = r.λ, r.X
317+
@test max_err(A*X - B*X*diagm(λ)) tol
318+
@test all(isapprox.(Ac_mul_B(X, B*X), eye(3), atol=n*tol))
319+
end
320+
end
321+
end
322+
end
292323
end

0 commit comments

Comments
 (0)