Repository navigation
Fix DI prep type-mismatch fallbacks for hess/hv/fg/fgh/lag_h - #1408
ChrisRackauckas-Claude wants to merge 5 commits into
Conversation
Extend the existing `_prep_valid` gate beyond grad!/cons_j! so second-order and combined DI operators fall back to prep-free calls when θ/p eltypes or array types differ from construction, avoiding PreparationMismatchError. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Cursor Agent <noreply@cursor.com> Agent-Harness: Cursor Agent 2026.10.01-14929f9 Agent-Model: unknown (Cursor auto) Agent-Session: local session, transcript /home/crackauc/sandbox/goals/performance/jobs/opt/opt-prep/log.txt on amdci2
Share Type-param helper closures (incl. grad!/cons_j!/lag_h!), drop duplicated NullParameters branches, use value_gradient_and_hessian(!) for fgh!, and gate hv! on the tangent type. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Cursor Agent <noreply@cursor.com> Agent-Harness: Cursor Agent 2026.10.01-14929f9 Agent-Model: unknown (Cursor auto) Agent-Session: local session, transcript /home/crackauc/sandbox/goals/performance/jobs/opt/opt-prep/log.txt on amdci2
Named _iip/_oop_ad_cons_jac_clos match the other Type-param factories, build hv prep tangents from zero(x) so SVector v hits the prepared path, and cover IIP packed-vector plus OOP lag_h foreign types. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Cursor Agent <noreply@cursor.com> Agent-Harness: Cursor Agent 2026.10.01-14929f9 Agent-Model: unknown (Cursor auto) Agent-Session: local session, transcript /home/crackauc/sandbox/goals/performance/jobs/opt/opt-prep/log.txt on amdci2
Independent review (Devin CLI 3000.11.3, model fusion-claude-opus-5-5-high-sidekick-swe-2-medium): MERGE, risk medium. Full reviewVERDICT: MERGE The change sits on the hot path of every DI-backed Blocking findingsNone. What I confirmed by running (details under "What I ran"):
Non-blocking findings
What I ranAll runs used Julia 1.11 with DI 0.7.21, ForwardDiff 1.4.6, ReverseDiff 1.18.5, FiniteDiff 2.33.0, Enzyme 0.13.216 and SciMLBase 3.57.0. I used two scratch envs that differ only in the OptimizationBase source: PR head, or an
What I did not verify
Links
🤖 Posted by an AI agent — harness: Devin CLI 3000.11.3 (review), Claude Code 2.1.285 (posting) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium |
The in-place Lagrangian differentiated `cons_oop(θ)`, which closes over the construction `p`, so `lag_h(H, θ, σ, λ, p)` with any other `p` returned the constraint term at the construction parameters. With the prep-free fallback accepting a foreign `p` type, that wrong result was returned silently instead of a PreparationMismatchError. Both Lagrangians now share a `p`-accepting constraint term `cons_lagrangian(θ, λ, p)`. σ = 0: the in-place path uses the prepared per-constraint Hessians only at the construction θ type and for the construction `p` object (they close over it). Otherwise, and when no AD constraint-Hessian preparation exists (a user-supplied `cons_h`), it takes the prep-free Hessian of `cons_lagrangian` at the call-time `p`. The out-of-place σ = 0 path returned `λ .* cons_h!(θ)` (a vector of matrices, and a MethodError when `cons_h = false`); it now returns the Hessian of `cons_lagrangian`. The Lagrangian preparation is built at `Constant(σ0)`/`Constant(λ0)`; their types join the prep gate, so an `Int` σ or a `Float32`/view λ takes the prep-free path instead of throwing PreparationMismatchError. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.301 Agent-Model: claude-opus-5-5 Agent-Session: local session ID 71155362-c106-40f7-b5a3-f1cf335f4339 (headless claude -p, no URL)
Summary
Collapses the DI prep-validity fallbacks in
OptimizationDIExt.jlonto shared Type-parameter helper closures (includinggrad!/cons_j!/lag_h!/_ad_cons_jac), fixing the duplicatedNullParametersbranches. Also switches AD-generatedfgh!from DI's scalarvalue_derivative_and_second_derivative(!)tovalue_gradient_and_hessian(!)so the matching-type fast path works, gateshv!on the tangentvtype (built fromzero(x)soSVectortangents hit the prepared path), and adds foreign-type coverage for IIP packed-vector and OOPlag_h.Out of scope (still broken, not addressed here): Dual
pwith second-order ops raisesDualMismatchError(ForwardDiff tag nesting, even with a fresh Dual-pprep); OOPSVectorsolves through OptimJL/Optimisers remain wrapper bugs beyond the unit-levelinstantiate_functionfallback.Verification
Regression (dual_tolerant_tests.jl):
matching-type fgh!→MethodErroronvalue_derivative_and_second_derivative!Matching-type path unchanged (n=10, min ns, master vs PR; identical allocations):
Test groups (Julia 1,
OPTIMIZATION_TEST_GROUP):Probes: Float32/BigFloat/SVector/hv-with-mismatched-
vand matchingfgh!match fresh-prep results; Dual-psecond-order stillDualMismatchError(out of scope).Not verified
a4_generic.jlOptim/Optimisers SVector solve matrix (unit-level covered; wrapper bugs out of scope).OptimizationDISparseExtsecond-order gates (still only cons_j-style).Reviewer pushback
psecond-order / Optim SVector wrappers should be in-scope follow-ups.Please ignore until reviewed by @ChrisRackauckas.
Risk assessment
Independent review: Claude Code (claude-opus-5-5), 3 rounds; round 3 MERGE, risk low (confidence medium-high): matching-type path identical to master (prepared calls, allocations, timings), 46 foreign-type probe cases match a fresh prep, AD 1045/1045 and QA pass; remaining CI failures are known on master (MTK downstream, Reactant downgrade, Manopt Core) or Python-setup infrastructure (PyCMA, SciPy).
🤖 Generated with Cursor Agent 2026.10.01-14929f9 (model: unknown (Cursor auto)), transcript /opt-prep/log.txt; orchestrated by Claude Code (claude-opus-5-5[1m]) https://claude.ai/code/session_01LPHREnnonfLg1VcE1EJovv
Review findings addressed
Review by codex-cli 0.162.1 / gpt-6.1-sol (CHANGES at 7a060d1); fixed in d18b4c6.
pLagrangian fallback differentiated constraints at the constructionp. Fixed. The in-place Lagrangian now evaluates constraints through ap-accepting termcons_lagrangian(θ, λ, p), which the out-of-place method shares. Matrix and packed-vectorlag_htherefore use the call-timepon both the prepared and the prep-free path. This also fixes a same-typep ≠ p0, which returned the wrong Hessian before this PR too; Ipopt hits that case when it forwards acache.pthatreinit!replaced.p ===the constructionp, because those preps close over it. In every other case it takes the prep-free Hessian ofcons_lagrangianat the call-timep; that includes a user-suppliedcons_h, where no AD prep exists. Out-of-place σ = 0 used to returnλ .* cons_h!(θ), which is a vector of matrices and throws aMethodErrorwhenevercons_h = false. It now returns the Hessian ofcons_lagrangian.lag_hprep gate now also checks the types of σ and λ. Before, anIntσ or aFloat32/viewλ threwPreparationMismatchError. The new tests compare against an independentForwardDiff.hessianof the explicit Lagrangian.Verification
lag_h at the call-time p, σ = 0, and foreign σ/λ typesintest/AD/dual_tolerant_tests.jl. It runs AutoForwardDiff and AutoReverseDiff over 10 cases: a call-timepof the same type and of a foreign type, a Float32 θ, σ = 0 at the defaultp, at anotherpand with a Float32 θ, σ::Int, a Float32 λ and a view λ. Each case is checked for IIP matrix, IIP packed and OOP. It also covers σ = 0 with a usercons_h.Pass 70, Fail 8, Error 3. The failures are wrong Hessians at a call-timep,MethodError: objects of type Nothing are not callableat OOP σ = 0, andPreparationMismatchErrorat σ = 0 with a Float32 θ. The testset aborts at the first uncaught error.dual_tolerant_tests.jl126/126 on Julia 1.13.2, and 126/126 on 1.10.12 (LTS).OPTIMIZATION_TEST_GROUP=AD(1.13.2): 1106/1106.Core: 73/73.QA: 20 pass, 1 broken (pre-existing).lag_h(IIP σ = 1, IIP σ = 0, IIP packed, OOP), checked under ForwardDiff and ReverseDiff. Allocations are identical under ForwardDiff and within 400 B under ReverseDiff; timings are unchanged.AutoReverseDiff(compile = true): IIP, packed and OOPlag_hat σ = 1 and σ = 0 match the reference. Runic and typos report nothing.Still open (pre-existing, not changed here)
AutoSymbolicswithlag_h = truefails at instantiation, with and without this commit. The error isMethodError: erase_cache_vars!from DI's Symbolics extension, raised while preparing the Lagrangian Hessian.AutoFiniteDiffsecond-order results are inaccurate. DI's ownhessian(f, SecondOrder(AutoFiniteDiff(), AutoFiniteDiff()), x)gives[24 -12; -12 8]where the exact Hessian is[23.24 -8.4; -8.4 6], and OptimizationBase mapsAutoFiniteDiffto thatSecondOrder.OptimizationZygoteExtandOptimizationDISparseExtstill usecons_oop(θ), which closes over the constructionp. They have the same call-time-pbug.por θ type, and to every out-of-place σ = 0 call.🤖 Posted by an AI agent — harness: Claude Code 2.1.301 · model: claude-opus-5-5
Conversation: local session ID 71155362-c106-40f7-b5a3-f1cf335f4339 (headless, no URL)
Risk assessment (independent review, codex-cli gpt-6.1-sol, reviewed head d18b4c6)
d18b4c6b3af4e5900f2169c3daa083c715ac0bf4, unchanged at the final check. Julia 1.13.0, four threads: regression file 126/126, official Core group 73/73, independent polynomial probe 738/738, and the earlier review's exact reproductions 2/2. The current regression file on pre-fix head7a060d15f0d8c5573873ee15f5ffbd828a795c74produces 70 pass, 8 fail, 3 error. All 14 CI checks were pending at both the initial and final checks; there are no failed PR jobs/logs to compare yet. CI: https://github.com/SciML/Optimization.jl/actions/runs/38924793086 and https://github.com/SciML/Optimization.jl/actions/runs/38924793437 . Documentation: https://github.com/SciML/Optimization.jl/actions/runs/38924792563 . Downstream: https://github.com/SciML/Optimization.jl/actions/runs/38924793325 . No CI-green claim is made.🤖 Posted by an AI agent — harness: codex-cli · model: gpt-6.1-sol (independent reviewer; appended by Claude Code 2.1.301 · claude-opus-5-5)
Conversation: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq