@@ -321,3 +321,148 @@ end
321321function NonlinearLeastSquaresProblem (f, u0, p = NullParameters (); kwargs... )
322322 return NonlinearLeastSquaresProblem (NonlinearFunction (f), u0, p; kwargs... )
323323end
324+
325+ @doc doc"""
326+ SCCNonlinearProblem(probs, explicitfuns!)
327+
328+ Defines an SCC-split nonlinear system to be solved iteratively.
329+
330+ ## Mathematical Specification of an SCC-Split Nonlinear Problem
331+
332+ An SCC-Split Nonlinear Problem is a system of nonlinear equations
333+
334+ ```math
335+ f(u,p) = 0
336+ ```
337+
338+ with the special property that its Jacobian is in block-lower-triangular
339+ form. In this form, the nonlinear problem can be decomposed into a system
340+ of nonlinear systems.
341+
342+ ```math
343+ f_1(u_1,p) = 0
344+ f_2(u_2,u_1,p) = 0
345+ f_3(u_3,u_2,u_1,p) = 0
346+ \v dots
347+ f_n(u_n,\l dots,u_3,u_2,u_1,p) = 0
348+ ```
349+
350+ Splitting the system in this form can have multiple advantages, including:
351+
352+ * Improved numerical stability and robustness of the solving process
353+ * Improved performance due to using smaller Jacobians
354+
355+ The SCC-Split Nonlinear Problem is the ordered collection of nonlinear systems
356+ to solve in order solve the system in the optimized split form.
357+
358+ ## Representation
359+
360+ The representation of the SCCNonlinearProblem is via an ordered collection
361+ of `NonlinearProblem`s, `probs`, with an attached explicit function for pre-processing
362+ a cache. This can be interpreted as follows:
363+
364+ ```math
365+ p_1 = g_1(u,p)
366+ f_1(u_1,p_1) = 0
367+ p_2 = g_2(u,p)
368+ f_2(u_2,u_1,p_2) = 0
369+ p_3 = g_3(u,p)
370+ f_3(u_3,u_2,u_1,p_3) = 0
371+ \v dots
372+ p_n = g_n(u,p)
373+ f_n(u_n,\l dots,u_3,u_2,u_1,p_n) = 0
374+ ```
375+
376+ where ``g_i`` is `explicitfuns![i]`. In a computational sense, `explictfuns!`
377+ is instead a mutating function `explictfuns` which
378+ updates the values of prob.probs[i] using the previous solutions `sols[i-1]`
379+ and below.
380+
381+ !!! warn
382+ For the purposes of differentiation, it's assumed that `explictfuns!` does
383+ not modify tunable parameters!
384+
385+ !!! warn
386+ While `explictfuns![i]` could in theory use `sols[i+1]` in its computation,
387+ these values will not be updated. It is thus the contract of the interface
388+ to not use those values except for as caches to be overriden.
389+
390+ !!! note
391+ prob.probs[i].p can be aliased with each other as a performance / memory
392+ optimization. If they are aliased before the construction of the `probs`
393+ the runtime guarantees the aliasing behavior is kept.
394+
395+ ## Example
396+
397+ For the following nonlinear problem:
398+
399+ ```julia
400+ function f(du,u,p)
401+ du[1] = cos(u[2]) - u[1]
402+ du[2] = sin(u[1] + u[2]) + u[2]
403+ du[3] = 2u[4] + u[3] + 1.0
404+ du[4] = u[5]^2 + u[4]
405+ du[5] = u[3]^2 + u[5]
406+ du[6] = u[1] + u[2] + u[3] + u[4] + u[5] + 2.0u[6] + 2.5u[7] + 1.5u[8]
407+ du[7] = u[1] + u[2] + u[3] + 2.0u[4] + u[5] + 4.0u[6] - 1.5u[7] + 1.5u[8]
408+ du[8] = u[1] + 2.0u[2] + 3.0u[3] + 5.0u[4] + 6.0u[5] + u[6] - u[7] - u[8]
409+ end
410+ prob = NonlinearProblem(f, zeros(8))
411+ sol = solve(prob)
412+ ```
413+
414+ The split SCC form is:
415+
416+ ```julia
417+ cache = zeros(3)
418+
419+ function f1(du,u,cache)
420+ du[1] = cos(u[2]) - u[1]
421+ du[2] = sin(u[1] + u[2]) + u[2]
422+ end
423+ explicitfun1(cache,sols) = nothing
424+ prob1 = NonlinearProblem(NonlinearFunction{true, SciMLBase.NoSpecialize}(f1), zeros(2), cache)
425+ sol1 = solve(prob1, NewtonRaphson())
426+
427+ function f2(du,u,cache)
428+ du[1] = 2u[2] + u[1] + 1.0
429+ du[2] = u[3]^2 + u[2]
430+ du[3] = u[1]^2 + u[3]
431+ end
432+ explicitfun2(cache,sols) = nothing
433+ prob2 = NonlinearProblem(NonlinearFunction{true, SciMLBase.NoSpecialize}(f2), zeros(3), cache)
434+ sol2 = solve(prob2, NewtonRaphson())
435+
436+ function f3(du,u,cache)
437+ du[1] = cache[1] + 2.0u[1] + 2.5u[2] + 1.5u[3]
438+ du[2] = cache[2] + 4.0u[1] - 1.5u[2] + 1.5u[3]
439+ du[3] = cache[3] + + u[1] - u[2] - u[3]
440+ end
441+ prob3 = NonlinearProblem(NonlinearFunction{true, SciMLBase.NoSpecialize}(f3), zeros(3), cache)
442+ function explicitfun3(cache,sols)
443+ cache[1] = sols[1][1] + sols[1][2] + sols[2][1] + sols[2][2] + sols[2][3]
444+ cache[2] = sols[1][1] + sols[1][2] + sols[2][1] + 2.0sols[2][2] + sols[2][3]
445+ cache[3] = sols[1][1] + 2.0sols[1][2] + 3.0sols[2][1] + 5.0sols[2][2] + 6.0sols[2][3]
446+ end
447+ explicitfun3(cache,[sol1,sol2])
448+ sol3 = solve(prob3, NewtonRaphson())
449+ manualscc = [sol1; sol2; sol3]
450+
451+ sccprob = SciMLBase.SCCNonlinearProblem([prob1,prob2,prob3], SciMLBase.Void{Any}.([explicitfun1,explicitfun2,explicitfun3]))
452+ ```
453+
454+ Note that this example aliases the parameters together for a memory-reduced representation.
455+
456+ ## Problem Type
457+
458+ ### Constructors
459+
460+ ### Fields
461+
462+ * `probs`: the collection of problems to solve
463+ * `explictfuns!`: the explicit functions for mutating the parameter set
464+ """
465+ mutable struct SCCNonlinearProblem{P, E}
466+ probs:: P
467+ explictfuns!:: E
468+ end
0 commit comments