Skip to content

Commit db2f47c

Browse files
committed
Addition of blockFGMRES + testing. Previously was in multigrid package.
1 parent 4bf807f commit db2f47c

File tree

7 files changed

+258
-16
lines changed

7 files changed

+258
-16
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "KrylovMethods"
22
uuid = "9a2cd570-f05c-5dc1-9209-93ad6f5727f7"
3-
version = "0.5.0"
3+
version = "0.6.0"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

src/KrylovMethods.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module KrylovMethods
1313
include("blockBiCGSTB.jl")
1414
include("gmres.jl")
1515
include("fgmres.jl")
16+
include("blockFGMRES.jl")
1617
include("lanczosBidiag.jl")
1718
include("ssor.jl")
1819
include("lsqr.jl")

src/blockFGMRES.jl

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
2+
import Base.isempty
3+
using LinearAlgebra
4+
export blockFGMRES
5+
6+
function blockFGMRES(A::SparseMatrixCSC{T1,Int},b::Array{T2,2},restrt::Int; kwargs...) where {T1,T2}
7+
Ax = zeros(promote_type(T1,T2),size(b))
8+
return blockFGMRES(X -> mul!(Ax,A,X,1.0,0.0),b,restrt;kwargs...)
9+
end
10+
11+
blockFGMRES(A,b::Array{Any,2},restrt;kwargs...) = blockFGMRES(x -> A*x ,b,restrt;kwargs...)
12+
13+
14+
"""
15+
x,flag,err,iter,resvec = blockFGMRES(A,b,restrt,tol=1e-2,maxIter=100,M=1,x=[],out=0)
16+
17+
Block (flexible) Generalized Minimal residual ( (F)GMRES(m) ) method with restarts applied to A*x = b.
18+
19+
This is the "right preconditioning" version of blockGMRES: A*M^{-1}Mx = b.
20+
21+
Input:
22+
23+
A - function computing A*x
24+
b - right hand side matrix (set of vectors)
25+
restrt - number of iterations between restarts
26+
tol - error tolerance
27+
maxIter - maximum number of iterations
28+
M - preconditioner, function computing M\\x
29+
x - starting guess
30+
out - flag for output (0 : only errors, 1 : final status, 2: error at each iteration)
31+
32+
Output:
33+
34+
x - approximate solution
35+
flag - exit flag ( 0 : desired tolerance achieved,
36+
-1 : maxIter reached without converging
37+
-9 : right hand side was zero)
38+
err - norm of relative residual, i.e., norm(A*x-b)/norm(b)
39+
iter - number of iterations
40+
resvec - norm of relative residual at each iteration
41+
42+
preconditioner M(r) must return a copy of a matrix. Cannot reuse memory of r.
43+
"""
44+
function blockFGMRES(A::Function,B::Array,restrt::Int; tol::Real=1e-2,maxIter::Int=100,M::Function=t->copy(t),X::Array=[],out::Int=0,flexible::Bool=false,mem::FGMRESmem = getEmptyFGMRESmem())
45+
# initialization
46+
n = size(B,1)
47+
m = size(B,2)
48+
TYPE = eltype(B)
49+
mem = checkMemorySize(mem,n,restrt,TYPE,flexible,m);
50+
51+
if norm(B)==0.0
52+
return zeros(TYPE,n,m),-9,0.0,0,[0.0];
53+
end
54+
55+
if Base.isempty(X)
56+
X = zeros(eltype(B),n,m)
57+
R = copy(B);
58+
elseif norm(X) < eps(real(TYPE))
59+
R = copy(B);
60+
else
61+
R = copy(B);
62+
R.-=A(X);
63+
end
64+
65+
if eltype(B) <: Complex
66+
X = complex(X)
67+
end
68+
if issparse(X)
69+
error("X is sparse");
70+
end
71+
72+
rnorm0 = norm(B);
73+
74+
err = norm(R)/rnorm0
75+
if err < tol; return X, err; end
76+
77+
constOne = one(TYPE);
78+
constZero = zero(TYPE);
79+
80+
restrt = min(restrt,n-1)
81+
Zbig = mem.Z;
82+
Vbig = mem.V;
83+
H = zeros(TYPE,(restrt+1)*m,restrt*m);
84+
xi = zeros(TYPE,(restrt+1)*m,m);
85+
T = zeros(TYPE,restrt*m,m);
86+
87+
W = zeros(TYPE,0);
88+
Z = zeros(TYPE,0);
89+
90+
resvec = zeros(restrt*maxIter)
91+
if out==2
92+
println(@sprintf("=== blockFGMRES ===\n%4s\t%7s\n","iter","relres"))
93+
end
94+
95+
96+
flag = -1
97+
counter = 0
98+
iter = 0
99+
while iter < maxIter
100+
iter+=1;
101+
Q = qr!(R);
102+
Betta = Q.R;
103+
R[:] = Matrix(Q.Q); # this code is equivalent to (W,Betta) = qr(r);
104+
xi[1:m,:] = Betta;
105+
#BLAS.scal!(n,(1/betta)*constOne,r,1); # w = w./betta
106+
H[:] .= 0.0;
107+
T[:] .= 0.0;
108+
109+
if out==2;; print(@sprintf("%3d\t", iter));end
110+
111+
for j = 1:restrt
112+
colSet_j = (((j-1)*m)+1):j*m
113+
if j==1
114+
Vbig[:,colSet_j] = R; # no memory problem with this line....
115+
Z = M(R);
116+
else
117+
Vbig[:,colSet_j] = W; # no memory problem with this line....
118+
Z = M(W);
119+
end
120+
if flexible
121+
Zbig[:,colSet_j] = Z;
122+
end
123+
W = A(Z);
124+
counter += 1;
125+
# Gram Schmidt (much faster than MGS even though zeros are multiplied, does relatively well):
126+
BLAS.gemm!('C','N', constOne, Vbig, W,constZero,T); # t = V'*w;
127+
T[(j*m+1):end,:] .= 0.0;
128+
H[1:(restrt*m),colSet_j] = T;
129+
BLAS.gemm!('N','N', -constOne, Vbig, T,constOne,W); # w = w - V*t
130+
Q = qr!(W); Betta = Q.R; W[:] = Matrix(Q.Q); # this code is equivalent to (W,Betta) = qr(W);
131+
H[((j*m)+1):(j+1)*m,colSet_j] = Betta;
132+
y = H[1:(j+1)*m,1:j*m]\xi[1:(j+1)*m,:];
133+
err = norm(H[1:(j+1)*m,1:j*m]*y - xi[1:(j+1)*m,:])/rnorm0
134+
135+
if out==2 print(@sprintf("%1.1e ", err)); end
136+
resvec[counter] = err;
137+
if err <= tol
138+
if flexible
139+
Zbig[:,(j*m+1):end] .= 0.0;
140+
else
141+
Vbig[:,(j*m+1):end] .= 0.0;
142+
end
143+
if out==2; print("\n"); end
144+
flag = 0; break
145+
end
146+
end # end for j to restrt
147+
148+
y = pinv(H)*xi;
149+
150+
if flexible
151+
BLAS.gemm!('N','N', constOne, Zbig, y,constZero,W); # w = Z*y #This is the correction that corresponds to the residual.
152+
else
153+
# W = Vbig*y;
154+
BLAS.gemm!('N','N', constOne, Vbig, y, constZero, W);
155+
Z = M(W);
156+
W[:] = Z;
157+
end
158+
X .+= W;
159+
160+
if out==2; print("\n"); end
161+
if err <= tol
162+
flag = 0;
163+
break
164+
end
165+
if iter < maxIter
166+
R[:] = B;
167+
W = A(X);
168+
R .-= W;
169+
end
170+
end #for iter to maxiter
171+
if out>=0
172+
if flag==-1
173+
println(@sprintf("blockFGMRES iterated maxIter (=%d) outer iterations without achieving the desired tolerance. Acheived: %1.2e",maxIter,err))
174+
elseif flag==0 && out>=1
175+
println(@sprintf("blockFGMRES achieved desired tolerance at inner iteration %d. Residual norm is %1.2e.",counter,resvec[counter]))
176+
end
177+
end
178+
return X,flag,resvec[counter],iter,resvec[1:counter]
179+
end #blockFGMRES

src/fgmres.jl

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ mutable struct FGMRESmem
1515
Z ::Array
1616
end
1717

18-
function getFGMRESmem(n::Int,flexible::Bool,T::Type,k::Int)
18+
function getFGMRESmem(n::Int,flexible::Bool,T::Type,k::Int,nrhs::Int=1)
1919
if flexible
20-
return FGMRESmem(zeros(T,n,k),zeros(T,n,k));
20+
return FGMRESmem(zeros(T,n,k*nrhs),zeros(T,n,k*nrhs));
2121
else
22-
return FGMRESmem(zeros(T,n,k),zeros(T,0));
22+
return FGMRESmem(zeros(T,n,k*nrhs),zeros(T,0));
2323
end
2424
end
2525

@@ -31,14 +31,14 @@ function isempty(mem::FGMRESmem)
3131
return size(mem.V,1)==0;
3232
end
3333

34-
function checkMemorySize(mem::FGMRESmem,n::Int,k::Int,TYPE::Type,flexible::Bool)
34+
function checkMemorySize(mem::FGMRESmem,n::Int,k::Int,TYPE::Type,flexible::Bool,nrhs::Int=1)
3535
if isempty(mem)
3636
# warn("Allocating memory in FGMRES")
37-
mem = getFGMRESmem(n,flexible,TYPE,k);
37+
mem = getFGMRESmem(n,flexible,TYPE,k,nrhs);
3838
return mem
3939
else
40-
if size(mem.V,2)!= k
41-
error("FGMRES: size of Krylov subspace is different than inner");
40+
if size(mem.V,2)!= k*nrhs
41+
error("FGMRES: size of Krylov subspace is different than inner*nrhs");
4242
end
4343
end
4444
end
@@ -71,7 +71,7 @@ flag - exit flag ( 0 : desired tolerance achieved,
7171
iter - number of iterations
7272
resvec - norm of relative residual at each iteration
7373
74-
preconditioner M(r) must return a copy of a vector. Cannot reuse memory of
74+
preconditioner M(r) must return a copy of a vector. Cannot reuse memory of r.
7575
"""
7676
function fgmres(A::Function,b::Vector,restrt::Int; tol::Real=1e-2,maxIter::Int=100,M::Function=t->copy(t),x::Vector=[],out::Int=0,storeInterm::Bool=false,flexible::Bool=false,mem::FGMRESmem = getEmptyFGMRESmem())
7777
# initialization
@@ -91,7 +91,8 @@ function fgmres(A::Function,b::Vector,restrt::Int; tol::Real=1e-2,maxIter::Int=1
9191
elseif norm(x) < eps(real(TYPE))
9292
r = copy(b);
9393
else
94-
r = b-A(x);
94+
r = copy(b);
95+
r.-=A(x);
9596
end
9697

9798
if eltype(b) <: Complex
@@ -214,8 +215,9 @@ function fgmres(A::Function,b::Vector,restrt::Int; tol::Real=1e-2,maxIter::Int=1
214215
flag = 0;
215216
break
216217
end
217-
if maxIter > 1
218-
r = b - A(x);
218+
if iter < maxIter
219+
r = copy(b);
220+
r.-=A(x);
219221
betta = norm(r)
220222
end
221223
# if out==2; print(@sprintf("\t %1.1e\n", err)); end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ include("testCGLS.jl")
1717
include("testGS.jl")
1818
include("testGMRES.jl")
1919
include("testFGMRES.jl")
20+
include("testBlockFGMRES.jl")
2021
include("testLANCZOS.jl")
2122
include("testSSOR.jl")
2223
include("testLSQR.jl")

test/testBlockFGMRES.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
println("*******************************************************")
3+
@testset "blockFGMRES" begin
4+
@testset "real matrix" begin
5+
n = 50;
6+
A = sprandn(n,n,.1) + SparseMatrixCSC(10.0I, n, n)
7+
m = 2;
8+
D = Vector(diag(A))
9+
M2 = x -> D.\x
10+
rhs = randn(n,m)
11+
tol = 1e-6;
12+
13+
# test printing and behaviour for early stopping
14+
xtt = blockFGMRES(A,rhs ,3,tol=1e-12,maxIter=3,out=2)
15+
@test xtt[2]==-1
16+
17+
# test behaviour for zero rhs
18+
xtt = blockFGMRES(A,0*rhs,3,tol=tol,maxIter=10,out=2)
19+
@test xtt[2]==-9
20+
@test all(xtt[1].==0.0)
21+
@test length(xtt[1])==m*n
22+
@test eltype(xtt[1])==eltype(rhs)
23+
24+
println("-----------------------------------")
25+
x1 = blockFGMRES(A,rhs ,5,tol=tol,maxIter=100,out=1)
26+
x3 = blockFGMRES(A,rhs,5,tol=tol,maxIter=100,X=randn(size(rhs)),flexible=true)
27+
x4 = blockFGMRES(A,rhs,5,tol=tol,maxIter=100,M=M2,flexible = true)
28+
29+
@test norm(A*x1[1]-rhs)/norm(rhs) < tol
30+
@test norm(A*x3[1]-rhs)/norm(rhs) < tol
31+
@test norm(A*x4[1]-rhs)/norm(rhs) < tol
32+
@test norm(x3[1]-x1[1])/norm(x1[1]) < 1e-5
33+
end
34+
println("*****************************************************************************")
35+
@testset "complex matrix" begin
36+
A = sprandn(100,100,.1) + SparseMatrixCSC(10.0I, 100, 100) + 1im*(sprandn(100,100,.1) + SparseMatrixCSC(10.0I, 100, 100) )
37+
m = 3;
38+
D = Vector(diag(A))
39+
M3 = x -> x./D
40+
rhs = complex(randn(100,m))
41+
tol = 1e-6;
42+
43+
# test behaviour for zero rhs
44+
xtt = blockFGMRES(A,0.0*rhs,5,tol=tol,maxIter=10,out=2)
45+
@test xtt[2]==-9
46+
@test all(xtt[1].==0)
47+
@test length(xtt[1])==100*m
48+
@test eltype(xtt[1])==eltype(rhs)
49+
50+
51+
x1 = blockFGMRES(A,rhs ,5,tol=tol,maxIter=100)
52+
x3 = blockFGMRES(A,rhs,5,tol=tol,maxIter=100,X=randn(size(rhs)),flexible = true)
53+
x4 = blockFGMRES(A,rhs,5,tol=tol,maxIter=100,M=M3,flexible = true)
54+
55+
@test norm(A*x1[1]-rhs)/norm(rhs) < tol
56+
@test norm(A*x3[1]-rhs)/norm(rhs) < tol
57+
@test norm(x3[1]-x1[1])/norm(x1[1]) < 1e-5
58+
end
59+
end

test/testFGMRES.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ println("*******************************************************")
55
A = sprandn(100,100,.1) + SparseMatrixCSC(10.0I, 100, 100)
66
n = size(A,2)
77
D = diag(A)
8-
M2 = x -> D.\x
8+
M2 = x -> Vector(D.\x)
99
rhs = randn(100)
1010
tol = 1e-6;
1111

1212
# test printing and behaviour for early stopping
13-
xtt = fgmres(A,rhs ,3,tol=1e-10,maxIter=3,out=2,storeInterm=true)
13+
xtt = fgmres(A,rhs ,3,tol=1e-12,maxIter=3,out=2,storeInterm=true)
1414
@test xtt[2]==-1
1515

1616
# test behaviour for zero rhs
@@ -22,8 +22,8 @@ println("*******************************************************")
2222

2323

2424
x1 = fgmres(A,rhs ,5,tol=tol,maxIter=100,out=1)
25-
x3 = fgmres(A,rhs,5,tol=tol,maxIter=100,x=randn(size(rhs)))
26-
x4 = fgmres(A,rhs,5,tol=tol,maxIter=100,M=M2)
25+
x3 = fgmres(A,rhs,5,tol=tol,maxIter=100,x=randn(size(rhs)),flexible = true)
26+
x4 = fgmres(A,rhs,5,tol=tol,maxIter=100,M=M2,flexible = true)
2727

2828
@test norm(A*x1[1]-rhs)/norm(rhs) < tol
2929
@test norm(A*x3[1]-rhs)/norm(rhs) < tol

0 commit comments

Comments
 (0)