Skip to content

Commit 3e4fafe

Browse files
authored
Merge pull request #169 from gridap/autodiff
AD for multifield and skeletons
2 parents b5a5944 + 86d0d93 commit 3e4fafe

File tree

7 files changed

+306
-79
lines changed

7 files changed

+306
-79
lines changed

NEWS.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.4.7] 2025-03-04
9+
10+
### Added
11+
12+
- Extended support for automatic differentiation to multi-field spaces and skeleton triangulations. Since PR[#169](https://github.com/gridap/GridapDistributed.jl/pull/169).
13+
814
## [0.4.6] 2024-12-03
915

1016
### Added

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "GridapDistributed"
22
uuid = "f9701e48-63b3-45aa-9a63-9bc6c271f355"
33
authors = ["S. Badia <santiago.badia@monash.edu>", "A. F. Martin <alberto.f.martin@anu.edu.au>", "F. Verdugo <f.verdugo.rojano@vu.nl>"]
4-
version = "0.4.6"
4+
version = "0.4.7"
55

66
[deps]
77
BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"

src/Autodiff.jl

+187-40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11

2-
function Fields.gradient(f::Function,uh::DistributedCellField)
2+
const DistributedADTypes = Union{DistributedCellField,DistributedMultiFieldCellField}
3+
4+
# Distributed counterpart of: src/FESpaces/FEAutodiff.jl
5+
6+
function Fields.gradient(f::Function,uh::DistributedADTypes)
37
fuh = f(uh)
48
FESpaces._gradient(f,uh,fuh)
59
end
@@ -9,15 +13,15 @@ function FESpaces._gradient(f,uh,fuh::DistributedDomainContribution)
913
local_domains = tuple_of_arrays(map(Tupleget_domains,local_views(fuh)))
1014
for local_trians in local_domains
1115
g = FESpaces._change_argument(gradient,f,local_trians,uh)
12-
cell_u = map(get_cell_dof_values,local_views(uh))
16+
cell_u = map(FESpaces.get_cell_dof_values,local_views(uh))
1317
cell_id = map(FESpaces._compute_cell_ids,local_views(uh),local_trians)
1418
cell_grad = distributed_autodiff_array_gradient(g,cell_u,cell_id)
1519
map(add_contribution!,local_terms,local_trians,cell_grad)
1620
end
1721
DistributedDomainContribution(local_terms)
1822
end
1923

20-
function Fields.jacobian(f::Function,uh::DistributedCellField)
24+
function Fields.jacobian(f::Function,uh::DistributedADTypes)
2125
fuh = f(uh)
2226
FESpaces._jacobian(f,uh,fuh)
2327
end
@@ -27,79 +31,222 @@ function FESpaces._jacobian(f,uh,fuh::DistributedDomainContribution)
2731
local_domains = tuple_of_arrays(map(Tupleget_domains,local_views(fuh)))
2832
for local_trians in local_domains
2933
g = FESpaces._change_argument(jacobian,f,local_trians,uh)
30-
cell_u = map(get_cell_dof_values,local_views(uh))
34+
cell_u = map(FESpaces.get_cell_dof_values,local_views(uh))
3135
cell_id = map(FESpaces._compute_cell_ids,local_views(uh),local_trians)
3236
cell_grad = distributed_autodiff_array_jacobian(g,cell_u,cell_id)
3337
map(add_contribution!,local_terms,local_trians,cell_grad)
3438
end
3539
DistributedDomainContribution(local_terms)
3640
end
3741

38-
function FESpaces._change_argument(op,f,trian,uh::DistributedCellField)
39-
spaces = map(get_fe_space,local_views(uh))
40-
function g(cell_u)
41-
cf = DistributedCellField(
42-
map(CellField,spaces,cell_u),
43-
get_triangulation(uh),
42+
function Fields.hessian(f::Function,uh::DistributedADTypes)
43+
fuh = f(uh)
44+
FESpaces._hessian(f,uh,fuh)
45+
end
46+
47+
function FESpaces._hessian(f,uh,fuh::DistributedDomainContribution)
48+
local_terms = map(r -> DomainContribution(), get_parts(fuh))
49+
local_domains = tuple_of_arrays(map(Tupleget_domains,local_views(fuh)))
50+
for local_trians in local_domains
51+
g = FESpaces._change_argument(hessian,f,local_trians,uh)
52+
cell_u = map(FESpaces.get_cell_dof_values,local_views(uh))
53+
cell_id = map(FESpaces._compute_cell_ids,local_views(uh),local_trians)
54+
cell_grad = distributed_autodiff_array_hessian(g,cell_u,cell_id)
55+
map(add_contribution!,local_terms,local_trians,cell_grad)
56+
end
57+
DistributedDomainContribution(local_terms)
58+
end
59+
60+
function FESpaces._change_argument(op,f,local_trians,uh::DistributedADTypes)
61+
function dist_cf(uh::DistributedCellField,cfs)
62+
DistributedCellField(cfs,get_triangulation(uh))
63+
end
64+
function dist_cf(uh::DistributedMultiFieldCellField,cfs)
65+
sf_cfs = map(DistributedCellField,
66+
[tuple_of_arrays(map(cf -> Tuple(cf.single_fields),cfs))...],
67+
map(get_triangulation,uh)
4468
)
45-
cell_grad = f(cf)
46-
map(get_contribution,local_views(cell_grad),trian)
69+
DistributedMultiFieldCellField(sf_cfs,cfs)
70+
end
71+
72+
uhs = local_views(uh)
73+
spaces = map(get_fe_space,uhs)
74+
function g(cell_u)
75+
cfs = map(CellField,spaces,cell_u)
76+
cf = dist_cf(uh,cfs)
77+
cg = f(cf)
78+
map(get_contribution,local_views(cg),local_trians)
4779
end
4880
g
4981
end
5082

83+
# Distributed counterpart of: src/Arrays/Autodiff.jl
5184
# autodiff_array_xxx
5285

5386
function distributed_autodiff_array_gradient(a,i_to_x)
54-
dummy_forwarddiff_tag = ()->()
55-
i_to_xdual = map(i_to_x) do i_to_x
56-
lazy_map(DualizeMap(ForwardDiff.gradient,dummy_forwarddiff_tag),i_to_x)
87+
dummy_tag = ()->()
88+
i_to_cfg = map(i_to_x) do i_to_x
89+
lazy_map(ConfigMap(ForwardDiff.gradient,dummy_tag),i_to_x)
90+
end
91+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
92+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
5793
end
5894
i_to_ydual = a(i_to_xdual)
59-
i_to_result = map(i_to_ydual,i_to_x) do i_to_ydual,i_to_x
60-
i_to_cfg = lazy_map(ConfigMap(ForwardDiff.gradient,dummy_forwarddiff_tag),i_to_x)
61-
lazy_map(AutoDiffMap(ForwardDiff.gradient),i_to_ydual,i_to_x,i_to_cfg)
95+
i_to_result = map(i_to_cfg,i_to_ydual) do i_to_cfg,i_to_ydual
96+
lazy_map(AutoDiffMap(),i_to_cfg,i_to_ydual)
6297
end
6398
return i_to_result
6499
end
65100

66101
function distributed_autodiff_array_jacobian(a,i_to_x)
67-
dummy_forwarddiff_tag = ()->()
68-
i_to_xdual = map(i_to_x) do i_to_x
69-
lazy_map(DualizeMap(ForwardDiff.jacobian,dummy_forwarddiff_tag),i_to_x)
102+
dummy_tag = ()->()
103+
i_to_cfg = map(i_to_x) do i_to_x
104+
lazy_map(ConfigMap(ForwardDiff.jacobian,dummy_tag),i_to_x)
105+
end
106+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
107+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
70108
end
71109
i_to_ydual = a(i_to_xdual)
72-
i_to_result = map(i_to_ydual,i_to_x) do i_to_ydual,i_to_x
73-
i_to_cfg = lazy_map(ConfigMap(ForwardDiff.jacobian,dummy_forwarddiff_tag),i_to_x)
74-
lazy_map(AutoDiffMap(ForwardDiff.jacobian),i_to_ydual,i_to_x,i_to_cfg)
110+
i_to_result = map(i_to_cfg,i_to_ydual) do i_to_cfg,i_to_ydual
111+
lazy_map(AutoDiffMap(),i_to_cfg,i_to_ydual)
75112
end
76-
i_to_result
113+
return i_to_result
114+
end
115+
116+
function distributed_autodiff_array_hessian(a,i_to_x)
117+
agrad = i_to_y -> distributed_autodiff_array_gradient(a,i_to_y)
118+
distributed_autodiff_array_jacobian(agrad,i_to_x)
77119
end
78120

79121
function distributed_autodiff_array_gradient(a,i_to_x,j_to_i)
80-
dummy_forwarddiff_tag = ()->()
81-
i_to_xdual = map(i_to_x) do i_to_x
82-
lazy_map(DualizeMap(ForwardDiff.gradient,dummy_forwarddiff_tag),i_to_x)
122+
dummy_tag = ()->()
123+
i_to_cfg = map(i_to_x) do i_to_x
124+
lazy_map(ConfigMap(ForwardDiff.gradient,dummy_tag),i_to_x)
125+
end
126+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
127+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
83128
end
84129
j_to_ydual = a(i_to_xdual)
85-
j_to_result = map(i_to_x,j_to_i,j_to_ydual) do i_to_x,j_to_i,j_to_ydual
86-
j_to_x = lazy_map(Reindex(i_to_x),j_to_i)
87-
j_to_cfg = lazy_map(ConfigMap(ForwardDiff.gradient,dummy_forwarddiff_tag),j_to_x)
88-
lazy_map(AutoDiffMap(ForwardDiff.gradient),j_to_ydual,j_to_x,j_to_cfg)
130+
j_to_result = map(i_to_cfg,j_to_i,j_to_ydual) do i_to_cfg,j_to_i,j_to_ydual
131+
j_to_cfg = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i)
132+
lazy_map(AutoDiffMap(),j_to_cfg,j_to_ydual)
89133
end
90134
return j_to_result
91135
end
92136

93137
function distributed_autodiff_array_jacobian(a,i_to_x,j_to_i)
94-
dummy_forwarddiff_tag = ()->()
95-
i_to_xdual = map(i_to_x) do i_to_x
96-
lazy_map(DualizeMap(ForwardDiff.jacobian,dummy_forwarddiff_tag),i_to_x)
138+
dummy_tag = ()->()
139+
i_to_cfg = map(i_to_x) do i_to_x
140+
lazy_map(ConfigMap(ForwardDiff.jacobian,dummy_tag),i_to_x)
141+
end
142+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
143+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
97144
end
98145
j_to_ydual = a(i_to_xdual)
99-
j_to_result = map(i_to_x,j_to_i,j_to_ydual) do i_to_x,j_to_i,j_to_ydual
100-
j_to_x = lazy_map(Reindex(i_to_x),j_to_i)
101-
j_to_cfg = lazy_map(ConfigMap(ForwardDiff.jacobian,dummy_forwarddiff_tag),j_to_x)
102-
lazy_map(AutoDiffMap(ForwardDiff.jacobian),j_to_ydual,j_to_x,j_to_cfg)
146+
j_to_result = map(i_to_cfg,j_to_i,j_to_ydual) do i_to_cfg,j_to_i,j_to_ydual
147+
j_to_cfg = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i)
148+
lazy_map(AutoDiffMap(),j_to_cfg,j_to_ydual)
103149
end
104-
j_to_result
150+
return j_to_result
151+
end
152+
153+
function distributed_autodiff_array_hessian(a,i_to_x,i_to_j)
154+
agrad = i_to_y -> distributed_autodiff_array_gradient(a,i_to_y,i_to_j)
155+
distributed_autodiff_array_jacobian(agrad,i_to_x,i_to_j)
156+
end
157+
158+
# Skeleton AD
159+
160+
function FESpaces._change_argument(op,f,local_trians::AbstractArray{<:SkeletonTriangulation},uh::DistributedADTypes)
161+
function dist_cf(uh::DistributedCellField,cfs)
162+
DistributedCellField(cfs, get_triangulation(uh))
163+
end
164+
function dist_cf(uh::DistributedMultiFieldCellField,cfs)
165+
sf_cfs = map(DistributedCellField,
166+
[tuple_of_arrays(map(cf -> Tuple(cf.single_fields),cfs))...],
167+
map(get_triangulation,uh)
168+
)
169+
DistributedMultiFieldCellField(sf_cfs,cfs)
170+
end
171+
172+
uhs = local_views(uh)
173+
spaces = map(get_fe_space,uhs)
174+
function g(cell_u)
175+
uhs_dual = map(CellField,spaces,cell_u)
176+
cf_plus = dist_cf(uh,map(SkeletonCellFieldPair,uhs_dual,uhs))
177+
cf_minus = dist_cf(uh,map(SkeletonCellFieldPair,uhs,uhs_dual))
178+
cg_plus = f(cf_plus)
179+
cg_minus = f(cf_minus)
180+
plus = map(get_contribution,local_views(cg_plus),local_trians)
181+
minus = map(get_contribution,local_views(cg_minus),local_trians)
182+
plus, minus
183+
end
184+
g
185+
end
186+
187+
function distributed_autodiff_array_gradient(a, i_to_x, j_to_i::AbstractArray{<:SkeletonPair})
188+
dummy_tag = ()->()
189+
i_to_cfg = map(i_to_x) do i_to_x
190+
lazy_map(ConfigMap(ForwardDiff.gradient,dummy_tag),i_to_x)
191+
end
192+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
193+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
194+
end
195+
196+
# dual output of both sides at once
197+
j_to_ydual_plus, j_to_ydual_minus = a(i_to_xdual)
198+
199+
j_to_result = map(i_to_cfg,j_to_i,j_to_ydual_plus,j_to_ydual_minus) do i_to_cfg,j_to_i,j_to_ydual_plus,j_to_ydual_minus
200+
# Work for plus side
201+
j_to_cfg_plus = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i.plus)
202+
j_to_result_plus = lazy_map(AutoDiffMap(),j_to_cfg_plus,j_to_ydual_plus)
203+
204+
# Work for minus side
205+
j_to_cfg_minus = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i.minus)
206+
j_to_result_minus = lazy_map(AutoDiffMap(),j_to_cfg_minus,j_to_ydual_minus)
207+
208+
# Assemble on SkeletonTriangulation expects an array of interior of facets
209+
# where each entry is a 2-block BlockVector with the first block being the
210+
# contribution of the plus side and the second, the one of the minus side
211+
is_single_field = eltype(eltype(j_to_result_plus)) <: Number
212+
k = is_single_field ? BlockMap(2,[1,2]) : Fields.BlockBroadcasting(BlockMap(2,[1,2]))
213+
lazy_map(k,j_to_result_plus,j_to_result_minus)
214+
end
215+
216+
return j_to_result
217+
end
218+
219+
function distributed_autodiff_array_jacobian(a, i_to_x, j_to_i::AbstractArray{<:SkeletonPair})
220+
dummy_tag = ()->()
221+
i_to_cfg = map(i_to_x) do i_to_x
222+
lazy_map(ConfigMap(ForwardDiff.jacobian,dummy_tag),i_to_x)
223+
end
224+
i_to_xdual = map(i_to_cfg,i_to_x) do i_to_cfg, i_to_x
225+
lazy_map(DualizeMap(),i_to_cfg,i_to_x)
226+
end
227+
228+
# dual output of both sides at once
229+
j_to_ydual_plus, j_to_ydual_minus = a(i_to_xdual)
230+
231+
j_to_result = map(i_to_cfg,j_to_i,j_to_ydual_plus,j_to_ydual_minus) do i_to_cfg,j_to_i,j_to_ydual_plus,j_to_ydual_minus
232+
# Work for plus side
233+
j_to_cfg_plus = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i.plus)
234+
j_to_result_plus = lazy_map(AutoDiffMap(),j_to_cfg_plus,j_to_ydual_plus)
235+
236+
# Work for minus side
237+
j_to_cfg_minus = Arrays.autodiff_array_reindex(i_to_cfg,j_to_i.minus)
238+
j_to_result_minus = lazy_map(AutoDiffMap(),j_to_cfg_minus,j_to_ydual_minus)
239+
240+
# Merge the columns into a 2x2 block matrix
241+
# I = [[(CartesianIndex(i,),CartesianIndex(i,j)) for i in 1:2] for j in 1:2]
242+
I = [
243+
[(CartesianIndex(1,), CartesianIndex(1, 1)), (CartesianIndex(2,), CartesianIndex(2, 1))], # Plus -> First column
244+
[(CartesianIndex(1,), CartesianIndex(1, 2)), (CartesianIndex(2,), CartesianIndex(2, 2))] # Minus -> Second column
245+
]
246+
is_single_field = eltype(eltype(j_to_result_plus)) <: AbstractArray
247+
k = is_single_field ? Fields.MergeBlockMap((2,2),I) : Fields.BlockBroadcasting(Fields.MergeBlockMap((2,2),I))
248+
lazy_map(k,j_to_result_plus,j_to_result_minus)
249+
end
250+
251+
return j_to_result
105252
end

0 commit comments

Comments
 (0)