@@ -44,8 +44,7 @@ properties.
44
44
`nlsolve` is not `missing`.
45
45
- `autodiff`: The autodifferentiation algorithm to use to compute the Jacobian if
46
46
`manifold_jacobian` is not specified. This must be specified if `manifold_jacobian` is
47
- not specified and `nlsolve` is `missing`. If `nlsolve` is not `missing`, then
48
- `autodiff` is ignored.
47
+ not specified.
49
48
- `manifold_jacobian`: The Jacobian of the manifold (wrt the state). This has the same
50
49
signature as `manifold` and the first argument is the Jacobian if inplace.
51
50
@@ -118,13 +117,7 @@ function (proj::ManifoldProjection)(integrator)
118
117
proj. manifold_jacobian != = nothing && (proj. manifold_jacobian. t = integrator. t)
119
118
120
119
SciMLBase. reinit! (proj. nlcache, integrator. u; integrator. p)
121
-
122
- if proj. nlsolve === missing
123
- _, u, retcode = SciMLBase. solve! (proj. nlcache)
124
- else
125
- sol = SciMLBase. solve! (proj. nlcache)
126
- (; u, retcode) = sol
127
- end
120
+ _, u, retcode = SciMLBase. solve! (proj. nlcache)
128
121
129
122
if ! SciMLBase. successful_retcode (retcode)
130
123
SciMLBase. terminate! (integrator, retcode)
@@ -146,17 +139,17 @@ function initialize_manifold_projection(affect!::ManifoldProjection, u, t, integ
146
139
(affect!. manifold_jacobian. autonomous = autonomous)
147
140
end
148
141
142
+ affect!. manifold. t = t
143
+ affect!. manifold_jacobian != = nothing && (affect!. manifold_jacobian. t = t)
144
+
149
145
if affect!. nlsolve === missing
150
- affect!. manifold. t = t
151
- affect!. manifold_jacobian != = nothing && (affect!. manifold_jacobian. t = t)
152
146
cache = init_manifold_projection (
153
147
Val (SciMLBase. isinplace (integrator. f)), affect!. manifold, affect!. autodiff,
154
148
affect!. manifold_jacobian, u, integrator. p; affect!. kwargs... )
155
149
else
156
- # nlfunc = NonlinearFunction{iip}(affect!.g; affect!.resid_prototype)
157
- # nlprob = NonlinearProblem(nlfunc, u, integrator.p)
158
- # affect!.nlcache = init(nlprob, affect!.nlsolve; affect!.kwargs...)
159
- error (" Not Implemented" )
150
+ cache = init_manifold_projection_nonlinear_problem (
151
+ Val (SciMLBase. isinplace (integrator. f)), affect!. manifold, affect!. autodiff,
152
+ affect!. manifold_jacobian, u, integrator. p, affect!. nlsolve; affect!. kwargs... )
160
153
end
161
154
affect!. nlcache = cache
162
155
u_modified! (integrator, false )
@@ -187,6 +180,97 @@ function (f::UntypedNonAutonomousFunction)(res, u, p)
187
180
end
188
181
(f:: UntypedNonAutonomousFunction )(u, p) = f. autonomous ? f. f (u, p) : f. f (u, p, f. t)
189
182
183
+ # This is solving the langrange multiplier formulation. This is more accurate but at the
184
+ # same time significantly more expensive.
185
+ @concrete mutable struct NonlinearSolveManifoldProjectionCache{iip}
186
+ manifold
187
+ p
188
+ λ
189
+ z
190
+ ũ
191
+ gu_cache
192
+ nlcache
193
+
194
+ first_call:: Bool
195
+ J
196
+ manifold_jacobian
197
+ autodiff
198
+ di_extras
199
+ end
200
+
201
+ function SciMLBase. reinit! (
202
+ cache:: NonlinearSolveManifoldProjectionCache{iip} , u; p = cache. p) where {iip}
203
+ if ! cache. first_call || (cache. ũ != = u || cache. p != = p)
204
+ compute_manifold_jacobian! (cache. J, cache. manifold_jacobian, cache. autodiff,
205
+ Val (iip), cache. manifold, cache. gu_cache, u, p, cache. di_extras)
206
+ end
207
+ cache. first_call = false
208
+ cache. ũ = u
209
+ cache. p = p
210
+
211
+ cache. z[1 : length (cache. λ)] .= false
212
+ cache. z[(length (cache. λ) + 1 ): end ] .= vec (u)
213
+ SciMLBase. reinit! (cache. nlcache, cache. z; p = (u, cache. J, p))
214
+ end
215
+
216
+ function init_manifold_projection_nonlinear_problem (
217
+ IIP:: Val{iip} , manifold, autodiff, manifold_jacobian, ũ, p, alg;
218
+ resid_prototype = nothing , kwargs... ) where {iip}
219
+ if iip
220
+ if resid_prototype != = nothing
221
+ gu = similar (resid_prototype)
222
+ λ = similar (resid_prototype)
223
+ else
224
+ @warn " `resid_prototype` not provided for in-place problem. Assuming size of \
225
+ output is the same as input. This might be incorrect." maxlog= 1
226
+ gu = similar (ũ)
227
+ λ = similar (ũ)
228
+ end
229
+ else
230
+ gu = nothing
231
+ λ = manifold (ũ, p)
232
+ end
233
+
234
+ J, di_extras = setup_manifold_jacobian (manifold_jacobian, autodiff, IIP, manifold,
235
+ gu, ũ, p)
236
+ z = vcat (vec (λ), vec (ũ))
237
+
238
+ nlfunc = if iip
239
+ let λlen = length (λ), λsz = size (λ), zsz = size (ũ)
240
+ @views (resid, u, ps) -> begin
241
+ ũ2, J2, p2 = ps
242
+ λ2, z2 = u[1 : λlen], u[(λlen + 1 ): end ]
243
+ manifold (reshape (resid[1 : λlen], λsz), reshape (z2, zsz), p2)
244
+ resid[(λlen + 1 ): end ] .= z2 .- vec (ũ2) .+ vec (vec (J2' * λ2))
245
+ end
246
+ end
247
+ else
248
+ let λlen = length (λ), zsz = size (ũ)
249
+ @views (u, ps) -> begin
250
+ ũ2, J2, p2 = ps
251
+ λ2, z2 = u[1 : λlen], u[(λlen + 1 ): end ]
252
+ gz = vec (manifold (reshape (z2, zsz), p2))
253
+ resid = z2 .- vec (ũ2) .+ vec (J2' * λ2)
254
+ return vcat (gz, resid)
255
+ end
256
+ end
257
+ end
258
+
259
+ nlprob = NonlinearProblem (NonlinearFunction {iip} (nlfunc), z, (ũ, J, p))
260
+ nlcache = SciMLBase. init (nlprob, alg; kwargs... )
261
+
262
+ return NonlinearSolveManifoldProjectionCache {iip} (
263
+ manifold, p, λ, z, ũ, gu, nlcache, true , J, manifold_jacobian, autodiff, di_extras)
264
+ end
265
+
266
+ @views function SciMLBase. solve! (cache:: NonlinearSolveManifoldProjectionCache{iip} ) where {iip}
267
+ sol = SciMLBase. solve! (cache. nlcache)
268
+ (; u, retcode) = sol
269
+ λ = reshape (u[1 : length (cache. λ)], size (cache. λ))
270
+ ũ = reshape (u[(length (cache. λ) + 1 ): end ], size (cache. ũ))
271
+ return λ, ũ, retcode
272
+ end
273
+
190
274
# This is the algorithm described in Hairer III.
191
275
@concrete mutable struct SingleFactorizeManifoldProjectionCache{iip}
192
276
manifold
@@ -225,7 +309,7 @@ default_abstol(::Type{T}) where {T} = real(oneunit(T)) * (eps(real(one(T))))^(4
225
309
226
310
function init_manifold_projection (IIP:: Val{iip} , manifold, autodiff, manifold_jacobian, ũ,
227
311
p; abstol = default_abstol (eltype (ũ)), maxiters = 1000 ,
228
- resid_prototype = nothing ) where {iip}
312
+ resid_prototype = nothing , kwargs ... ) where {iip}
229
313
if iip
230
314
if resid_prototype != = nothing
231
315
gu = similar (resid_prototype)
@@ -309,6 +393,11 @@ function setup_manifold_jacobian(
309
393
return J, di_extras
310
394
end
311
395
396
+ function setup_manifold_jacobian (
397
+ :: Nothing , :: Nothing , :: Val{iip} , manifold, gu, ũ, p) where {iip}
398
+ error (" `autodiff` is set to `nothing` and analytic manifold jacobian is not provided." )
399
+ end
400
+
312
401
function compute_manifold_jacobian! (J, manifold_jacobian, autodiff, :: Val{iip} ,
313
402
manifold, gu, ũ, p, di_extras) where {iip}
314
403
if iip
@@ -329,10 +418,6 @@ function compute_manifold_jacobian!(J, ::Nothing, autodiff, ::Val{iip}, manifold
329
418
return J
330
419
end
331
420
332
- function setup_manifold_jacobian (:: Nothing , :: Nothing , args... )
333
- error (" `autodiff` is set to `nothing` and analytic manifold jacobian is not provided." )
334
- end
335
-
336
421
function safe_factorize! (A:: AbstractMatrix )
337
422
if issquare (A)
338
423
fact = LinearAlgebra. cholesky (A; check = false )
0 commit comments