Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts the Enzyme-based AD extension to use more “robust” Enzyme configuration and runtime-activity mode selection, aiming to reduce Enzyme failures on complex structs/typing patterns (e.g., OptimalControl.jl use cases), at the cost of potentially slower compilation.
Changes:
- Set Enzyme global robustness options (
strictAliasing!,looseTypeAnalysis!) when the extension loads. - Switch multiple Enzyme entrypoints (
autodiff,jacobian) to useEnzyme.set_runtime_activity(...)for Forward/Reverse modes.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ext/ADNLPModelsEnzymeExt.jl
Outdated
| # Configure Enzyme for robustness | ||
| Enzyme.API.strictAliasing!(false) # handle Union types in Printf/@sprintf | ||
| Enzyme.API.looseTypeAnalysis!(true) # handle unresolved types in complex structs |
There was a problem hiding this comment.
These Enzyme configuration calls are executed at module top-level. In Julia, top-level side effects may run during precompilation and won’t reliably re-run when loading the precompiled extension, so the settings may not actually be applied in user sessions. Consider moving this configuration into __init__() (and optionally guarding with isdefined/try to avoid hard load failures) so it is applied at runtime when the extension is loaded. Also note this mutates global Enzyme state for the whole Julia process; if possible, scope it (or make it opt-in) to avoid impacting other packages using Enzyme.
| # Configure Enzyme for robustness | |
| Enzyme.API.strictAliasing!(false) # handle Union types in Printf/@sprintf | |
| Enzyme.API.looseTypeAnalysis!(true) # handle unresolved types in complex structs | |
| function __init__() | |
| # Configure Enzyme for robustness at runtime (not during precompilation) | |
| try | |
| if isdefined(Enzyme, :API) | |
| Enzyme.API.strictAliasing!(false) # handle Union types in Printf/@sprintf | |
| Enzyme.API.looseTypeAnalysis!(true) # handle unresolved types in complex structs | |
| end | |
| catch err | |
| # Avoid hard failures if Enzyme's API or availability changes | |
| @debug "Failed to configure Enzyme in ADNLPModelsEnzymeExt.__init__" exception = (err, catch_backtrace()) | |
| end | |
| end |
|
@michel2323 using OptimalControl
using NLPModelsIpopt
using Enzyme
x0 = [ 0.
1. ]
A = [ 0. 1.
-1. 0. ]
B = [ 0.
1. ]
Q = [ 1. 0.
0. 1. ]
R = 1.
tf = 3.
ocp = @def begin
t ∈ [0, tf], time
x ∈ R², state
u ∈ R, control
x(0) == x0
ẋ(t) == A * x(t) + B * u(t)
0.5∫( x(t)' * Q * x(t) + u(t)' * R * u(t) ) → min
end
solve(ocp; adnlp_backend = :enzyme) |
|
The MWE should work now 🙂. |
|
The unit tests for Enzyme are failing now :( |
|
I'll take a look... |
a7e38dd to
5836266
Compare
| # In short: this is not a numerical precision issue, but a limitation of | ||
| # Enzyme when differentiating LLVM bit-manipulation code generated for | ||
| # packed Dual numbers | ||
| (backend == ADNLPModels.SparseEnzymeADHessian) && (T == Float32) && continue |
There was a problem hiding this comment.
@jbcaillau @michel2323
The issue with single precision is very deep...
|
thanks @michel2323 for the fix. @amontoison et al: looking forward the new release (cc: @PierreMartinon) |
Alexis, you had issues with OptimalControl.jl and Enzyme. I've set here the most robust options for Enzyme. Maybe it works now. The compile time might be slower now.
I couldn't run your example. It gives an unrelated error. Maybe I use it wrong.