|
1 |
| -struct JacobianWrapper{fType, pType} |
2 |
| - f::fType |
3 |
| - p::pType |
| 1 | +@concrete struct JacobianWrapper |
| 2 | + f |
| 3 | + p |
4 | 4 | end
|
5 | 5 |
|
6 | 6 | (uf::JacobianWrapper)(u) = uf.f(u, uf.p)
|
7 | 7 | (uf::JacobianWrapper)(res, u) = uf.f(res, u, uf.p)
|
8 | 8 |
|
9 |
| -struct NonlinearSolveTag end |
10 |
| - |
11 |
| -function sparsity_colorvec(f, x) |
12 |
| - sparsity = f.sparsity |
13 |
| - colorvec = DiffEqBase.has_colorvec(f) ? f.colorvec : |
14 |
| - (isnothing(sparsity) ? (1:length(x)) : matrix_colors(sparsity)) |
15 |
| - sparsity, colorvec |
16 |
| -end |
17 |
| - |
18 |
| -function jacobian_finitediff_forward!(J, f, x, jac_config, forwardcache, cache) |
19 |
| - (FiniteDiff.finite_difference_jacobian!(J, f, x, jac_config, forwardcache); |
20 |
| - maximum(jac_config.colorvec)) |
21 |
| -end |
22 |
| -function jacobian_finitediff!(J, f, x, jac_config, cache) |
23 |
| - (FiniteDiff.finite_difference_jacobian!(J, f, x, jac_config); |
24 |
| - 2 * maximum(jac_config.colorvec)) |
25 |
| -end |
| 9 | +# function sparsity_colorvec(f, x) |
| 10 | +# sparsity = f.sparsity |
| 11 | +# colorvec = DiffEqBase.has_colorvec(f) ? f.colorvec : |
| 12 | +# (isnothing(sparsity) ? (1:length(x)) : matrix_colors(sparsity)) |
| 13 | +# sparsity, colorvec |
| 14 | +# end |
26 | 15 |
|
27 | 16 | # NoOp for Jacobian if it is not a Abstract Array -- For eg, JacVec Operator
|
28 |
| -jacobian!(J, cache) = J |
29 |
| -function jacobian!(J::AbstractMatrix{<:Number}, cache) |
30 |
| - f = cache.f |
31 |
| - uf = cache.uf |
32 |
| - x = cache.u |
33 |
| - fx = cache.fu |
34 |
| - jac_config = cache.jac_config |
35 |
| - alg = cache.alg |
36 |
| - |
37 |
| - if SciMLBase.has_jac(f) |
38 |
| - f.jac(J, x, cache.p) |
39 |
| - elseif alg_autodiff(alg) |
40 |
| - forwarddiff_color_jacobian!(J, uf, x, jac_config) |
41 |
| - #cache.destats.nf += 1 |
| 17 | +jacobian!!(J, _) = J |
| 18 | +# `!!` notation is from BangBang.jl since J might be jacobian in case of oop `f.jac` |
| 19 | +# and we don't want wasteful `copyto!` |
| 20 | +function jacobian!!(J::Union{AbstractMatrix{<:Number}, Nothing}, cache) |
| 21 | + @unpack f, uf, u, p, jac_cache, alg, fu2 = cache |
| 22 | + iip = isinplace(cache) |
| 23 | + if iip |
| 24 | + has_jac(f) ? f.jac(J, u, p) : sparse_jacobian!(J, alg.ad, jac_cache, uf, fu2, u) |
42 | 25 | else
|
43 |
| - isforward = alg_difftype(alg) === Val{:forward} |
44 |
| - if isforward |
45 |
| - uf(fx, x) |
46 |
| - #cache.destats.nf += 1 |
47 |
| - tmp = jacobian_finitediff_forward!(J, uf, x, jac_config, fx, |
48 |
| - cache) |
49 |
| - else # not forward difference |
50 |
| - tmp = jacobian_finitediff!(J, uf, x, jac_config, cache) |
51 |
| - end |
52 |
| - #cache.destats.nf += tmp |
| 26 | + return has_jac(f) ? f.jac(u, p) : sparse_jacobian!(J, alg.ad, jac_cache, uf, u) |
53 | 27 | end
|
54 |
| - nothing |
| 28 | + return nothing |
55 | 29 | end
|
56 | 30 |
|
57 |
| -function build_jac_and_jac_config(alg, f::F1, uf::F2, du1, u, tmp, du2) where {F1, F2} |
| 31 | +# Build Jacobian Caches |
| 32 | +function jacobian_caches(alg::AbstractNonlinearSolveAlgorithm, f, u, p, |
| 33 | + ::Val{iip}) where {iip} |
| 34 | + uf = JacobianWrapper(f, p) |
| 35 | + |
58 | 36 | haslinsolve = hasfield(typeof(alg), :linsolve)
|
59 | 37 |
|
60 |
| - has_analytic_jac = SciMLBase.has_jac(f) |
| 38 | + has_analytic_jac = has_jac(f) |
61 | 39 | linsolve_needs_jac = (concrete_jac(alg) === nothing &&
|
62 | 40 | (!haslinsolve || (haslinsolve && (alg.linsolve === nothing ||
|
63 |
| - LinearSolve.needs_concrete_A(alg.linsolve))))) |
64 |
| - alg_wants_jac = (concrete_jac(alg) !== nothing && concrete_jac(alg)) |
| 41 | + needs_concrete_A(alg.linsolve))))) |
| 42 | + alg_wants_jac = (concrete_jac(alg) === nothing && concrete_jac(alg)) |
65 | 43 |
|
| 44 | + fu = zero(u) # TODO: Use Prototype |
66 | 45 | if !has_analytic_jac && (linsolve_needs_jac || alg_wants_jac)
|
67 |
| - sparsity, colorvec = sparsity_colorvec(f, u) |
68 |
| - |
69 |
| - if alg_autodiff(alg) |
70 |
| - _chunksize = get_chunksize(alg) === Val(0) ? nothing : get_chunksize(alg) # SparseDiffEq uses different convection... |
71 |
| - |
72 |
| - T = if standardtag(alg) |
73 |
| - typeof(ForwardDiff.Tag(NonlinearSolveTag(), eltype(u))) |
74 |
| - else |
75 |
| - typeof(ForwardDiff.Tag(uf, eltype(u))) |
76 |
| - end |
77 |
| - jac_config = ForwardColorJacCache(uf, u, _chunksize; colorvec, sparsity, |
78 |
| - tag = T) |
79 |
| - else |
80 |
| - if alg_difftype(alg) !== Val{:complex} |
81 |
| - jac_config = FiniteDiff.JacobianCache(tmp, du1, du2, alg_difftype(alg); |
82 |
| - colorvec, sparsity) |
83 |
| - else |
84 |
| - jac_config = FiniteDiff.JacobianCache(Complex{eltype(tmp)}.(tmp), |
85 |
| - Complex{eltype(du1)}.(du1), nothing, alg_difftype(alg), eltype(u); |
86 |
| - colorvec, sparsity) |
87 |
| - end |
88 |
| - end |
| 46 | + # TODO: We need an Upstream Mode to allow using known sparsity and colorvec |
| 47 | + # TODO: We can use the jacobian prototype here |
| 48 | + sd = typeof(alg.ad) <: AbstractSparseADType ? SymbolicsSparsityDetection() : |
| 49 | + NoSparsityDetection() |
| 50 | + jac_cache = iip ? sparse_jacobian_cache(alg.ad, sd, uf, fu, u) : |
| 51 | + sparse_jacobian_cache(alg.ad, sd, uf, u; fx=fu) |
89 | 52 | else
|
90 |
| - jac_config = nothing |
| 53 | + jac_cache = nothing |
91 | 54 | end
|
92 | 55 |
|
93 | 56 | J = if !linsolve_needs_jac
|
94 | 57 | # We don't need to construct the Jacobian
|
95 |
| - JacVec(uf, u; autodiff = alg_autodiff(alg) ? AutoForwardDiff() : AutoFiniteDiff()) |
| 58 | + JacVec(uf, u; autodiff = alg.ad) |
96 | 59 | else
|
97 |
| - if f.jac_prototype === nothing |
98 |
| - ArrayInterface.undefmatrix(u) |
| 60 | + if has_analytic_jac |
| 61 | + iip ? undefmatrix(u) : nothing |
99 | 62 | else
|
100 |
| - f.jac_prototype |
| 63 | + f.jac_prototype === nothing ? __init_𝒥(jac_cache) : f.jac_prototype |
101 | 64 | end
|
102 | 65 | end
|
103 | 66 |
|
104 |
| - return J, jac_config |
105 |
| -end |
106 |
| - |
107 |
| -# Build Jacobian Caches |
108 |
| -function jacobian_caches(alg::Union{NewtonRaphson, LevenbergMarquardt, TrustRegion}, f, u, |
109 |
| - p, ::Val{true}) |
110 |
| - uf = JacobianWrapper(f, p) |
111 |
| - |
112 |
| - du1 = zero(u) |
113 |
| - du2 = zero(u) |
114 |
| - tmp = zero(u) |
115 |
| - J, jac_config = build_jac_and_jac_config(alg, f, uf, du1, u, tmp, du2) |
116 |
| - |
| 67 | + # FIXME: Assumes same sized `u` and `fu` -- Incorrect Assumption for Levenberg |
117 | 68 | linprob = LinearProblem(J, _vec(zero(u)); u0 = _vec(zero(u)))
|
| 69 | + |
118 | 70 | weight = similar(u)
|
119 | 71 | recursivefill!(weight, true)
|
120 | 72 |
|
121 | 73 | Pl, Pr = wrapprecs(alg.precs(J, nothing, u, p, nothing, nothing, nothing, nothing,
|
122 | 74 | nothing)..., weight)
|
123 | 75 | linsolve = init(linprob, alg.linsolve; alias_A = true, alias_b = true, Pl, Pr)
|
124 | 76 |
|
125 |
| - uf, linsolve, J, du1, jac_config |
126 |
| -end |
127 |
| - |
128 |
| -function get_chunksize(jac_config::ForwardDiff.JacobianConfig{ |
129 |
| - T, |
130 |
| - V, |
131 |
| - N, |
132 |
| - D, |
133 |
| -}) where {T, V, N, D |
134 |
| -} |
135 |
| - Val(N) |
136 |
| -end # don't degrade compile time information to runtime information |
137 |
| - |
138 |
| -function jacobian_finitediff(f, x, ::Type{diff_type}, dir, colorvec, sparsity, |
139 |
| - jac_prototype) where {diff_type} |
140 |
| - (FiniteDiff.finite_difference_derivative(f, x, diff_type, eltype(x), dir = dir), 2) |
141 |
| -end |
142 |
| -function jacobian_finitediff(f, x::AbstractArray, ::Type{diff_type}, dir, colorvec, |
143 |
| - sparsity, jac_prototype) where {diff_type} |
144 |
| - f_in = diff_type === Val{:forward} ? f(x) : similar(x) |
145 |
| - ret_eltype = eltype(f_in) |
146 |
| - J = FiniteDiff.finite_difference_jacobian(f, x, diff_type, ret_eltype, f_in, |
147 |
| - dir = dir, colorvec = colorvec, |
148 |
| - sparsity = sparsity, |
149 |
| - jac_prototype = jac_prototype) |
150 |
| - return J, _nfcount(maximum(colorvec), diff_type) |
151 |
| -end |
152 |
| -function jacobian(cache, f::F) where {F} |
153 |
| - x = cache.u |
154 |
| - alg = cache.alg |
155 |
| - uf = cache.uf |
156 |
| - local tmp |
157 |
| - |
158 |
| - if DiffEqBase.has_jac(cache.f) |
159 |
| - J = f.jac(cache.u, cache.p) |
160 |
| - elseif alg_autodiff(alg) |
161 |
| - J, tmp = jacobian_autodiff(uf, x, cache.f, alg) |
162 |
| - else |
163 |
| - jac_prototype = cache.f.jac_prototype |
164 |
| - sparsity, colorvec = sparsity_colorvec(cache.f, x) |
165 |
| - dir = true |
166 |
| - J, tmp = jacobian_finitediff(uf, x, alg_difftype(alg), dir, colorvec, sparsity, |
167 |
| - jac_prototype) |
168 |
| - end |
169 |
| - J |
170 |
| -end |
171 |
| - |
172 |
| -jacobian_autodiff(f, x, nonlinfun, alg) = (ForwardDiff.derivative(f, x), 1, alg) |
173 |
| -function jacobian_autodiff(f, x::AbstractArray, nonlinfun, alg) |
174 |
| - jac_prototype = nonlinfun.jac_prototype |
175 |
| - sparsity, colorvec = sparsity_colorvec(nonlinfun, x) |
176 |
| - maxcolor = maximum(colorvec) |
177 |
| - chunk_size = get_chunksize(alg) === Val(0) ? nothing : get_chunksize(alg) |
178 |
| - num_of_chunks = chunk_size === nothing ? |
179 |
| - Int(ceil(maxcolor / |
180 |
| - SparseDiffTools.getsize(ForwardDiff.pickchunksize(maxcolor)))) : |
181 |
| - Int(ceil(maxcolor / _unwrap_val(chunk_size))) |
182 |
| - (forwarddiff_color_jacobian(f, x, colorvec = colorvec, sparsity = sparsity, |
183 |
| - jac_prototype = jac_prototype, chunksize = chunk_size), |
184 |
| - num_of_chunks) |
| 77 | + return uf, linsolve, J, fu, jac_cache |
185 | 78 | end
|
0 commit comments