Skip to content

Allow Duals and other types to pass through OptimizationFunction grad#1229

Merged
ChrisRackauckas merged 10 commits into
SciML:masterfrom
jClugstor:dual-tolerant-grad
Jul 8, 2026
Merged

Allow Duals and other types to pass through OptimizationFunction grad#1229
ChrisRackauckas merged 10 commits into
SciML:masterfrom
jClugstor:dual-tolerant-grad

Conversation

@jClugstor

Copy link
Copy Markdown
Member

Checklist

  • Appropriate tests were added
  • Any code changes were done in a way that does not break public API
  • All documentation related to code changes were updated
  • The new code follows the
    contributor guidelines, in particular the SciML Style Guide and
    COLPRAC.
  • Any new documentation only uses public API

Additional context

Essentially checks if the input types are different from the prep object. If they are, just reprep for that call.

Specifically allows sensitivity analysis to use ForwardDiff with grad, to support SciML/SciMLSensitivity.jl#1444 .

# internal buffers are concrete (e.g. `Float64`) and reject inputs of any other type. That is
# correct and fast for the optimization solve, but a downstream sensitivity layer (e.g.
# SciMLSensitivity's `OptimizationAdjoint`) differentiates the KKT stationarity conditions
# w.r.t. the parameters by pushing `ForwardDiff.Dual`s through the gradient: a dual `p` (and a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, why is p also Dual? The optimization parameters can be pretty much any arbitrary user given struct.

@jClugstor jClugstor Jun 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The adjoint sensitivity finds the derivative of the solution of the optimization problem with respect to p. To find that we need:

$$\frac{\partial F}{\partial p}$$

where

$$F(w, p) = \begin{pmatrix} \nabla \mathcal{L}(w, p) \ g(w,p) \ h_\mathcal{I}(w,p) \end{pmatrix}^T$$

The problem I'm having is the $$\nabla \mathcal{L}(w, p)$$
because that's already a gradient with respect to $x$, and we need to then differentiate that with respect to $p$.

The optimization parameters can be pretty much any arbitrary user given struct.

Right, and if users want to get the sensitivity they'll either have to use types that are compatible with SciMLSensitivity or use SciMLStructures to tell SciMLSensitivity how to handle the user given struct. But that's already the case for ODEs and NonlinearSolves anyways, so it's still consistent with the rest of the ecosystem.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear I'm not sure this PR is a good idea, and it's incomplete anyway since I would also need the constraint jacobians to be able to take Duals.

But this or another workaround is what would be needed if we want to be able to reuse the stuff that optimization builds inside of the adjoint.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see. It doesn't seem too invasive.

@jClugstor

Copy link
Copy Markdown
Member Author

@SebastianM-C In order to support Enzyme for the optimization adjoint I needed to make some changes to the Enzyme extension. Mostly to allow the functions to accept other parameters, but there was also some boxing that Enzyme failed with, so I introduced some let blocks. There was also an issue where the closure was capturing a type value for the function annotation which Enzyme really did not like. So I changed it to precalculate so it would capture a Bool value instead. I don't think any of the changes are very intrusive but we definitely need to be careful with the Enzyme stuff, so if you see any issues let me know.

@jClugstor jClugstor force-pushed the dual-tolerant-grad branch 2 times, most recently from 0ff9b96 to 10ff293 Compare July 2, 2026 14:48
@jClugstor jClugstor marked this pull request as ready for review July 2, 2026 21:47
@jClugstor jClugstor force-pushed the dual-tolerant-grad branch from a068fbc to 181f01e Compare July 6, 2026 13:47
@jClugstor

Copy link
Copy Markdown
Member Author

@ChrisRackauckas @SebastianM-C

In order for SciMLSensitivity to be able to reuse the closures built in Optimization, this PR has the changes needed.

One, make it so that they all can accept a p.

Two, make it so that they can accept Dual values. The way I do that in this PR is to use anyeltypedual to check if p is Dual at all, and if it is then we can't reuse the DI prep. At first I thought this would add some overhead but profiling and looking at the typed IR it turns out that if p is the same type as the prep the anyeltypedual gets compiled away so there's no runtime overhead.

The changes in the Enzyme extension are to make sure that the closures can take a p, and to prevent some Boxing from happening. Also there was an issue with nested Enzyme where the function annotation type was being captured that Enzyme couldn't handle so I fixed that as well.

Comment thread lib/OptimizationBase/src/OptimizationDIExt.jl Outdated

@SebastianM-C SebastianM-C left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One, make it so that they all can accept a p.

Ah, nice, I wanted to have that anyway.

@ChrisRackauckas ChrisRackauckas merged commit 59bdb75 into SciML:master Jul 8, 2026
90 of 101 checks passed
@SebastianM-C SebastianM-C mentioned this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants