|
| 1 | +--- |
| 2 | +title: Brusselator sparse AD benchmarks |
| 3 | +author: Guillaume Dalle |
| 4 | +--- |
| 5 | + |
| 6 | +```julia |
| 7 | +using ADTypes |
| 8 | +using LinearAlgebra, SparseArrays |
| 9 | +using BenchmarkTools, DataFrames |
| 10 | +import DifferentiationInterface as DI |
| 11 | +using Plots |
| 12 | +import SparseDiffTools as SDT |
| 13 | +using SparseConnectivityTracer: TracerSparsityDetector |
| 14 | +using SparseMatrixColorings: GreedyColoringAlgorithm |
| 15 | +using Symbolics: SymbolicsSparsityDetector |
| 16 | +using Test |
| 17 | +``` |
| 18 | + |
| 19 | +## Definitions |
| 20 | + |
| 21 | +```julia |
| 22 | +brusselator_f(x, y, t) = (((x - 0.3)^2 + (y - 0.6)^2) <= 0.1^2) * (t >= 1.1) * 5.0 |
| 23 | + |
| 24 | +limit(a, N) = |
| 25 | + if a == N + 1 |
| 26 | + 1 |
| 27 | + elseif a == 0 |
| 28 | + N |
| 29 | + else |
| 30 | + a |
| 31 | + end; |
| 32 | + |
| 33 | +function brusselator_2d!(du, u) |
| 34 | + t = 0.0 |
| 35 | + N = size(u, 1) |
| 36 | + xyd = range(0; stop=1, length=N) |
| 37 | + p = (3.4, 1.0, 10.0, step(xyd)) |
| 38 | + A, B, alpha, dx = p |
| 39 | + alpha = alpha / dx^2 |
| 40 | + |
| 41 | + @inbounds for I in CartesianIndices((N, N)) |
| 42 | + i, j = Tuple(I) |
| 43 | + x, y = xyd[I[1]], xyd[I[2]] |
| 44 | + ip1, im1, jp1, jm1 = limit(i + 1, N), |
| 45 | + limit(i - 1, N), limit(j + 1, N), |
| 46 | + limit(j - 1, N) |
| 47 | + du[i, j, 1] = |
| 48 | + alpha * |
| 49 | + (u[im1, j, 1] + u[ip1, j, 1] + u[i, jp1, 1] + u[i, jm1, 1] - 4u[i, j, 1]) + |
| 50 | + B + |
| 51 | + u[i, j, 1]^2 * u[i, j, 2] - (A + 1) * u[i, j, 1] + brusselator_f(x, y, t) |
| 52 | + du[i, j, 2] = |
| 53 | + alpha * |
| 54 | + (u[im1, j, 2] + u[ip1, j, 2] + u[i, jp1, 2] + u[i, jm1, 2] - 4u[i, j, 2]) + |
| 55 | + A * u[i, j, 1] - u[i, j, 1]^2 * u[i, j, 2] |
| 56 | + end |
| 57 | +end; |
| 58 | + |
| 59 | +function init_brusselator_2d(N::Integer) |
| 60 | + xyd = range(0; stop=1, length=N) |
| 61 | + N = length(xyd) |
| 62 | + u = zeros(N, N, 2) |
| 63 | + for I in CartesianIndices((N, N)) |
| 64 | + x = xyd[I[1]] |
| 65 | + y = xyd[I[2]] |
| 66 | + u[I, 1] = 22 * (y * (1 - y))^(3 / 2) |
| 67 | + u[I, 2] = 27 * (x * (1 - x))^(3 / 2) |
| 68 | + end |
| 69 | + return u |
| 70 | +end; |
| 71 | +``` |
| 72 | + |
| 73 | +## Correctness |
| 74 | + |
| 75 | +```julia |
| 76 | +x0_32 = init_brusselator_2d(32); |
| 77 | +``` |
| 78 | + |
| 79 | +### Sparsity detection |
| 80 | + |
| 81 | +```julia |
| 82 | +S1 = ADTypes.jacobian_sparsity( |
| 83 | + brusselator_2d!, similar(x0_32), x0_32, TracerSparsityDetector() |
| 84 | +) |
| 85 | +S2 = ADTypes.jacobian_sparsity( |
| 86 | + brusselator_2d!, similar(x0_32), x0_32, SymbolicsSparsityDetector() |
| 87 | +) |
| 88 | +@test S1 == S2 |
| 89 | +``` |
| 90 | + |
| 91 | +### Coloring |
| 92 | + |
| 93 | +```julia |
| 94 | +c1 = ADTypes.column_coloring(S1, GreedyColoringAlgorithm()) |
| 95 | +c2 = SDT.matrix_colors(S1) |
| 96 | +@test c1 == c2 |
| 97 | +``` |
| 98 | + |
| 99 | +### Differentiation |
| 100 | + |
| 101 | +```julia |
| 102 | +backend = AutoSparse( |
| 103 | + AutoForwardDiff(); |
| 104 | + sparsity_detector=TracerSparsityDetector(), |
| 105 | + coloring_algorithm=GreedyColoringAlgorithm(), |
| 106 | +); |
| 107 | + |
| 108 | +extras = DI.prepare_jacobian(brusselator_2d!, similar(x0_32), backend, x0_32); |
| 109 | +J1 = DI.jacobian!( |
| 110 | + brusselator_2d!, similar(x0_32), similar(S1, eltype(x0_32)), backend, x0_32, extras |
| 111 | +) |
| 112 | + |
| 113 | +cache = SDT.sparse_jacobian_cache( |
| 114 | + backend, |
| 115 | + SDT.JacPrototypeSparsityDetection(; jac_prototype=S1), |
| 116 | + brusselator_2d!, |
| 117 | + similar(x0_32), |
| 118 | + x0_32, |
| 119 | +); |
| 120 | +J2 = SDT.sparse_jacobian!( |
| 121 | + similar(S1, eltype(x0_32)), backend, cache, brusselator_2d!, similar(x0_32), x0_32 |
| 122 | +) |
| 123 | + |
| 124 | +@test J1 == J2 |
| 125 | +``` |
| 126 | + |
| 127 | +## Benchmarks |
| 128 | + |
| 129 | +```julia |
| 130 | +N_values = 2 .^ (2:8) |
| 131 | +``` |
| 132 | + |
| 133 | +### Sparsity detection |
| 134 | + |
| 135 | +```julia |
| 136 | +td1, td2 = zeros(length(N_values)), zeros(length(N_values)) |
| 137 | +for (i, N) in enumerate(N_values) |
| 138 | + @info "Benchmarking sparsity detection: N=$N" |
| 139 | + x0 = init_brusselator_2d(N) |
| 140 | + td1[i] = @belapsed ADTypes.jacobian_sparsity( |
| 141 | + $brusselator_2d!, $(similar(x0)), $x0, TracerSparsityDetector() |
| 142 | + ) |
| 143 | + td2[i] = @belapsed ADTypes.jacobian_sparsity( |
| 144 | + $brusselator_2d!, $(similar(x0)), $x0, SymbolicsSparsityDetector() |
| 145 | + ) |
| 146 | +end |
| 147 | + |
| 148 | +pld = plot(; |
| 149 | + title="Sparsity detection on the Brusselator", |
| 150 | + xlabel="Input size N", |
| 151 | + ylabel="Runtime [s]", |
| 152 | +) |
| 153 | +plot!( |
| 154 | + pld, |
| 155 | + N_values, |
| 156 | + td1; |
| 157 | + lw=2, |
| 158 | + linestyle=:auto, |
| 159 | + markershape=:auto, |
| 160 | + label="SparseConnectivityTracer", |
| 161 | +) |
| 162 | +plot!(pld, N_values, td2; lw=2, linestyle=:auto, markershape=:auto, label="Symbolics") |
| 163 | +plot!(pld; xscale=:log10, yscale=:log10, legend=:topleft, minorgrid=true) |
| 164 | +pld |
| 165 | +``` |
| 166 | + |
| 167 | +### Coloring |
| 168 | + |
| 169 | +```julia |
| 170 | +tc1, tc2 = zeros(length(N_values)), zeros(length(N_values)) |
| 171 | +for (i, N) in enumerate(N_values) |
| 172 | + @info "Benchmarking coloring: N=$N" |
| 173 | + x0 = init_brusselator_2d(N) |
| 174 | + S = ADTypes.jacobian_sparsity( |
| 175 | + brusselator_2d!, similar(x0), x0, TracerSparsityDetector() |
| 176 | + ) |
| 177 | + tc1[i] = @belapsed ADTypes.column_coloring($S, GreedyColoringAlgorithm()) |
| 178 | + tc2[i] = @belapsed SDT.matrix_colors($S) |
| 179 | +end |
| 180 | + |
| 181 | +plc = plot(; |
| 182 | + title="Coloring on the Brusselator", xlabel="Input size N", ylabel="Runtime [s]" |
| 183 | +) |
| 184 | +plot!( |
| 185 | + plc, |
| 186 | + N_values, |
| 187 | + tc1; |
| 188 | + lw=2, |
| 189 | + linestyle=:auto, |
| 190 | + markershape=:auto, |
| 191 | + label="SparseMatrixColorings", |
| 192 | +) |
| 193 | +plot!(plc, N_values, tc2; lw=2, linestyle=:auto, markershape=:auto, label="SparseDiffTools") |
| 194 | +plot!(plc; xscale=:log10, yscale=:log10, legend=:topleft, minorgrid=true) |
| 195 | +plc |
| 196 | +``` |
| 197 | + |
| 198 | +### Differentiation |
| 199 | + |
| 200 | +```julia |
| 201 | +tj1, tj2 = zeros(length(N_values)), zeros(length(N_values)) |
| 202 | +for (i, N) in enumerate(N_values) |
| 203 | + @info "Benchmarking differentiation: N=$N" |
| 204 | + x0 = init_brusselator_2d(N) |
| 205 | + S = ADTypes.jacobian_sparsity( |
| 206 | + brusselator_2d!, similar(x0), x0, TracerSparsityDetector() |
| 207 | + ) |
| 208 | + J = similar(S, eltype(x0)) |
| 209 | + |
| 210 | + tj1[i] = @belapsed DI.jacobian!($brusselator_2d!, _y, _J, $backend, $x0, _extras) setup = ( |
| 211 | + _y = similar($x0); |
| 212 | + _J = similar($J); |
| 213 | + _extras = DI.prepare_jacobian($brusselator_2d!, similar($x0), $backend, $x0) |
| 214 | + ) evals = 1 |
| 215 | + |
| 216 | + tj2[i] = @belapsed SDT.sparse_jacobian!(_J, $backend, _cache, $brusselator_2d!, _y, $x0) setup = ( |
| 217 | + _y = similar($x0); |
| 218 | + _J = similar($J); |
| 219 | + _cache = SDT.sparse_jacobian_cache( |
| 220 | + $backend, |
| 221 | + SDT.JacPrototypeSparsityDetection(; jac_prototype=$S), |
| 222 | + $brusselator_2d!, |
| 223 | + similar($x0), |
| 224 | + $x0, |
| 225 | + ) |
| 226 | + ) evals = 1 |
| 227 | +end |
| 228 | + |
| 229 | +plj = plot(; |
| 230 | + title="Sparse Jacobian on the Brusselator", xlabel="Input size N", ylabel="Runtime [s]" |
| 231 | +) |
| 232 | +plot!( |
| 233 | + plj, |
| 234 | + N_values, |
| 235 | + tj1; |
| 236 | + lw=2, |
| 237 | + linestyle=:auto, |
| 238 | + markershape=:auto, |
| 239 | + label="DifferentiationInterface", |
| 240 | +) |
| 241 | +plot!(plj, N_values, tj2; lw=2, linestyle=:auto, markershape=:auto, label="SparseDiffTools") |
| 242 | +plot!(plj; xscale=:log10, yscale=:log10, legend=:topleft, minorgrid=true) |
| 243 | +plj |
| 244 | +``` |
| 245 | + |
| 246 | +### Summary |
| 247 | + |
| 248 | +```julia |
| 249 | +pl = plot(; |
| 250 | + title="Is the new pipeline worth it?\nTest case: Brusselator", |
| 251 | + xlabel="Input size N", |
| 252 | + ylabel="Runtime ratio DI / SparseDiffTools", |
| 253 | +) |
| 254 | +plot!( |
| 255 | + pl, |
| 256 | + N_values, |
| 257 | + td2 ./ td1; |
| 258 | + lw=2, |
| 259 | + linestyle=:auto, |
| 260 | + markershape=:auto, |
| 261 | + label="sparsity detection speedup", |
| 262 | +) |
| 263 | +plot!( |
| 264 | + pl, |
| 265 | + N_values, |
| 266 | + tc2 ./ tc1; |
| 267 | + lw=2, |
| 268 | + linestyle=:auto, |
| 269 | + markershape=:auto, |
| 270 | + label="coloring speedup", |
| 271 | +) |
| 272 | +plot!( |
| 273 | + pl, |
| 274 | + N_values, |
| 275 | + tj2 ./ tj1; |
| 276 | + lw=2, |
| 277 | + linestyle=:auto, |
| 278 | + markershape=:auto, |
| 279 | + label="differentiation speedup", |
| 280 | +) |
| 281 | +plot!(pl, N_values, ones(length(N_values)); lw=3, color=:black, label="same speed") |
| 282 | +plot!(pl; xscale=:log10, yscale=:log10, minorgrid=true, legend=:right) |
| 283 | +pl |
| 284 | +``` |
0 commit comments