Skip to content

Commit 1714fa4

Browse files
authored
Merge pull request #77 from Alexander-Barth/master
implement W and F cycles and n-dimensional Poisson matrix
2 parents 1d7c830 + f6d0b49 commit 1714fa4

File tree

6 files changed

+138
-18
lines changed

6 files changed

+138
-18
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![codecov.io](http://codecov.io/github/JuliaLinearAlgebra/AlgebraicMultigrid.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaLinearAlgebra/AlgebraicMultigrid.jl?branch=master)
66
[![Build status](https://ci.appveyor.com/api/projects/status/0wnj4lhk1rvl5pjp?svg=true)](https://ci.appveyor.com/project/ranjanan/algebraicmultigrid-jl)
77

8-
This package lets you solve sparse linear systems using Algebraic Multigrid (AMG). This works especially well for symmetric positive definite matrices.
8+
This package lets you solve sparse linear systems using Algebraic Multigrid (AMG). This works especially well for symmetric positive definite matrices.
99

1010
## Usage
1111

@@ -40,18 +40,18 @@ You can use AMG as a preconditioner for Krylov methods such as Conjugate Gradien
4040
```julia
4141
import IterativeSolvers: cg
4242
p = aspreconditioner(ml)
43-
c = cg(A, A*ones(1000), Pl = p)
43+
c = cg(A, A*ones(1000), Pl = p)
4444
```
4545

4646
## Features and Roadmap
4747

48-
This package currently supports:
48+
This package currently supports:
4949

5050
AMG Styles:
5151
* Ruge-Stuben Solver
5252
* Smoothed Aggregation (SA)
5353

54-
Strength of Connection:
54+
Strength of Connection:
5555
* Classical Strength of Connection
5656
* Symmetric Strength of Connection
5757

@@ -60,12 +60,12 @@ Smoothers:
6060
* Damped Jacobi
6161

6262
Cycling:
63-
* V cycle
63+
* V, W and F cycles
6464

6565
In the future, this package will support:
6666
1. Other splitting methods (like CLJP)
6767
2. SOR smoother
68-
3. W, F, AMLI cycles
68+
3. AMLI cycles
6969

7070
#### Acknowledgements
71-
This package has been heavily inspired by the [`PyAMG`](http://github.com/pyamg/pyamg) project.
71+
This package has been heavily inspired by the [`PyAMG`](http://github.com/pyamg/pyamg) project.

src/gallery.jl

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,63 @@
1-
poisson(T, n) = sparse(Tridiagonal(fill(T(-1), n-1),
1+
poisson(T, n) = sparse(Tridiagonal(fill(T(-1), n-1),
22
fill(T(2), n), fill(T(-1), n-1)))
33
poisson(n) = poisson(Float64, n)
4+
5+
function stencil_grid(T,stencil,sz)
6+
# upper-bound for storage
7+
n = prod(sz) * sum(.!iszero,stencil)
8+
9+
# indices and value of nonzero elements
10+
Si = zeros(Int,n)
11+
Sj = zeros(Int,n)
12+
Ss = zeros(T,n)
13+
14+
linindices = LinearIndices(sz)
15+
nnz = 0
16+
17+
stencil_sz = size(stencil)
18+
offset = CartesianIndex((stencil_sz .+ 1) 2)
19+
20+
for i in CartesianIndices(sz)
21+
for k in CartesianIndices(stencil_sz)
22+
if stencil[k] != 0
23+
j = i + k - offset
24+
if checkbounds(Bool,linindices,j)
25+
nnz = nnz + 1
26+
Si[nnz] = linindices[i]
27+
Sj[nnz] = linindices[j]
28+
Ss[nnz] = stencil[k]
29+
end
30+
end
31+
end
32+
end
33+
34+
sparse((@view Si[1:nnz]),
35+
(@view Sj[1:nnz]),
36+
(@view Ss[1:nnz]),
37+
prod(sz),prod(sz))
38+
end
39+
40+
41+
42+
function poisson(T,sz::NTuple{N,Int}) where N
43+
#=
44+
In 2D stencil has the value:
45+
stencil = [0 -1 0;
46+
-1 4 -1;
47+
0 -1 0]
48+
49+
=#
50+
stencil = zeros(T,ntuple(i -> 3,Val(N)))
51+
for i = 0:N-1
52+
Ipre = CartesianIndex(ntuple(l -> 2,i))
53+
Ipost = CartesianIndex(ntuple(l -> 2,N-i-1))
54+
stencil[Ipre, 1, Ipost] = -1
55+
stencil[Ipre, 3, Ipost] = -1
56+
end
57+
Icenter = CartesianIndex(ntuple(l -> 2,Val(N)))
58+
stencil[Icenter] = 2*N
59+
60+
stencil_grid(T,stencil,sz)
61+
end
62+
63+
poisson(sz::NTuple{N,Int}) where N = poisson(Float64,sz)

src/multilevel.jl

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ function Base.show(io::IO, ml::MultiLevel)
6666
op = operator_complexity(ml)
6767
g = grid_complexity(ml)
6868
c = ml.coarse_solver
69-
total_nnz = nnz(ml.final_A)
70-
if !isempty(ml.levels)
69+
total_nnz = nnz(ml.final_A)
70+
if !isempty(ml.levels)
7171
total_nnz += sum(nnz(level.A) for level in ml.levels)
7272
end
7373
lstr = ""
@@ -119,6 +119,12 @@ abstract type Cycle end
119119
struct V <: Cycle
120120
end
121121

122+
struct W <: Cycle
123+
end
124+
125+
struct F <: Cycle
126+
end
127+
122128
"""
123129
solve(ml::MultiLevel, b::AbstractArray, cycle, kwargs...)
124130
@@ -140,7 +146,7 @@ Keyword Arguments
140146
141147
"""
142148
function solve(ml::MultiLevel, b::AbstractArray, args...; kwargs...)
143-
n = length(ml) == 1 ? size(ml.final_A, 1) : size(ml.levels[1].A, 1)
149+
n = length(ml) == 1 ? size(ml.final_A, 1) : size(ml.levels[1].A, 1)
144150
V = promote_type(eltype(ml.workspace), eltype(b))
145151
x = zeros(V, size(b))
146152
return solve!(x, ml, b, args...; kwargs...)
@@ -183,8 +189,22 @@ function solve!(x, ml::MultiLevel, b::AbstractArray{T},
183189
# @show residuals
184190
log ? (x, residuals) : x
185191
end
186-
function __solve!(x, ml, v::V, b, lvl)
187192

193+
function __solve_next!(x, ml, cycle::V, b, lvl)
194+
__solve!(x, ml, cycle, b, lvl)
195+
end
196+
197+
function __solve_next!(x, ml, cycle::W, b, lvl)
198+
__solve!(x, ml, cycle, b, lvl)
199+
__solve!(x, ml, cycle, b, lvl)
200+
end
201+
202+
function __solve_next!(x, ml, cycle::F, b, lvl)
203+
__solve!(x, ml, cycle, b, lvl)
204+
__solve!(x, ml, V(), b, lvl)
205+
end
206+
207+
function __solve!(x, ml, cycle::Cycle, b, lvl)
188208
A = ml.levels[lvl].A
189209
ml.presmoother(A, x, b)
190210

@@ -200,7 +220,7 @@ function __solve!(x, ml, v::V, b, lvl)
200220
if lvl == length(ml.levels)
201221
ml.coarse_solver(coarse_x, coarse_b)
202222
else
203-
coarse_x = __solve!(coarse_x, ml, v, coarse_b, lvl + 1)
223+
coarse_x = __solve_next!(coarse_x, ml, cycle, coarse_b, lvl + 1)
204224
end
205225

206226
mul!(res, ml.levels[lvl].P, coarse_x)

src/preconditioner.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
struct Preconditioner{ML<:MultiLevel}
1+
struct Preconditioner{ML<:MultiLevel,C<:Cycle}
22
ml::ML
33
init::Symbol
4+
cycle::C
45
end
5-
Preconditioner(ml) = Preconditioner(ml, :zero)
6+
Preconditioner(ml, cycle::Cycle) = Preconditioner(ml, :zero, cycle)
67

7-
aspreconditioner(ml::MultiLevel) = Preconditioner(ml)
8+
aspreconditioner(ml::MultiLevel, cycle::Cycle = V()) = Preconditioner(ml,cycle)
89

910
import LinearAlgebra: \, *, ldiv!, mul!
1011
ldiv!(p::Preconditioner, b) = copyto!(b, p \ b)
@@ -14,7 +15,7 @@ function ldiv!(x, p::Preconditioner, b)
1415
else
1516
x .= b
1617
end
17-
solve!(x, p.ml, b, maxiter = 1, calculate_residual = false)
18+
solve!(x, p.ml, b, p.cycle, maxiter = 1, calculate_residual = false)
1819
end
1920
mul!(b, p::Preconditioner, x) = mul!(b, p.ml.levels[1].A, x)
2021

test/cycle_tests.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import AlgebraicMultigrid: ruge_stuben, smoothed_aggregation,
2+
poisson, aspreconditioner
3+
4+
import IterativeSolvers: cg
5+
6+
function test_cycles()
7+
8+
A = poisson((50,50))
9+
b = A * ones(size(A,1))
10+
11+
reltol = 1e-8
12+
13+
for method in [ruge_stuben, smoothed_aggregation]
14+
ml = method(A)
15+
16+
for cycle in [AlgebraicMultigrid.V(),AlgebraicMultigrid.W(),AlgebraicMultigrid.F()]
17+
x,convhist = solve(ml, b, cycle; reltol = reltol, log = true)
18+
19+
@debug "number of iterations for $cycle using $method: $(length(convhist))"
20+
@test norm(b - A*x) < reltol * norm(b)
21+
end
22+
23+
for cycle in [AlgebraicMultigrid.V(),AlgebraicMultigrid.W(),AlgebraicMultigrid.F()]
24+
p = aspreconditioner(ml,cycle)
25+
26+
x,convhist = cg(A, b, Pl = p; reltol = reltol, log = true)
27+
@debug "CG: number of iterations for $cycle using $method: $(convhist.iters)"
28+
@test norm(b - A*x) <= reltol * norm(b)
29+
end
30+
end
31+
end

test/runtests.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ using FileIO
88
using Random: seed!
99

1010
include("sa_tests.jl")
11+
include("cycle_tests.jl")
1112

1213
@testset "AlgebraicMultigrid Tests" begin
1314

@@ -139,6 +140,9 @@ ml = ruge_stuben(A)
139140
x = solve(ml, A * ones(100))
140141
@test sum(abs2, x - zeros(100)) < 1e-6
141142

143+
144+
145+
142146
end
143147

144148
@testset "Preconditioning" begin
@@ -237,7 +241,7 @@ for (T,V) in ((Float64, Float64), (Float32,Float32),
237241
b = V.(b)
238242
c = cg(a, b, maxiter = 10)
239243
@test eltype(solve(ml, b)) == eltype(c)
240-
end
244+
end
241245

242246
end
243247

@@ -271,6 +275,10 @@ end
271275
test_jacobi_prolongator()
272276
end
273277

278+
@testset "Cycles" begin
279+
test_cycles()
280+
end
281+
274282
@testset "Int32 support" begin
275283
a = sparse(Int32.(1:10), Int32.(1:10), rand(10))
276284
@inferred smoothed_aggregation(a)

0 commit comments

Comments
 (0)