Skip to content

Commit

Permalink
First test of vcycles but it's only for a 1-D rectilinear grid
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinDCarlson committed Sep 25, 2024
1 parent f609905 commit 3ea1cd7
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 19 deletions.
5 changes: 2 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ DataMigrations = "0c4ad18d-0c49-4bc2-90d5-5bca8f00d6ae"
Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7"
LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e"
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
MeshIO = "7269a6da-0436-5bbc-96c2-40638cbb6118"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
RowEchelon = "af85af4c-bcd5-5d23-b03a-a909639aa875"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

Expand Down
4 changes: 0 additions & 4 deletions src/SimplicialComplexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,6 @@ end
#XX: make the restriction map smoother
"""
The geometric map from a deltaset's subdivision to itself.
Warning: the second returned map is not actually a valid geometric map as edges
of the primal delta set will run over multiple edges of the dual. So, careful composing
with it etc.
"""
function subdivision_map(primal_s::EmbeddedDeltaSet,alg=Barycenter())
prim = SimplicialComplex(primal_s)
Expand Down
106 changes: 94 additions & 12 deletions test/SimplicialComplexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ module TestSimplicialComplexes
using Test
using CombinatorialSpaces
using Catlab:@acset
using LinearAlgebra: I
using LinearAlgebra, Krylov
using GeometryBasics: Point2, Point3
using SparseArrays
Point2D = Point2{Float64}

# Triangulated commutative square.
Expand Down Expand Up @@ -40,7 +41,7 @@ t = GeometricPoint([0,0.5,0,0.5])

Δ⁰ = SimplicialComplex(@acset DeltaSet0D begin V=1 end)
Δ¹ = SimplicialComplex(@acset DeltaSet1D begin V=2; E=1; ∂v0 = [2]; ∂v1 = [1] end)
f = GeometricMap(Δ⁰,Δ¹,[1/3,2/3])
f = GeometricMap(Δ⁰,Δ¹,[GeometricPoint([1/3,2/3])])
A = [0.2 0.4
0 0
0.5 0
Expand All @@ -56,8 +57,8 @@ primal_s = EmbeddedDeltaSet2D{Bool,Point2D}()
add_vertices!(primal_s, 3, point=[Point2D(0,0), Point2D(1,0), Point2D(0,1)])
glue_triangle!(primal_s, 1, 2, 3, tri_orientation=true)
primal_s[:edge_orientation] = true
f,g = subdivision_maps(primal_s)
@test as_matrix(compose(g,f)) = I(3)*1.0
#f,g = subdivision_maps(primal_s)
#@test as_matrix(compose(g,f)) = I(3)*1.0

fake_laplacian_builder(s::HasDeltaSet) = I(nv(s))*1.0
fake_laplacian_builder(s::SimplicialComplex) = I(nv(s))*1.0
Expand Down Expand Up @@ -97,9 +98,9 @@ I'm not sure about convergence for general ω.
See Golub Van Loan 11.2 and 11.6.2.
"""
function WJ(A,b,ω)
D = diagm(diag(A))
D = SparseMatrixCSC(diagm(diag(A)))
c = ω * (D \ b)
G = (1-ω)*I-ω * (D\(A-D))
G = SparseMatrixCSC((1-ω)*I-ω * (D\(A-D)))
G,c
end
spectral_radius(A) = maximum(abs.(eigvals(A)))
Expand Down Expand Up @@ -127,13 +128,20 @@ b = ones(7)
G,c = WJ(A,b,1)
@test norm(A*it(G,c,u₀,25)- b)<10^-10

function multigrid_vcycle(u,b,primal_s,depth)
mats = multigrid_setup(primal_s,depth)
"""
Solve ∇²u = b on a `depth`-fold subdivided simplical complex using a multigrid V-cycle.
"""
function multigrid_vcycle(u,b,primal_s,depth,smoothing_steps)
center(_multigrid_vcycle(u,b,multigrid_setup(primal_s,depth)...,smoothing_steps))
end
function multigrid_setup(primal_s,depth,alg=Barycenter())
prim = primal_s
map(1:depth+1) do i
duco = dualize(prim,alg)
duco = dualize(prim,alg)
pt = typeof(prim)()
add_vertex!(pt)
laplacians = [∇²(0,duco)]
interpolations = [GeometricMap(SimplicialComplex(prim),SimplicialComplex(pt),[1. 1. 1.])]
for i in 1:depth
dual = extract_dual(duco)
mat = zeros(Float64,nv(prim),nv(dual))
pvs = map(i->primal_vertices(duco,i),1:nv(dual))
Expand All @@ -143,10 +151,84 @@ function multigrid_setup(primal_s,depth,alg=Barycenter())
mat[v,j] = weights[j]
end
end
L,f=∇²(0,duco),GeometricMap(SimplicialComplex(dual),SimplicialComplex(prim),mat)
f=GeometricMap(SimplicialComplex(dual),SimplicialComplex(prim),mat)
prim = dual
(L=L,f=f)
duco = dualize(prim,alg)
L = ∇²(0,duco)
push!(laplacians,L)
push!(interpolations,f)
end
reverse(laplacians), reverse(as_matrix.(interpolations[2:end]))
end
function _multigrid_vcycle(u,b,laplacians,interpolations,prolongations,smoothing_steps,cycles=1)
cycles == 0 && return u
u = gmres(laplacians[1],b,u,itmax=smoothing_steps)[1]
if length(laplacians) == 1 #on coarsest grid
return center(u)
end
#smooth, update error, restrict, recurse, prolong, smooth
r_f = b - laplacians[1]*u #residual
r_c = interpolations[1] * r_f #restrict
z = _multigrid_vcycle(zeros(size(r_c)),r_c,laplacians[2:end],interpolations[2:end],prolongations[2:end],smoothing_steps,cycles) #recurse
u += prolongations[1] * z #prolong.
u = gmres(laplacians[1],b,u,itmax=smoothing_steps)[1] #smooth
_multigrid_vcycle(u,b,laplacians,interpolations,prolongations,smoothing_steps,cycles-1)
end
center(u) = u .- sum(u)/length(u)
row_normalize(A) = A./(sum.(eachrow(A)))
re(u,b) = norm(u-b)/norm(b)

N = 4
function test_vcycle_heat(N)
ls, is = multigrid_setup(primal_s,N)
n_fine = size(ls[1],1)
b = ls[1]*[i for i in 1:n_fine]
u = zeros(n_fine)
tts = []
for i in 1:n_fine
if i % 10 == 0 println(i) end
push!(tts,re(ls[1]*(gmres(ls[1],b,itmax=i)[1]),b)/re(ls[1]*(_multigrid_vcycle(u,b,ls,is,i)),b))
end
tts
end

square_laplacian(N) = diagm(0 => 2*ones(N),1 =>-1*ones(N-1),-1 =>-1*ones(N-1))
function sparse_square_laplacian(k)
N,h = 2^k-1, 1/(2^k)
A = spzeros(N,N)
for i in 1:N
A[i,i] = 2
if i > 1 A[i,i-1] = -1 end
if i < N A[i,i+1] = -1 end
end
1/h^2 * A
end
function sparse_restriction(k)
N,M = 2^k-1, 2^(k-1)-1
A = spzeros(M,N)
for i in 1:M
A[i,2i-1:2i+1] = [1,2,1]
end
0.25*A
end
sparse_prolongation(k) = 2*transpose(sparse_restriction(k))
#Note that for k=10 a single v-cycle maximizes payoff at around i = 58!
#You get insane accuracy with smoothing_steps=7 and cycles=3. Jesus.
function test_vcycle_square(k,s,c)
b=rand(2^k-1)
N = 2^k-1
ls = reverse([sparse_square_laplacian(k′) for k′ in 1:k])
is = reverse([sparse_restriction(k′) for k′ in 2:k])
ps = reverse([sparse_prolongation(k′) for k′ in 2:k])
u = zeros(N)
re(ls[1]*_multigrid_vcycle(u,b,ls,is,ps,s,c),b)
end
#This takes a few dozen seconds on a cheap machine and would take
#maybe days for weighted Jacobi or GMRES (although it's a tridiagonal
#matrix so there's a faster way in this case))
#Most of the work is in the setup; to go bigger than this you'd probably
#want to compute the coarser matrices from the finer by indexing instead
#of building them all manually.
@test test_vcycle_square(15,10,3)< 10^-8

end

0 comments on commit 3ea1cd7

Please sign in to comment.